diff --git a/.changeset/config.json b/.changeset/config.json index fca9d48f..faede07a 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -2,12 +2,12 @@ "$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json", "changelog": "@changesets/cli/changelog", "commit": false, - "fixed": [["@lovo/matter", "@lovo/matter-react", "@lovo/matter-cli"]], + "fixed": [["@mattermix/shaders", "@mattermix/shaders-react", "@mattermix/shaders-cli"]], "linked": [], "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": ["@matter/registry", "@matter/tsconfig", "@matter/docs", "@matter/docs-tests"], + "ignore": ["@shaders/registry", "@shaders/tsconfig", "@shaders/docs", "@shaders/docs-tests"], "___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": { "onlyUpdatePeerDependentsWhenOutOfRange": true } diff --git a/.changeset/stable-hash-backend-parity.md b/.changeset/stable-hash-backend-parity.md index 2a3decaf..e1efcec1 100644 --- a/.changeset/stable-hash-backend-parity.md +++ b/.changeset/stable-hash-backend-parity.md @@ -1,5 +1,5 @@ --- -'@lovo/matter': minor +'@mattermix/shaders': minor --- Seeded randomness now renders the same pattern on the WebGPU and WebGL2 backends. three's TSL `hash()` writes its PCG constants as float literals, which GLSL rounds to a different hash than WGSL computes, so the same `seed` produced a different Voronoi layout in Safari than in Chrome. The new `stableHash` and `stableHashUint` exports run the same PCG with integer-typed constants and chain hash streams u32-to-u32, and `voronoiCells`, `grain`, `metaballs`, and `ditherPattern` now draw from them. diff --git a/.claude/skills/resolve-coderabbit-feedback/SKILL.md b/.claude/skills/resolve-coderabbit-feedback/SKILL.md index 7945d51f..be19053d 100644 --- a/.claude/skills/resolve-coderabbit-feedback/SKILL.md +++ b/.claude/skills/resolve-coderabbit-feedback/SKILL.md @@ -267,7 +267,7 @@ pnpm test --filter Four repo traps apply here: -- If a fix changed source under `packages/matter` or `packages/matter-react`, run `pnpm --filter @lovo/matter build`. The docs site consumes `dist`, so an unbuilt fix looks like no fix at all. +- If a fix changed source under `packages/shaders` or `packages/shaders-react`, run `pnpm --filter @mattermix/shaders build`. The docs site consumes `dist`, so an unbuilt fix looks like no fix at all. - If a fix changed a dependency in any `package.json`, commit the updated `pnpm-lock.yaml` with it, and check that the lockfile still pins `node@runtime` at `version: 22.22.2` with `hasBin: true`. Every pnpm resolution step rewrites that entry to `0.0.0`, and CI then dies at install in every job. - Never run `pnpm snap` as part of this workflow. Ask first. It needs Docker and Node 22, it takes a long time, and it corrupts a running docs or editor dev server. - If you ran Playwright or `pnpm snap` for any reason, tell the user to restart the dev server before trusting the browser. The procedure is in `AGENTS.md` under the environment gotchas. @@ -296,7 +296,7 @@ Pick `` from the file class the approved fixes touched, and add no AI attr | A workflow under `.github/` | `ci` | | Tests, tooling config, or a lockfile on its own | `chore` | -The command above supplies the colon, so these values carry none. Scope is the package name without the `@lovo/` prefix. When a run spans classes, name the class that carries the substantive fix, so a code fix that drags a lockfile with it stays `fix()`. The user already saw the commit line in the Step 6 preview, so change it there rather than asking again here. +The command above supplies the colon, so these values carry none. Scope is the package name without the `@mattermix/` prefix. When a run spans classes, name the class that carries the substantive fix, so a code fix that drags a lockfile with it stays `fix()`. The user already saw the commit line in the Step 6 preview, so change it there rather than asking again here. ## Step 10: Reply and resolve the threads diff --git a/.fallowrc.json b/.fallowrc.json index b3032ff3..9d793107 100644 --- a/.fallowrc.json +++ b/.fallowrc.json @@ -13,7 +13,7 @@ "apps/docs/content/**", "**/*.mdx", "apps/playground/**", - "packages/matter-cli/src/test-fixtures/**" + "packages/shaders-cli/src/test-fixtures/**" ], "ignoreDependencies": [ "@tweakpane/core", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67b7da53..e860c795 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,7 +94,7 @@ jobs: cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm build - - run: pnpm --filter @matter/docs-tests test:visual + - run: pnpm --filter @shaders/docs-tests test:visual - name: Upload visual diffs on failure if: failure() uses: actions/upload-artifact@v4 diff --git a/.prettierignore b/.prettierignore index 7dce3e67..f60fadbd 100644 --- a/.prettierignore +++ b/.prettierignore @@ -22,4 +22,4 @@ pnpm-lock.yaml # Isolated workspaces / vendored fixtures .worktrees/ .fallow/ -packages/matter-cli/src/test-fixtures/ +packages/shaders-cli/src/test-fixtures/ diff --git a/AGENTS.md b/AGENTS.md index faeab49c..61d4b97b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,12 +1,12 @@ -# AGENTS.md: Matter project guide +# AGENTS.md: Shaders project guide -You are working on **Matter**, a React shader component library built on WebGPU and Three.js TSL. This file orients any coding agent at the start of a session, including Claude Code, Codex, Cursor, and Gemini CLI. Read it fully before doing anything else. +You are working on **Shaders**, a React shader component library built on WebGPU and Three.js TSL. This file orients any coding agent at the start of a session, including Claude Code, Codex, Cursor, and Gemini CLI. Read it fully before doing anything else. ## Where to find things | You need… | Read… | | -------------------------------------- | ---------------------------------------------------- | -| The full design, what we're building and why | `docs/superpowers/specs/2026-05-02-matter-design.md` | +| The full design, what we're building and why | `docs/superpowers/specs/2026-05-02-shaders-design.md` | | Feature specs and implementation plans | `docs/superpowers/specs/`, `docs/superpowers/plans/` | > **Note.** `docs/superpowers/` holds the specs and plans, and it is gitignored, so it exists only on machines it has been synced to. On a fresh clone those paths are absent. This file plus git history are the portable orientation. @@ -18,7 +18,7 @@ Milestone history lives in git tags and `docs/superpowers/plans/`. Don't trust a ## Project shape (30-second version) - **Three-tier model.** Tier 1 is the polished components such as ``, delivered by shadcn-style CLI copy-paste from `registry/`. Tier 2 is the TSL primitives in the engine package, such as `fractalNoise` and `voronoi`. Tier 3 is recipes: TSL snippets in the docs site. -- **Three packages.** `@lovo/matter` is the framework-agnostic engine, `@lovo/matter-react` is the React binding, and `@lovo/matter-cli` handles copy-paste delivery. Two apps sit alongside them: `@matter/docs` is the docs site, and `@matter/editor` is the node editor from MAT-94. The editor is a React Flow canvas over the same Tier 2 primitives, with an eject-to-code emitter. A permanent parity gate pixel-compares that emitter's output against the live compiler. +- **Three packages.** `@mattermix/shaders` is the framework-agnostic engine, `@mattermix/shaders-react` is the React binding, and `@mattermix/shaders-cli` handles copy-paste delivery. Two apps sit alongside them: `@shaders/docs` is the docs site, and `@shaders/editor` is the node editor from MAT-94. The editor is a React Flow canvas over the same Tier 2 primitives, with an eject-to-code emitter. A permanent parity gate pixel-compares that emitter's output against the live compiler. - **The editor app is layered by dependency direction**, not by file type. `src/editor/graph/` is the framework-free core, holding the node registry, graph model, param store, live TSL compiler, and code emitter. `src/editor/preset/` handles save, load, undo, and copy-paste, and is also React-free. `src/editor/state/` is the React Flow glue. `canvas/`, `params/`, and `panels/` are UI. Dependencies point one way, toward `graph/`. Siblings import each other as `./x` and everything else as `@/editor//`. `vitest.config.ts` has to declare that `@` alias itself, because Vitest doesn't read tsconfig `paths`. - **Two rendering modes**, with no auto-detection of `@react-three/fiber`. In Mode 1 every Tier 1 component is bare and requires an explicit `` wrap, and you compose by stacking children in one scene. In Mode 2 you call `useShaderMaterial` inside your own r3f ``. @@ -37,11 +37,11 @@ Read the spec for architecture, public APIs, the component catalog, and the anim - **Rejected alternatives only when a reviewer would otherwise propose them.** Dead ends you hit along the way are session debrief. PR #93 spent a paragraph on a rejected triangle wave and another on PNG versus JPEG byte counts, and neither changes an approve or request-changes call. - **Anything already recorded in the diff gets a clause, not a section.** If the reasoning is in a code comment, in AGENTS.md, or in a changeset this PR adds, write "documented in X" and stop. Restating it makes a second copy that drifts from the first. - **Run user-facing prose through the `technical-writing` skill**, which also applies `unslop` for the slop-pattern catalog. Look for the skill in the repo's `.claude/skills/` first, then in whatever directory your harness keeps skills in, which is `~/.claude/skills/` for Claude Code. PR bodies, commit messages, changelog entries, release notes, and docs get a pass before the PR opens or the text ships. The skill sets `disable-model-invocation: true`, so an agent that honors that field never loads it on its own. Read its `SKILL.md` and work the review checklist at the end. Its first rule is to cut every word that does no work, so it pulls toward the PR word budget rather than against it. Diátaxis mode selection is the one layer a PR body skips. -- **Commit messages use Conventional Commits**, such as `feat(scope): …`, `fix(scope): …`, `chore: …`, `docs: …`, and `ci: …`. Scope is the package name without the `@lovo/` prefix. +- **Commit messages use Conventional Commits**, such as `feat(scope): …`, `fix(scope): …`, `chore: …`, `docs: …`, and `ci: …`. Scope is the package name without the `@mattermix/` prefix. ## Shader development process -These rules exist because Matter doubles as a shader-learning project for its author. The author is fluent in React, TypeScript, and build tooling. The gap is GPU concepts: uniforms, sampler space, noise types, domain warping, smoothstep, and render passes. Spend explanation budget there. +These rules exist because Shaders doubles as a shader-learning project for its author. The author is fluent in React, TypeScript, and build tooling. The gap is GPU concepts: uniforms, sampler space, noise types, domain warping, smoothstep, and render passes. Spend explanation budget there. 1. **Rebuilds go step by step.** When you improve or rebuild a shader component, translate the design into TSL one step at a time and explain each TSL and GPU concept as it appears. Don't silently refactor existing TSL. 2. **Target structure is the Aurora split.** Write `registry//.tsx` for the component wrapper, holding props, uniforms, and mesh lifecycle in roughly 80 lines, plus `registry//shader.tsx` for the TSL shader function, isolated and reusable. @@ -85,10 +85,10 @@ These rules exist because Matter doubles as a shader-learning project for its au ## Environment and build gotchas - **Node 22, exactly.** The docs production build (`next build`, static export) **silently fails on Node 23**. It exits 0 and writes no `out/`, and that missing directory then breaks pagefind and `pnpm snap`. The fix is environmental, so run the pinned Node 22 rather than changing config. `.node-version` at 22.22.2 is the source of truth. `.nvmrc` at 22 is the loose duplicate fnm actually honors. -- **The docs site consumes built `dist`, not source**, for `@lovo/matter` and `@lovo/matter-react`. `@matter/registry` is the exception, and reaches the site as raw `.tsx` via `transpilePackages`. After you edit engine or binding source, run `pnpm --filter @lovo/matter build` AND restart the docs dev server, or a correct fix looks like a no-op. Before you re-debug a "fix that didn't work," check `dist` mtime against `src`. +- **The docs site consumes built `dist`, not source**, for `@mattermix/shaders` and `@mattermix/shaders-react`. `@shaders/registry` is the exception, and reaches the site as raw `.tsx` via `transpilePackages`. After you edit engine or binding source, run `pnpm --filter @mattermix/shaders build` AND restart the docs dev server, or a correct fix looks like a no-op. Before you re-debug a "fix that didn't work," check `dist` mtime against `src`. - **CI runs more than package-scoped checks.** Five traps: 1. `pnpm install --frozen-lockfile` runs first in every job. Any `package.json` dep change must ship with the updated `pnpm-lock.yaml`, or every job dies at install, and `ERR_PNPM_OUTDATED_LOCKFILE` masquerades as "everything failing". **Read the lockfile diff before you commit it.** Any resolution step (`pnpm add`, `pnpm remove`, `pnpm install --lockfile-only`) also rewrites the `node@runtime` entry from `version: 22.22.2` and `hasBin: true` to `version: 0.0.0`. That entry is the resolved form of the root `devEngines` pin, the thing that makes every `pnpm` script run Node 22, so restore those two lines by hand and re-check `pnpm install --frozen-lockfile` before committing. This has slipped through twice. - 2. CI runs whole-repo Prettier through root `pnpm format:check`, not just lint. The import-sort plugin orders React and external imports before `@lovo/*` and `@matter/*`. Run Prettier on changed files before you commit. + 2. CI runs whole-repo Prettier through root `pnpm format:check`, not just lint. The import-sort plugin orders React and external imports before `@mattermix/*` and `@shaders/*`. Run Prettier on changed files before you commit. 3. Visual regression screenshots the canvas itself, through `page.locator('canvas').first()`, not the full page and not the `[data-shader-demo]` container. A change that resizes or repositions the canvas invalidates baselines. A change confined to the control panel's own styling does not. That wasn't always true. Before the panel moved into a sidebar outside `[data-shader-demo]`, it sat absolutely positioned inside the canvas's bounding box, so any panel change showed up in the shot too. It no longer does, so you can restyle the panel freely without regenerating anything. But `.demo-layout`'s grid columns and padding are CSS that sizes the canvas, so layout changes there still invalidate baselines, and `DemoPoster`'s poster image sits inside `[data-shader-demo]` but outside the canvas, so nothing captures it either way. @@ -115,7 +115,7 @@ These rules exist because Matter doubles as a shader-learning project for its au - `turbo.json`'s `build` task declares `INCLUDE_DEV_ROUTES` under `env`, because turbo 2 runs strict env mode and would otherwise drop the variable before `next build` ever sees it. Verify a change by building both ways and checking whether `apps/docs/out/dev/` exists. This is also the convention a throwaway prototype route must follow. The `prototype` skill's default advice is to hide a variant switcher behind `process.env.NODE_ENV !== 'production'`, and that check does not work here for the same reason: Playwright builds the production bundle. -- **`apps/docs/tsconfig.json` uses a relative `extends` on purpose.** It points at `../../tooling/tsconfig/base.json` rather than the `@matter/tsconfig` package form every other workspace uses. Fallow's resolver drops `paths` when `extends` goes through a workspace package. Don't "normalize" it. +- **`apps/docs/tsconfig.json` uses a relative `extends` on purpose.** It points at `../../tooling/tsconfig/base.json` rather than the `@shaders/tsconfig` package form every other workspace uses. Fallow's resolver drops `paths` when `extends` goes through a workspace package. Don't "normalize" it. ## Technical gotchas (read before touching TSL or the build) @@ -126,8 +126,8 @@ These rules exist because Matter doubles as a shader-learning project for its au 5. **`uniform(vec2(...))` loses the Vector2 mutator API.** Use `uniform(new Vector2(...))` when you need `.set()`. 6. **`setClearColor` accepts only `Color` in three 0.170 and later.** Convert with `new Color(...)`. 7. **Vitest exits 1 with no test files.** Set `passWithNoTests: true` in per-package configs. -8. **The docs site needs `@matter/registry` plus `transpilePackages`** to import raw `.tsx` from a workspace dep. -9. **`three/webgpu` references `self` at module load, so it cannot SSR.** Anything that genuinely reaches the renderer needs `next/dynamic` with `{ ssr: false }`, and all eight component pages load their `scene.tsx` that way. Scalar code that merely ships in the same package does not, and each piece has an import path that never pulls in three: CPU color math at `@lovo/matter/color`, and `useDisplayGamut` at `@lovo/matter-react/gamut`. A `no-restricted-imports` rule scoped to `apps/docs/**` rejects both roots for those names, so the wrong import fails at lint rather than at render, and each subpath carries a `// @vitest-environment node` test that throws if three creeps back into its graph. Reach for a subpath before you reach for `ssr: false`. +8. **The docs site needs `@shaders/registry` plus `transpilePackages`** to import raw `.tsx` from a workspace dep. +9. **`three/webgpu` references `self` at module load, so it cannot SSR.** Anything that genuinely reaches the renderer needs `next/dynamic` with `{ ssr: false }`, and all eight component pages load their `scene.tsx` that way. Scalar code that merely ships in the same package does not, and each piece has an import path that never pulls in three: CPU color math at `@mattermix/shaders/color`, and `useDisplayGamut` at `@mattermix/shaders-react/gamut`. A `no-restricted-imports` rule scoped to `apps/docs/**` rejects both roots for those names, so the wrong import fails at lint rather than at render, and each subpath carries a `// @vitest-environment node` test that throws if three creeps back into its graph. Reach for a subpath before you reach for `ssr: false`. 10. **`tweakpane@4` ships a broken `@tweakpane/core` reference.** Add published `@tweakpane/core` 2.x as a devDep for typecheck. 11. **Consume vec-typed `uniform(...)` as an argument, not a chained receiver, in TSL math.** `uv().sub(cursorUniform)` works. Chaining methods off a raw vec2 or vec3 uniform node silently produces wrong GPU values despite typechecking. Build expressions from `uv()` and `vec2(...)`, and pass vec uniforms as args. Scalar float uniforms are safe as chained receivers. wave-lines chains them throughout and seven visual gates validated it. 12. **three ships two standalone bundles**, `three.module.js` and `three.webgpu.js`. Importing both duplicates three core, which shows up as `Cannot read properties of undefined (reading 'usedTimes')` on dispose. Alias all three subpaths to the webgpu bundle. See `apps/docs/next.config.ts`. @@ -138,7 +138,7 @@ These rules exist because Matter doubles as a shader-learning project for its au 17. **Output dithering is scene-wide, in display space.** `ShaderScene` builds `outputNode = dither(renderOutput(composed))` with `outputColorTransform = false`. Never add per-component `dither()` in a `colorNode`. It double-dithers and runs in linear space. The exported `dither()` primitive is for Mode 2 only. Gamut, like dither, is scene-level, so keep both off per-component panels. 18. **Light-emitting transparent layers need `material.premultipliedAlpha = true`.** Any component whose colorNode emits light-contribution rgb with coverage alpha, aurora-style, double-multiplies by alpha under default NormalBlending. Soft wisps dim quadratically as a result. 19. **When the shader looks cropped, compressed, or zoomed, check `renderer.getSize()` against the canvas client size FIRST**, not uv or camera math. The renderer once stuck at the 300×150 canvas default, and a logical-size guard plus a ResizeObserver fixed it. Headless Playwright falls back to WebGL2 here, where `navigator.gpu` is truthy but device init fails. -20. **Wide-gamut P3 output reaches into renderer internals**, because three 0.170 has no native WebGPU P3 path. We register the ColorSpaces addon through `ColorManagement.define` and manually re-`configure()` the `GPUCanvasContext` in `packages/matter/src/runtime/create-renderer/gamut.ts`. A future Three.js version bump should delete the manual reconfigure. You can't pixel-assert P3 output in headless Playwright. `parseColorString` unit tests prove the decode, and you validate the widening by eye on a P3 display. The `hsl` and `hsv` color spaces clamp to sRGB first, because a negative-channel `pow()` breaks WGSL const-eval otherwise. +20. **Wide-gamut P3 output reaches into renderer internals**, because three 0.170 has no native WebGPU P3 path. We register the ColorSpaces addon through `ColorManagement.define` and manually re-`configure()` the `GPUCanvasContext` in `packages/shaders/src/runtime/create-renderer/gamut.ts`. A future Three.js version bump should delete the manual reconfigure. You can't pixel-assert P3 output in headless Playwright. `parseColorString` unit tests prove the decode, and you validate the widening by eye on a P3 display. The `hsl` and `hsv` color spaces clamp to sRGB first, because a negative-channel `pow()` breaks WGSL const-eval otherwise. 21. **Colors commit on release in the demo panels, numbers commit live.** The registry components pass literal stops to `colorRamp` and rebuild their `NodeMaterial` when colors change, covered in the colorRamp gotcha, where the primitive can take uniform-driven stops now but the components haven't been migrated. A continuously-firing color drag would therefore recompile the shader every frame. `ColorInput` holds a draft and writes to the store on pointer release. `SliderInput` writes on every change, because numeric props ride stable `uniform(...)` nodes. 22. **Subscribing to a container in the demo control store (docs demo panels) re-renders everything under it.** `writeAtPath` rebuilds every object and array along the written path, so a component that subscribes to the root params object, or to a list's array, re-renders on every write anywhere inside it. The control components are deliberately unmemoized, so that re-render cascades. The cascade bit three separate times on one branch before the pattern below stuck. Subscribe to a leaf, or to a stable primitive like a list's `length`, and read containers non-reactively at event time through `useControlStore()`. See `ControlPanel`'s copy buttons and `ListInput`'s add and remove. 23. **Never build a running minimum or argmin as an unrolled JS select() chain, because the tab hangs before the shader compiles.** A select-based accumulator references itself twice per step, once in the comparison and once in the else-branch, and three's `getNodeType` recursion has no cross-reference memoization, so type resolution goes exponential in chain depth. `voronoiCells`' 34-step chain froze headless Chromium indefinitely at first render, with a CDP-interrupted stack showing nothing but `getNodeType`. Adding `.toVar()` per step does NOT help, because VarNode delegates its type lookup inward. Additive chains such as fbm and wave-lines are safe, because they reference the accumulator once per step. The fix is TSL's imperative side: `Fn` plus `Loop` or `If` plus `.assign()`. That emits a real GPU `for` loop. See `voronoiCells`. Three's own MaterialX worley uses the same pattern, and it works on both WebGPU and the WebGL2 fallback with fixed integer bounds. The fbm caveat about "no clean loop primitive" is about dynamic counts like uniform-driven octaves, not about this. @@ -147,7 +147,7 @@ These rules exist because Matter doubles as a shader-learning project for its au ## Color system (shipped) -- **`colorSpace` prop** (the interpolation space) on the six components that blend two colors: `Aurora`, `LinearGradient`, `MeshGradient`, `SimplexNoise`, and `WaveLines` all go through `colorRamp`, and `Vignette` goes through `mixColor`. The default is `oklab` on all six. `DotField` and `Grain` don't take it, because they never compute a midpoint. The primitive defaults differ from each other: `colorRamp` defaults to `linear`, and `mixColor` defaults to `oklab`. It lives in `packages/matter/src/primitives/color-space/`. +- **`colorSpace` prop** (the interpolation space) on the six components that blend two colors: `Aurora`, `LinearGradient`, `MeshGradient`, `SimplexNoise`, and `WaveLines` all go through `colorRamp`, and `Vignette` goes through `mixColor`. The default is `oklab` on all six. `DotField` and `Grain` don't take it, because they never compute a midpoint. The primitive defaults differ from each other: `colorRamp` defaults to `linear`, and `mixColor` defaults to `oklab`. It lives in `packages/shaders/src/primitives/color-space/`. - **`hueInterpolation` prop** on five of those six. `WaveLines` omits it and takes `colorRamp`'s default arc. Only the cylindrical spaces read it: `oklch`, `lch`, `hsl`, and `hsv`. - **`gamut` prop** on ``, typed `'auto' | 'srgb' | 'p3'` and defaulting to `auto`, which detects through `(color-gamut: p3)` and re-resolves on monitor change. - **Separate concerns.** `colorSpace` is the mixing math and `gamut` is the output framebuffer. Wide-gamut **input** is just the decode, where `oklch()` and `oklab()` strings go through `parseColor` to unclamped linear-sRGB, and it needs zero mixing props. Aurora is additive but still blends along a depth-indexed ramp, and that is why it takes `colorSpace`. Being additive is not the test. Computing a midpoint is. @@ -155,7 +155,7 @@ These rules exist because Matter doubles as a shader-learning project for its au ## Out of scope (don't drift) -The spec puts all of these at v2 or later, even where they'd be easy: image and video filters, particle systems, 3D objects and materials, text effects, cursor effects, Vue and Svelte bindings, a hosted registry endpoint, audio-reactive primitives, a built-in animation library (Matter accepts MotionValue-shaped signals instead), CSS custom-property theming, and per-component material hooks. +The spec puts all of these at v2 or later, even where they'd be easy: image and video filters, particle systems, 3D objects and materials, text effects, cursor effects, Vue and Svelte bindings, a hosted registry endpoint, audio-reactive primitives, a built-in animation library (Shaders accepts MotionValue-shaped signals instead), CSS custom-property theming, and per-component material hooks. ## Deployment diff --git a/LICENSE b/LICENSE index 7c8ffebb..ef1a0273 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 Hunter Garrett / Lovo +Copyright (c) 2026 Hunter Garrett / Mattermix Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 484e97fd..780450e0 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,26 @@ -# Matter +# Shaders React shader components powered by WebGPU and Three.js TSL. -> **Status:** v0.1.0 shipped to npm. `npm install -D @lovo/matter-cli && npx matter-cli init && npx matter-cli add linear-gradient` to scaffold your first component. +> **Status:** v0.1.0 shipped to npm. `npm install -D @mattermix/shaders-cli && npx shaders-cli init && npx shaders-cli add linear-gradient` to scaffold your first component. -## What is Matter? +## What is Shaders? -Matter is a React component library for shader-driven backgrounds and interactive surfaces. It ships polished drop-in components like ``, ``, and `` for developers who don't want to write shaders, alongside a primitives library and recipe gallery for those who do. +Shaders is a React component library for shader-driven backgrounds and interactive surfaces. It ships polished drop-in components like ``, ``, and `` for developers who don't want to write shaders, alongside a primitives library and recipe gallery for those who do. ## Repository structure ``` apps/ -├── docs/ # @matter/docs — Next.js docs site (Tweakpane-driven demos) -└── playground/ # @matter/playground — Vite app with M1 manual harnesses +├── docs/ # @shaders/docs — Next.js docs site (Tweakpane-driven demos) +└── playground/ # @shaders/playground — Vite app with M1 manual harnesses packages/ -├── matter/ # @lovo/matter — engine: TSL primitives, renderer, scheduler -├── matter-react/ # @lovo/matter-react — React binding -└── matter-cli/ # @lovo/matter-cli — copy-paste CLI +├── shaders/ # @mattermix/shaders — engine: TSL primitives, renderer, scheduler +├── shaders-react/ # @mattermix/shaders-react — React binding +└── shaders-cli/ # @mattermix/shaders-cli — copy-paste CLI -registry/ # @matter/registry — Tier 1 component source files (CLI consumes) +registry/ # @shaders/registry — Tier 1 component source files (CLI consumes) tooling/ ├── eslint-config/ # shared ESLint flat config @@ -41,20 +41,20 @@ pnpm install pnpm build # build all packages + apps pnpm typecheck # typecheck all packages + apps pnpm lint # lint all packages + apps -pnpm test # run all tests (Vitest in @lovo/matter) +pnpm test # run all tests (Vitest in @mattermix/shaders) # Live shader demo -pnpm --filter @matter/docs dev # Next.js docs at http://localhost:3000 +pnpm --filter @shaders/docs dev # Next.js docs at http://localhost:3000 # Engine playground (per-phase manual harnesses) -pnpm --filter @matter/playground dev # Vite at http://localhost:5173 +pnpm --filter @shaders/playground dev # Vite at http://localhost:5173 ``` ## Roadmap - ✅ **Milestone 0** — Repo bootstrap - ✅ **Milestone 1** — Vertical slice: `` end-to-end -- ✅ **Milestone 2** — `@lovo/matter-cli` (copy-paste delivery) +- ✅ **Milestone 2** — `@mattermix/shaders-cli` (copy-paste delivery) - ✅ **Milestone 3** — The other 5 v1 components (MeshGradient, Aurora, DotField, NoiseField, Waves) - ✅ **Milestone 4** — Docs site polish - ✅ **Milestone 5** — Performance, testing, accessibility diff --git a/apps/docs-tests/editor/export-import.spec.ts b/apps/docs-tests/editor/export-import.spec.ts index f9fe2f43..4d5cfc3b 100644 --- a/apps/docs-tests/editor/export-import.spec.ts +++ b/apps/docs-tests/editor/export-import.spec.ts @@ -25,7 +25,7 @@ test('export, reload, import round-trips the graph', async ({ page }) => { const download = await downloadPromise; - expect(download.suggestedFilename()).toBe('matter-graph.json'); + expect(download.suggestedFilename()).toBe('shaders-graph.json'); const downloadPath = await download.path(); diff --git a/apps/docs-tests/package.json b/apps/docs-tests/package.json index d515dddd..550170ca 100644 --- a/apps/docs-tests/package.json +++ b/apps/docs-tests/package.json @@ -1,5 +1,5 @@ { - "name": "@matter/docs-tests", + "name": "@shaders/docs-tests", "version": "0.0.0", "private": true, "type": "module", @@ -12,7 +12,7 @@ }, "devDependencies": { "@axe-core/playwright": "^4.10.0", - "@matter/tsconfig": "workspace:*", + "@shaders/tsconfig": "workspace:*", "@playwright/test": "^1.48.0", "@types/node": "^22", "typescript": "^5" diff --git a/apps/docs-tests/playwright.config.ts b/apps/docs-tests/playwright.config.ts index 032746a8..f2bd2ed7 100644 --- a/apps/docs-tests/playwright.config.ts +++ b/apps/docs-tests/playwright.config.ts @@ -24,7 +24,7 @@ export default defineConfig({ // than running the dev server, so without the flag those specs 404. The // deploy build omits it, which is the point — see next.config.ts. command: - 'INCLUDE_DEV_ROUTES=1 pnpm turbo run build --filter=@matter/docs --force && pnpm --filter @matter/docs preview', + 'INCLUDE_DEV_ROUTES=1 pnpm turbo run build --filter=@shaders/docs --force && pnpm --filter @shaders/docs preview', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, timeout: 180_000, @@ -34,7 +34,7 @@ export default defineConfig({ // build as the docs entry above, and the same dev-routes flag, which is // what puts the parity routes in the bundle these specs load. command: - 'INCLUDE_DEV_ROUTES=1 pnpm turbo run build --filter=@matter/editor --force && pnpm --filter @matter/editor preview', + 'INCLUDE_DEV_ROUTES=1 pnpm turbo run build --filter=@shaders/editor --force && pnpm --filter @shaders/editor preview', url: 'http://localhost:3010', reuseExistingServer: !process.env.CI, timeout: 180_000, diff --git a/apps/docs-tests/tsconfig.json b/apps/docs-tests/tsconfig.json index 3eb0c40c..0bf5e15e 100644 --- a/apps/docs-tests/tsconfig.json +++ b/apps/docs-tests/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@matter/tsconfig/library.json", + "extends": "@shaders/tsconfig/library.json", "compilerOptions": { "noEmit": true, "module": "ESNext", diff --git a/apps/docs-tests/visual/gamut.spec.ts b/apps/docs-tests/visual/gamut.spec.ts index 92d60471..57359b23 100644 --- a/apps/docs-tests/visual/gamut.spec.ts +++ b/apps/docs-tests/visual/gamut.spec.ts @@ -13,7 +13,7 @@ import { waitForShader } from './helpers'; * the P3-vs-sRGB difference we'd want to measure. So output-widening is validated * by eye on a P3 display; the deterministic automated proof that wide-gamut colors * decode to extended linear-sRGB lives in the `parseColorString` unit tests - * (`packages/matter`). This spec guards that the P3 output path itself stays alive + * (`packages/shaders`). This spec guards that the P3 output path itself stays alive * (the manual context.configure into Display P3 doesn't throw or blank the canvas). */ test('gamut probe — sRGB and P3 output paths both render without error', async ({ page }) => { diff --git a/apps/docs-tests/visual/helpers.ts b/apps/docs-tests/visual/helpers.ts index f64dcdd1..da90b325 100644 --- a/apps/docs-tests/visual/helpers.ts +++ b/apps/docs-tests/visual/helpers.ts @@ -2,14 +2,14 @@ import type { Page } from '@playwright/test'; /** * Waits for the shader to finish its first deterministic frame and settle. - * Pages set `window.__matterTestReady = true` via VisualTestPause after the + * Pages set `window.__shadersTestReady = true` via VisualTestPause after the * second frame. The extra 50ms absorbs any micro-jitter before the screenshot. * The timeout budgets for SwiftShader on 2-core CI runners, where a heavy * raymarch (aurora: 60 slices x 5 fbm octaves) needs several seconds per frame. */ export async function waitForShader(page: Page): Promise { await page.waitForFunction( - () => (window as unknown as { __matterTestReady?: boolean }).__matterTestReady === true, + () => (window as unknown as { __shadersTestReady?: boolean }).__shadersTestReady === true, undefined, { timeout: 60_000 }, ); diff --git a/apps/docs/content/docs/changelog.mdx b/apps/docs/content/docs/changelog.mdx index 9d2910d8..dbf25e84 100644 --- a/apps/docs/content/docs/changelog.mdx +++ b/apps/docs/content/docs/changelog.mdx @@ -1,19 +1,19 @@ --- title: Changelog -description: Notable changes per release across the Matter packages. +description: Notable changes per release across the Shaders packages. section: overview order: 30 --- # Changelog -Matter ships as three coordinated npm packages: `@lovo/matter` (engine), `@lovo/matter-react` (React binding), and `@lovo/matter-cli` (copy-paste CLI). Each package has its own `CHANGELOG.md` in the repo; this page summarizes the headline changes. +Shaders ships as three coordinated npm packages: `@mattermix/shaders` (engine), `@mattermix/shaders-react` (React binding), and `@mattermix/shaders-cli` (copy-paste CLI). Each package has its own `CHANGELOG.md` in the repo; this page summarizes the headline changes. ## Unreleased **Breaking change — `Waves` is now `WaveLines`.** -The old name read as water. The component draws glowing lines, so it's renamed to match: add it with `matter-cli add wave-lines`, import `WaveLines`, and pass `lines` instead of `layers` (the per-line type is `WaveLine`, formerly `WaveLayer`). Nothing else moved: same props, same rendering, no visual change. +The old name read as water. The component draws glowing lines, so it's renamed to match: add it with `shaders-cli add wave-lines`, import `WaveLines`, and pass `lines` instead of `layers` (the per-line type is `WaveLine`, formerly `WaveLayer`). Nothing else moved: same props, same rendering, no visual change. Components ship by copy-paste, so installed copies keep working under the old name; re-add `wave-lines` through the CLI to pick up the new one. @@ -43,32 +43,32 @@ Same copy-paste rule: installed copies keep rendering as before; re-add `waves` ## 0.2.0 -**Breaking change — `@lovo/matter` no longer re-exports pure TSL primitives.** +**Breaking change — `@mattermix/shaders` no longer re-exports pure TSL primitives.** -The following 15 nodes are no longer exported by `@lovo/matter`. Import them directly from `three/tsl`: +The following 15 nodes are no longer exported by `@mattermix/shaders`. Import them directly from `three/tsl`: `uv`, `vec2`, `vec3`, `vec4`, `uniform`, `mix`, `smoothstep`, `mod`, `sin`, `cos`, `length`, `dot`, `normalize`, `max`, `min` ```ts // Before (0.1.x) -import { vec3, uv, time } from '@lovo/matter' +import { vec3, uv, time } from '@mattermix/shaders' // After (0.2.0) import { vec3, uv } from 'three/tsl' -import { time } from '@lovo/matter' // still here — reduced-motion-gated +import { time } from '@mattermix/shaders' // still here — reduced-motion-gated ``` -`time` continues to be exported from `@lovo/matter` because Matter owns its semantics (reduced-motion gating). For raw uncapped time, import from `three/tsl` directly. +`time` continues to be exported from `@mattermix/shaders` because Matter owns its semantics (reduced-motion gating). For raw uncapped time, import from `three/tsl` directly. -All Matter-owned primitives (`fbm`, `noise`, `voronoi`, `colorRamp`, `sdfCircle`, `displace`, `cursorRipple`, `quantize`) remain exported from `@lovo/matter` unchanged. Registry component sources at 0.2.0 use the new convention. If you copied a component at 0.1.x, update its imports from `@lovo/matter` to `three/tsl` for the dropped symbols (or re-add the component via the CLI to pull the 0.2.0 source). +All Matter-owned primitives (`fbm`, `noise`, `voronoi`, `colorRamp`, `sdfCircle`, `displace`, `cursorRipple`, `quantize`) remain exported from `@mattermix/shaders` unchanged. Registry component sources at 0.2.0 use the new convention. If you copied a component at 0.1.x, update its imports from `@mattermix/shaders` to `three/tsl` for the dropped symbols (or re-add the component via the CLI to pull the 0.2.0 source). -**Why:** Re-exporting pure TSL primitives provided no value beyond shared import paths — no rename, no added docs, no future-swap benefit. Dropping them clarifies the layer boundary: Matter ships value-add primitives, TSL provides the math. See the [matter engine reference](/reference/matter) for the current public surface. +**Why:** Re-exporting pure TSL primitives provided no value beyond shared import paths — no rename, no added docs, no future-swap benefit. Dropping them clarifies the layer boundary: Matter ships value-add primitives, TSL provides the math. See the [shaders engine reference](/reference/shaders) for the current public surface. ## 0.1.0 — Initial public release The first public release of Matter — React shader components on WebGPU + Three.js TSL. -### `@lovo/matter` +### `@mattermix/shaders` Framework-agnostic engine. Exports: @@ -77,7 +77,7 @@ Framework-agnostic engine. Exports: - **Runtime:** `createRenderer`, `MatterScheduler`, visibility/intersection watchers, reduced-motion policy. - **Inputs:** `CursorInput` (framework-agnostic cursor source). -### `@lovo/matter-react` +### `@mattermix/shaders-react` React binding for the engine. Exports: @@ -87,13 +87,13 @@ React binding for the engine. Exports: - Animation glue — `useAnimatableUniform`, accepts any MotionValue-shaped signal. - Fallback / dev utilities — `FallbackBoundary`, `useStaticHint`, `MatterMonitor`. -### `@lovo/matter-cli` +### `@mattermix/shaders-cli` shadcn-style copy-paste CLI: `init`, `list`, `add`, `update`. The default registry tracks the CLI's published version tag (`v0.1.0`), so the component source you copy is stable per release. ### v1 components -Available via `matter-cli add `: `linear-gradient`, `mesh-gradient`, `aurora`, `dot-field`, `simplex-noise`, `waves`. Each lands in your project as a regular `.tsx` file — you own and edit it from that point. +Available via `shaders-cli add `: `linear-gradient`, `mesh-gradient`, `aurora`, `dot-field`, `simplex-noise`, `waves`. Each lands in your project as a regular `.tsx` file — you own and edit it from that point. ### Requirements @@ -102,5 +102,5 @@ Available via `matter-cli add `: `linear-gradient`, `mesh-gradient`, `auro - React ^19, Three.js ^0.170. - Detailed per-package changelogs live in the repo: [`packages/matter/CHANGELOG.md`](https://github.com/lovo-hq/matter/blob/main/packages/matter/CHANGELOG.md), [`packages/matter-react/CHANGELOG.md`](https://github.com/lovo-hq/matter/blob/main/packages/matter-react/CHANGELOG.md), [`packages/matter-cli/CHANGELOG.md`](https://github.com/lovo-hq/matter/blob/main/packages/matter-cli/CHANGELOG.md). + Detailed per-package changelogs live in the repo: [`packages/shaders/CHANGELOG.md`](https://github.com/mattermix/shaders/blob/main/packages/shaders/CHANGELOG.md), [`packages/shaders-react/CHANGELOG.md`](https://github.com/mattermix/shaders/blob/main/packages/shaders-react/CHANGELOG.md), [`packages/shaders-cli/CHANGELOG.md`](https://github.com/mattermix/shaders/blob/main/packages/shaders-cli/CHANGELOG.md). diff --git a/apps/docs/content/docs/cli.mdx b/apps/docs/content/docs/cli.mdx index f8fa9bf5..efac1152 100644 --- a/apps/docs/content/docs/cli.mdx +++ b/apps/docs/content/docs/cli.mdx @@ -1,18 +1,18 @@ --- title: CLI -description: Copy components from the Matter registry into your project with @lovo/matter-cli. +description: Copy components from the Shaders registry into your project with @mattermix/shaders-cli. section: overview order: 20 --- # CLI -`@lovo/matter-cli` copies polished shader components from the Matter registry directly into your project. It's a shadcn-style copy-paste workflow — once a component is in your repo, it's yours to edit. There's no runtime CLI dependency; you only need it installed during development. +`@mattermix/shaders-cli` copies polished shader components from the Shaders registry directly into your project. It's a shadcn-style copy-paste workflow — once a component is in your repo, it's yours to edit. There's no runtime CLI dependency; you only need it installed during development. ## Install ```bash -pnpm add -D @lovo/matter-cli +pnpm add -D @mattermix/shaders-cli ``` Requires Node 22+. @@ -21,18 +21,18 @@ Requires Node 22+. ### `init` -Run once per project to write a `matter.config.json`: +Run once per project to write a `shaders.config.json`: ```bash -npx matter-cli init +npx shaders-cli init ``` Default config: ```json { - "componentsDir": "src/components/matter", - "registryUrl": "https://raw.githubusercontent.com/lovo-hq/matter/${ref}/registry", + "componentsDir": "src/components/shaders", + "registryUrl": "https://raw.githubusercontent.com/mattermix/shaders/${ref}/registry", "aliases": { "@/": "src/" }, "tsx": true } @@ -43,7 +43,7 @@ The `${ref}` placeholder resolves to the CLI's published version tag (e.g. `v0.1 ### `list` ```bash -npx matter-cli list +npx shaders-cli list ``` Prints the components in the configured registry. @@ -51,8 +51,8 @@ Prints the components in the configured registry. ### `add` ```bash -npx matter-cli add linear-gradient -npx matter-cli add aurora dot-field wave-lines +npx shaders-cli add linear-gradient +npx shaders-cli add aurora dot-field wave-lines ``` Copies the component file (and any utility files it depends on) into `componentsDir`. The fetched source is byte-identical to what's documented on this site. @@ -60,16 +60,16 @@ Copies the component file (and any utility files it depends on) into `components ### `update` ```bash -npx matter-cli update linear-gradient # one component -npx matter-cli update --force # refresh all, overwriting local edits +npx shaders-cli update linear-gradient # one component +npx shaders-cli update --force # refresh all, overwriting local edits ``` Without `--force`, `update` refuses to overwrite a file you've modified — you keep your edits unless you explicitly ask to discard them. ## Why not a runtime dependency? -Tier 1 components (``, ``, etc.) aren't published to npm. They live in [the registry](https://github.com/lovo-hq/matter/tree/main/registry) and get copied into your project, where you can edit the shader, swap props, or refactor the JSX freely. The two packages you do depend on at runtime — `@lovo/matter` and `@lovo/matter-react` — provide the primitives, hooks, and `` that those components call into. +Tier 1 components (``, ``, etc.) aren't published to npm. They live in [the registry](https://github.com/mattermix/shaders/tree/main/registry) and get copied into your project, where you can edit the shader, swap props, or refactor the JSX freely. The two packages you do depend on at runtime — `@mattermix/shaders` and `@mattermix/shaders-react` — provide the primitives, hooks, and `` that those components call into. - There is no hosted registry endpoint. The CLI fetches sources from GitHub raw URLs at the pinned `${ref}`. If you want to host your own fork, change `registryUrl` in `matter.config.json` to point at your fork's raw URL. + There is no hosted registry endpoint. The CLI fetches sources from GitHub raw URLs at the pinned `${ref}`. If you want to host your own fork, change `registryUrl` in `shaders.config.json` to point at your fork's raw URL. diff --git a/apps/docs/content/docs/examples.mdx b/apps/docs/content/docs/examples.mdx index de80c43c..9933abab 100644 --- a/apps/docs/content/docs/examples.mdx +++ b/apps/docs/content/docs/examples.mdx @@ -1,6 +1,6 @@ --- title: Examples -description: Curated combinations of Matter components in real-world patterns. Coming soon. +description: Curated combinations of Shaders components in real-world patterns. Coming soon. section: overview order: 40 status: draft @@ -14,6 +14,6 @@ In the meantime: - The [component pages](/components) each include a live demo, a props playground, and the exact source the CLI copies into your project. - The [primitives pages](/primitives) show the lower-level TSL building blocks. If you want to write your own shader, start there. -- The [Shared scenes guide](/guides/shared-scenes) shows how to combine multiple Matter components inside one ``. +- The [Shared scenes guide](/guides/shared-scenes) shows how to combine multiple Shaders components inside one ``. -If you build something with Matter, [open a PR on GitHub](https://github.com/lovo-hq/matter) — selected examples will land on this page. +If you build something with Shaders, [open a PR on GitHub](https://github.com/mattermix/shaders) — selected examples will land on this page. diff --git a/apps/docs/content/docs/getting-started.mdx b/apps/docs/content/docs/getting-started.mdx index f4ec6afa..fc256d29 100644 --- a/apps/docs/content/docs/getting-started.mdx +++ b/apps/docs/content/docs/getting-started.mdx @@ -1,16 +1,16 @@ --- title: Get Started -description: Install Matter, copy your first component, and render it inside ShaderScene. +description: Install Shaders, copy your first component, and render it inside ShaderScene. section: overview order: 10 --- # Get Started -Matter is a React component library for WebGPU shaders built on Three.js TSL. Components are delivered shadcn-style — the CLI copies polished `.tsx` files into your project, where you own and edit them. +Shaders is a React component library for WebGPU shaders built on Three.js TSL. Components are delivered shadcn-style — the CLI copies polished `.tsx` files into your project, where you own and edit them. - **WebGPU only.** Matter requires a WebGPU-capable browser (recent Chromium, Safari Technology Preview, or Firefox Nightly with the flag). Plan a fallback for users without WebGPU — see [SSR and fallbacks](/react/guides/ssr-and-fallbacks). + **WebGPU only.** Shaders requires a WebGPU-capable browser (recent Chromium, Safari Technology Preview, or Firefox Nightly with the flag). Plan a fallback for users without WebGPU — see [SSR and fallbacks](/react/guides/ssr-and-fallbacks). ## Install @@ -18,8 +18,8 @@ Matter is a React component library for WebGPU shaders built on Three.js TSL. Co Add the engine and the React binding to your app, and the CLI as a dev dependency: ```bash -pnpm add @lovo/matter @lovo/matter-react -pnpm add -D @lovo/matter-cli +pnpm add @mattermix/shaders @mattermix/shaders-react +pnpm add -D @mattermix/shaders-cli ``` Requirements: @@ -27,45 +27,45 @@ Requirements: - React 19 - Three.js `^0.170` - Node 22+ (for the CLI) -- Next.js 15+ if you're using one (Matter doesn't require Next, but the docs and recipes assume it) +- Next.js 15+ if you're using one (Shaders doesn't require Next, but the docs and recipes assume it) ## Initialize
  • - **Initialize the project** to write a `matter.config.json` at your repo root: + **Initialize the project** to write a `shaders.config.json` at your repo root: ```bash - npx matter-cli init + npx shaders-cli init ``` - This creates a config with sensible defaults — components land in `src/components/matter/`, the registry tracks the CLI's published version tag, and TSX is enabled. + This creates a config with sensible defaults — components land in `src/components/shaders/`, the registry tracks the CLI's published version tag, and TSX is enabled.
  • **List available components:** ```bash - npx matter-cli list + npx shaders-cli list ```
  • **Copy your first component into your project:** ```bash - npx matter-cli add linear-gradient + npx shaders-cli add linear-gradient ``` - The component lands in `src/components/matter/linear-gradient.tsx`. From that point on, the file is yours — edit it however you want. + The component lands in `src/components/shaders/linear-gradient.tsx`. From that point on, the file is yours — edit it however you want.
  • ## Render it -Every Matter component is bare — it needs a `` parent to provide the WebGPU canvas. The simplest valid usage: +Every Shaders component is bare — it needs a `` parent to provide the WebGPU canvas. The simplest valid usage: ```tsx -import { ShaderScene } from '@lovo/matter-react' -import { LinearGradient } from '@/components/matter/linear-gradient' +import { ShaderScene } from '@mattermix/shaders-react' +import { LinearGradient } from '@/components/shaders/linear-gradient' export function Hero() { return ( @@ -76,11 +76,11 @@ export function Hero() { } ``` -`` owns the canvas and the WebGPU renderer. Multiple Matter components can share the same scene — just stack them as children. See [Shared scenes](/guides/shared-scenes). +`` owns the canvas and the WebGPU renderer. Multiple Shaders components can share the same scene — just stack them as children. See [Shared scenes](/guides/shared-scenes). ## What's next - [Animation](/guides/animation) — signal-shaped props for cursor, scroll, and any MotionValue-compatible library. -- [Performance](/guides/perf) — what Matter does for you automatically, and what you can tune. +- [Performance](/guides/perf) — what Shaders does for you automatically, and what you can tune. - [The CLI](/cli) — `add`, `update`, refresh workflows, and registry refs. -- [Three / r3f](/react/guides/three-r3f) — using Matter inside a `` you already own. +- [Three / r3f](/react/guides/three-r3f) — using Shaders inside a `` you already own. diff --git a/apps/docs/content/docs/guides/animation.mdx b/apps/docs/content/docs/guides/animation.mdx index e8de4b64..e0996fca 100644 --- a/apps/docs/content/docs/guides/animation.mdx +++ b/apps/docs/content/docs/guides/animation.mdx @@ -1,13 +1,13 @@ --- title: Animation -description: Signal-shaped props, MotionValue compatibility, and how Matter animates uniforms on the GPU. +description: Signal-shaped props, MotionValue compatibility, and how Shaders animates uniforms on the GPU. section: guides order: 10 --- # Animation -Matter doesn't ship its own animation library. Component props that need to animate accept either a static value or a **signal** — anything with a `get()` and `on('change', callback)` shape. Motion's `MotionValue`, Jotai atoms with a wrapper, custom signal libraries — they all work, because Matter just reads the current value and subscribes to changes. +Shaders doesn't ship its own animation library. Component props that need to animate accept either a static value or a **signal** — anything with a `get()` and `on('change', callback)` shape. Motion's `MotionValue`, Jotai atoms with a wrapper, custom signal libraries — they all work, because Shaders just reads the current value and subscribes to changes. ## The signal contract @@ -20,7 +20,7 @@ interface AnimatableSignal { } ``` -When a Matter component receives a signal as a prop, it: +When a Shaders component receives a signal as a prop, it: 1. Reads `signal.get()` to seed the uniform — on mount, and again whenever the signal is swapped for another. 2. Subscribes with `on('change', …)` and writes each new value into the GPU uniform. @@ -34,8 +34,8 @@ The animation engine doesn't run on the CPU per-frame — values flow straight i ```tsx import { useMotionValue, useTransform, useScroll } from 'motion/react' -import { ShaderScene } from '@lovo/matter-react' -import { DotField } from '@/components/matter/dot-field' +import { ShaderScene } from '@mattermix/shaders-react' +import { DotField } from '@/components/shaders/dot-field' export function Hero() { const { scrollYProgress } = useScroll() @@ -49,18 +49,18 @@ export function Hero() { } ``` -`reach` is a `MotionValue` — Matter subscribes to it and pipes updates into the `reach` uniform. +`reach` is a `MotionValue` — Shaders subscribes to it and pipes updates into the `reach` uniform. ## Built-in input signals -Matter ships three React hooks that produce ready-to-use signals — they handle the canvas-aware math and Strict Mode lifecycle correctly: +Shaders ships three React hooks that produce ready-to-use signals — they handle the canvas-aware math and Strict Mode lifecycle correctly: - `useCursor()` — returns a `CursorSignal` with `value` ([0,1] across the canvas rect, not the viewport). - `useScroll()` — returns a `ScrollSignal` with normalized scroll progress. - `useResize()` — returns a `ResizeSignal` with current canvas dimensions. ```tsx -import { ShaderScene, useCursor } from '@lovo/matter-react' +import { ShaderScene, useCursor } from '@mattermix/shaders-react' function App() { return ( @@ -82,10 +82,10 @@ function Cursor() { If you're wiring up a custom shader, `useAnimatableUniform` is the lower-level escape hatch — pass it a static value or a signal and it returns a TSL uniform node that tracks it. `useAnimatablePoint` does the same for `[x, y]` pairs like `center`. See [the React API](/react/api) for the full signatures. -## What Matter does NOT ship +## What Shaders does NOT ship - No built-in spring / keyframe / timeline library. - No ``-style component primitives. - No state machine glue. -Bring your own animation library. Matter just consumes signals. +Bring your own animation library. Shaders just consumes signals. diff --git a/apps/docs/content/docs/guides/color.mdx b/apps/docs/content/docs/guides/color.mdx index 5aa63d3a..87654483 100644 --- a/apps/docs/content/docs/guides/color.mdx +++ b/apps/docs/content/docs/guides/color.mdx @@ -7,7 +7,7 @@ order: 15 # Color -Matter has three color controls that sound related and are not. `colorSpace` decides how two colors blend. `gamut` decides what the framebuffer can hold. Wide-gamut input is neither — it's just what happens when you write a color that sRGB can't express. Most confusion here comes from assuming one of them does another's job. +Shaders has three color controls that sound related and are not. `colorSpace` decides how two colors blend. `gamut` decides what the framebuffer can hold. Wide-gamut input is neither — it's just what happens when you write a color that sRGB can't express. Most confusion here comes from assuming one of them does another's job. ## `colorSpace` — how colors blend @@ -55,7 +55,7 @@ Whatever the gamut, every scene quietly dithers its final output: noise smaller ## Wide-gamut input -This is the part that needs no props at all. Write a color in `oklch()` or `oklab()` and Matter decodes it to unclamped linear-sRGB, so a color outside the sRGB cube stays outside it, all the way to the framebuffer: +This is the part that needs no props at all. Write a color in `oklch()` or `oklab()` and Shaders decodes it to unclamped linear-sRGB, so a color outside the sRGB cube stays outside it, all the way to the framebuffer: ```tsx // Greener than sRGB can express. Survives if the output gamut allows it. @@ -74,4 +74,4 @@ Think of it as a pipeline, one control per stage: They compose independently. A wide-gamut `oklch()` input blended in `oklab` and written to a `p3` framebuffer is the common case, and none of those three choices constrains the others. Reaching for `colorSpace` to fix a color that looks dull on a wide display is the usual mistake — that's stage 3's job. -For the CPU-side functions behind stage 1 — `parseColorString`, the conversions, the gamut helpers — see [the engine reference](/reference/matter#color-math--lovomattercolor). They come from `@lovo/matter/color`, which has no path to three, so you can call them wherever you need them: in the browser, or during a server render where the root entry would crash. +For the CPU-side functions behind stage 1 — `parseColorString`, the conversions, the gamut helpers — see [the engine reference](/reference/shaders#color-math--mattermixshaderscolor). They come from `@mattermix/shaders/color`, which has no path to three, so you can call them wherever you need them: in the browser, or during a server render where the root entry would crash. diff --git a/apps/docs/content/docs/guides/perf.mdx b/apps/docs/content/docs/guides/perf.mdx index ebf75afd..be5fc391 100644 --- a/apps/docs/content/docs/guides/perf.mdx +++ b/apps/docs/content/docs/guides/perf.mdx @@ -1,19 +1,19 @@ --- title: Performance -description: What Matter does automatically and what you can tune for offscreen pausing, DPR, reduced motion, and static renders. +description: What Shaders does automatically and what you can tune for offscreen pausing, DPR, reduced motion, and static renders. section: guides order: 20 --- # Performance -Matter applies five engine-level defaults you don't have to wire up yourself. This page covers what's automatic, what's tunable, and how to opt out when you need to. +Shaders applies five engine-level defaults you don't have to wire up yourself. This page covers what's automatic, what's tunable, and how to opt out when you need to. ## Pause when offscreen Every `` registers an `IntersectionObserver` on its canvas. When the canvas leaves the viewport, the scheduler unsubscribes the scene — no GPU frames are produced. When the canvas re-enters, rendering resumes from where it left off. -This is on by default and applies to all Matter components without per-component code. You don't need to do anything. +This is on by default and applies to all Shaders components without per-component code. You don't need to do anything. If you depend on continuous rendering — for example, you're capturing the canvas as a texture for another effect — opt out of offscreen pause by setting `pauseOffscreen={false}` on ``. @@ -35,26 +35,26 @@ Drop to `dprMax={1}` for the cheapest possible render, or set `dprMax={Infinity} ## Reduced motion -Matter reads `prefers-reduced-motion: reduce` on mount and respects the user's choice automatically. Three policies are available via `setReducedMotionPolicy`: +Shaders reads `prefers-reduced-motion: reduce` on mount and respects the user's choice automatically. Three policies are available via `setReducedMotionPolicy`: - `'respect'` (default) — when the user prefers reduced motion, animation time scales by `0` (effectively pausing time). - `'slow'` — time scales by `0.3`. -- `'ignore'` — Matter ignores the OS hint entirely. +- `'ignore'` — Shaders ignores the OS hint entirely. ```ts -import { setReducedMotionPolicy } from '@lovo/matter' +import { setReducedMotionPolicy } from '@mattermix/shaders' setReducedMotionPolicy('slow') ``` -This is global to the page and runs once at module init. The hook `useStaticSceneHint` from `@lovo/matter-react` can read the current scale per-render if you need to swap visuals based on it. +This is global to the page and runs once at module init. The hook `useStaticSceneHint` from `@mattermix/shaders-react` can read the current scale per-render if you need to swap visuals based on it. ## Static / render-on-demand -For decorative shaders that don't actually need to animate every frame, you can hint to Matter that the scene is "static" — it renders once on mount, then only on prop / signal changes. +For decorative shaders that don't actually need to animate every frame, you can hint to Shaders that the scene is "static" — it renders once on mount, then only on prop / signal changes. ```tsx -import { ShaderScene, useStaticSceneHint } from '@lovo/matter-react' +import { ShaderScene, useStaticSceneHint } from '@mattermix/shaders-react' function Hero() { useStaticSceneHint(true) @@ -77,7 +77,7 @@ Beyond intersection-based pausing, `` also listens to `document.vis Drop `` into your dev pages to get a corner overlay showing FPS, active component count, and scheduler state. It's a dev tool — not for production — and tree-shakes out when guarded behind `import.meta.env.DEV` or similar. ```tsx -import { ShaderMonitor } from '@lovo/matter-react' +import { ShaderMonitor } from '@mattermix/shaders-react' {process.env.NODE_ENV === 'development' && } ``` diff --git a/apps/docs/content/docs/guides/shared-scenes.mdx b/apps/docs/content/docs/guides/shared-scenes.mdx index fd722602..799fbf50 100644 --- a/apps/docs/content/docs/guides/shared-scenes.mdx +++ b/apps/docs/content/docs/guides/shared-scenes.mdx @@ -7,14 +7,14 @@ order: 30 # Shared scenes -Every Matter component renders to a WebGPU canvas owned by its parent ``. Components are bare — they don't bring their own canvas. To compose multiple effects, you stack them as children of the same ``. +Every Shaders component renders to a WebGPU canvas owned by its parent ``. Components are bare — they don't bring their own canvas. To compose multiple effects, you stack them as children of the same ``. ## The pattern ```tsx -import { ShaderScene } from '@lovo/matter-react' -import { Aurora } from '@/components/matter/aurora' -import { DotField } from '@/components/matter/dot-field' +import { ShaderScene } from '@mattermix/shaders-react' +import { Aurora } from '@/components/shaders/aurora' +import { DotField } from '@/components/shaders/dot-field' export function Hero() { return ( @@ -63,4 +63,4 @@ Each component renders to its own quad in the scene — there's no built-in blen ## Multiple scenes on one page -There's no restriction on how many `` instances a page can host. Each owns its own WebGPU device, so they're truly independent. The home-page mock for Matter uses four scenes (hero, feature row, section divider, footer) and pauses each one when it scrolls out of view. +There's no restriction on how many `` instances a page can host. Each owns its own WebGPU device, so they're truly independent. The home-page mock for Shaders uses four scenes (hero, feature row, section divider, footer) and pauses each one when it scrolls out of view. diff --git a/apps/docs/content/docs/react/api.mdx b/apps/docs/content/docs/react/api.mdx index b67d553c..63572d57 100644 --- a/apps/docs/content/docs/react/api.mdx +++ b/apps/docs/content/docs/react/api.mdx @@ -1,20 +1,20 @@ --- title: React API -description: Public exports from @lovo/matter-react — ShaderScene, hooks, fallbacks, dev tools. +description: Public exports from @mattermix/shaders-react — ShaderScene, hooks, fallbacks, dev tools. section: react.api order: 10 --- # React API -Everything exported from `@lovo/matter-react`. This page is hand-written for the launch milestone; a generated reference from TypeDoc lands post-launch. +Everything exported from `@mattermix/shaders-react`. This page is hand-written for the launch milestone; a generated reference from TypeDoc lands post-launch. ## `` -The scene wrapper. Owns the WebGPU canvas, creates the renderer, runs the scheduler. Every Tier 1 Matter component must render inside one. +The scene wrapper. Owns the WebGPU canvas, creates the renderer, runs the scheduler. Every Tier 1 Shaders component must render inside one. ```tsx -import { ShaderScene } from '@lovo/matter-react' +import { ShaderScene } from '@mattermix/shaders-react' void` | — | Called once if renderer init fails (e.g. no GPU backend). Receives a `MatterError` with `code: 'renderer-init'` and the original error on `.cause`. | +| `children` | `ReactNode` | — | One or more Shaders components. | +| `onError` | `(error: ShadersError) => void` | — | Called once if renderer init fails (e.g. no GPU backend). Receives a `ShadersError` with `code: 'renderer-init'` and the original error on `.cause`. | ## `useShaderMaterial` -The lower-level hook for users who already own a `` from `@react-three/fiber`. Use this when you want Matter primitives inside your own r3f scene — see [Three / r3f](/react/guides/three-r3f). +The lower-level hook for users who already own a `` from `@react-three/fiber`. Use this when you want Shaders primitives inside your own r3f scene — see [Three / r3f](/react/guides/three-r3f). ```tsx -import { useShaderMaterial } from '@lovo/matter-react' +import { useShaderMaterial } from '@mattermix/shaders-react' import { uv } from 'three/tsl' -import { fractalNoise } from '@lovo/matter' +import { fractalNoise } from '@mattermix/shaders' function Plane() { const material = useShaderMaterial({ @@ -69,7 +69,7 @@ All three are Strict-Mode-safe (no leaked listeners on dev double-mount) and mus Low-level animation glue. Pass an animatable prop — a static value or a signal — and get back a stable TSL uniform node that tracks it: ```tsx -import { useAnimatableUniform, type AnimatableProp } from '@lovo/matter-react' +import { useAnimatableUniform, type AnimatableProp } from '@mattermix/shaders-react' function Inner({ reach }: { reach: AnimatableProp }) { const reachUniform = useAnimatableUniform(reach) @@ -84,7 +84,7 @@ The returned node keeps its identity for the component's lifetime, so a material The `[x, y]` sibling, for props like `center`. Same contract — static pair or signal in, one stable `vec2` uniform out, backed by a single `Vector2` mutated in place: ```tsx -import { useAnimatablePoint, type AnimatableProp } from '@lovo/matter-react' +import { useAnimatablePoint, type AnimatableProp } from '@mattermix/shaders-react' function Inner({ center }: { center: AnimatableProp }) { const centerUniform = useAnimatablePoint(center, { screenOrigin: true }) @@ -99,7 +99,7 @@ function Inner({ center }: { center: AnimatableProp } The animation glue behind every registry component's `speed` prop. It does not return a speed uniform — it returns a scalar **phase** uniform, integrating speed over time on the CPU: ```tsx -import { useAnimatableSpeed, type AnimatableProp } from '@lovo/matter-react' +import { useAnimatableSpeed, type AnimatableProp } from '@mattermix/shaders-react' function Inner({ speed }: { speed: AnimatableProp }) { const phaseUniform = useAnimatableSpeed(speed) @@ -111,7 +111,7 @@ Each scheduler tick adds `speed × min(delta, 0.1)` (scaled for `prefers-reduced ## `` -Renders fallback content when WebGPU is unavailable or initialization fails. Wrap any Matter subtree: +Renders fallback content when WebGPU is unavailable or initialization fails. Wrap any Shaders subtree: ```tsx }> @@ -132,7 +132,7 @@ A static stand-in shown until the shader's first content frame is on screen, and Imported from its own entry point, which has no path to three and so server-renders: ```tsx -import { ShaderPoster } from '@lovo/matter-react/poster' +import { ShaderPoster } from '@mattermix/shaders-react/poster' }> @@ -146,7 +146,7 @@ import { ShaderPoster } from '@lovo/matter-react/poster' Resolves a gamut preference to what the display can actually show. Pass `'srgb'` or `'p3'` to fix it, or `'auto'` to query `(color-gamut: p3)` and re-resolve when the window moves to another monitor. ```tsx -import { useDisplayGamut } from '@lovo/matter-react/gamut' +import { useDisplayGamut } from '@mattermix/shaders-react/gamut' const gamut = useDisplayGamut('auto') // 'srgb' | 'p3' ``` diff --git a/apps/docs/content/docs/react/guides/ssr-and-fallbacks.mdx b/apps/docs/content/docs/react/guides/ssr-and-fallbacks.mdx index e90a142a..390674bc 100644 --- a/apps/docs/content/docs/react/guides/ssr-and-fallbacks.mdx +++ b/apps/docs/content/docs/react/guides/ssr-and-fallbacks.mdx @@ -7,30 +7,30 @@ order: 10 # SSR and fallbacks -Matter is fundamentally a client runtime — it needs `navigator.gpu`, which doesn't exist on the server. This page covers the patterns for using Matter in Next.js (or any SSR framework) and handling browsers that don't support WebGPU at all. +Shaders is fundamentally a client runtime — it needs `navigator.gpu`, which doesn't exist on the server. This page covers the patterns for using Shaders in Next.js (or any SSR framework) and handling browsers that don't support WebGPU at all. ## The core constraint `three/webgpu` references `self` and `navigator` at module load time. Importing it in a server bundle (RSC, getServerSideProps, etc.) throws. So: -- Matter components are always client components. -- Pages that import them either need `'use client'` themselves, or they need to load Matter via `next/dynamic` with SSR disabled. +- Shaders components are always client components. +- Pages that import them either need `'use client'` themselves, or they need to load Shaders via `next/dynamic` with SSR disabled. That applies to anything reaching the renderer. It does not apply to every export both packages have, which is what the subpaths below are for. ## The three-free subpaths -Some of what Matter ships is plain arithmetic that never touches the GPU: parsing a color string, converting between OKLCH and linear sRGB, asking whether a display can show a color. Those reach `three/webgpu` only because the root entry re-exports the renderer alongside them, so each has a second entry point that doesn't. +Some of what Shaders ships is plain arithmetic that never touches the GPU: parsing a color string, converting between OKLCH and linear sRGB, asking whether a display can show a color. Those reach `three/webgpu` only because the root entry re-exports the renderer alongside them, so each has a second entry point that doesn't. | Import from | Instead of | For | | --- | --- | --- | -| `@lovo/matter/color` | `@lovo/matter` | `parseColorString`, the OKLab/OKLCH conversions, the gamut helpers, the sRGB transfer functions | -| `@lovo/matter-react/gamut` | `@lovo/matter-react` | `useDisplayGamut` | -| `@lovo/matter-react/poster` | `@lovo/matter-react` | `` | +| `@mattermix/shaders/color` | `@mattermix/shaders` | `parseColorString`, the OKLab/OKLCH conversions, the gamut helpers, the sRGB transfer functions | +| `@mattermix/shaders-react/gamut` | `@mattermix/shaders-react` | `useDisplayGamut` | +| `@mattermix/shaders-react/poster` | `@mattermix/shaders-react` | `` | ```tsx // Server-renders fine — no path to three. -import { parseColorString, linearSrgbToOklch } from '@lovo/matter/color' +import { parseColorString, linearSrgbToOklch } from '@mattermix/shaders/color' export default function Swatch({ color }: { color: string }) { const [lightness] = linearSrgbToOklch(...parseColorString(color)) @@ -57,7 +57,7 @@ export default function Page() { } ``` -`Hero` is a regular client component that imports `@lovo/matter-react` and the registry component. The page itself can stay an RSC. +`Hero` is a regular client component that imports `@mattermix/shaders-react` and the registry component. The page itself can stay an RSC. ## Webpack alias for three (Next 14+) @@ -91,11 +91,11 @@ This forces every `three` import to resolve to the unified bundle. Same idea app ## Fallback for browsers without WebGPU -Some browsers (Firefox stable, Safari < 18, older Chrome) don't ship WebGPU yet. Wrap any Matter subtree in ``: +Some browsers (Firefox stable, Safari < 18, older Chrome) don't ship WebGPU yet. Wrap any Shaders subtree in ``: ```tsx -import { FallbackBoundary, ShaderScene } from '@lovo/matter-react' -import { Aurora } from '@/components/matter/aurora' +import { FallbackBoundary, ShaderScene } from '@mattermix/shaders-react' +import { Aurora } from '@/components/shaders/aurora' export function Hero() { return ( @@ -126,12 +126,12 @@ To show something in its place, wrap it in ``: the poster stays up ``` -`onError` receives a `MatterError` with `code: 'renderer-init'`; the original thrown error is on `.cause`. This is separate from ``, which covers SSR and hydration — not renderer failure. +`onError` receives a `ShadersError` with `code: 'renderer-init'`; the original thrown error is on `.cause`. This is separate from ``, which covers SSR and hydration — not renderer failure. ## What gets server-rendered If you wrap a scene in `next/dynamic` with `ssr: false`, nothing from that subtree ships in the server HTML — which is right, because a canvas has nothing useful to say before it has a GPU. The fallback inside `` is what users see during hydration and on unsupported browsers. -The subpaths are the exception, and worth remembering when you reach for `ssr: false` reflexively. A page that reads colors with `@lovo/matter/color`, or checks the display gamut with `@lovo/matter-react/gamut`, or shows a `` while the scene loads, renders all of that on the server normally. Only the scene itself has to wait. +The subpaths are the exception, and worth remembering when you reach for `ssr: false` reflexively. A page that reads colors with `@mattermix/shaders/color`, or checks the display gamut with `@mattermix/shaders-react/gamut`, or shows a `` while the scene loads, renders all of that on the server normally. Only the scene itself has to wait. -If you have SEO-critical content that's currently hidden behind a Matter component, lift it out into the surrounding RSC. Matter is for decoration; the surrounding page is for content. +If you have SEO-critical content that's currently hidden behind a Shaders component, lift it out into the surrounding RSC. Shaders is for decoration; the surrounding page is for content. diff --git a/apps/docs/content/docs/react/guides/three-r3f.mdx b/apps/docs/content/docs/react/guides/three-r3f.mdx index ffd079a8..cb7494de 100644 --- a/apps/docs/content/docs/react/guides/three-r3f.mdx +++ b/apps/docs/content/docs/react/guides/three-r3f.mdx @@ -1,16 +1,16 @@ --- title: Three / r3f -description: Use Matter primitives inside a Canvas you already own (Mode 2). +description: Use Shaders primitives inside a Canvas you already own (Mode 2). section: react.guides order: 20 --- # Three / r3f -Matter has two rendering modes: +Shaders has two rendering modes: -- **Mode 1: Matter-managed scene.** `` owns the canvas. Tier 1 components (``, ``, etc.) only work in Mode 1. This is what [Get Started](/getting-started) covers. -- **Mode 2: Bring your own Canvas.** You already have an `@react-three/fiber` `` for a 3D scene, and you want Matter's TSL primitives in a material. This page is for Mode 2. +- **Mode 1: Shaders-managed scene.** `` owns the canvas. Tier 1 components (``, ``, etc.) only work in Mode 1. This is what [Get Started](/getting-started) covers. +- **Mode 2: Bring your own Canvas.** You already have an `@react-three/fiber` `` for a 3D scene, and you want Shaders' TSL primitives in a material. This page is for Mode 2. Mode 2 doesn't auto-detect r3f — it's an explicit opt-in via the `useShaderMaterial` hook. @@ -20,8 +20,8 @@ Mode 2 doesn't auto-detect r3f — it's an explicit opt-in via the `useShaderMat ```tsx import { Canvas } from '@react-three/fiber' -import { useShaderMaterial } from '@lovo/matter-react' -import { fractalNoise, uv, vec3, mix } from '@lovo/matter' +import { useShaderMaterial } from '@mattermix/shaders-react' +import { fractalNoise, uv, vec3, mix } from '@mattermix/shaders' function ShaderPlane() { const material = useShaderMaterial({ @@ -44,13 +44,13 @@ export function Scene() { } ``` -The material plays by r3f's rules — dispose, frameloop, demand-rendering all work as r3f expects. Matter just provides the TSL math; r3f provides the scene graph. +The material plays by r3f's rules — dispose, frameloop, demand-rendering all work as r3f expects. Shaders just provides the TSL math; r3f provides the scene graph. ## When to use Mode 2 - You're building a 3D scene (geometry, lights, cameras) that also wants shader-y materials. - You need fine control over the render loop (custom frameloop, post-processing chain). -- You want to use Matter primitives (`fractalNoise`, `voronoi`, `colorRamp`, …) without the constraints of a single full-bleed 2D quad. +- You want to use Shaders primitives (`fractalNoise`, `voronoi`, `colorRamp`, …) without the constraints of a single full-bleed 2D quad. ## What you lose in Mode 2 @@ -63,5 +63,5 @@ The material plays by r3f's rules — dispose, frameloop, demand-rendering all w Default to Mode 1. Mode 2 exists for the case where you already have an r3f scene and the alternative is duplicating shader work in two canvases. If you're not building a 3D scene, you don't need Mode 2 — `` does everything you want. - Matter and r3f can coexist on the same page in separate canvases — one `` for your hero shader, one r3f `` for a 3D model below it. They don't interfere. Mode 2 is only relevant when you want to *combine* them in one canvas. + Shaders and r3f can coexist on the same page in separate canvases — one `` for your hero shader, one r3f `` for a 3D model below it. They don't interfere. Mode 2 is only relevant when you want to *combine* them in one canvas. diff --git a/apps/docs/content/docs/reference/matter.mdx b/apps/docs/content/docs/reference/shaders.mdx similarity index 75% rename from apps/docs/content/docs/reference/matter.mdx rename to apps/docs/content/docs/reference/shaders.mdx index b0d1f03e..c67286d4 100644 --- a/apps/docs/content/docs/reference/matter.mdx +++ b/apps/docs/content/docs/reference/shaders.mdx @@ -1,13 +1,13 @@ --- title: Engine API -description: '@lovo/matter — primitives, runtime, inputs, and reduced-motion-gated time.' +description: '@mattermix/shaders — primitives, runtime, inputs, and reduced-motion-gated time.' section: reference order: 10 --- # Engine API -`@lovo/matter` is the framework-agnostic engine. This page lists everything it exports. A generated TypeDoc reference replaces this hand-written page post-launch. +`@mattermix/shaders` is the framework-agnostic engine. This page lists everything it exports. A generated TypeDoc reference replaces this hand-written page post-launch. ## Tier 2 primitives @@ -25,23 +25,23 @@ TSL building blocks for writing shader expressions. Each primitive is documented ## Layering and `elapsedTime` -Matter sits on top of Three.js's TSL. Pure TSL primitives (`uv`, `vec2`, `vec3`, `vec4`, `uniform`, `mix`, `smoothstep`, `mod`, `sin`, `cos`, `length`, `dot`, `normalize`, `max`, `min`) live in `three/tsl` — import them directly from there: +Shaders sits on top of Three.js's TSL. Pure TSL primitives (`uv`, `vec2`, `vec3`, `vec4`, `uniform`, `mix`, `smoothstep`, `mod`, `sin`, `cos`, `length`, `dot`, `normalize`, `max`, `min`) live in `three/tsl` — import them directly from there: ```ts import { uv, vec3, uniform, mix } from 'three/tsl' ``` -Matter exports one TSL node of its own: **`elapsedTime`**. It's the TSL built-in `time` multiplied by an engine-owned reduced-motion scale uniform, so every component that animates against `elapsedTime` from `@lovo/matter` automatically respects `prefers-reduced-motion` and the policy override set via `setReducedMotionPolicy`. +Shaders exports one TSL node of its own: **`elapsedTime`**. It's the TSL built-in `time` multiplied by an engine-owned reduced-motion scale uniform, so every component that animates against `elapsedTime` from `@mattermix/shaders` automatically respects `prefers-reduced-motion` and the policy override set via `setReducedMotionPolicy`. ```ts -import { elapsedTime } from '@lovo/matter' +import { elapsedTime } from '@mattermix/shaders' // → equivalent to three/tsl's `time.mul(reducedMotionScale)` // If you specifically want raw uncapped time (e.g. a debug overlay): import { time as rawTime } from 'three/tsl' ``` -> **Migrating from 0.1.x?** Before 0.2.0, `@lovo/matter` re-exported the TSL primitives listed above as a convenience. They're gone in 0.2.0 — replace `import { vec3, uv, … } from '@lovo/matter'` with `import { vec3, uv, … } from 'three/tsl'`. `elapsedTime` (named `time` before 0.5.0) is the only carry-over and stays imported from `@lovo/matter`. +> **Migrating from 0.1.x?** Before 0.2.0, `@mattermix/shaders` re-exported the TSL primitives listed above as a convenience. They're gone in 0.2.0 — replace `import { vec3, uv, … } from '@mattermix/shaders'` with `import { vec3, uv, … } from 'three/tsl'`. `elapsedTime` (named `time` before 0.5.0) is the only carry-over and stays imported from `@mattermix/shaders`. ## Runtime @@ -51,7 +51,7 @@ import { time as rawTime } from 'three/tsl' ## Inputs -- **`CursorInput`** — framework-agnostic cursor source. `useCursor()` in `@lovo/matter-react` wraps it; in Mode 2 or non-React contexts, you instantiate it directly. +- **`CursorInput`** — framework-agnostic cursor source. `useCursor()` in `@mattermix/shaders-react` wraps it; in Mode 2 or non-React contexts, you instantiate it directly. ## Reduced motion @@ -60,14 +60,14 @@ import { time as rawTime } from 'three/tsl' - **`getReducedMotionTimeScale()`** — get the current animation time scale (0, 0.3, or 1). - **`createReducedMotionWatcher()`** — subscribe to policy changes. -## Color math — `@lovo/matter/color` +## Color math — `@mattermix/shaders/color` CPU-side color conversion, run once in JavaScript when a prop is parsed. The GPU only ever sees the resulting numbers. These are also exported from the root entry, but prefer the subpath: it has no path to three, so it can be imported during a server render, where the root entry cannot. See [SSR and fallbacks](/react/guides/ssr-and-fallbacks#the-three-free-subpaths). ```ts -import { parseColorString, linearSrgbToOklch } from '@lovo/matter/color' +import { parseColorString, linearSrgbToOklch } from '@mattermix/shaders/color' ``` - `parseColorString(input)` → extended linear-sRGB. Accepts `#rrggbb`, `#rrggbbaa` (alpha dropped), `oklab(L a b)`, and `oklch(L C H)`. Throws on anything else, including components that aren't numbers. @@ -83,5 +83,5 @@ import { parseColorString, linearSrgbToOklch } from '@lovo/matter/color' The engine exports types for everything above: `GpuRenderer`, `CreateRendererOptions`, `GpuBackend`, `SchedulerTick`, `SchedulerClient`, `CursorInputOptions`, `Vector2`, `ColorRampStop`, `TSLNode`, `FractalNoiseOptions`, `FractalFold`, `CursorRippleOptions`, `ReducedMotionPolicy`, `ReducedMotionWatcher`, `VisibilityWatcher`, `IntersectionWatcher`, `OutputGamut`, `ColorSpace`, `HueInterpolation`. - For React-specific exports (``, hooks, ``), see [the React API](/react/api). This page only covers `@lovo/matter`. + For React-specific exports (``, hooks, ``), see [the React API](/react/api). This page only covers `@mattermix/shaders`. diff --git a/apps/docs/next.config.ts b/apps/docs/next.config.ts index 8e0df4de..c95efce9 100644 --- a/apps/docs/next.config.ts +++ b/apps/docs/next.config.ts @@ -38,7 +38,7 @@ const nextConfig: NextConfig = { // `next build` lint too just runs the same rules a second time. Turning it off // here is not a way of skipping the check — it is saying where the check lives. eslint: { ignoreDuringBuilds: true }, - transpilePackages: ['@lovo/matter', '@lovo/matter-react', '@matter/registry'], + transpilePackages: ['@mattermix/shaders', '@mattermix/shaders-react', '@shaders/registry'], webpack(config: WebpackConfig): WebpackConfig { config.resolve = config.resolve ?? {}; const webgpuBundle = resolve(threeDir, 'build/three.webgpu.js'); diff --git a/apps/docs/package.json b/apps/docs/package.json index b335c6e8..078d817f 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -1,5 +1,5 @@ { - "name": "@matter/docs", + "name": "@shaders/docs", "version": "0.0.0", "private": true, "type": "module", @@ -14,9 +14,9 @@ }, "dependencies": { "@base-ui/react": "^1.6.0", - "@lovo/matter": "workspace:*", - "@lovo/matter-react": "workspace:*", - "@matter/registry": "workspace:*", + "@mattermix/shaders": "workspace:*", + "@mattermix/shaders-react": "workspace:*", + "@shaders/registry": "workspace:*", "@xyflow/react": "^12.11.2", "github-slugger": "^2.0.0", "gray-matter": "^4.0.3", @@ -38,7 +38,7 @@ "zod": "^4.4.3" }, "devDependencies": { - "@matter/tsconfig": "workspace:*", + "@shaders/tsconfig": "workspace:*", "@tweakpane/core": "^2.0.5", "@types/culori": "^4.0.1", "@types/node": "^22.7.0", diff --git a/apps/docs/src/app/components/aurora/params.ts b/apps/docs/src/app/components/aurora/params.ts index 0cd84eee..68841657 100644 --- a/apps/docs/src/app/components/aurora/params.ts +++ b/apps/docs/src/app/components/aurora/params.ts @@ -1,4 +1,4 @@ -import type { ColorSpace, HueInterpolation } from '@lovo/matter'; +import type { ColorSpace, HueInterpolation } from '@mattermix/shaders'; import { paletteOklch } from '../../../lib/palette'; diff --git a/apps/docs/src/app/components/aurora/scene.tsx b/apps/docs/src/app/components/aurora/scene.tsx index 81b1f7f6..5b99d2d6 100644 --- a/apps/docs/src/app/components/aurora/scene.tsx +++ b/apps/docs/src/app/components/aurora/scene.tsx @@ -2,8 +2,8 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { Aurora, type ColorStop } from '@matter/registry/aurora'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { Aurora, type ColorStop } from '@shaders/registry/aurora'; import { type AuroraParams, INITIAL } from './params'; diff --git a/apps/docs/src/app/components/blobs/params.ts b/apps/docs/src/app/components/blobs/params.ts index 0a7aa6b8..3f121f5c 100644 --- a/apps/docs/src/app/components/blobs/params.ts +++ b/apps/docs/src/app/components/blobs/params.ts @@ -1,4 +1,4 @@ -import type { ColorSpace, HueInterpolation } from '@lovo/matter'; +import type { ColorSpace, HueInterpolation } from '@mattermix/shaders'; import { paletteOklch } from '../../../lib/palette'; diff --git a/apps/docs/src/app/components/blobs/scene.tsx b/apps/docs/src/app/components/blobs/scene.tsx index 9a62d112..431033cf 100644 --- a/apps/docs/src/app/components/blobs/scene.tsx +++ b/apps/docs/src/app/components/blobs/scene.tsx @@ -2,10 +2,10 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { Blobs } from '@matter/registry/blobs'; -import type { ColorStop } from '@matter/registry/blobs'; -import { LinearGradient } from '@matter/registry/linear-gradient'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { Blobs } from '@shaders/registry/blobs'; +import type { ColorStop } from '@shaders/registry/blobs'; +import { LinearGradient } from '@shaders/registry/linear-gradient'; import { INITIAL, type Params } from './params'; diff --git a/apps/docs/src/app/components/conic-gradient/params.ts b/apps/docs/src/app/components/conic-gradient/params.ts index fc592de4..f2e24160 100644 --- a/apps/docs/src/app/components/conic-gradient/params.ts +++ b/apps/docs/src/app/components/conic-gradient/params.ts @@ -1,4 +1,4 @@ -import type { ColorSpace, HueInterpolation } from '@lovo/matter'; +import type { ColorSpace, HueInterpolation } from '@mattermix/shaders'; import { paletteOklch } from '../../../lib/palette'; diff --git a/apps/docs/src/app/components/conic-gradient/scene.tsx b/apps/docs/src/app/components/conic-gradient/scene.tsx index 5289d4d9..f5104e67 100644 --- a/apps/docs/src/app/components/conic-gradient/scene.tsx +++ b/apps/docs/src/app/components/conic-gradient/scene.tsx @@ -2,8 +2,8 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { ConicGradient } from '@matter/registry/conic-gradient'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { ConicGradient } from '@shaders/registry/conic-gradient'; import { INITIAL, type Params } from './params'; diff --git a/apps/docs/src/app/components/dither/params.ts b/apps/docs/src/app/components/dither/params.ts index cf1e257b..d0a887f9 100644 --- a/apps/docs/src/app/components/dither/params.ts +++ b/apps/docs/src/app/components/dither/params.ts @@ -1,4 +1,4 @@ -import type { DitherPattern } from '@matter/registry/dither'; +import type { DitherPattern } from '@shaders/registry/dither'; export interface DitherParams { pixelSize: number; diff --git a/apps/docs/src/app/components/dither/scene.tsx b/apps/docs/src/app/components/dither/scene.tsx index 0727b2ea..2f3915c4 100644 --- a/apps/docs/src/app/components/dither/scene.tsx +++ b/apps/docs/src/app/components/dither/scene.tsx @@ -2,9 +2,9 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { Dither } from '@matter/registry/dither'; -import { MeshGradient } from '@matter/registry/mesh-gradient'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { Dither } from '@shaders/registry/dither'; +import { MeshGradient } from '@shaders/registry/mesh-gradient'; import { type DitherParams, INITIAL } from './params'; diff --git a/apps/docs/src/app/components/dot-field/scene.tsx b/apps/docs/src/app/components/dot-field/scene.tsx index cec58122..16ee5351 100644 --- a/apps/docs/src/app/components/dot-field/scene.tsx +++ b/apps/docs/src/app/components/dot-field/scene.tsx @@ -2,8 +2,8 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { DotField } from '@matter/registry/dot-field'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { DotField } from '@shaders/registry/dot-field'; import { INITIAL, type Params } from './params'; diff --git a/apps/docs/src/app/components/fractal-noise/page.tsx b/apps/docs/src/app/components/fractal-noise/page.tsx index a0e890f9..22a4cb68 100644 --- a/apps/docs/src/app/components/fractal-noise/page.tsx +++ b/apps/docs/src/app/components/fractal-noise/page.tsx @@ -8,8 +8,8 @@ import dynamic from 'next/dynamic'; import { useEffect, useMemo, useRef } from 'react'; -import type { FractalNoiseStyle } from '@matter/registry/fractal-noise'; -import { STYLE_DIAL_DEFAULTS } from '@matter/registry/fractal-noise/style-dial-defaults'; +import type { FractalNoiseStyle } from '@shaders/registry/fractal-noise'; +import { STYLE_DIAL_DEFAULTS } from '@shaders/registry/fractal-noise/style-dial-defaults'; import { COLOR_SPACE_OPTIONS, diff --git a/apps/docs/src/app/components/fractal-noise/params.ts b/apps/docs/src/app/components/fractal-noise/params.ts index 0f1e3bed..0bc349ac 100644 --- a/apps/docs/src/app/components/fractal-noise/params.ts +++ b/apps/docs/src/app/components/fractal-noise/params.ts @@ -1,6 +1,6 @@ -import type { ColorSpace, HueInterpolation } from '@lovo/matter'; -import type { FractalNoiseStyle } from '@matter/registry/fractal-noise'; -import { STYLE_DIAL_DEFAULTS } from '@matter/registry/fractal-noise/style-dial-defaults'; +import type { ColorSpace, HueInterpolation } from '@mattermix/shaders'; +import type { FractalNoiseStyle } from '@shaders/registry/fractal-noise'; +import { STYLE_DIAL_DEFAULTS } from '@shaders/registry/fractal-noise/style-dial-defaults'; import { paletteOklch } from '../../../lib/palette'; diff --git a/apps/docs/src/app/components/fractal-noise/scene.tsx b/apps/docs/src/app/components/fractal-noise/scene.tsx index 134e27b1..ba4ae5cd 100644 --- a/apps/docs/src/app/components/fractal-noise/scene.tsx +++ b/apps/docs/src/app/components/fractal-noise/scene.tsx @@ -2,9 +2,9 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { FractalNoise } from '@matter/registry/fractal-noise'; -import type { ColorStop } from '@matter/registry/fractal-noise'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { FractalNoise } from '@shaders/registry/fractal-noise'; +import type { ColorStop } from '@shaders/registry/fractal-noise'; import { INITIAL, type Params } from './params'; diff --git a/apps/docs/src/app/components/god-rays/scene.tsx b/apps/docs/src/app/components/god-rays/scene.tsx index 89465c0c..b51d4848 100644 --- a/apps/docs/src/app/components/god-rays/scene.tsx +++ b/apps/docs/src/app/components/god-rays/scene.tsx @@ -2,8 +2,8 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { GodRays } from '@matter/registry/god-rays'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { GodRays } from '@shaders/registry/god-rays'; import { type GodRaysParams, INITIAL } from './params'; diff --git a/apps/docs/src/app/components/grain/params.ts b/apps/docs/src/app/components/grain/params.ts index 6b20f074..8d892546 100644 --- a/apps/docs/src/app/components/grain/params.ts +++ b/apps/docs/src/app/components/grain/params.ts @@ -1,4 +1,4 @@ -import type { GrainBlend } from '@matter/registry/grain'; +import type { GrainBlend } from '@shaders/registry/grain'; export interface GrainParams { intensity: number; diff --git a/apps/docs/src/app/components/grain/scene.tsx b/apps/docs/src/app/components/grain/scene.tsx index c37d89f7..961b0620 100644 --- a/apps/docs/src/app/components/grain/scene.tsx +++ b/apps/docs/src/app/components/grain/scene.tsx @@ -2,9 +2,9 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { Grain } from '@matter/registry/grain'; -import { LinearGradient } from '@matter/registry/linear-gradient'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { Grain } from '@shaders/registry/grain'; +import { LinearGradient } from '@shaders/registry/linear-gradient'; import { type GrainParams, INITIAL } from './params'; diff --git a/apps/docs/src/app/components/linear-gradient/page.tsx b/apps/docs/src/app/components/linear-gradient/page.tsx index 32591ad1..9a1868cd 100644 --- a/apps/docs/src/app/components/linear-gradient/page.tsx +++ b/apps/docs/src/app/components/linear-gradient/page.tsx @@ -94,7 +94,7 @@ export default function LinearGradientPage() {

    <LinearGradient />

    -

    Animated linear gradient. The simplest, foundational Matter component.

    +

    Animated linear gradient. The simplest, foundational Shaders component.

    .',
    +  description: 'Tier 1 shader components delivered shadcn-style via shaders-cli add .',
     };
     
     export default async function ComponentsIndex() {
    @@ -15,7 +15,7 @@ export default async function ComponentsIndex() {
           

    Components

    Tier 1 — polished shader components delivered shadcn-style via{' '} - matter-cli add <name>. Each component is yours to edit after copy-in. + shaders-cli add <name>. Each component is yours to edit after copy-in. Each page below has a live demo, a props playground, and the byte-identical source the CLI copies into your project.

    diff --git a/apps/docs/src/app/components/radial-gradient/params.ts b/apps/docs/src/app/components/radial-gradient/params.ts index 51aaa680..ab8666b8 100644 --- a/apps/docs/src/app/components/radial-gradient/params.ts +++ b/apps/docs/src/app/components/radial-gradient/params.ts @@ -1,4 +1,4 @@ -import type { ColorSpace, HueInterpolation } from '@lovo/matter'; +import type { ColorSpace, HueInterpolation } from '@mattermix/shaders'; import { paletteOklch } from '../../../lib/palette'; diff --git a/apps/docs/src/app/components/radial-gradient/scene.tsx b/apps/docs/src/app/components/radial-gradient/scene.tsx index b37052dd..580a443d 100644 --- a/apps/docs/src/app/components/radial-gradient/scene.tsx +++ b/apps/docs/src/app/components/radial-gradient/scene.tsx @@ -2,8 +2,8 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { RadialGradient } from '@matter/registry/radial-gradient'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { RadialGradient } from '@shaders/registry/radial-gradient'; import { INITIAL, type Params } from './params'; diff --git a/apps/docs/src/app/components/simplex-noise/page.tsx b/apps/docs/src/app/components/simplex-noise/page.tsx index 34ffc68e..59ddbe1b 100644 --- a/apps/docs/src/app/components/simplex-noise/page.tsx +++ b/apps/docs/src/app/components/simplex-noise/page.tsx @@ -104,8 +104,8 @@ export default function SimplexNoisePage() { fontSize: '0.85rem', }} > - {`import { ShaderScene } from '@lovo/matter-react' -import { SimplexNoise } from '@/components/matter/simplex-noise' + {`import { ShaderScene } from '@mattermix/shaders-react' +import { SimplexNoise } from '@/components/shaders/simplex-noise' diff --git a/apps/docs/src/app/components/simplex-noise/params.ts b/apps/docs/src/app/components/simplex-noise/params.ts index 1729b060..eacb4f4e 100644 --- a/apps/docs/src/app/components/simplex-noise/params.ts +++ b/apps/docs/src/app/components/simplex-noise/params.ts @@ -1,4 +1,4 @@ -import type { ColorSpace, HueInterpolation } from '@lovo/matter'; +import type { ColorSpace, HueInterpolation } from '@mattermix/shaders'; import { paletteOklch } from '../../../lib/palette'; diff --git a/apps/docs/src/app/components/simplex-noise/scene.tsx b/apps/docs/src/app/components/simplex-noise/scene.tsx index 86e4c467..c3b6df79 100644 --- a/apps/docs/src/app/components/simplex-noise/scene.tsx +++ b/apps/docs/src/app/components/simplex-noise/scene.tsx @@ -2,9 +2,9 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { SimplexNoise } from '@matter/registry/simplex-noise'; -import type { ColorStop } from '@matter/registry/simplex-noise'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { SimplexNoise } from '@shaders/registry/simplex-noise'; +import type { ColorStop } from '@shaders/registry/simplex-noise'; import { INITIAL, type Params } from './params'; diff --git a/apps/docs/src/app/components/vignette/params.ts b/apps/docs/src/app/components/vignette/params.ts index c6993a69..b1d65ab3 100644 --- a/apps/docs/src/app/components/vignette/params.ts +++ b/apps/docs/src/app/components/vignette/params.ts @@ -1,4 +1,4 @@ -import type { ColorSpace, HueInterpolation } from '@lovo/matter'; +import type { ColorSpace, HueInterpolation } from '@mattermix/shaders'; export interface VignetteParams { intensity: number; diff --git a/apps/docs/src/app/components/vignette/scene.tsx b/apps/docs/src/app/components/vignette/scene.tsx index 9df2b51f..eb00dc02 100644 --- a/apps/docs/src/app/components/vignette/scene.tsx +++ b/apps/docs/src/app/components/vignette/scene.tsx @@ -2,9 +2,9 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { LinearGradient } from '@matter/registry/linear-gradient'; -import { Vignette } from '@matter/registry/vignette'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { LinearGradient } from '@shaders/registry/linear-gradient'; +import { Vignette } from '@shaders/registry/vignette'; import { INITIAL, type VignetteParams } from './params'; diff --git a/apps/docs/src/app/components/voronoi/params.ts b/apps/docs/src/app/components/voronoi/params.ts index 3a8cce6d..38783482 100644 --- a/apps/docs/src/app/components/voronoi/params.ts +++ b/apps/docs/src/app/components/voronoi/params.ts @@ -1,4 +1,4 @@ -import type { ColorSpace, HueInterpolation } from '@lovo/matter'; +import type { ColorSpace, HueInterpolation } from '@mattermix/shaders'; import { paletteOklch } from '../../../lib/palette'; diff --git a/apps/docs/src/app/components/voronoi/scene.tsx b/apps/docs/src/app/components/voronoi/scene.tsx index c71878c1..6d65da71 100644 --- a/apps/docs/src/app/components/voronoi/scene.tsx +++ b/apps/docs/src/app/components/voronoi/scene.tsx @@ -2,9 +2,9 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { Voronoi } from '@matter/registry/voronoi'; -import type { ColorStop } from '@matter/registry/voronoi'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { Voronoi } from '@shaders/registry/voronoi'; +import type { ColorStop } from '@shaders/registry/voronoi'; import { INITIAL, type Params } from './params'; diff --git a/apps/docs/src/app/components/wave-lines/params.ts b/apps/docs/src/app/components/wave-lines/params.ts index 20fb3865..d4395972 100644 --- a/apps/docs/src/app/components/wave-lines/params.ts +++ b/apps/docs/src/app/components/wave-lines/params.ts @@ -1,4 +1,4 @@ -import type { ColorSpace } from '@lovo/matter'; +import type { ColorSpace } from '@mattermix/shaders'; import { paletteOklch } from '../../../lib/palette'; diff --git a/apps/docs/src/app/components/wave-lines/scene.tsx b/apps/docs/src/app/components/wave-lines/scene.tsx index aee98982..c495a707 100644 --- a/apps/docs/src/app/components/wave-lines/scene.tsx +++ b/apps/docs/src/app/components/wave-lines/scene.tsx @@ -2,9 +2,9 @@ import type { ReactNode } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; -import { WaveLines } from '@matter/registry/wave-lines'; -import type { WaveLine } from '@matter/registry/wave-lines'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { WaveLines } from '@shaders/registry/wave-lines'; +import type { WaveLine } from '@shaders/registry/wave-lines'; import { INITIAL, type Params } from './params'; diff --git a/apps/docs/src/app/dev/color-space-probe/ProbeGrid.tsx b/apps/docs/src/app/dev/color-space-probe/ProbeGrid.tsx index 0ed4b181..ea6324ee 100644 --- a/apps/docs/src/app/dev/color-space-probe/ProbeGrid.tsx +++ b/apps/docs/src/app/dev/color-space-probe/ProbeGrid.tsx @@ -2,9 +2,9 @@ import { useEffect } from 'react'; -import type { ColorSpace } from '@lovo/matter'; -import { mixColor } from '@lovo/matter'; -import { ShaderScene, useShaderContext } from '@lovo/matter-react'; +import type { ColorSpace } from '@mattermix/shaders'; +import { mixColor } from '@mattermix/shaders'; +import { ShaderScene, useShaderContext } from '@mattermix/shaders-react'; import { mix, step, uv, vec3, vec4 } from 'three/tsl'; import { addPlaneMesh } from '@/lib/meshUtils'; diff --git a/apps/docs/src/app/dev/dot-field-stack-probe/probe-scene.tsx b/apps/docs/src/app/dev/dot-field-stack-probe/probe-scene.tsx index f41016f3..7004b4ae 100644 --- a/apps/docs/src/app/dev/dot-field-stack-probe/probe-scene.tsx +++ b/apps/docs/src/app/dev/dot-field-stack-probe/probe-scene.tsx @@ -6,9 +6,9 @@ // the canvas shows the page background (black) there instead. The paired // spec (visual/dot-field-stack.spec.ts) asserts pixel-color fractions, so // there is no screenshot baseline to regenerate. -import { ShaderScene } from '@lovo/matter-react'; -import { DotField } from '@matter/registry/dot-field'; -import { LinearGradient } from '@matter/registry/linear-gradient'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { DotField } from '@shaders/registry/dot-field'; +import { LinearGradient } from '@shaders/registry/linear-gradient'; import { VisualTestPause } from '@/lib/visualTestHooks'; diff --git a/apps/docs/src/app/dev/gamut-probe/ProbeGrid.tsx b/apps/docs/src/app/dev/gamut-probe/ProbeGrid.tsx index a14ad01d..7f0604c8 100644 --- a/apps/docs/src/app/dev/gamut-probe/ProbeGrid.tsx +++ b/apps/docs/src/app/dev/gamut-probe/ProbeGrid.tsx @@ -2,8 +2,8 @@ import { useEffect } from 'react'; -import { ShaderScene, useShaderContext } from '@lovo/matter-react'; -import { oklchToLinearSrgb } from '@lovo/matter/color'; +import { ShaderScene, useShaderContext } from '@mattermix/shaders-react'; +import { oklchToLinearSrgb } from '@mattermix/shaders/color'; import { vec3, vec4 } from 'three/tsl'; import { addPlaneMesh } from '@/lib/meshUtils'; diff --git a/apps/docs/src/app/dev/hsl-gamut-probe/ProbeGrid.tsx b/apps/docs/src/app/dev/hsl-gamut-probe/ProbeGrid.tsx index 70ab2431..48c9ef5b 100644 --- a/apps/docs/src/app/dev/hsl-gamut-probe/ProbeGrid.tsx +++ b/apps/docs/src/app/dev/hsl-gamut-probe/ProbeGrid.tsx @@ -1,7 +1,7 @@ 'use client'; -import { ShaderScene } from '@lovo/matter-react'; -import { LinearGradient } from '@matter/registry/linear-gradient'; +import { ShaderScene } from '@mattermix/shaders-react'; +import { LinearGradient } from '@shaders/registry/linear-gradient'; import { VisualTestPause } from '@/lib/visualTestHooks'; diff --git a/apps/docs/src/app/dev/hue-arc-probe/ProbeGrid.tsx b/apps/docs/src/app/dev/hue-arc-probe/ProbeGrid.tsx index 3bd18aed..60255f3b 100644 --- a/apps/docs/src/app/dev/hue-arc-probe/ProbeGrid.tsx +++ b/apps/docs/src/app/dev/hue-arc-probe/ProbeGrid.tsx @@ -2,10 +2,10 @@ import { useEffect } from 'react'; -import type { HueInterpolation } from '@lovo/matter'; -import { colorRamp } from '@lovo/matter'; -import { ShaderScene, useShaderContext } from '@lovo/matter-react'; -import { srgbChannelToLinear } from '@lovo/matter/color'; +import type { HueInterpolation } from '@mattermix/shaders'; +import { colorRamp } from '@mattermix/shaders'; +import { ShaderScene, useShaderContext } from '@mattermix/shaders-react'; +import { srgbChannelToLinear } from '@mattermix/shaders/color'; import type { ShaderNodeObject } from 'three/tsl'; import { mix, step, uv, vec3, vec4 } from 'three/tsl'; import type { Node } from 'three/webgpu'; diff --git a/apps/docs/src/app/dev/voronoi-probe/probe-scene.tsx b/apps/docs/src/app/dev/voronoi-probe/probe-scene.tsx index 9dbfd1b1..f2498e25 100644 --- a/apps/docs/src/app/dev/voronoi-probe/probe-scene.tsx +++ b/apps/docs/src/app/dev/voronoi-probe/probe-scene.tsx @@ -7,8 +7,8 @@ // bisector distance works. Swap the colorNode lines below to isolate fields. import { useEffect } from 'react'; -import { voronoiCells } from '@lovo/matter'; -import { ShaderScene, useShaderContext } from '@lovo/matter-react'; +import { voronoiCells } from '@mattermix/shaders'; +import { ShaderScene, useShaderContext } from '@mattermix/shaders-react'; import { smoothstep, uniform, uv, vec3 } from 'three/tsl'; import { Mesh, MeshBasicNodeMaterial, PlaneGeometry } from 'three/webgpu'; diff --git a/apps/docs/src/app/layout.tsx b/apps/docs/src/app/layout.tsx index 19e4dd64..4a6e69a9 100644 --- a/apps/docs/src/app/layout.tsx +++ b/apps/docs/src/app/layout.tsx @@ -8,7 +8,7 @@ import './globals.css'; import { Providers } from './providers'; export const metadata = { - title: 'Matter — React shader components', + title: 'Shaders — React shader components', description: 'WebGPU + TSL shader components for React.', }; @@ -32,7 +32,7 @@ export default function RootLayout({ children }: { children: ReactNode }) { }} > - Matter + Shaders
    diff --git a/apps/docs/src/app/page.tsx b/apps/docs/src/app/page.tsx index 58fd8514..3b71e2bf 100644 --- a/apps/docs/src/app/page.tsx +++ b/apps/docs/src/app/page.tsx @@ -13,7 +13,7 @@ const COMPONENTS = [ export default function Home() { return (
    -

    Matter

    +

    Shaders

    React shader components powered by WebGPU and Three.js TSL.

    Status: pre-release, M3 complete — six v1 components live.

    Components

    diff --git a/apps/docs/src/app/palette/LightnessGrid.tsx b/apps/docs/src/app/palette/LightnessGrid.tsx index 8982f180..87e26a5e 100644 --- a/apps/docs/src/app/palette/LightnessGrid.tsx +++ b/apps/docs/src/app/palette/LightnessGrid.tsx @@ -7,7 +7,7 @@ // rather than snapped to the neutral ladder, which is now a formality for the // accents — every one of them sits on a tick — but still matters for limeScale, // whose ramp has a ladder of its own. -import { linearSrgbToOklch, parseColorString } from '@lovo/matter/color'; +import { linearSrgbToOklch, parseColorString } from '@mattermix/shaders/color'; import { palette, paletteOklch } from '@/lib/palette'; diff --git a/apps/docs/src/app/palette/PaletteView.tsx b/apps/docs/src/app/palette/PaletteView.tsx index 71f98bd6..b0612e64 100644 --- a/apps/docs/src/app/palette/PaletteView.tsx +++ b/apps/docs/src/app/palette/PaletteView.tsx @@ -281,7 +281,7 @@ export function PaletteView() { }} >
    -

    Matter palette

    +

    Shaders palette

    Gray, moss, and all twelve accents share one twelve-step lightness ladder; the brand lime scale runs a ladder of its own. diff --git a/apps/docs/src/app/palette/page.tsx b/apps/docs/src/app/palette/page.tsx index 1ddd4e59..8ceeb580 100644 --- a/apps/docs/src/app/palette/page.tsx +++ b/apps/docs/src/app/palette/page.tsx @@ -1,8 +1,8 @@ import { PaletteView } from './PaletteView'; export const metadata = { - title: 'Palette — Matter', - description: 'The brand-aligned color palette used by every Matter example.', + title: 'Palette — Shaders', + description: 'The brand-aligned color palette used by every Shaders example.', }; export default function PalettePage() { diff --git a/apps/docs/src/app/primitives/page.tsx b/apps/docs/src/app/primitives/page.tsx index b72af3e6..a6cbe56d 100644 --- a/apps/docs/src/app/primitives/page.tsx +++ b/apps/docs/src/app/primitives/page.tsx @@ -5,7 +5,7 @@ import { PRIMITIVES } from '@/data/primitives'; export const metadata = { title: 'Primitives', description: - 'Tier 2 — pure TSL functions exported from @lovo/matter. Compose them into your own shaders.', + 'Tier 2 — pure TSL functions exported from @mattermix/shaders. Compose them into your own shaders.', }; export default function PrimitivesIndex() { @@ -13,8 +13,8 @@ export default function PrimitivesIndex() {

    Primitives

    - Tier 2 — pure TSL functions exported from @lovo/matter. Use them inside your - own shaders or compose them into Tier 1 components. + Tier 2 — pure TSL functions exported from @mattermix/shaders. Use them inside + your own shaders or compose them into Tier 1 components.

      {PRIMITIVES.map((p) => ( diff --git a/apps/docs/src/app/recipes/_builds.ts b/apps/docs/src/app/recipes/_builds.ts index 93b2ddb1..09922193 100644 --- a/apps/docs/src/app/recipes/_builds.ts +++ b/apps/docs/src/app/recipes/_builds.ts @@ -5,7 +5,7 @@ import { fractalNoise, quantize, voronoi, -} from '@lovo/matter'; +} from '@mattermix/shaders'; import type UniformNode from 'three/src/nodes/core/UniformNode.js'; import { length, max, sin, smoothstep, uv, vec2, vec3, vec4 } from 'three/tsl'; import type { ShaderNodeObject } from 'three/tsl'; diff --git a/apps/docs/src/components/DemoPoster.tsx b/apps/docs/src/components/DemoPoster.tsx index 5f343714..031eacb6 100644 --- a/apps/docs/src/components/DemoPoster.tsx +++ b/apps/docs/src/components/DemoPoster.tsx @@ -3,7 +3,7 @@ import Image from 'next/image'; import type { ReactNode } from 'react'; -import { ShaderPoster } from '@lovo/matter-react/poster'; +import { ShaderPoster } from '@mattermix/shaders-react/poster'; export interface DemoPosterProps { src: string; diff --git a/apps/docs/src/components/PrimitiveScene.tsx b/apps/docs/src/components/PrimitiveScene.tsx index a8b834e6..b98bbfc8 100644 --- a/apps/docs/src/components/PrimitiveScene.tsx +++ b/apps/docs/src/components/PrimitiveScene.tsx @@ -14,8 +14,8 @@ import { signedDistanceFieldCircle, simplexNoise, voronoi, -} from '@lovo/matter'; -import { ShaderScene, useShaderContext } from '@lovo/matter-react'; +} from '@mattermix/shaders'; +import { ShaderScene, useShaderContext } from '@mattermix/shaders-react'; import type { ShaderNodeObject } from 'three/tsl'; import { mix, sin, smoothstep, uniform, uv, vec2, vec3, vec4 } from 'three/tsl'; import { Vector2 } from 'three/webgpu'; diff --git a/apps/docs/src/components/RecipeScene.tsx b/apps/docs/src/components/RecipeScene.tsx index 77c4ccca..01810601 100644 --- a/apps/docs/src/components/RecipeScene.tsx +++ b/apps/docs/src/components/RecipeScene.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo } from 'react'; -import { ShaderScene, useCursor, useShaderContext } from '@lovo/matter-react'; +import { ShaderScene, useCursor, useShaderContext } from '@mattermix/shaders-react'; import { uniform } from 'three/tsl'; import { Vector2 } from 'three/webgpu'; diff --git a/apps/docs/src/components/controls/ColorInput.tsx b/apps/docs/src/components/controls/ColorInput.tsx index 5ffa3c51..16dcff1f 100644 --- a/apps/docs/src/components/controls/ColorInput.tsx +++ b/apps/docs/src/components/controls/ColorInput.tsx @@ -8,7 +8,7 @@ * This used to load the popover through `next/dynamic` with `ssr: false`, * because reaching the color math meant importing three/webgpu, which reads * `self` at module load. Both halves of that now come from three-free subpaths - * (`@lovo/matter/color` and `@lovo/matter-react/gamut`), so it is a plain + * (`@mattermix/shaders/color` and `@mattermix/shaders-react/gamut`), so it is a plain * import. */ import { Popover } from '@base-ui/react/popover'; diff --git a/apps/docs/src/components/controls/ColorPopoverContents.tsx b/apps/docs/src/components/controls/ColorPopoverContents.tsx index 00c3b685..d0f23485 100644 --- a/apps/docs/src/components/controls/ColorPopoverContents.tsx +++ b/apps/docs/src/components/controls/ColorPopoverContents.tsx @@ -8,8 +8,8 @@ */ import { type ChangeEvent, useEffect, useRef, useState } from 'react'; -import { useDisplayGamut } from '@lovo/matter-react/gamut'; -import { oklchInGamut, oklchToGamut } from '@lovo/matter/color'; +import { useDisplayGamut } from '@mattermix/shaders-react/gamut'; +import { oklchInGamut, oklchToGamut } from '@mattermix/shaders/color'; import { ChannelSlider } from './color/ChannelSlider'; import { formatOklch, type OklchColor, parseToOklch } from './color/oklch'; diff --git a/apps/docs/src/components/controls/color/ChannelSlider.tsx b/apps/docs/src/components/controls/color/ChannelSlider.tsx index 36a6be74..e310dcbe 100644 --- a/apps/docs/src/components/controls/color/ChannelSlider.tsx +++ b/apps/docs/src/components/controls/color/ChannelSlider.tsx @@ -24,7 +24,7 @@ import { linearSrgbToLinearDisplayP3, oklchInGamut, oklchToLinearSrgb, -} from '@lovo/matter/color'; +} from '@mattermix/shaders/color'; import { MAX_CHROMA, type OklchColor } from './oklch'; diff --git a/apps/docs/src/components/controls/color/oklch.ts b/apps/docs/src/components/controls/color/oklch.ts index be2abf36..01162e3a 100644 --- a/apps/docs/src/components/controls/color/oklch.ts +++ b/apps/docs/src/components/controls/color/oklch.ts @@ -5,7 +5,7 @@ * oklab(), and throws on anything else, so emitting rgb() or hsl() would crash * the shader. */ -import { linearSrgbToOklch, parseColorString } from '@lovo/matter/color'; +import { linearSrgbToOklch, parseColorString } from '@mattermix/shaders/color'; /** * OKLCH in one line: lightness is how bright (0 black, 1 white), chroma is how diff --git a/apps/docs/src/data/primitives.ts b/apps/docs/src/data/primitives.ts index 19d8a6a1..238d085c 100644 --- a/apps/docs/src/data/primitives.ts +++ b/apps/docs/src/data/primitives.ts @@ -165,7 +165,7 @@ interface CursorRippleOptions { signature: `const elapsedTime: TSLNode // Equals three/tsl's built-in time * reducedMotionScale. Honors // prefers-reduced-motion and any setReducedMotionPolicy override. -// Import from '@lovo/matter'. For raw uncapped time, import from +// Import from '@mattermix/shaders'. For raw uncapped time, import from // 'three/tsl' directly.`, usedBy: ['linear-gradient', 'simplex-noise', 'wave-lines', 'mesh-gradient', 'aurora'], controls: [], diff --git a/apps/docs/src/data/recipes.ts b/apps/docs/src/data/recipes.ts index c6d0c35e..edfd0f29 100644 --- a/apps/docs/src/data/recipes.ts +++ b/apps/docs/src/data/recipes.ts @@ -21,7 +21,7 @@ export const RECIPES: readonly RecipeEntry[] = [ 'Warm/cool vertical bands that scroll horizontally. Simplest combination of sin, time, and colorRamp.', primitivesUsed: ['time', 'color-ramp'], source: `import { uv, vec3, vec4, sin } from 'three/tsl' -import { elapsedTime, colorRamp } from '@lovo/matter' +import { elapsedTime, colorRamp } from '@mattermix/shaders' const stripe = sin(uv().x.mul(20).add(elapsedTime.mul(2))) const normalizedStripe = stripe.mul(0.5).add(0.5).clamp(0, 1) @@ -87,7 +87,7 @@ material.colorNode = vec4(glow, glow.mul(0.7), glow.mul(1.5), 1)`, description: 'FBM-driven color swirl. The canonical "shader-y" look from one primitive.', primitivesUsed: ['fbm', 'time', 'color-ramp'], source: `import { uv, vec2, vec3, vec4 } from 'three/tsl' -import { elapsedTime, fractalNoise, colorRamp } from '@lovo/matter' +import { elapsedTime, fractalNoise, colorRamp } from '@mattermix/shaders' const scrolledTime = elapsedTime.mul(0.3) const samplePosition = uv().mul(2).add(vec2(scrolledTime, scrolledTime)) @@ -118,7 +118,7 @@ material.colorNode = vec4(colorRamp(noiseValue, stops), 1)`, 'Voronoi cells flattened into 4 discrete sepia bands — a mosaic / stained-glass / low-poly aesthetic. Use as a hand-crafted-feeling background where each region renders one solid color rather than a gradient.', primitivesUsed: ['voronoi', 'quantize', 'color-ramp'], source: `import { uv, vec4 } from 'three/tsl' -import { voronoi, quantize } from '@lovo/matter' +import { voronoi, quantize } from '@mattermix/shaders' const cells = voronoi(uv().mul(8)) const tiered = quantize(cells, 4) diff --git a/apps/docs/src/lib/VisualTestPause.tsx b/apps/docs/src/lib/VisualTestPause.tsx index 67d89fae..95c6e4bc 100644 --- a/apps/docs/src/lib/VisualTestPause.tsx +++ b/apps/docs/src/lib/VisualTestPause.tsx @@ -2,9 +2,9 @@ import { useEffect } from 'react'; -import { resetRendererClock, setReducedMotionPolicy } from '@lovo/matter'; -import type { ReducedMotionPolicy, SchedulerTick } from '@lovo/matter'; -import { useShaderContext } from '@lovo/matter-react'; +import { resetRendererClock, setReducedMotionPolicy } from '@mattermix/shaders'; +import type { ReducedMotionPolicy, SchedulerTick } from '@mattermix/shaders'; +import { useShaderContext } from '@mattermix/shaders-react'; const TARGET_FRAME = 2; @@ -17,7 +17,7 @@ const isReducedMotionPolicy = (policyName: string): policyName is ReducedMotionP declare global { interface Window { - __matterTestReady?: boolean; + __shadersTestReady?: boolean; } } @@ -58,7 +58,7 @@ function useVisualTestPause(): void { if (frame > TARGET_FRAME) { ctx.scheduler.remove(client); ctx.scheduler.pause(); - window.__matterTestReady = true; + window.__shadersTestReady = true; } }; diff --git a/apps/docs/src/lib/meshUtils.ts b/apps/docs/src/lib/meshUtils.ts index fd61bcb7..d84dc245 100644 --- a/apps/docs/src/lib/meshUtils.ts +++ b/apps/docs/src/lib/meshUtils.ts @@ -1,4 +1,4 @@ -import type { ShaderContextValue } from '@lovo/matter-react'; +import type { ShaderContextValue } from '@mattermix/shaders-react'; import type { ShaderNodeObject } from 'three/tsl'; import { Mesh, MeshBasicNodeMaterial, PlaneGeometry } from 'three/webgpu'; import type { Node } from 'three/webgpu'; diff --git a/apps/docs/src/lib/palette.test.ts b/apps/docs/src/lib/palette.test.ts index 97e3262a..ebb1939a 100644 --- a/apps/docs/src/lib/palette.test.ts +++ b/apps/docs/src/lib/palette.test.ts @@ -1,8 +1,8 @@ // Guards the brand palette against the two mistakes that produced the 2026-07-26 // correction: colors no display can show, and neutral scales drifting off their -// intended tint. Reads L/C/h back through @lovo/matter's own conversions rather +// intended tint. Reads L/C/h back through @mattermix/shaders's own conversions rather // than parsing strings, so the assertions use the same math the renderer does. -import { linearSrgbToOklch, oklchInGamut, parseColorString } from '@lovo/matter/color'; +import { linearSrgbToOklch, oklchInGamut, parseColorString } from '@mattermix/shaders/color'; import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -409,7 +409,7 @@ describe('brand palette', () => { // `keeps globals.css hex literals in sync`, aimed at that second surface. const registryRoot = join( dirname(fileURLToPath(import.meta.url)), - '../../node_modules/@matter/registry', + '../../node_modules/@shaders/registry', ); const registryFiles = [ 'aurora/aurora.tsx', diff --git a/apps/docs/src/lib/palette.ts b/apps/docs/src/lib/palette.ts index ff8b4ad7..1c579906 100644 --- a/apps/docs/src/lib/palette.ts +++ b/apps/docs/src/lib/palette.ts @@ -23,7 +23,7 @@ * it. Before the 2026-07-26 pass, the accent then called `sky.dark` asked for * 0.145 chroma where P3 allows 0.109, and clipping landed it 14.7 degrees off * its stated hue. So: keep every oklch value inside P3 (`oklchInGamut` from - * `@lovo/matter` is the check), and derive the hex from it with + * `@mattermix/shaders` is the check), and derive the hex from it with * `oklchToGamut(..., 'srgb')` rather than by clipping. */ import { accents } from './accents'; diff --git a/apps/docs/vitest.config.ts b/apps/docs/vitest.config.ts index 5e48f563..1e7a51ff 100644 --- a/apps/docs/vitest.config.ts +++ b/apps/docs/vitest.config.ts @@ -1,17 +1,17 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ - // Mirrors packages/matter/vitest.config.ts: Vite 8 OXC cannot resolve + // Mirrors packages/shaders/vitest.config.ts: Vite 8 OXC cannot resolve // ${configDir} in our shared tsconfig, so the essential option is inline. // @ts-expect-error -- oxc is not in UserConfig types yet; this is the documented workaround oxc: { tsconfig: { compilerOptions: { verbatimModuleSyntax: true } } }, test: { // No DOM environment on purpose. These tests are pure color math, and // running them in plain Node means a future test that imports root - // @lovo/matter fails here — three/webgpu reads `self` at module load — the + // @mattermix/shaders fails here — three/webgpu reads `self` at module load — the // same way it would fail in a server render. Import from - // @lovo/matter/color instead. - name: '@matter/docs', + // @mattermix/shaders/color instead. + name: '@shaders/docs', include: ['src/**/*.test.{ts,tsx}'], passWithNoTests: true, }, diff --git a/apps/editor/next.config.ts b/apps/editor/next.config.ts index 53da2a24..4e5b7de8 100644 --- a/apps/editor/next.config.ts +++ b/apps/editor/next.config.ts @@ -38,7 +38,7 @@ const nextConfig: NextConfig = { // `next build` lint too just runs the same rules a second time. Turning it off // here is not a way of skipping the check — it is saying where the check lives. eslint: { ignoreDuringBuilds: true }, - transpilePackages: ['@lovo/matter', '@lovo/matter-react'], + transpilePackages: ['@mattermix/shaders', '@mattermix/shaders-react'], webpack(config: WebpackConfig): WebpackConfig { config.resolve = config.resolve ?? {}; const webgpuBundle = resolve(threeDir, 'build/three.webgpu.js'); diff --git a/apps/editor/package.json b/apps/editor/package.json index cfbd2af9..26ad0cf8 100644 --- a/apps/editor/package.json +++ b/apps/editor/package.json @@ -1,5 +1,5 @@ { - "name": "@matter/editor", + "name": "@shaders/editor", "version": "0.0.0", "private": true, "type": "module", @@ -14,8 +14,8 @@ }, "dependencies": { "@base-ui/react": "^1.6.0", - "@lovo/matter": "workspace:*", - "@lovo/matter-react": "workspace:*", + "@mattermix/shaders": "workspace:*", + "@mattermix/shaders-react": "workspace:*", "@xyflow/react": "^12.11.2", "next": "^15.5.18", "react": "^19.0.0", @@ -23,7 +23,7 @@ "three": "^0.170.0" }, "devDependencies": { - "@matter/tsconfig": "workspace:*", + "@shaders/tsconfig": "workspace:*", "@types/node": "^22.7.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", diff --git a/apps/editor/src/app/layout.tsx b/apps/editor/src/app/layout.tsx index ad614763..6d9e3d3a 100644 --- a/apps/editor/src/app/layout.tsx +++ b/apps/editor/src/app/layout.tsx @@ -7,7 +7,7 @@ import type { ReactNode } from 'react'; // styles. import '@/controls/controls.css'; -export const metadata = { title: 'Matter Editor' }; +export const metadata = { title: 'Shaders Editor' }; export default function RootLayout({ children }: { children: ReactNode }) { return ( diff --git a/apps/editor/src/app/parity/generated.gen.tsx b/apps/editor/src/app/parity/generated.gen.tsx index d1451806..d401b6e2 100644 --- a/apps/editor/src/app/parity/generated.gen.tsx +++ b/apps/editor/src/app/parity/generated.gen.tsx @@ -1,14 +1,18 @@ 'use client'; -// Generated by the Matter editor. A bare Matter component: mount it inside a +// Generated by the Shaders editor. A bare Shaders component: mount it inside a // ShaderScene. Fields are functions of the sample position — warping is // calling a field at a shifted point — and every editor dial arrived below // as a prop with the editor's value as its default. import { useEffect } from 'react'; -import { colorRamp, displace, simplexNoise } from '@lovo/matter'; -import { useAnimatableSpeed, useAnimatableUniform, useShaderContext } from '@lovo/matter-react'; -import { parseColorString } from '@lovo/matter/color'; +import { colorRamp, displace, simplexNoise } from '@mattermix/shaders'; +import { + useAnimatableSpeed, + useAnimatableUniform, + useShaderContext, +} from '@mattermix/shaders-react'; +import { parseColorString } from '@mattermix/shaders/color'; import { clamp, cos, diff --git a/apps/editor/src/app/parity/generated/scene.tsx b/apps/editor/src/app/parity/generated/scene.tsx index d74bf794..4611286b 100644 --- a/apps/editor/src/app/parity/generated/scene.tsx +++ b/apps/editor/src/app/parity/generated/scene.tsx @@ -6,7 +6,7 @@ // full-viewport ShaderScene. The visual spec drives this and /parity/runtime // to the same screenshot baseline; parity.test.ts keeps the file honest // against the emitter. -import { ShaderScene } from '@lovo/matter-react'; +import { ShaderScene } from '@mattermix/shaders-react'; import VisualTestPause from '@/lib/VisualTestPause'; diff --git a/apps/editor/src/app/parity/runtime/scene.tsx b/apps/editor/src/app/parity/runtime/scene.tsx index 1155abc3..945c1428 100644 --- a/apps/editor/src/app/parity/runtime/scene.tsx +++ b/apps/editor/src/app/parity/runtime/scene.tsx @@ -8,7 +8,7 @@ // see page.dev.tsx beside this file. import { useMemo } from 'react'; -import { ShaderScene } from '@lovo/matter-react'; +import { ShaderScene } from '@mattermix/shaders-react'; import { CompiledMesh } from '@/editor/canvas/OutputPreview'; import { structuralKeyOf } from '@/editor/graph/graph'; diff --git a/apps/editor/src/controls/ColorPopoverContents.tsx b/apps/editor/src/controls/ColorPopoverContents.tsx index 85d191c1..68a1c5b9 100644 --- a/apps/editor/src/controls/ColorPopoverContents.tsx +++ b/apps/editor/src/controls/ColorPopoverContents.tsx @@ -20,8 +20,8 @@ */ import { type ChangeEvent, useEffect, useRef, useState } from 'react'; -import { useDisplayGamut } from '@lovo/matter-react/gamut'; -import { oklchInGamut, oklchToGamut } from '@lovo/matter/color'; +import { useDisplayGamut } from '@mattermix/shaders-react/gamut'; +import { oklchInGamut, oklchToGamut } from '@mattermix/shaders/color'; import { ChannelSlider } from './color/ChannelSlider'; import { formatOklch, type OklchColor, parseToOklch } from './color/oklch'; diff --git a/apps/editor/src/controls/color/ChannelSlider.tsx b/apps/editor/src/controls/color/ChannelSlider.tsx index f30d46d3..b1f9e00f 100644 --- a/apps/editor/src/controls/color/ChannelSlider.tsx +++ b/apps/editor/src/controls/color/ChannelSlider.tsx @@ -28,7 +28,7 @@ import { linearSrgbToLinearDisplayP3, oklchInGamut, oklchToLinearSrgb, -} from '@lovo/matter/color'; +} from '@mattermix/shaders/color'; import { MAX_CHROMA, type OklchColor } from './oklch'; diff --git a/apps/editor/src/controls/color/oklch.ts b/apps/editor/src/controls/color/oklch.ts index fa0432d4..8f6ee43e 100644 --- a/apps/editor/src/controls/color/oklch.ts +++ b/apps/editor/src/controls/color/oklch.ts @@ -10,7 +10,7 @@ * wiring around it was left behind (ColorInput/ColorPopoverContents here take * plain value/onChange/onCommit props instead). */ -import { linearSrgbToOklch, parseColorString } from '@lovo/matter/color'; +import { linearSrgbToOklch, parseColorString } from '@mattermix/shaders/color'; /** * OKLCH in one line: lightness is how bright (0 black, 1 white), chroma is how diff --git a/apps/editor/src/editor/canvas/CardParams.tsx b/apps/editor/src/editor/canvas/CardParams.tsx index 6c9d0ab8..5ea51494 100644 --- a/apps/editor/src/editor/canvas/CardParams.tsx +++ b/apps/editor/src/editor/canvas/CardParams.tsx @@ -5,7 +5,7 @@ // one place that writes node data) and passes it down here. Split out of // CardNode.tsx (MAT-94 Task 11.5) so the card shell stays under the // 300-line bar. -import { parseColorString } from '@lovo/matter/color'; +import { parseColorString } from '@mattermix/shaders/color'; import { ColorInput } from '@/controls/ColorInput'; import { colorParamOf, rampStopsOf } from '@/editor/graph/graph'; diff --git a/apps/editor/src/editor/canvas/OutputPreview.tsx b/apps/editor/src/editor/canvas/OutputPreview.tsx index 788390de..930263a5 100644 --- a/apps/editor/src/editor/canvas/OutputPreview.tsx +++ b/apps/editor/src/editor/canvas/OutputPreview.tsx @@ -6,9 +6,9 @@ // graph's STRUCTURE changes — drags and selections never recompile. import { useEffect } from 'react'; -import { getReducedMotionTimeScale } from '@lovo/matter'; -import type { SchedulerTick } from '@lovo/matter'; -import { ShaderScene, useShaderContext } from '@lovo/matter-react'; +import { getReducedMotionTimeScale } from '@mattermix/shaders'; +import type { SchedulerTick } from '@mattermix/shaders'; +import { ShaderScene, useShaderContext } from '@mattermix/shaders-react'; import { Mesh, MeshBasicNodeMaterial, PlaneGeometry } from 'three/webgpu'; import { compileOutputColor } from '@/editor/graph/compile'; diff --git a/apps/editor/src/editor/graph/compile.ts b/apps/editor/src/editor/graph/compile.ts index 51429cf9..9734d004 100644 --- a/apps/editor/src/editor/graph/compile.ts +++ b/apps/editor/src/editor/graph/compile.ts @@ -1,5 +1,5 @@ // The graph-to-TSL compiler: walks the card graph from an Output node -// backward and assembles a TSL color expression from Matter's Tier 2 +// backward and assembles a TSL color expression from Shaders' Tier 2 // primitives. The core representation: a compiled field is a FUNCTION of the // sample position, `(p) => value`, not a value — that's what lets Warp work, // because warping IS calling the upstream field at a shifted position. Slider @@ -17,8 +17,8 @@ import { mixColor, simplexNoise, voronoiCells, -} from '@lovo/matter'; -import { parseColorString } from '@lovo/matter/color'; +} from '@mattermix/shaders'; +import { parseColorString } from '@mattermix/shaders/color'; import { add, clamp, diff --git a/apps/editor/src/editor/graph/emit.test.ts b/apps/editor/src/editor/graph/emit.test.ts index 85387b48..4b9397f4 100644 --- a/apps/editor/src/editor/graph/emit.test.ts +++ b/apps/editor/src/editor/graph/emit.test.ts @@ -142,7 +142,7 @@ describe('emitComponentSource on the starter graph', () => { it('animates speed through useAnimatableSpeed, outside the effect deps', () => { // The generated component is a fixed graph, so hooks are callable — one // per speed dial, placed before the effect. - expect(source).toContain("from '@lovo/matter-react'"); + expect(source).toContain("from '@mattermix/shaders-react'"); expect(source).toContain('const noiseSpeedPhase = useAnimatableSpeed(noiseSpeed);'); // The phase uniform absorbs speed changes, so the speed prop must NOT // rebuild the material: it stays out of the deps array. @@ -163,7 +163,7 @@ describe('emitComponentSource on the starter graph', () => { expect(source).toContain("{ position: 0.5, color: vec3(...parseColorString('#7C3AED')) }"); expect(source).toContain("{ position: 1, color: vec3(...parseColorString('#F472B6')) }"); expect(source).not.toContain('stops?:'); - expect(source).toContain("from '@lovo/matter/color'"); + expect(source).toContain("from '@mattermix/shaders/color'"); }); it('routes the ramp color into the material', () => { @@ -172,7 +172,7 @@ describe('emitComponentSource on the starter graph', () => { }); it('says it was generated by the editor', () => { - expect(source).toContain('Generated by the Matter editor'); + expect(source).toContain('Generated by the Shaders editor'); }); }); diff --git a/apps/editor/src/editor/graph/emit.ts b/apps/editor/src/editor/graph/emit.ts index 87186518..164d5dac 100644 --- a/apps/editor/src/editor/graph/emit.ts +++ b/apps/editor/src/editor/graph/emit.ts @@ -54,8 +54,8 @@ class Emission { hookLines: string[] = []; props: PropLine[] = []; /** Named imports actually used, per module, so the file imports stay clean. */ - matterImports = new Set(); - matterReactImports = new Set(['useShaderContext']); + shadersImports = new Set(); + shadersReactImports = new Set(['useShaderContext']); tslImports = new Set(); usesParseColor = false; usesColorSpaces = false; @@ -105,7 +105,7 @@ function grainBlendOf(node: GraphNode): string { /** * Emits a complete, drop-in component file for the subgraph feeding - * `outputId`. The result is a bare Matter component (mount inside a + * `outputId`. The result is a bare Shaders component (mount inside a * ShaderScene); unwired inputs fall back exactly like the runtime compiler, * so the generated file renders the same image as the Output card. */ @@ -224,7 +224,7 @@ export function emitComponentSource( const propName = claimDialProp(node, baseName, 'speed'); - emission.matterReactImports.add('useAnimatableSpeed'); + emission.shadersReactImports.add('useAnimatableSpeed'); emission.hookLines.push(`const ${propName}Phase = useAnimatableSpeed(${propName});`); dialNames.set(dialKey, `${propName}Phase`); speedPropNames.set(node.id, propName); @@ -251,7 +251,7 @@ export function emitComponentSource( const gateName = emission.claim(`${propName}Uniform`); - emission.matterReactImports.add('useAnimatableUniform'); + emission.shadersReactImports.add('useAnimatableUniform'); emission.hookLines.push(`const ${gateName} = useAnimatableUniform(${propName});`); speedGateNames.set(node.id, gateName); @@ -389,7 +389,7 @@ export function emitComponentSource( const balance = emitDial(node, base, 'balance'); const phase = emitSpeedDial(node, base); - emission.matterImports.add('simplexNoise'); + emission.shadersImports.add('simplexNoise'); for (const used of ['clamp', 'vec3']) emission.tslImports.add(used); emission.helperLines.push( `// 3D simplex with the animation phase on z: the pattern morphs in`, @@ -424,7 +424,7 @@ export function emitComponentSource( const style = fractalStyleOf(node); const { stretch, lift } = FRACTAL_STYLE_REMAP[style]; - emission.matterImports.add('fractalNoise'); + emission.shadersImports.add('fractalNoise'); for (const used of ['clamp', 'float', 'mix', 'vec3']) emission.tslImports.add(used); emission.helperLines.push( `// The detail dial maps onto fBm gain — the per-octave amplitude`, @@ -467,7 +467,7 @@ export function emitComponentSource( const drift = emitDial(node, base, 'drift'); const phase = emitSpeedDial(node, base); - emission.matterImports.add('voronoiCells'); + emission.shadersImports.add('voronoiCells'); for (const used of ['clamp', 'mix']) emission.tslImports.add(used); emission.helperLines.push( `// Two fields from one cell walk, blended by shading: edgeDistance`, @@ -502,7 +502,7 @@ export function emitComponentSource( const centerY = emitDial(node, base, 'center.y'); const phase = emitSpeedDial(node, base); - emission.matterImports.add('metaballs'); + emission.shadersImports.add('metaballs'); for (const used of ['float', 'fwidth', 'smoothstep', 'vec2']) emission.tslImports.add(used); emission.helperLines.push( `// metaballs wants centered pattern space — subtracting center puts`, @@ -561,7 +561,7 @@ export function emitComponentSource( const name = `${base}Field`; const amount = emitDial(node, base, 'amount'); - emission.matterImports.add('displace'); + emission.shadersImports.add('displace'); emission.tslImports.add('vec2'); emission.helperLines.push( `// Domain warp: two far-apart taps of the driver become the x/y of a`, @@ -728,7 +728,7 @@ export function emitComponentSource( const centerY = emitDial(node, base, 'center.y'); const tint = emitColorDial(node, base, 'color'); - emission.matterImports.add('mixColor'); + emission.shadersImports.add('mixColor'); for (const used of ['vec2', 'vec3', 'uv', 'length', 'smoothstep', 'max', 'screenSize']) emission.tslImports.add(used); emission.helperLines.push( @@ -755,7 +755,7 @@ export function emitComponentSource( const phase = emitSpeedDial(node, base); const subtractive = grainBlendOf(node) === 'subtractive'; - emission.matterImports.add('grain'); + emission.shadersImports.add('grain'); for (const used of [subtractive ? 'sub' : 'add', 'floor', 'vec3']) emission.tslImports.add(used); emission.helperLines.push( @@ -794,7 +794,7 @@ export function emitComponentSource( const base = emission.claim('ramp'); const name = `${base}Color`; - emission.matterImports.add('colorRamp'); + emission.shadersImports.add('colorRamp'); emission.usesParseColor = true; emission.tslImports.add('vec3'); emission.tslImports.add('uv'); @@ -878,11 +878,11 @@ export function emitComponentSource( // --------------------------------------------------------------------------- function assembleFile(emission: Emission, finalColorExpr: string): string { - if (emission.usesColorSpaces) emission.matterImports.add('colorSpaces'); + if (emission.usesColorSpaces) emission.shadersImports.add('colorSpaces'); const sortedTsl = [...emission.tslImports].sort(); - const sortedMatter = [...emission.matterImports].sort(); - const sortedMatterReact = [...emission.matterReactImports].sort(); + const sortedShadersImports = [...emission.shadersImports].sort(); + const sortedShadersReactImports = [...emission.shadersReactImports].sort(); const propsInterface = emission.props .map((prop) => ` /** ${prop.jsdoc} */\n ${prop.name}?: ${prop.tsType};`) @@ -955,21 +955,23 @@ function assembleFile(emission: Emission, finalColorExpr: string): string { return `import {\n${names.map((name) => ` ${name},`).join('\n')}\n} from '${moduleName}';`; }; - const matterImportLine = - sortedMatter.length > 0 ? `${importLineOf(sortedMatter, '@lovo/matter')}\n` : ''; + const shadersImportLine = + sortedShadersImports.length > 0 + ? `${importLineOf(sortedShadersImports, '@mattermix/shaders')}\n` + : ''; const parseColorLine = emission.usesParseColor - ? `import { parseColorString } from '@lovo/matter/color';\n` + ? `import { parseColorString } from '@mattermix/shaders/color';\n` : ''; return `'use client'; -// Generated by the Matter editor. A bare Matter component: mount it inside a +// Generated by the Shaders editor. A bare Shaders component: mount it inside a // ShaderScene. Fields are functions of the sample position — warping is // calling a field at a shifted point — and every editor dial arrived below // as a prop with the editor's value as its default. import { useEffect } from 'react'; -${matterImportLine}${importLineOf(sortedMatterReact, '@lovo/matter-react')} +${shadersImportLine}${importLineOf(sortedShadersReactImports, '@mattermix/shaders-react')} ${parseColorLine}${importLineOf(sortedTsl, 'three/tsl')} import type { ShaderNodeObject } from 'three/tsl'; import { Mesh, MeshBasicNodeMaterial, PlaneGeometry } from 'three/webgpu'; diff --git a/apps/editor/src/editor/graph/parity.test.ts b/apps/editor/src/editor/graph/parity.test.ts index 4cd4f7ff..71940e96 100644 --- a/apps/editor/src/editor/graph/parity.test.ts +++ b/apps/editor/src/editor/graph/parity.test.ts @@ -4,8 +4,8 @@ // generator, never by hand-edit. // // Two modes, same test: -// check (default): pnpm --filter @matter/editor test parity -// regenerate: REGEN_PARITY=1 pnpm --filter @matter/editor test parity +// check (default): pnpm --filter @shaders/editor test parity +// regenerate: REGEN_PARITY=1 pnpm --filter @shaders/editor test parity // // The pixel half lives in apps/docs-tests/visual/editor-parity.spec.ts, // which drives /parity/runtime (the editor's live compiler) and @@ -44,14 +44,14 @@ describe('eject parity (source half)', () => { } catch { throw new Error( `generated.gen.tsx is missing — regenerate it:\n` + - ` REGEN_PARITY=1 pnpm --filter @matter/editor test parity`, + ` REGEN_PARITY=1 pnpm --filter @shaders/editor test parity`, ); } expect( checkedIn, `generated.gen.tsx drifted from the emitter. If the emitter change is intentional, regenerate:\n` + - ` REGEN_PARITY=1 pnpm --filter @matter/editor test parity`, + ` REGEN_PARITY=1 pnpm --filter @shaders/editor test parity`, ).toBe(source); }); }); diff --git a/apps/editor/src/editor/graph/registry.ts b/apps/editor/src/editor/graph/registry.ts index e888e71b..849e77b3 100644 --- a/apps/editor/src/editor/graph/registry.ts +++ b/apps/editor/src/editor/graph/registry.ts @@ -1,5 +1,5 @@ // Node vocabulary for the shader editor. Each entry is one "macro node": a -// whole Matter primitive presented as a single card, never a raw TSL op. +// whole Shaders primitive presented as a single card, never a raw TSL op. // Users only ever see two port types — field (a grayscale value per pixel) and // color. Every port carries exactly one small word: "in"/"out" for the main // flow, prepositions ("by", "with", "using") for modifier inputs — never vague diff --git a/apps/editor/src/editor/panels/EditorActions.tsx b/apps/editor/src/editor/panels/EditorActions.tsx index 7693db92..4a2439c9 100644 --- a/apps/editor/src/editor/panels/EditorActions.tsx +++ b/apps/editor/src/editor/panels/EditorActions.tsx @@ -58,7 +58,7 @@ export function EditorActions({ }; const exportFile = () => { - downloadTextFile('matter-graph.json', serializePreset(buildPreset()), 'application/json'); + downloadTextFile('shaders-graph.json', serializePreset(buildPreset()), 'application/json'); }; const importFile = (event: ChangeEvent) => { diff --git a/apps/editor/src/editor/params/RampParam.tsx b/apps/editor/src/editor/params/RampParam.tsx index 7d33bca3..11bdfa59 100644 --- a/apps/editor/src/editor/params/RampParam.tsx +++ b/apps/editor/src/editor/params/RampParam.tsx @@ -8,7 +8,7 @@ // compiled mix chain's arity (structuralKeyOf, graph.ts), so adding or // removing a stop is expected to rebuild; that only happens on the add/remove // buttons, never mid-drag. -import { parseColorString } from '@lovo/matter/color'; +import { parseColorString } from '@mattermix/shaders/color'; import { ColorInput } from '@/controls/ColorInput'; import type { ColorStop } from '@/editor/graph/registry'; diff --git a/apps/editor/src/editor/preset/flow-preset.ts b/apps/editor/src/editor/preset/flow-preset.ts index 0dfee0db..14630636 100644 --- a/apps/editor/src/editor/preset/flow-preset.ts +++ b/apps/editor/src/editor/preset/flow-preset.ts @@ -12,7 +12,7 @@ // must serialize byte-identically to `p`, because that identity is what stops // an undo from recording itself as a fresh history entry (see // use-editor-history.ts). -import { parseColorString } from '@lovo/matter/color'; +import { parseColorString } from '@mattermix/shaders/color'; import type { Edge } from '@xyflow/react'; import type { CardNodeType } from '@/editor/canvas/CardNode'; diff --git a/apps/editor/src/editor/preset/preset.ts b/apps/editor/src/editor/preset/preset.ts index e6ce9a94..120a081a 100644 --- a/apps/editor/src/editor/preset/preset.ts +++ b/apps/editor/src/editor/preset/preset.ts @@ -8,7 +8,7 @@ // spec's default instead of failing the whole load. Kept dependency-free // (hand-rolled validation, no schema library) so every consumer -- including // the code emitter, which stays three-free -- can pull it in cheaply. -import { parseColorString } from '@lovo/matter/color'; +import { parseColorString } from '@mattermix/shaders/color'; import type { ColorStop, ParamSpec, ParamValue, SpecId } from '@/editor/graph/registry'; import { NODE_SPECS, xyKeysOf } from '@/editor/graph/registry'; diff --git a/apps/editor/src/lib/VisualTestPause.tsx b/apps/editor/src/lib/VisualTestPause.tsx index 55e91ce2..4c040041 100644 --- a/apps/editor/src/lib/VisualTestPause.tsx +++ b/apps/editor/src/lib/VisualTestPause.tsx @@ -3,13 +3,13 @@ // Visual-test harness, ported verbatim from apps/docs/src/lib (apps can't // import each other's source): behind `?visualTest=1`, rewinds both time // sources (renderer clock + scheduler phase accumulators), lets exactly two -// frames render, pauses, and flips `__matterTestReady` for waitForShader. +// frames render, pauses, and flips `__shadersTestReady` for waitForShader. // Mounted inside the parity scenes' ShaderScene trees. import { useEffect } from 'react'; -import { resetRendererClock, setReducedMotionPolicy } from '@lovo/matter'; -import type { ReducedMotionPolicy, SchedulerTick } from '@lovo/matter'; -import { useShaderContext } from '@lovo/matter-react'; +import { resetRendererClock, setReducedMotionPolicy } from '@mattermix/shaders'; +import type { ReducedMotionPolicy, SchedulerTick } from '@mattermix/shaders'; +import { useShaderContext } from '@mattermix/shaders-react'; const TARGET_FRAME = 2; @@ -22,7 +22,7 @@ const isReducedMotionPolicy = (policyName: string): policyName is ReducedMotionP declare global { interface Window { - __matterTestReady?: boolean; + __shadersTestReady?: boolean; } } @@ -63,7 +63,7 @@ function useVisualTestPause(): void { if (frame > TARGET_FRAME) { ctx.scheduler.remove(client); ctx.scheduler.pause(); - window.__matterTestReady = true; + window.__shadersTestReady = true; } }; diff --git a/docs/development/visual-regression.md b/docs/development/visual-regression.md index a96c594e..d6917078 100644 --- a/docs/development/visual-regression.md +++ b/docs/development/visual-regression.md @@ -1,7 +1,7 @@ # Visual regression testing -Matter's shader components are validated by Playwright visual regression -tests that live in [`apps/docs-tests/visual/`](../../apps/docs-tests/visual/). +Shader components are validated by Playwright visual regression tests +that live in [`apps/docs-tests/visual/`](../../apps/docs-tests/visual/). Each test navigates to a deterministic visual-test route on the docs site (`/components/?visualTest=1`), screenshots the canvas, and compares against a committed baseline PNG. @@ -75,7 +75,7 @@ docker run --rm \ corepack enable && corepack prepare "$(node -p "require(\"./package.json\").packageManager")" --activate && pnpm install --frozen-lockfile && - pnpm --filter @matter/docs-tests exec playwright test \ + pnpm --filter @shaders/docs-tests exec playwright test \ --update-snapshots --grep "Aurora" ' ``` @@ -117,7 +117,7 @@ future runs match it. Run natively, no Docker: ```bash -pnpm --filter @matter/docs-tests exec playwright test \ +pnpm --filter @shaders/docs-tests exec playwright test \ --update-snapshots --grep "Aurora" ``` diff --git a/eslint.config.js b/eslint.config.js index 26a168ee..a6f08338 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -26,7 +26,7 @@ export default defineConfig([ 'apps/editor/out/**', 'apps/editor/.next/**', 'apps/editor/next-env.d.ts', - 'packages/matter-cli/src/test-fixtures/**', + 'packages/shaders-cli/src/test-fixtures/**', ]), { files: ['**/*.{js,mjs,cjs,jsx}'], @@ -57,8 +57,8 @@ export default defineConfig([ parserOptions: { projectService: { allowDefaultProject: [ - 'packages/matter/*.config.{ts,mts,cts}', - 'packages/matter-react/*.config.{ts,mts,cts}', + 'packages/shaders/*.config.{ts,mts,cts}', + 'packages/shaders-react/*.config.{ts,mts,cts}', 'apps/docs/vitest.config.ts', 'apps/editor/vitest.config.ts', 'packages/*/posters/*.{ts,tsx}', @@ -172,8 +172,8 @@ export default defineConfig([ // reaches three/webgpu — the engine's through the renderer, the binding's // through ShaderScene. three/webgpu reads `self` at module load, so the // wrong import crashes at render time, which is far too late to notice. - // Each has a three-free subpath carrying the same code: @lovo/matter/color - // and @lovo/matter-react/gamut. + // Each has a three-free subpath carrying the same code: @mattermix/shaders/color + // and @mattermix/shaders-react/gamut. // // Banning the names rather than the specifiers is deliberate: it lets /dev // playgrounds keep importing colorRamp, ShaderScene and friends from the @@ -186,7 +186,7 @@ export default defineConfig([ { paths: [ { - name: '@lovo/matter', + name: '@mattermix/shaders', importNames: [ 'linearChannelToSrgb', 'linearSrgbToLinearDisplayP3', @@ -200,14 +200,14 @@ export default defineConfig([ ], allowTypeImports: true, message: - "Import CPU color math from '@lovo/matter/color'. The root entry pulls in three/webgpu, which reads `self` at module load and crashes any server render.", + "Import CPU color math from '@mattermix/shaders/color'. The root entry pulls in three/webgpu, which reads `self` at module load and crashes any server render.", }, { - name: '@lovo/matter-react', + name: '@mattermix/shaders-react', importNames: ['useDisplayGamut'], allowTypeImports: true, message: - "Import useDisplayGamut from '@lovo/matter-react/gamut'. The root entry re-exports ShaderScene and so pulls in three/webgpu, which reads `self` at module load and crashes any server render.", + "Import useDisplayGamut from '@mattermix/shaders-react/gamut'. The root entry re-exports ShaderScene and so pulls in three/webgpu, which reads `self` at module load and crashes any server render.", }, ], }, diff --git a/package.json b/package.json index 5ebebb80..28c3b08f 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "matter-monorepo", + "name": "shaders-monorepo", "version": "0.0.0", "private": true, - "description": "Monorepo root for Matter — React shader components on WebGPU + TSL", + "description": "Monorepo root for Shaders — React shader components on WebGPU + TSL", "license": "MIT", "type": "module", "scripts": { @@ -15,12 +15,12 @@ "smoke": "node scripts/smoke-test-cli.mjs", "format": "prettier --write .", "format:check": "prettier --check .", - "dev:docs": "turbo run dev --filter=@matter/docs", - "build:docs": "turbo run build --filter=@matter/docs", - "preview:docs": "turbo run preview --filter=@matter/docs", - "dev:editor": "turbo run dev --filter=@matter/editor", - "build:editor": "turbo run build --filter=@matter/editor", - "preview:editor": "turbo run preview --filter=@matter/editor", + "dev:docs": "turbo run dev --filter=@shaders/docs", + "build:docs": "turbo run build --filter=@shaders/docs", + "preview:docs": "turbo run preview --filter=@shaders/docs", + "dev:editor": "turbo run dev --filter=@shaders/editor", + "build:editor": "turbo run build --filter=@shaders/editor", + "preview:editor": "turbo run preview --filter=@shaders/editor", "test:visual": "turbo run test:visual", "test:visual:update": "turbo run test:visual:update", "snap": "bash scripts/snap.sh", diff --git a/packages/matter-cli/src/config/matterConfig.test.ts b/packages/matter-cli/src/config/matterConfig.test.ts deleted file mode 100644 index 9c012a11..00000000 --- a/packages/matter-cli/src/config/matterConfig.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import { afterEach, beforeEach, describe, expect, it } from 'vitest'; - -import { - DEFAULT_MATTER_CONFIG, - type MatterConfig, - readMatterConfig, - writeMatterConfig, -} from './matterConfig.js'; - -describe('DEFAULT_MATTER_CONFIG.registryUrl', () => { - it('points at the lovo-hq/matter org (NOT lovo/matter — that is a 404)', () => { - expect(DEFAULT_MATTER_CONFIG.registryUrl).toContain('/lovo-hq/matter/'); - expect(DEFAULT_MATTER_CONFIG.registryUrl).not.toMatch(/\/lovo\/matter\//); - }); - - it('contains the ${ref} placeholder for resolveRef substitution', () => { - expect(DEFAULT_MATTER_CONFIG.registryUrl).toContain('${ref}'); - }); - - it('targets the registry/ subdirectory', () => { - expect(DEFAULT_MATTER_CONFIG.registryUrl).toMatch(/\/registry$|\/registry\/$/); - }); -}); - -let dir: string; - -beforeEach(async () => { - dir = await mkdtemp(join(tmpdir(), 'matter-config-test-')); -}); - -afterEach(async () => { - await rm(dir, { recursive: true, force: true }); -}); - -describe('matterConfig', () => { - it('writes the default config when none exists', async () => { - await writeMatterConfig(dir, DEFAULT_MATTER_CONFIG); - const raw = await readFile(join(dir, 'matter.config.json'), 'utf-8'); - - expect(JSON.parse(raw)).toEqual(DEFAULT_MATTER_CONFIG); - }); - - it('reads back what it wrote', async () => { - const matterConfig: MatterConfig = { - ...DEFAULT_MATTER_CONFIG, - componentsDir: 'app/matter', - }; - - await writeMatterConfig(dir, matterConfig); - const read = await readMatterConfig(dir); - - expect(read).toEqual(matterConfig); - }); - - it('throws a clear error when matter.config.json is missing', async () => { - await expect(readMatterConfig(dir)).rejects.toThrow(/matter\.config\.json not found/); - }); - - it('throws a clear error when matter.config.json is malformed JSON', async () => { - await writeFile(join(dir, 'matter.config.json'), '{ bad json }', 'utf-8'); - await expect(readMatterConfig(dir)).rejects.toThrow(/not valid JSON/); - }); - - it('throws when required fields are missing', async () => { - await writeFile(join(dir, 'matter.config.json'), JSON.stringify({}), 'utf-8'); - await expect(readMatterConfig(dir)).rejects.toThrow(/componentsDir/); - }); -}); diff --git a/packages/matter-react/README.md b/packages/matter-react/README.md deleted file mode 100644 index 333a88f3..00000000 --- a/packages/matter-react/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# @lovo/matter-react - -React binding for **Matter** — shader components on WebGPU + Three.js TSL. - -This package wraps the engine ([`@lovo/matter`](https://www.npmjs.com/package/@lovo/matter)) with React-friendly primitives: a shared `` canvas, a `useShaderMaterial` hook for `@react-three/fiber` integration, and input hooks (`useCursor`, `useScroll`). - -## Install - -```bash -npm install @lovo/matter @lovo/matter-react react three -``` - -`react` (^19), `@lovo/matter`, and `three` (^0.170) are peer dependencies. - -## Three rendering modes - -Matter components work in three configurations: - -1. **Drop-in** — each component manages its own canvas. Simplest path; one canvas per effect. -2. **Shared scene** — wrap multiple Matter components in a single `` to share one canvas (faster, layered effects). -3. **Inside `@react-three/fiber`** — use `useShaderMaterial` directly inside a r3f `` you already own. - -## Minimal usage (Mode 2: shared scene) - -```tsx -import { ShaderScene } from '@lovo/matter-react' -// LinearGradient is copy-pasted into your project via @lovo/matter-cli -import { LinearGradient } from '@/components/matter/linear-gradient' - -export default function Hero() { - return ( - - - - ) -} -``` - -## Getting components - -Polished drop-in components (``, ``, ``, ``, ``, ``) ship via the shadcn-style copy-paste CLI. Install it once: - -```bash -npm install -D @lovo/matter-cli -npx matter-cli init -npx matter-cli add linear-gradient -``` - -The component lands in `src/components/matter/linear-gradient.tsx` and is yours to edit. - -## Docs - - - -## Migration from 0.3.x - -`MatterScene`, `MatterMonitor`, `useMatterContext`, and related types have been renamed to `ShaderScene`, `ShaderMonitor`, `useShaderContext`, `ShaderContextValue`, etc. The old names are deprecated and still work — remove them at your leisure before 0.5.0. - -## License - -MIT — see [LICENSE](./LICENSE). diff --git a/packages/matter-react/src/errors/index.ts b/packages/matter-react/src/errors/index.ts deleted file mode 100644 index 8f8fef67..00000000 --- a/packages/matter-react/src/errors/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { MatterError } from './matter-error.js'; -export type { MatterErrorCode } from './matter-error.js'; diff --git a/packages/matter-react/src/errors/matter-error.ts b/packages/matter-react/src/errors/matter-error.ts deleted file mode 100644 index 316af3b3..00000000 --- a/packages/matter-react/src/errors/matter-error.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Discriminator for a MatterError. Open union — new codes may be added - * without a breaking change. Only 'renderer-init' is emitted today. - */ -export type MatterErrorCode = 'renderer-init'; - -/** - * A typed error surfaced by @lovo/matter-react. The original thrown value is - * always available on `cause`. - */ -export class MatterError extends Error { - readonly code: MatterErrorCode; - - constructor(code: MatterErrorCode, message: string, options?: { cause?: unknown }) { - super(message, options); - this.name = 'MatterError'; - this.code = code; - } -} diff --git a/packages/matter/README.md b/packages/matter/README.md deleted file mode 100644 index f65c9179..00000000 --- a/packages/matter/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# @lovo/matter - -Framework-agnostic engine for **Matter** — React shader components on WebGPU + Three.js TSL. - -This package contains the TSL primitives, the renderer, and the scheduler. It has no React dependency. If you're using React, install [`@lovo/matter-react`](https://www.npmjs.com/package/@lovo/matter-react) alongside this package — it adds React-friendly wrappers (a shared ``, input hooks, and `@react-three/fiber` integration) on top of this engine. - -## Install - -```bash -npm install @lovo/matter three -# or: pnpm add @lovo/matter three -``` - -`three` is a peer dependency. Matter targets `three@^0.170.0` and uses the WebGPU TSL API exclusively. - -## What's inside - -- **TSL primitives**: `fractalNoise`, `voronoi`, `colorRamp`, `quantize`, and a handful of others — composable shader fragments for procedural visuals. -- **Renderer**: thin wrapper around `WebGPURenderer` that handles canvas resize, DPR, and `setClearColor`. -- **Scheduler**: visibility/intersection-aware render loop that pauses when the canvas is off-screen or the tab is hidden. - -## Minimal usage - -```typescript -import { fractalNoise, colorRamp } from '@lovo/matter' -import { uv, vec3, time } from 'three/tsl' - -// Inside your TSL fragment graph: -const noise = fractalNoise(uv().mul(4).add(time.mul(0.1))) -const color = colorRamp(noise, [ - { stop: 0.0, color: vec3(0.05, 0.05, 0.1) }, - { stop: 1.0, color: vec3(0.3, 0.5, 0.95) }, -]) -``` - -For polished drop-in components like `` and ``, install [`@lovo/matter-cli`](https://www.npmjs.com/package/@lovo/matter-cli) and copy them into your project. - -## Docs - -Full docs and live demos: - -## Migration from 0.3.x - -`MatterScheduler`, `MatterRenderer`, and `MatterBackend` have been renamed to `FrameScheduler`, `GpuRenderer`, and `GpuBackend`. The old names are deprecated and still work — remove them at your leisure before 0.5.0. - -## License - -MIT — see [LICENSE](./LICENSE). diff --git a/packages/matter-cli/.tsbuildinfo-tooling b/packages/shaders-cli/.tsbuildinfo-tooling similarity index 100% rename from packages/matter-cli/.tsbuildinfo-tooling rename to packages/shaders-cli/.tsbuildinfo-tooling diff --git a/packages/matter-cli/CHANGELOG.md b/packages/shaders-cli/CHANGELOG.md similarity index 86% rename from packages/matter-cli/CHANGELOG.md rename to packages/shaders-cli/CHANGELOG.md index 0eaf173a..97168bc5 100644 --- a/packages/matter-cli/CHANGELOG.md +++ b/packages/shaders-cli/CHANGELOG.md @@ -1,4 +1,4 @@ -# @lovo/matter-cli +# @mattermix/shaders-cli ## 3.9.0 @@ -74,11 +74,11 @@ - `list`/`add`/`update`: `--ref` → `--reference` - `poster`: `--from` → `--source`, `--out` → `--output`, `--type` → `--format`, `--export` → `--export-name`, `--time` → `--capture-delay` - - `matter.config.json`: removed the `tsx` boolean key (it was validated but never read by any command) + - `shaders.config.json`: removed the `tsx` boolean key (it was validated but never read by any command) Kept: `--registry`, `--quality`, `--width`, `--height`, `--force`, and the config keys `componentsDir`, `registryUrl`, `aliases`. - Migration: update any scripts that pass the old flags. You can delete the `tsx` key from your `matter.config.json` if present — it is no longer used (unknown keys are ignored). Re-running `matter-cli init` regenerates a config without it. + Migration: update any scripts that pass the old flags. You can delete the `tsx` key from your `shaders.config.json` if present — it is no longer used (unknown keys are ignored). Re-running `shaders-cli init` regenerates a config without it. ## 0.4.1 @@ -90,7 +90,7 @@ ### Minor Changes -- No API changes. Bumped alongside `@lovo/matter` 0.2.0 because the three packages ship as a fixed version group. See [`@lovo/matter`'s 0.2.0 changelog](../matter/CHANGELOG.md#020) for the engine-level breaking change. +- No API changes. Bumped alongside `@mattermix/shaders` 0.2.0 because the three packages ship as a fixed version group. See [`@mattermix/shaders`'s 0.2.0 changelog](../matter/CHANGELOG.md#020) for the engine-level breaking change. ## 0.1.0 @@ -98,12 +98,12 @@ - Initial public release of Matter — React shader components on WebGPU + Three.js TSL. - **`@lovo/matter`** — Framework-agnostic engine: TSL primitives (`fbm`, `voronoi`, `colorRamp`, `quantize`, …), WebGPU renderer wrapper, visibility/intersection-aware scheduler. + **`@mattermix/shaders`** — Framework-agnostic engine: TSL primitives (`fbm`, `voronoi`, `colorRamp`, `quantize`, …), WebGPU renderer wrapper, visibility/intersection-aware scheduler. - **`@lovo/matter-react`** — React binding: `` (shared canvas), `useShaderMaterial` (r3f-compatible), input hooks (`useCursor`, `useScroll`). + **`@mattermix/shaders-react`** — React binding: `` (shared canvas), `useShaderMaterial` (r3f-compatible), input hooks (`useCursor`, `useScroll`). - **`@lovo/matter-cli`** — shadcn-style copy-paste CLI: `init`, `list`, `add`, `update`. Default registry tracks the CLI's published version tag (`v0.1.0`) so component code is stable per release. + **`@mattermix/shaders-cli`** — shadcn-style copy-paste CLI: `init`, `list`, `add`, `update`. Default registry tracks the CLI's published version tag (`v0.1.0`) so component code is stable per release. - **v1 components** (via `matter-cli add `): `linear-gradient`, `mesh-gradient`, `aurora`, `dot-field`, `noise-field`, `waves`. Each component is yours to edit after copy-in. + **v1 components** (via `shaders-cli add `): `linear-gradient`, `mesh-gradient`, `aurora`, `dot-field`, `noise-field`, `waves`. Each component is yours to edit after copy-in. **Requirements:** Node 22+ for the CLI. WebGPU-capable browser (Chromium-based, Safari TP, Firefox Nightly with the flag). Three.js ^0.170. React ^19. diff --git a/packages/matter-react/LICENSE b/packages/shaders-cli/LICENSE similarity index 95% rename from packages/matter-react/LICENSE rename to packages/shaders-cli/LICENSE index 7c8ffebb..ef1a0273 100644 --- a/packages/matter-react/LICENSE +++ b/packages/shaders-cli/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 Hunter Garrett / Lovo +Copyright (c) 2026 Hunter Garrett / Mattermix Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/packages/matter-cli/README.md b/packages/shaders-cli/README.md similarity index 73% rename from packages/matter-cli/README.md rename to packages/shaders-cli/README.md index 6068fd3d..e498413f 100644 --- a/packages/matter-cli/README.md +++ b/packages/shaders-cli/README.md @@ -1,12 +1,12 @@ -# @lovo/matter-cli +# @mattermix/shaders-cli -shadcn-style copy-paste CLI for **Matter** — fetch polished shader components from the registry into your project, where they're yours to edit. +shadcn-style copy-paste CLI for **Shaders** — fetch polished shader components from the registry into your project, where they're yours to edit. ## Install ```bash -npm install -D @lovo/matter-cli -# or run ad-hoc: npx @lovo/matter-cli +npm install -D @mattermix/shaders-cli +# or run ad-hoc: npx @mattermix/shaders-cli ``` Requires Node 22+. @@ -16,15 +16,15 @@ Requires Node 22+. ### One-time setup ```bash -npx matter-cli init +npx shaders-cli init ``` -Writes `matter.config.json` to your project root with sensible defaults: +Writes `shaders.config.json` to your project root with sensible defaults: ```json { - "componentsDir": "src/components/matter", - "registryUrl": "https://raw.githubusercontent.com/lovo-hq/matter/${ref}/registry", + "componentsDir": "src/components/shaders", + "registryUrl": "https://raw.githubusercontent.com/mattermix/shaders/${ref}/registry", "aliases": { "@/": "src/" } } ``` @@ -34,35 +34,35 @@ The `${ref}` placeholder is auto-substituted with the CLI's published version ta ### List available components ```bash -npx matter-cli list +npx shaders-cli list ``` ### Copy a component into your project ```bash -npx matter-cli add linear-gradient +npx shaders-cli add linear-gradient # or multiple at once: -npx matter-cli add linear-gradient aurora dot-field +npx shaders-cli add linear-gradient aurora dot-field ``` -The component lands in `componentsDir` (default `src/components/matter/`) — you own it from that point forward. +The component lands in `componentsDir` (default `src/components/shaders/`) — you own it from that point forward. ### Refresh a previously-added component ```bash # Refresh one (errors if you have local edits): -npx matter-cli update linear-gradient +npx shaders-cli update linear-gradient # Refresh all, overwriting local edits: -npx matter-cli update --force +npx shaders-cli update --force ``` ### Render a static fallback image -Render a Matter component tree to an image for use as the `poster` in `` — eliminates the visible blank canvas during WebGPU initialization. +Render a Shaders component tree to an image for use as the `poster` in `` — eliminates the visible blank canvas during WebGPU initialization. ```bash -npx matter-cli poster --source --output [options] +npx shaders-cli poster --source --output [options] ``` | Flag | Default | Description | @@ -100,19 +100,19 @@ pnpm exec playwright install chromium ```bash # Default — writes ./public/hero.jpg (JPEG q80) -npx matter-cli poster --source ./src/components/matter/hero.tsx --output ./public/hero.jpg +npx shaders-cli poster --source ./src/components/shaders/hero.tsx --output ./public/hero.jpg # Posterized shader — PNG compresses smaller -npx matter-cli poster --source ./gradient.tsx --output ./public/gradient.png --format png +npx shaders-cli poster --source ./gradient.tsx --output ./public/gradient.png --format png # Higher quality JPEG -npx matter-cli poster --source ./aurora.tsx --output ./public/aurora.jpg --quality 92 +npx shaders-cli poster --source ./aurora.tsx --output ./public/aurora.jpg --quality 92 ``` Wire it up: ```tsx -import { ShaderPoster } from '@lovo/matter-react/poster'; +import { ShaderPoster } from '@mattermix/shaders-react/poster'; }> @@ -131,15 +131,15 @@ import { ShaderPoster } from '@lovo/matter-react/poster'; `linear-gradient`, `mesh-gradient`, `aurora`, `dot-field`, `simplex-noise`, `wave-lines`. -Each component depends on `@lovo/matter` and `@lovo/matter-react`, which you install separately: +Each component depends on `@mattermix/shaders` and `@mattermix/shaders-react`, which you install separately: ```bash -npm install @lovo/matter @lovo/matter-react three +npm install @mattermix/shaders @mattermix/shaders-react three ``` ## Docs - + ## License diff --git a/packages/matter-cli/package.json b/packages/shaders-cli/package.json similarity index 65% rename from packages/matter-cli/package.json rename to packages/shaders-cli/package.json index 81dad716..4384c5d5 100644 --- a/packages/matter-cli/package.json +++ b/packages/shaders-cli/package.json @@ -1,28 +1,28 @@ { - "name": "@lovo/matter-cli", + "name": "@mattermix/shaders-cli", "version": "3.9.0", - "description": "CLI for Matter — copy-paste components from the registry into your project.", + "description": "CLI for Shaders — copy-paste components from the registry into your project.", "keywords": [ "cli", "components", - "matter", + "shaders", "scaffold", "shadcn", "shader" ], - "homepage": "https://github.com/lovo-hq/matter#readme", + "homepage": "https://github.com/mattermix/shaders#readme", "bugs": { - "url": "https://github.com/lovo-hq/matter/issues" + "url": "https://github.com/mattermix/shaders/issues" }, "license": "MIT", - "author": "lovo-hq", + "author": "mattermix", "repository": { "type": "git", - "url": "git+https://github.com/lovo-hq/matter.git", - "directory": "packages/matter-cli" + "url": "git+https://github.com/mattermix/shaders.git", + "directory": "packages/shaders-cli" }, "bin": { - "matter-cli": "./dist/index.js" + "shaders-cli": "./dist/index.js" }, "files": [ "dist", @@ -46,10 +46,10 @@ "esbuild": "^0.27.0" }, "devDependencies": { - "@lovo/matter": "workspace:*", - "@lovo/matter-react": "workspace:*", - "@matter/registry": "workspace:*", - "@matter/tsconfig": "workspace:*", + "@mattermix/shaders": "workspace:*", + "@mattermix/shaders-react": "workspace:*", + "@shaders/registry": "workspace:*", + "@shaders/tsconfig": "workspace:*", "@types/node": "^22.7.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", diff --git a/packages/matter-cli/src/commands/add.test.ts b/packages/shaders-cli/src/commands/add.test.ts similarity index 85% rename from packages/matter-cli/src/commands/add.test.ts rename to packages/shaders-cli/src/commands/add.test.ts index 0cf36267..48bef677 100644 --- a/packages/matter-cli/src/commands/add.test.ts +++ b/packages/shaders-cli/src/commands/add.test.ts @@ -4,7 +4,7 @@ import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { DEFAULT_MATTER_CONFIG, writeMatterConfig } from '../config/matterConfig.js'; +import { DEFAULT_SHADERS_CONFIG, writeShadersConfig } from '../config/shadersConfig.js'; import { runAdd } from './add.js'; const FIXTURE_BASE = `file://${fileURLToPath( @@ -15,18 +15,18 @@ const VERSION = '0.0.0'; let dir: string; beforeEach(async () => { - dir = await mkdtemp(join(tmpdir(), 'matter-add-test-')); + dir = await mkdtemp(join(tmpdir(), 'shaders-add-test-')); }); afterEach(async () => { await rm(dir, { recursive: true, force: true }); }); -async function seedConfig(overrides: Partial = {}) { - await writeMatterConfig(dir, { - ...DEFAULT_MATTER_CONFIG, +async function seedConfig(overrides: Partial = {}) { + await writeShadersConfig(dir, { + ...DEFAULT_SHADERS_CONFIG, registryUrl: FIXTURE_BASE, - componentsDir: 'src/components/matter', + componentsDir: 'src/components/shaders', ...overrides, }); } @@ -35,7 +35,7 @@ describe('runAdd (single component, no aliases)', () => { it('writes the component source to componentsDir/.tsx', async () => { await seedConfig(); await runAdd(['synthetic-component'], { cliVersion: VERSION }, { cwd: dir, log: vi.fn() }); - const target = join(dir, 'src/components/matter/synthetic-component.tsx'); + const target = join(dir, 'src/components/shaders/synthetic-component.tsx'); const written = await readFile(target, 'utf-8'); expect(written).toContain('SyntheticComponent'); @@ -43,9 +43,9 @@ describe('runAdd (single component, no aliases)', () => { }); it('creates componentsDir if it does not exist', async () => { - await seedConfig({ componentsDir: 'app/very/nested/matter' }); + await seedConfig({ componentsDir: 'app/very/nested/shaders' }); await runAdd(['synthetic-component'], { cliVersion: VERSION }, { cwd: dir, log: vi.fn() }); - const target = join(dir, 'app/very/nested/matter/synthetic-component.tsx'); + const target = join(dir, 'app/very/nested/shaders/synthetic-component.tsx'); const written = await readFile(target, 'utf-8'); expect(written).toContain('SyntheticComponent'); @@ -53,9 +53,9 @@ describe('runAdd (single component, no aliases)', () => { it('refuses to overwrite an existing file without --force', async () => { await seedConfig(); - await mkdir(join(dir, 'src/components/matter'), { recursive: true }); + await mkdir(join(dir, 'src/components/shaders'), { recursive: true }); await writeFile( - join(dir, 'src/components/matter/synthetic-component.tsx'), + join(dir, 'src/components/shaders/synthetic-component.tsx'), 'existing', 'utf-8', ); @@ -66,15 +66,15 @@ describe('runAdd (single component, no aliases)', () => { it('overwrites with --force', async () => { await seedConfig(); - await mkdir(join(dir, 'src/components/matter'), { recursive: true }); - await writeFile(join(dir, 'src/components/matter/synthetic-component.tsx'), 'old', 'utf-8'); + await mkdir(join(dir, 'src/components/shaders'), { recursive: true }); + await writeFile(join(dir, 'src/components/shaders/synthetic-component.tsx'), 'old', 'utf-8'); await runAdd( ['synthetic-component'], { force: true, cliVersion: VERSION }, { cwd: dir, log: vi.fn() }, ); const written = await readFile( - join(dir, 'src/components/matter/synthetic-component.tsx'), + join(dir, 'src/components/shaders/synthetic-component.tsx'), 'utf-8', ); @@ -105,7 +105,7 @@ describe('runAdd (multi-file components)', () => { it('writes every file the entry lists, not just the entry point', async () => { await seedConfig(); await runAdd(['nested-component'], { cliVersion: VERSION }, { cwd: dir, log: vi.fn() }); - const base = join(dir, 'src/components/matter'); + const base = join(dir, 'src/components/shaders'); expect(await readFile(join(base, 'nested-component/nested-component.tsx'), 'utf-8')).toContain( 'NestedComponent', @@ -118,7 +118,7 @@ describe('runAdd (multi-file components)', () => { it('refuses the whole set when a non-entry file already exists, writing nothing', async () => { await seedConfig(); - const base = join(dir, 'src/components/matter'); + const base = join(dir, 'src/components/shaders'); await mkdir(join(base, 'nested-component'), { recursive: true }); await writeFile(join(base, 'nested-component/shader.tsx'), 'mine', 'utf-8'); @@ -158,7 +158,7 @@ describe('runAdd (multi-file components)', () => { it('skips a file already on disk with identical content, so a later add succeeds', async () => { await seedConfig(); - const base = join(dir, 'src/components/matter'); + const base = join(dir, 'src/components/shaders'); // Last week: add one component, which brings utils/color.ts with it. await runAdd(['nested-component'], { cliVersion: VERSION }, { cwd: dir, log: vi.fn() }); @@ -180,7 +180,7 @@ describe('runAdd (multi-file components)', () => { it('refuses when a file on disk has diverged from the registry copy', async () => { await seedConfig(); - const base = join(dir, 'src/components/matter'); + const base = join(dir, 'src/components/shaders'); await runAdd(['nested-component'], { cliVersion: VERSION }, { cwd: dir, log: vi.fn() }); await writeFile(join(base, 'utils/color.ts'), '// my own edits\n', 'utf-8'); @@ -196,7 +196,7 @@ describe('runAdd (multi-file components)', () => { }); it('refuses a registry entry whose file escapes componentsDir', async () => { - const inlineDir = await mkdtemp(join(tmpdir(), 'matter-escape-fixture-')); + const inlineDir = await mkdtemp(join(tmpdir(), 'shaders-escape-fixture-')); await writeFile( join(inlineDir, 'registry.json'), @@ -226,7 +226,7 @@ describe('runAdd (multi-file components)', () => { it('treats a file differing only in line endings as unchanged', async () => { await seedConfig(); - const base = join(dir, 'src/components/matter'); + const base = join(dir, 'src/components/shaders'); await runAdd(['nested-component'], { cliVersion: VERSION }, { cwd: dir, log: vi.fn() }); @@ -254,11 +254,11 @@ describe('runAdd (multi-file components)', () => { await rm(join(dir, 'src'), { recursive: true, force: true }); await seedConfig(); await mkdir(outside, { recursive: true }); - await mkdir(join(dir, 'src/components/matter/utils'), { recursive: true }); + await mkdir(join(dir, 'src/components/shaders/utils'), { recursive: true }); if (name === 'existing') await writeFile(linkTarget, '// theirs\n', 'utf-8'); // A dangling link is the sharper case: the existence check reads through // it, finds nothing, and would happily create the file outside. - await symlink(linkTarget, join(dir, 'src/components/matter/utils/color.ts')); + await symlink(linkTarget, join(dir, 'src/components/shaders/utils/color.ts')); await expect( runAdd(['nested-component'], { cliVersion: VERSION }, { cwd: dir, log: vi.fn() }), @@ -274,7 +274,7 @@ describe('runAdd (multi-file components)', () => { it('refuses to write through a symlink that escapes componentsDir', async () => { await seedConfig(); - const base = join(dir, 'src/components/matter'); + const base = join(dir, 'src/components/shaders'); const outside = join(dir, 'outside'); await mkdir(outside, { recursive: true }); @@ -296,7 +296,7 @@ describe('runAdd (multi-file components)', () => { it('overwrites a diverged file with --force', async () => { await seedConfig(); - const base = join(dir, 'src/components/matter'); + const base = join(dir, 'src/components/shaders'); await runAdd(['nested-component'], { cliVersion: VERSION }, { cwd: dir, log: vi.fn() }); await writeFile(join(base, 'utils/color.ts'), '// my own edits\n', 'utf-8'); @@ -313,7 +313,7 @@ describe('runAdd (multi-file components)', () => { describe('runAdd (multi-component + dedup + alias rewriting)', () => { it('writes multiple components in one invocation against a custom registry', async () => { - const inlineDir = await mkdtemp(join(tmpdir(), 'matter-multi-fixture-')); + const inlineDir = await mkdtemp(join(tmpdir(), 'shaders-multi-fixture-')); await writeFile( join(inlineDir, 'registry.json'), @@ -334,8 +334,8 @@ describe('runAdd (multi-component + dedup + alias rewriting)', () => { await runAdd(['alpha', 'beta'], { cliVersion: VERSION }, { cwd: dir, log }); - const a = await readFile(join(dir, 'src/components/matter/alpha.tsx'), 'utf-8'); - const b = await readFile(join(dir, 'src/components/matter/beta.tsx'), 'utf-8'); + const a = await readFile(join(dir, 'src/components/shaders/alpha.tsx'), 'utf-8'); + const b = await readFile(join(dir, 'src/components/shaders/beta.tsx'), 'utf-8'); expect(a).toContain('alpha = 1'); expect(b).toContain('beta = 2'); @@ -349,10 +349,10 @@ describe('runAdd (multi-component + dedup + alias rewriting)', () => { await rm(inlineDir, { recursive: true, force: true }); }); - it('rewrites @matter-internal imports per matter.config.json aliases', async () => { + it('rewrites @matter-internal imports per shaders.config.json aliases', async () => { await seedConfig({ aliases: { '@matter-internal/': '@/lib/matter/' } }); await runAdd(['synthetic-component'], { cliVersion: VERSION }, { cwd: dir, log: vi.fn() }); - const target = join(dir, 'src/components/matter/synthetic-component.tsx'); + const target = join(dir, 'src/components/shaders/synthetic-component.tsx'); const written = await readFile(target, 'utf-8'); expect(written).toContain(`from '@/lib/matter/lib'`); @@ -362,7 +362,7 @@ describe('runAdd (multi-component + dedup + alias rewriting)', () => { describe('runAdd (--ref handling)', () => { it('substitutes ${ref} into the registry URL when present', async () => { - const inlineDir = await mkdtemp(join(tmpdir(), 'matter-ref-fixture-')); + const inlineDir = await mkdtemp(join(tmpdir(), 'shaders-ref-fixture-')); await mkdir(join(inlineDir, 'main'), { recursive: true }); await writeFile( @@ -392,7 +392,7 @@ describe('runAdd (--ref handling)', () => { { ref: 'main', cliVersion: VERSION }, { cwd: dir, log: vi.fn() }, ); - const target = join(dir, 'src/components/matter/synthetic-component.tsx'); + const target = join(dir, 'src/components/shaders/synthetic-component.tsx'); const written = await readFile(target, 'utf-8'); expect(written).toContain('function X'); diff --git a/packages/matter-cli/src/commands/add.ts b/packages/shaders-cli/src/commands/add.ts similarity index 93% rename from packages/matter-cli/src/commands/add.ts rename to packages/shaders-cli/src/commands/add.ts index 5bf0f944..1a6a7824 100644 --- a/packages/matter-cli/src/commands/add.ts +++ b/packages/shaders-cli/src/commands/add.ts @@ -1,4 +1,4 @@ -// `matter-cli add`: the copy-paste flow the whole Tier 1 model is built on. +// `shaders-cli add`: the copy-paste flow the whole Tier 1 model is built on. // Fetch the registry index, resolve each requested slug to a source file, // download it, rewrite its import specifiers for the user's project (see // transforms/rewriteImports), write it into componentsDir, and finish by @@ -6,7 +6,7 @@ import { lstat, mkdir, readFile, realpath } from 'node:fs/promises'; import { dirname, resolve, sep } from 'node:path'; -import { readMatterConfig, resolveRegistryUrl } from '../config/matterConfig.js'; +import { readShadersConfig, resolveRegistryUrl } from '../config/shadersConfig.js'; import { writeFileNoFollow } from '../fs/writeFileNoFollow.js'; import { fetchComponentSource, @@ -38,9 +38,9 @@ export async function runAdd( throw new Error('add: at least one component name is required'); } - const matterConfig = await readMatterConfig(io.cwd); + const shadersConfig = await readShadersConfig(io.cwd); const ref = resolveRef(opts.ref, opts.cliVersion); - const registryUrl = resolveRegistryUrl(matterConfig, { registry: opts.registry, ref }); + const registryUrl = resolveRegistryUrl(shadersConfig, { registry: opts.registry, ref }); const registry = await fetchRegistry(registryUrl); const resolved = components.map((slug) => resolveComponent(slug, registry, registryUrl)); @@ -56,7 +56,7 @@ export async function runAdd( // `../` in an entry would otherwise read and write outside the project. The // separator on the prefix check matters — it keeps `matter` from matching a // sibling directory named `matter-elsewhere`. - const componentsRoot = resolve(io.cwd, matterConfig.componentsDir); + const componentsRoot = resolve(io.cwd, shadersConfig.componentsDir); const targets = sourceFiles.map((file) => { const targetPath = resolve(componentsRoot, file); @@ -74,7 +74,10 @@ export async function runAdd( const planned = await Promise.all( targets.map(async ({ file, targetPath }) => ({ targetPath, - contents: rewriteImports(await fetchComponentSource(registryUrl, file), matterConfig.aliases), + contents: rewriteImports( + await fetchComponentSource(registryUrl, file), + shadersConfig.aliases, + ), })), ); @@ -175,7 +178,7 @@ function resolveComponent( if (!entry) { throw new Error( - `Component "${slug}" not found in registry at ${registryUrl}. Run \`matter-cli list\` to see available components.`, + `Component "${slug}" not found in registry at ${registryUrl}. Run \`shaders-cli list\` to see available components.`, ); } diff --git a/packages/matter-cli/src/commands/init.test.ts b/packages/shaders-cli/src/commands/init.test.ts similarity index 55% rename from packages/matter-cli/src/commands/init.test.ts rename to packages/shaders-cli/src/commands/init.test.ts index 5e7d0aeb..5b620f72 100644 --- a/packages/matter-cli/src/commands/init.test.ts +++ b/packages/shaders-cli/src/commands/init.test.ts @@ -8,7 +8,7 @@ import { runInit } from './init.js'; let dir: string; beforeEach(async () => { - dir = await mkdtemp(join(tmpdir(), 'matter-init-test-')); + dir = await mkdtemp(join(tmpdir(), 'shaders-init-test-')); }); afterEach(async () => { @@ -16,27 +16,27 @@ afterEach(async () => { }); describe('runInit', () => { - it('writes matter.config.json with defaults', async () => { + it('writes shaders.config.json with defaults', async () => { await runInit({}, { cwd: dir, log: vi.fn() }); - const raw = await readFile(join(dir, 'matter.config.json'), 'utf-8'); - const matterConfig = JSON.parse(raw); + const raw = await readFile(join(dir, 'shaders.config.json'), 'utf-8'); + const shadersConfig = JSON.parse(raw); - expect(matterConfig.componentsDir).toBe('src/components/matter'); - expect(matterConfig.registryUrl).toContain('lovo-hq/matter'); + expect(shadersConfig.componentsDir).toBe('src/components/shaders'); + expect(shadersConfig.registryUrl).toContain('mattermix/shaders'); }); it('refuses to overwrite an existing config without --force', async () => { - await writeFile(join(dir, 'matter.config.json'), '{}', 'utf-8'); + await writeFile(join(dir, 'shaders.config.json'), '{}', 'utf-8'); await expect(runInit({}, { cwd: dir, log: vi.fn() })).rejects.toThrow(/already exists/); }); it('overwrites with --force', async () => { - await writeFile(join(dir, 'matter.config.json'), '{}', 'utf-8'); + await writeFile(join(dir, 'shaders.config.json'), '{}', 'utf-8'); await runInit({ force: true }, { cwd: dir, log: vi.fn() }); - const raw = await readFile(join(dir, 'matter.config.json'), 'utf-8'); - const matterConfig = JSON.parse(raw); + const raw = await readFile(join(dir, 'shaders.config.json'), 'utf-8'); + const shadersConfig = JSON.parse(raw); - expect(matterConfig.componentsDir).toBe('src/components/matter'); + expect(shadersConfig.componentsDir).toBe('src/components/shaders'); }); it('logs a confirmation message after writing', async () => { @@ -45,6 +45,6 @@ describe('runInit', () => { await runInit({}, { cwd: dir, log }); const output = log.mock.calls.map((c) => c[0]).join('\n'); - expect(output).toMatch(/created matter\.config\.json/i); + expect(output).toMatch(/created shaders\.config\.json/i); }); }); diff --git a/packages/matter-cli/src/commands/init.ts b/packages/shaders-cli/src/commands/init.ts similarity index 63% rename from packages/matter-cli/src/commands/init.ts rename to packages/shaders-cli/src/commands/init.ts index f0c8f316..0b84da74 100644 --- a/packages/matter-cli/src/commands/init.ts +++ b/packages/shaders-cli/src/commands/init.ts @@ -1,12 +1,12 @@ -// `matter-cli init`: one-time setup — writes the starter matter.config.json +// `shaders-cli init`: one-time setup — writes the starter shaders.config.json // (components directory, registry URL template, import aliases) into the // user's project and points them at the fields worth editing. import { configExists, configPath, - DEFAULT_MATTER_CONFIG, - writeMatterConfig, -} from '../config/matterConfig.js'; + DEFAULT_SHADERS_CONFIG, + writeShadersConfig, +} from '../config/shadersConfig.js'; export interface InitOptions { force?: boolean; @@ -24,10 +24,10 @@ export async function runInit( const exists = await configExists(io.cwd); if (exists && opts.force !== true) { - throw new Error(`matter.config.json already exists in ${io.cwd}. Pass --force to overwrite.`); + throw new Error(`shaders.config.json already exists in ${io.cwd}. Pass --force to overwrite.`); } - await writeMatterConfig(io.cwd, DEFAULT_MATTER_CONFIG); - io.log(`Created matter.config.json at ${configPath(io.cwd)}`); + await writeShadersConfig(io.cwd, DEFAULT_SHADERS_CONFIG); + io.log(`Created shaders.config.json at ${configPath(io.cwd)}`); io.log( 'Edit `componentsDir` if your project uses a different layout, ' + 'and adjust `aliases` to match your tsconfig paths.', diff --git a/packages/matter-cli/src/commands/list.test.ts b/packages/shaders-cli/src/commands/list.test.ts similarity index 77% rename from packages/matter-cli/src/commands/list.test.ts rename to packages/shaders-cli/src/commands/list.test.ts index ee78fe5e..e5f81f7f 100644 --- a/packages/matter-cli/src/commands/list.test.ts +++ b/packages/shaders-cli/src/commands/list.test.ts @@ -11,7 +11,7 @@ const FIXTURE_BASE = `file://${fileURLToPath(new URL('../test-fixtures/registry/ let dir: string; beforeEach(async () => { - dir = await mkdtemp(join(tmpdir(), 'matter-list-test-')); + dir = await mkdtemp(join(tmpdir(), 'shaders-list-test-')); }); afterEach(async () => { @@ -29,12 +29,13 @@ describe('runList', () => { expect(output).toContain('tier 1'); }); - it('reads matter.config.json when --registry is not supplied', async () => { + it('reads shaders.config.json when --registry is not supplied', async () => { // Write a minimal config pointing at the fixture (with ${ref} placeholder). - const { writeMatterConfig, DEFAULT_MATTER_CONFIG } = await import('../config/matterConfig.js'); + const { writeShadersConfig, DEFAULT_SHADERS_CONFIG } = + await import('../config/shadersConfig.js'); - await writeMatterConfig(dir, { - ...DEFAULT_MATTER_CONFIG, + await writeShadersConfig(dir, { + ...DEFAULT_SHADERS_CONFIG, registryUrl: FIXTURE_BASE, // no ${ref} — stays literal }); const log = vi.fn(); @@ -45,12 +46,12 @@ describe('runList', () => { expect(output).toContain('synthetic-component'); }); - it('propagates errors when matter.config.json is malformed (does NOT silently fall back)', async () => { - // Pre-fix, runList caught everything and fell back to DEFAULT_MATTER_CONFIG + it('propagates errors when shaders.config.json is malformed (does NOT silently fall back)', async () => { + // Pre-fix, runList caught everything and fell back to DEFAULT_SHADERS_CONFIG // — silently masking malformed config. After the fix, the error propagates. const { writeFile } = await import('node:fs/promises'); - await writeFile(join(dir, 'matter.config.json'), '{ this is not valid json', 'utf-8'); + await writeFile(join(dir, 'shaders.config.json'), '{ this is not valid json', 'utf-8'); await expect(runList({ cliVersion: '0.0.0' }, { cwd: dir, log: vi.fn() })).rejects.toThrow( /not valid JSON/, ); diff --git a/packages/matter-cli/src/commands/list.ts b/packages/shaders-cli/src/commands/list.ts similarity index 74% rename from packages/matter-cli/src/commands/list.ts rename to packages/shaders-cli/src/commands/list.ts index b878bd89..df79c21c 100644 --- a/packages/matter-cli/src/commands/list.ts +++ b/packages/shaders-cli/src/commands/list.ts @@ -1,8 +1,12 @@ -// `matter-cli list`: prints the registry's component catalog (slug, +// `shaders-cli list`: prints the registry's component catalog (slug, // description, tier). Unlike the other commands it works without a -// matter.config.json — it falls back to the default registry URL so users +// shaders.config.json — it falls back to the default registry URL so users // can browse before running init. -import { configExists, DEFAULT_MATTER_CONFIG, readMatterConfig } from '../config/matterConfig.js'; +import { + configExists, + DEFAULT_SHADERS_CONFIG, + readShadersConfig, +} from '../config/shadersConfig.js'; import { fetchRegistry } from '../registry/fetchRegistry.js'; import { resolveRef } from '../registry/ref.js'; @@ -26,11 +30,11 @@ export async function runList( if (opts.registry !== undefined && opts.registry !== '') { baseUrl = opts.registry; } else if (await configExists(io.cwd)) { - const matterConfig = await readMatterConfig(io.cwd); + const shadersConfig = await readShadersConfig(io.cwd); - baseUrl = matterConfig.registryUrl; + baseUrl = shadersConfig.registryUrl; } else { - baseUrl = DEFAULT_MATTER_CONFIG.registryUrl; + baseUrl = DEFAULT_SHADERS_CONFIG.registryUrl; } const ref = resolveRef(opts.ref, opts.cliVersion); diff --git a/packages/matter-cli/src/commands/poster.test.ts b/packages/shaders-cli/src/commands/poster.test.ts similarity index 97% rename from packages/matter-cli/src/commands/poster.test.ts rename to packages/shaders-cli/src/commands/poster.test.ts index 6f513419..e0835933 100644 --- a/packages/matter-cli/src/commands/poster.test.ts +++ b/packages/shaders-cli/src/commands/poster.test.ts @@ -168,9 +168,9 @@ describe('runPoster — --background threading', () => { beforeEach(async () => { launchAndScreenshotMock.mockClear(); - sourceDir = await mkdtemp(join(tmpdir(), 'matter-poster-test-')); + sourceDir = await mkdtemp(join(tmpdir(), 'shaders-poster-test-')); sourceFile = join(sourceDir, 'scene.tsx'); - await writeFile(join(sourceDir, 'package.json'), '{"name":"matter-poster-test-fixture"}'); + await writeFile(join(sourceDir, 'package.json'), '{"name":"shaders-poster-test-fixture"}'); await writeFile(sourceFile, 'export default function Scene() { return null; }'); }); diff --git a/packages/matter-cli/src/commands/poster.ts b/packages/shaders-cli/src/commands/poster.ts similarity index 98% rename from packages/matter-cli/src/commands/poster.ts rename to packages/shaders-cli/src/commands/poster.ts index 7d18f474..bf9bf624 100644 --- a/packages/matter-cli/src/commands/poster.ts +++ b/packages/shaders-cli/src/commands/poster.ts @@ -1,4 +1,4 @@ -// `matter-cli poster`: renders a user's shader component to a static image +// `shaders-cli poster`: renders a user's shader component to a static image // for use as a stand-in. This file validates the flags and // runs the four-stage pipeline, each stage its own module: // 1. poster/bundle.ts — esbuild the harness + user file, in memory diff --git a/packages/matter-cli/src/commands/update.test.ts b/packages/shaders-cli/src/commands/update.test.ts similarity index 75% rename from packages/matter-cli/src/commands/update.test.ts rename to packages/shaders-cli/src/commands/update.test.ts index 3fa2f1aa..27c0e3ce 100644 --- a/packages/matter-cli/src/commands/update.test.ts +++ b/packages/shaders-cli/src/commands/update.test.ts @@ -4,7 +4,7 @@ import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { DEFAULT_MATTER_CONFIG, writeMatterConfig } from '../config/matterConfig.js'; +import { DEFAULT_SHADERS_CONFIG, writeShadersConfig } from '../config/shadersConfig.js'; import { runUpdate } from './update.js'; const FIXTURE_BASE = `file://${fileURLToPath(new URL('../test-fixtures/registry/', import.meta.url))}`; @@ -12,7 +12,7 @@ const FIXTURE_BASE = `file://${fileURLToPath(new URL('../test-fixtures/registry/ let dir: string; beforeEach(async () => { - dir = await mkdtemp(join(tmpdir(), 'matter-update-test-')); + dir = await mkdtemp(join(tmpdir(), 'shaders-update-test-')); }); afterEach(async () => { @@ -20,13 +20,13 @@ afterEach(async () => { }); async function seedConfigAndComponent() { - await writeMatterConfig(dir, { - ...DEFAULT_MATTER_CONFIG, + await writeShadersConfig(dir, { + ...DEFAULT_SHADERS_CONFIG, registryUrl: FIXTURE_BASE, }); - await mkdir(join(dir, 'src/components/matter'), { recursive: true }); + await mkdir(join(dir, 'src/components/shaders'), { recursive: true }); await writeFile( - join(dir, 'src/components/matter/synthetic-component.tsx'), + join(dir, 'src/components/shaders/synthetic-component.tsx'), 'export const STALE = true\n', 'utf-8', ); @@ -41,7 +41,7 @@ describe('runUpdate', () => { { cwd: dir, log: vi.fn() }, ); const written = await readFile( - join(dir, 'src/components/matter/synthetic-component.tsx'), + join(dir, 'src/components/shaders/synthetic-component.tsx'), 'utf-8', ); @@ -53,7 +53,7 @@ describe('runUpdate', () => { await seedConfigAndComponent(); await runUpdate([], { force: true, cliVersion: '0.0.0' }, { cwd: dir, log: vi.fn() }); const written = await readFile( - join(dir, 'src/components/matter/synthetic-component.tsx'), + join(dir, 'src/components/shaders/synthetic-component.tsx'), 'utf-8', ); @@ -61,22 +61,22 @@ describe('runUpdate', () => { }); it('errors clearly when componentsDir is empty and no names are given', async () => { - await writeMatterConfig(dir, { - ...DEFAULT_MATTER_CONFIG, + await writeShadersConfig(dir, { + ...DEFAULT_SHADERS_CONFIG, registryUrl: FIXTURE_BASE, }); - await mkdir(join(dir, 'src/components/matter'), { recursive: true }); + await mkdir(join(dir, 'src/components/shaders'), { recursive: true }); await expect( runUpdate([], { force: true, cliVersion: '0.0.0' }, { cwd: dir, log: vi.fn() }), ).rejects.toThrow(/no components/i); }); it('errors clearly when a named component is not present in componentsDir', async () => { - await writeMatterConfig(dir, { - ...DEFAULT_MATTER_CONFIG, + await writeShadersConfig(dir, { + ...DEFAULT_SHADERS_CONFIG, registryUrl: FIXTURE_BASE, }); - await mkdir(join(dir, 'src/components/matter'), { recursive: true }); + await mkdir(join(dir, 'src/components/shaders'), { recursive: true }); await expect( runUpdate( ['synthetic-component'], diff --git a/packages/matter-cli/src/commands/update.ts b/packages/shaders-cli/src/commands/update.ts similarity index 87% rename from packages/matter-cli/src/commands/update.ts rename to packages/shaders-cli/src/commands/update.ts index 94a2384e..f676470c 100644 --- a/packages/matter-cli/src/commands/update.ts +++ b/packages/shaders-cli/src/commands/update.ts @@ -1,4 +1,4 @@ -// `matter-cli update`: re-fetch components that already live in +// `shaders-cli update`: re-fetch components that already live in // componentsDir (all of them when no names are given). After figuring out // which local files correspond to registry entries, it delegates the actual // fetching and writing to `add` — update IS add, restricted to components @@ -7,7 +7,7 @@ import type { Dirent } from 'node:fs'; import { readdir } from 'node:fs/promises'; import { basename, extname, join } from 'node:path'; -import { readMatterConfig, resolveRegistryUrl } from '../config/matterConfig.js'; +import { readShadersConfig, resolveRegistryUrl } from '../config/shadersConfig.js'; import { fetchRegistry, type Registry } from '../registry/fetchRegistry.js'; import { resolveRef } from '../registry/ref.js'; import { runAdd } from './add.js'; @@ -29,11 +29,11 @@ export async function runUpdate( opts: UpdateOptions, io: UpdateIO = { cwd: process.cwd(), log: console.log }, ): Promise { - const matterConfig = await readMatterConfig(io.cwd); + const shadersConfig = await readShadersConfig(io.cwd); const ref = resolveRef(opts.ref, opts.cliVersion); - const registryUrl = resolveRegistryUrl(matterConfig, { registry: opts.registry, ref }); + const registryUrl = resolveRegistryUrl(shadersConfig, { registry: opts.registry, ref }); - const componentsDir = join(io.cwd, matterConfig.componentsDir); + const componentsDir = join(io.cwd, shadersConfig.componentsDir); const localEntries = await safeReaddir(componentsDir); // Recognize both layouts: top-level `.tsx` AND subdir `/.tsx` // (the latter is how multi-file components like aurora and linear-gradient live). @@ -53,7 +53,7 @@ export async function runUpdate( if (components.length === 0) { if (localSlugs.length === 0) { throw new Error( - `No components found in ${componentsDir}. Run \`matter-cli add \` first.`, + `No components found in ${componentsDir}. Run \`shaders-cli add \` first.`, ); } toUpdate = localSlugs.filter((slug) => slugIsInRegistry(slug, registry)); @@ -69,7 +69,7 @@ export async function runUpdate( if (!present) { throw new Error( - `Component "${slug}" is not present in ${componentsDir}. Use \`matter-cli add ${slug}\` instead.`, + `Component "${slug}" is not present in ${componentsDir}. Use \`shaders-cli add ${slug}\` instead.`, ); } } diff --git a/packages/shaders-cli/src/config/shadersConfig.test.ts b/packages/shaders-cli/src/config/shadersConfig.test.ts new file mode 100644 index 00000000..2d31e255 --- /dev/null +++ b/packages/shaders-cli/src/config/shadersConfig.test.ts @@ -0,0 +1,70 @@ +import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; + +import { + DEFAULT_SHADERS_CONFIG, + readShadersConfig, + type ShadersConfig, + writeShadersConfig, +} from './shadersConfig.js'; + +describe('DEFAULT_SHADERS_CONFIG.registryUrl', () => { + it('points at the mattermix/shaders org', () => { + expect(DEFAULT_SHADERS_CONFIG.registryUrl).toContain('/mattermix/shaders/'); + }); + + it('contains the ${ref} placeholder for resolveRef substitution', () => { + expect(DEFAULT_SHADERS_CONFIG.registryUrl).toContain('${ref}'); + }); + + it('targets the registry/ subdirectory', () => { + expect(DEFAULT_SHADERS_CONFIG.registryUrl).toMatch(/\/registry$|\/registry\/$/); + }); +}); + +let dir: string; + +beforeEach(async () => { + dir = await mkdtemp(join(tmpdir(), 'shaders-config-test-')); +}); + +afterEach(async () => { + await rm(dir, { recursive: true, force: true }); +}); + +describe('shadersConfig', () => { + it('writes the default config when none exists', async () => { + await writeShadersConfig(dir, DEFAULT_SHADERS_CONFIG); + const raw = await readFile(join(dir, 'shaders.config.json'), 'utf-8'); + + expect(JSON.parse(raw)).toEqual(DEFAULT_SHADERS_CONFIG); + }); + + it('reads back what it wrote', async () => { + const shadersConfig: ShadersConfig = { + ...DEFAULT_SHADERS_CONFIG, + componentsDir: 'app/shaders', + }; + + await writeShadersConfig(dir, shadersConfig); + const read = await readShadersConfig(dir); + + expect(read).toEqual(shadersConfig); + }); + + it('throws a clear error when shaders.config.json is missing', async () => { + await expect(readShadersConfig(dir)).rejects.toThrow(/shaders\.config\.json not found/); + }); + + it('throws a clear error when shaders.config.json is malformed JSON', async () => { + await writeFile(join(dir, 'shaders.config.json'), '{ bad json }', 'utf-8'); + await expect(readShadersConfig(dir)).rejects.toThrow(/not valid JSON/); + }); + + it('throws when required fields are missing', async () => { + await writeFile(join(dir, 'shaders.config.json'), JSON.stringify({}), 'utf-8'); + await expect(readShadersConfig(dir)).rejects.toThrow(/componentsDir/); + }); +}); diff --git a/packages/matter-cli/src/config/matterConfig.ts b/packages/shaders-cli/src/config/shadersConfig.ts similarity index 65% rename from packages/matter-cli/src/config/matterConfig.ts rename to packages/shaders-cli/src/config/shadersConfig.ts index c566a371..0dbdec01 100644 --- a/packages/matter-cli/src/config/matterConfig.ts +++ b/packages/shaders-cli/src/config/shadersConfig.ts @@ -1,4 +1,4 @@ -// matter.config.json: the one file the CLI reads from a user's project. +// shaders.config.json: the one file the CLI reads from a user's project. // Where copied components land (componentsDir), where they come from // (registryUrl — a template whose ${ref} slot is filled with a git tag, // branch, or commit at fetch time), and how import specifiers get rewritten @@ -7,21 +7,21 @@ import { access, readFile, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; -import { validateMatterConfig } from './validate.js'; +import { validateShadersConfig } from './validate.js'; -export interface MatterConfig { +export interface ShadersConfig { componentsDir: string; registryUrl: string; aliases: Record; } -export const DEFAULT_MATTER_CONFIG: MatterConfig = { - componentsDir: 'src/components/matter', - registryUrl: 'https://raw.githubusercontent.com/lovo-hq/matter/${ref}/registry', +export const DEFAULT_SHADERS_CONFIG: ShadersConfig = { + componentsDir: 'src/components/shaders', + registryUrl: 'https://raw.githubusercontent.com/mattermix/shaders/${ref}/registry', aliases: { '@/': 'src/' }, }; -const CONFIG_FILENAME = 'matter.config.json'; +const CONFIG_FILENAME = 'shaders.config.json'; export function configPath(projectRoot: string): string { return join(projectRoot, CONFIG_FILENAME); @@ -37,7 +37,7 @@ export async function configExists(projectRoot: string): Promise { } } -export async function readMatterConfig(projectRoot: string): Promise { +export async function readShadersConfig(projectRoot: string): Promise { const path = configPath(projectRoot); let raw: string; @@ -46,7 +46,7 @@ export async function readMatterConfig(projectRoot: string): Promise { const path = configPath(projectRoot); - const json = `${JSON.stringify(matterConfig, null, 2)}\n`; + const json = `${JSON.stringify(shadersConfig, null, 2)}\n`; await writeFile(path, json, 'utf-8'); } diff --git a/packages/matter-cli/src/config/validate.ts b/packages/shaders-cli/src/config/validate.ts similarity index 83% rename from packages/matter-cli/src/config/validate.ts rename to packages/shaders-cli/src/config/validate.ts index 9b952063..c31afac4 100644 --- a/packages/matter-cli/src/config/validate.ts +++ b/packages/shaders-cli/src/config/validate.ts @@ -1,12 +1,12 @@ -// Field-by-field validation of a parsed matter.config.json, with error +// Field-by-field validation of a parsed shaders.config.json, with error // messages prefixed by the file path so a bad config points at itself. -import type { MatterConfig } from './matterConfig.js'; +import type { ShadersConfig } from './shadersConfig.js'; function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null; } -export function validateMatterConfig(parsed: unknown, path: string): MatterConfig { +export function validateShadersConfig(parsed: unknown, path: string): ShadersConfig { if (!isRecord(parsed)) { throw new Error(`${path}: expected an object`); } diff --git a/packages/matter-cli/src/fs/writeFileNoFollow.test.ts b/packages/shaders-cli/src/fs/writeFileNoFollow.test.ts similarity index 95% rename from packages/matter-cli/src/fs/writeFileNoFollow.test.ts rename to packages/shaders-cli/src/fs/writeFileNoFollow.test.ts index 7a2d4db0..a3631146 100644 --- a/packages/matter-cli/src/fs/writeFileNoFollow.test.ts +++ b/packages/shaders-cli/src/fs/writeFileNoFollow.test.ts @@ -8,7 +8,7 @@ import { writeFileNoFollow } from './writeFileNoFollow.js'; let dir: string; beforeEach(async () => { - dir = await mkdtemp(join(tmpdir(), 'matter-nofollow-test-')); + dir = await mkdtemp(join(tmpdir(), 'shaders-nofollow-test-')); }); afterEach(async () => { diff --git a/packages/matter-cli/src/fs/writeFileNoFollow.ts b/packages/shaders-cli/src/fs/writeFileNoFollow.ts similarity index 100% rename from packages/matter-cli/src/fs/writeFileNoFollow.ts rename to packages/shaders-cli/src/fs/writeFileNoFollow.ts diff --git a/packages/matter-cli/src/harness/frameReady.ts b/packages/shaders-cli/src/harness/frameReady.ts similarity index 100% rename from packages/matter-cli/src/harness/frameReady.ts rename to packages/shaders-cli/src/harness/frameReady.ts diff --git a/packages/matter-cli/src/harness/index.html b/packages/shaders-cli/src/harness/index.html similarity index 90% rename from packages/matter-cli/src/harness/index.html rename to packages/shaders-cli/src/harness/index.html index fd0ff710..bece75de 100644 --- a/packages/matter-cli/src/harness/index.html +++ b/packages/shaders-cli/src/harness/index.html @@ -2,7 +2,7 @@ - matter poster + shaders poster