From ec49c595532f52ad5a79c923e4bdb8efc3d3e984 Mon Sep 17 00:00:00 2001 From: Pouya Saadeghi Date: Tue, 28 Jul 2026 19:45:50 +0300 Subject: [PATCH 1/3] Add skill files --- skills/paper-shaders/SKILL.md | 75 +++++ .../references/shaders/color-panels.md | 24 ++ .../references/shaders/dithering.md | 19 ++ .../references/shaders/dot-grid.md | 24 ++ .../references/shaders/dot-orbit.md | 20 ++ .../references/shaders/fluted-glass.md | 38 +++ .../references/shaders/gem-smoke.md | 27 ++ .../references/shaders/god-rays.md | 23 ++ .../references/shaders/grain-gradient.md | 20 ++ .../references/shaders/halftone-cmyk.md | 36 +++ .../references/shaders/halftone-dots.md | 26 ++ .../references/shaders/heatmap.md | 23 ++ .../references/shaders/image-dithering.md | 23 ++ .../references/shaders/liquid-metal.md | 26 ++ .../references/shaders/mesh-gradient.md | 18 ++ .../references/shaders/metaballs.md | 18 ++ .../references/shaders/neuro-noise.md | 18 ++ .../references/shaders/paper-texture.md | 32 ++ .../references/shaders/perlin-noise.md | 20 ++ .../references/shaders/pulsing-border.md | 32 ++ .../references/shaders/simplex-noise.md | 16 + .../references/shaders/smoke-ring.md | 21 ++ .../references/shaders/spiral.md | 23 ++ .../shaders/static-mesh-gradient.md | 22 ++ .../shaders/static-radial-gradient.md | 25 ++ .../paper-shaders/references/shaders/swirl.md | 22 ++ .../references/shaders/voronoi.md | 21 ++ .../paper-shaders/references/shaders/warp.md | 23 ++ .../paper-shaders/references/shaders/water.md | 24 ++ .../paper-shaders/references/shaders/waves.md | 23 ++ skills/paper-shaders/references/usage.md | 285 ++++++++++++++++++ 31 files changed, 1047 insertions(+) create mode 100644 skills/paper-shaders/SKILL.md create mode 100644 skills/paper-shaders/references/shaders/color-panels.md create mode 100644 skills/paper-shaders/references/shaders/dithering.md create mode 100644 skills/paper-shaders/references/shaders/dot-grid.md create mode 100644 skills/paper-shaders/references/shaders/dot-orbit.md create mode 100644 skills/paper-shaders/references/shaders/fluted-glass.md create mode 100644 skills/paper-shaders/references/shaders/gem-smoke.md create mode 100644 skills/paper-shaders/references/shaders/god-rays.md create mode 100644 skills/paper-shaders/references/shaders/grain-gradient.md create mode 100644 skills/paper-shaders/references/shaders/halftone-cmyk.md create mode 100644 skills/paper-shaders/references/shaders/halftone-dots.md create mode 100644 skills/paper-shaders/references/shaders/heatmap.md create mode 100644 skills/paper-shaders/references/shaders/image-dithering.md create mode 100644 skills/paper-shaders/references/shaders/liquid-metal.md create mode 100644 skills/paper-shaders/references/shaders/mesh-gradient.md create mode 100644 skills/paper-shaders/references/shaders/metaballs.md create mode 100644 skills/paper-shaders/references/shaders/neuro-noise.md create mode 100644 skills/paper-shaders/references/shaders/paper-texture.md create mode 100644 skills/paper-shaders/references/shaders/perlin-noise.md create mode 100644 skills/paper-shaders/references/shaders/pulsing-border.md create mode 100644 skills/paper-shaders/references/shaders/simplex-noise.md create mode 100644 skills/paper-shaders/references/shaders/smoke-ring.md create mode 100644 skills/paper-shaders/references/shaders/spiral.md create mode 100644 skills/paper-shaders/references/shaders/static-mesh-gradient.md create mode 100644 skills/paper-shaders/references/shaders/static-radial-gradient.md create mode 100644 skills/paper-shaders/references/shaders/swirl.md create mode 100644 skills/paper-shaders/references/shaders/voronoi.md create mode 100644 skills/paper-shaders/references/shaders/warp.md create mode 100644 skills/paper-shaders/references/shaders/water.md create mode 100644 skills/paper-shaders/references/shaders/waves.md create mode 100644 skills/paper-shaders/references/usage.md diff --git a/skills/paper-shaders/SKILL.md b/skills/paper-shaders/SKILL.md new file mode 100644 index 00000000..d238fddb --- /dev/null +++ b/skills/paper-shaders/SKILL.md @@ -0,0 +1,75 @@ +--- +name: paper-shaders +description: Implement, convert, customize, debug, or review Paper Shaders effects in React or vanilla JavaScript using `@paper-design/shaders-react` and `@paper-design/shaders`. +--- + +# Paper Shaders + +Implement Paper Shaders from the package source contract. Do not infer prop names, defaults, enum values, uniform mappings, color limits, or image behavior. + +## Workflow + +1. Inspect the target project's package manager and installed Paper Shaders package/version. Preserve the existing framework and dependency style. +2. Read [references/usage.md](references/usage.md) before writing integration code. +3. Read only the relevant file under [Shader references](#shader-references). Use its exact component export, fragment-shader export, props, defaults, enum options, and implementation capacity. +4. For React, prefer the named shader component. Use the low-level React `ShaderMount` only for a custom fragment shader or an explicitly requested uniform-level integration. +5. For vanilla JavaScript or TypeScript, reproduce the named React component's uniform construction. Apply every conversion and special requirement listed in `usage.md` and the matching shader reference. +6. Give the mount element an explicit rendered size. Keep shader props separate from layout styles and ordinary DOM props. +7. Verify with the project's type-check/build and, when rendering is available, inspect the result at the intended dimensions. + +## Source authority + +- Treat `packages/shaders/src/shaders/*.ts` and `packages/shaders-react/src/shaders/*.tsx` as authoritative for runtime behavior, types, defaults, enum mappings, and uniforms. +- Treat `packages/shaders/src/shader-mount.ts`, `packages/shaders/src/shader-sizing.ts`, and `packages/shaders-react/src/shader-mount.tsx` as authoritative for mounting, images, sizing, motion, and performance controls. +- Treat numeric ranges and steps in `docs/src/shader-defs/*-def.ts` as editor guidance, not runtime validation. Components pass values through without clamping. +- Resolve source contradictions in favor of the shader implementation and its exported types/constants. Never repeat a stale prose claim when an array size, type, constant, or component mapping contradicts it. +- Recheck current source when working against a different repository revision. Do not assume this reference overrides changed code. + +## Implementation rules + +- Import only public exports from the package entry point. +- Use `@paper-design/shaders-react` for named React components and presets. +- Use `@paper-design/shaders` for `ShaderMount`, fragment shaders, uniform types, enum maps, color conversion, noise textures, and image preprocessors. +- Supply all required initial vanilla uniforms. `ShaderMount` records uniform locations from the constructor's initial uniform object; later partial updates cannot introduce an unregistered uniform. +- Dispose vanilla mounts during teardown. +- Keep color arrays non-empty and do not exceed the shader reference's implementation capacity for colors or other fixed-size loops. +- Do not pass CSS named colors. The source color parser accepts hex, `rgb`/`rgba`, and `hsl`/`hsla` syntax. +- Do not use deprecated React aliases in new code. + +## References + +- [Usage and integration](references/usage.md): React, vanilla, common controls, uniform conversion, images, lifecycle, and special cases. + +## Shader references + +Read only the file for the shader being used: + +- [Color Panels](references/shaders/color-panels.md) +- [Dithering](references/shaders/dithering.md) +- [Dot Grid](references/shaders/dot-grid.md) +- [Dot Orbit](references/shaders/dot-orbit.md) +- [Fluted Glass](references/shaders/fluted-glass.md) +- [Gem Smoke](references/shaders/gem-smoke.md) +- [God Rays](references/shaders/god-rays.md) +- [Grain Gradient](references/shaders/grain-gradient.md) +- [Halftone CMYK](references/shaders/halftone-cmyk.md) +- [Halftone Dots](references/shaders/halftone-dots.md) +- [Heatmap](references/shaders/heatmap.md) +- [Image Dithering](references/shaders/image-dithering.md) +- [Liquid Metal](references/shaders/liquid-metal.md) +- [Mesh Gradient](references/shaders/mesh-gradient.md) +- [Metaballs](references/shaders/metaballs.md) +- [Neuro Noise](references/shaders/neuro-noise.md) +- [Paper Texture](references/shaders/paper-texture.md) +- [Perlin Noise](references/shaders/perlin-noise.md) +- [Pulsing Border](references/shaders/pulsing-border.md) +- [Simplex Noise](references/shaders/simplex-noise.md) +- [Smoke Ring](references/shaders/smoke-ring.md) +- [Spiral](references/shaders/spiral.md) +- [Static Mesh Gradient](references/shaders/static-mesh-gradient.md) +- [Static Radial Gradient](references/shaders/static-radial-gradient.md) +- [Swirl](references/shaders/swirl.md) +- [Voronoi](references/shaders/voronoi.md) +- [Warp](references/shaders/warp.md) +- [Water](references/shaders/water.md) +- [Waves](references/shaders/waves.md) diff --git a/skills/paper-shaders/references/shaders/color-panels.md b/skills/paper-shaders/references/shaders/color-panels.md new file mode 100644 index 00000000..15c33e5f --- /dev/null +++ b/skills/paper-shaders/references/shaders/color-panels.md @@ -0,0 +1,24 @@ +# Color Panels + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Pseudo-3D semi-transparent panels rotating around a central axis. + +- React: `ColorPanels` and `colorPanelsPresets` from `@paper-design/shaders-react`. +- Vanilla: `colorPanelsFragmentShader` and `ColorPanelsParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0.5, frame=0, fit="contain", scale=0.8, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/color-panels.ts`, `packages/shaders-react/src/shaders/color-panels.tsx`, `docs/src/shader-defs/color-panels-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colors` | `string[]` | no | `["#ff9d00","#fd4f30","#809bff","#6d2eff","#333aff","#f15cff","#ffd557"]` | — | Colors used by the shader; implementation capacity is 7. | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `angle1` | `number` | no | `0` | editor range: -1…1 | Skew angle applied to all panes | +| `angle2` | `number` | no | `0` | editor range: -1…1 | Skew angle applied to all panes | +| `length` | `number` | no | `1.1` | editor range: 0…3 | Panel length (relative to total height) | +| `edges` | `boolean` | no | `false` | options: "true", "false" | Color highlight on the panels edges | +| `blur` | `number` | no | `0` | editor range: 0…0.5 | Side blur (0 for sharp edges) | +| `fadeIn` | `number` | no | `1` | editor range: 0…1 | Transparency near central axis | +| `fadeOut` | `number` | no | `0.3` | editor range: 0…1 | Transparency near viewer | +| `density` | `number` | no | `3` | editor range: 0.25…7 | Controls the angular spacing between panels | +| `gradient` | `number` | no | `0` | editor range: 0…1 | Color mixing within a panel (0 = solid panel color, 1 = gradient of two colors) | diff --git a/skills/paper-shaders/references/shaders/dithering.md b/skills/paper-shaders/references/shaders/dithering.md new file mode 100644 index 00000000..65d6a2e4 --- /dev/null +++ b/skills/paper-shaders/references/shaders/dithering.md @@ -0,0 +1,19 @@ +# Dithering + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Animated 2-color dithering over multiple pattern sources (noise, warp, dots, waves, ripple, swirl, sphere). + +- React: `Dithering` and `ditheringPresets` from `@paper-design/shaders-react`. +- Vanilla: `ditheringFragmentShader` and `DitheringParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="none", scale=0.6, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/dithering.ts`, `packages/shaders-react/src/shaders/dithering.tsx`, `docs/src/shader-defs/dithering-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colorFront` | `string` | no | `"#00b2ff"` | — | The foreground (ink) color | +| `shape` | `DitheringShape` | no | `"sphere"` | options: "simplex", "warp", "dots", "wave", "ripple", "swirl", "sphere" | Shape pattern type | +| `type` | `DitheringType` | no | `"4x4"` | options: "random", "2x2", "4x4", "8x8" | Dithering type | +| `size` | `number` | no | `2` | editor range: 1…20; shader source documents 0.5…20 | Pixel size of dithering grid | +| `pxSize` | `number` | no | — | — | React-only. @deprecated use `size` instead | diff --git a/skills/paper-shaders/references/shaders/dot-grid.md b/skills/paper-shaders/references/shaders/dot-grid.md new file mode 100644 index 00000000..37fc46d2 --- /dev/null +++ b/skills/paper-shaders/references/shaders/dot-grid.md @@ -0,0 +1,24 @@ +# Dot Grid + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Static grid pattern made of circles, diamonds, squares or triangles. + +- React: `DotGrid` and `dotGridPresets` from `@paper-design/shaders-react`. +- Vanilla: `dotGridFragmentShader` and `DotGridParams` from `@paper-design/shaders`. +- Common controls: sizing. Defaults: fit="none", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- React performance override: `maxPixelCount` defaults to `6016 * 3384`. +- Source: `packages/shaders/src/shaders/dot-grid.ts`, `packages/shaders-react/src/shaders/dot-grid.tsx`, `docs/src/shader-defs/dot-grid-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colorFill` | `string` | no | `"#ffffff"` | — | Shape fill color | +| `colorStroke` | `string` | no | `"#ffaa00"` | — | Shape stroke color | +| `size` | `number` | no | `2` | editor range: 1…100 | Base size of each shape, pixels | +| `gapX` | `number` | no | `32` | editor range: 2…500 | Pattern horizontal spacing, pixels | +| `gapY` | `number` | no | `32` | editor range: 2…500 | Pattern vertical spacing, pixels | +| `strokeWidth` | `number` | no | `0` | editor range: 0…50 | The outline stroke width, pixels | +| `sizeRange` | `number` | no | `0` | editor range: 0…1 | Random variation in shape size (0 = uniform size, higher = random value up to base size) | +| `opacityRange` | `number` | no | `0` | editor range: 0…1 | Random variation in shape opacity (0 = all shapes opaque, higher = semi-transparent dots) | +| `shape` | `DotGridShape` | no | `"circle"` | options: "circle", "diamond", "square", "triangle" | The shape type | diff --git a/skills/paper-shaders/references/shaders/dot-orbit.md b/skills/paper-shaders/references/shaders/dot-orbit.md new file mode 100644 index 00000000..915f8729 --- /dev/null +++ b/skills/paper-shaders/references/shaders/dot-orbit.md @@ -0,0 +1,20 @@ +# Dot Orbit + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Animated multi-color dots pattern with each dot orbiting around its cell center. Supports up to 10 colors and various shape and motion controls. + +- React: `DotOrbit` and `dotOrbitPresets` from `@paper-design/shaders-react`. +- Vanilla: `dotOrbitFragmentShader` and `DotOrbitParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1.5, frame=0, fit="none", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`. +- Source: `packages/shaders/src/shaders/dot-orbit.ts`, `packages/shaders-react/src/shaders/dot-orbit.tsx`, `docs/src/shader-defs/dot-orbit-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colors` | `string[]` | no | `["#ffc96b","#ff6200","#ff2f00","#421100","#1a0000"]` | — | Colors used by the shader; implementation capacity is 10. | +| `size` | `number` | no | `1` | editor range: 0…1 | Dot radius relative to cell size | +| `sizeRange` | `number` | no | `0` | editor range: 0…1 | Random variation in shape size (0 = uniform size, higher = random value up to base size) | +| `spreading` | `number` | no | `1` | editor range: 0…1 | Maximum orbit distance | +| `stepsPerColor` | `number` | no | `4` | editor range: 1…4; step: 1 | Number of extra colors between base colors (1 = N color palette, 2 = 2×N color palette, 3 = 3×N color palette, etc) | diff --git a/skills/paper-shaders/references/shaders/fluted-glass.md b/skills/paper-shaders/references/shaders/fluted-glass.md new file mode 100644 index 00000000..b55f189c --- /dev/null +++ b/skills/paper-shaders/references/shaders/fluted-glass.md @@ -0,0 +1,38 @@ +# Fluted Glass + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Fluted glass image filter that transforms an image into streaked, ribbed distortions, giving a mix of clarity and obscurity. + +- React: `FlutedGlass` and `flutedGlassPresets` from `@paper-design/shaders-react`. +- Vanilla: `flutedGlassFragmentShader` and `FlutedGlassParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0, frame=0, fit="cover", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: pass `["u_image"]` as the `ShaderMount` mipmaps argument. +- Enum source note: the documentation UI definition contains stale `facete`; the exported `GlassDistortionShapes` mapping and component type use `flat`. +- Source: `packages/shaders/src/shaders/fluted-glass.ts`, `packages/shaders-react/src/shaders/fluted-glass.tsx`, `docs/src/shader-defs/fluted-glass-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `image` | `HTMLImageElement \| string` | no | `""` | — | The image to use for the effect | +| `colorBack` | `string` | no | `"#00000000"` | — | Background color | +| `colorShadow` | `string` | no | `"#000000"` | — | Shadows color | +| `colorHighlight` | `string` | no | `"#ffffff"` | — | Highlights color | +| `shadows` | `number` | no | `0.25` | editor range: 0…1 | A color gradient added over both image and background, following the distortion shape | +| `size` | `number` | no | `0.5` | editor range: 0…1; step: 0.001 | The size of the distortion shape grid | +| `angle` | `number` | no | `0` | editor range: 0…180 | Direction of the grid relative to the image | +| `distortion` | `number` | no | `0.5` | editor range: 0…1 | The power of distortion applied within each stripe | +| `shift` | `number` | no | `0` | editor range: -1…1 | Texture shift in direction opposite to the grid | +| `blur` | `number` | no | `0` | editor range: 0…1 | One-directional blur over the image and extra blur around the edges | +| `edges` | `number` | no | `0.25` | editor range: 0…1 | Glass distortion and softness on the image edges | +| `margin` | `number` | no | `0` | editor range: 0…1 | Distance from image edges to the effect | +| `marginLeft` | `number` | no | `0` | editor range: 0…1 | Distance from the left edge to the effect | +| `marginRight` | `number` | no | `0` | editor range: 0…1 | Distance from the right edge to the effect | +| `marginTop` | `number` | no | `0` | editor range: 0…1 | Distance from the top edge to the effect | +| `marginBottom` | `number` | no | `0` | editor range: 0…1 | Distance from the bottom edge to the effect | +| `stretch` | `number` | no | `0` | editor range: 0…1 | Extra distortion along the grid lines | +| `distortionShape` | `GlassDistortionShape` | no | `"prism"` | options: "prism", "lens", "contour", "cascade", "flat" | The shape of the distortion | +| `highlights` | `number` | no | `0.1` | editor range: 0…1 | Thin strokes along the distortion shape; useful for antialiasing on a small grid | +| `shape` | `GlassGridShape` | no | `"lines"` | options: "lines", "linesIrregular", "wave", "zigzag", "pattern" | The shape of the grid | +| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to the shapes’ edges | +| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing b/w grain overlay | +| `count` | `number` | no | — | — | React-only. @deprecated use `size` instead | diff --git a/skills/paper-shaders/references/shaders/gem-smoke.md b/skills/paper-shaders/references/shaders/gem-smoke.md new file mode 100644 index 00000000..0416f283 --- /dev/null +++ b/skills/paper-shaders/references/shaders/gem-smoke.md @@ -0,0 +1,27 @@ +# Gem Smoke + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Animated color fields placed over uploaded logo shape; gives the illusion of smoky noise behind the glassy shape. + +- React: `GemSmoke` and `gemSmokePresets` from `@paper-design/shaders-react`. +- Vanilla: `gemSmokeFragmentShader` and `GemSmokeParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="contain", scale=0.6, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: preprocess image input with `toProcessedGemSmoke`; pass `["u_image"]` as the `ShaderMount` mipmaps argument. +- Source: `packages/shaders/src/shaders/gem-smoke.ts`, `packages/shaders-react/src/shaders/gem-smoke.tsx`, `docs/src/shader-defs/gem-smoke-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colors` | `string[]` | no | `["#333333","#e7e6df"]` | — | Colors used by the shader; implementation capacity is 6. | +| `colorBack` | `string` | no | `"#f0efea"` | — | Background color | +| `image` | `HTMLImageElement \| string \| undefined` | no | `""` | — | An optional image used as an effect mask. A transparent background is required. If no image is provided, the shader defaults to one of the predefined shapes. | +| `innerDistortion` | `number` | no | `0.8` | editor range: 0…1 | The power of smoke distortion inside the input shape (shape defined by alpha channel) | +| `outerDistortion` | `number` | no | `0.6` | editor range: 0…1 | The power of smoke distortion outside the input shape (shape defined by alpha channel) | +| `outerGlow` | `number` | no | `0.55` | editor range: 0…1 | The visibility of smoke shape out of the input shape (shape defined by alpha channel) | +| `innerGlow` | `number` | no | `1` | editor range: 0…1 | The visibility of smoke shape inside the input shape (shape defined by alpha channel) | +| `colorInner` | `string` | no | `"#fafaf5"` | — | Additional color inside the input shape, mixing with smoke | +| `offset` | `number` | no | `0` | editor range: -1…1 | Vertical offset of smoke inside the shape | +| `angle` | `number` | no | `0` | editor range: 0…360 | Smoke direction | +| `size` | `number` | no | `0.8` | editor range: 0…1 | The size of smoke shape relative to the image box | +| `shape` | `GemSmokeShape` | no | `"diamond"` | options: "none", "circle", "daisy", "diamond", "metaballs" | The predefined shape used as an effect mask when no image is provided. | +| `suspendWhenProcessingImage` | `boolean` | no | `false` | — | React-only. Suspends the component when the image is being processed. | diff --git a/skills/paper-shaders/references/shaders/god-rays.md b/skills/paper-shaders/references/shaders/god-rays.md new file mode 100644 index 00000000..156b0802 --- /dev/null +++ b/skills/paper-shaders/references/shaders/god-rays.md @@ -0,0 +1,23 @@ +# God Rays + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Animated rays of light radiating from the center, blended with up to 5 colors. + +- React: `GodRays` and `godRaysPresets` from `@paper-design/shaders-react`. +- Vanilla: `godRaysFragmentShader` and `GodRaysParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0.75, frame=0, fit="contain", scale=1, rotation=0, offsetX=0, offsetY=-0.55, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`. +- Source: `packages/shaders/src/shaders/god-rays.ts`, `packages/shaders-react/src/shaders/god-rays.tsx`, `docs/src/shader-defs/god-rays-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colorBloom` | `string` | no | `"#0000ff"` | — | Color overlay blended with the rays | +| `colors` | `string[]` | no | `["#a600ff6e","#6200fff0","#ffffff","#33fff5"]` | — | Colors used by the shader; implementation capacity is 5. | +| `spotty` | `number` | no | `0.3` | editor range: 0…1 | The length of the rays | +| `midSize` | `number` | no | `0.2` | editor range: 0…1 | Size of the circular glow shape in the center | +| `midIntensity` | `number` | no | `0.4` | editor range: 0…1 | Brightness/intensity of the central glow | +| `density` | `number` | no | `0.3` | editor range: 0…1 | The number of rays | +| `intensity` | `number` | no | `0.8` | editor range: 0…1 | Visibility/strength of the rays | +| `bloom` | `number` | no | `0.4` | editor range: 0…1 | Strength of the bloom/overlay effect | diff --git a/skills/paper-shaders/references/shaders/grain-gradient.md b/skills/paper-shaders/references/shaders/grain-gradient.md new file mode 100644 index 00000000..fd313104 --- /dev/null +++ b/skills/paper-shaders/references/shaders/grain-gradient.md @@ -0,0 +1,20 @@ +# Grain Gradient + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Multi-color gradients with grainy, noise-textured distortion available in 7 animated abstract forms. + +- React: `GrainGradient` and `grainGradientPresets` from `@paper-design/shaders-react`. +- Vanilla: `grainGradientFragmentShader` and `GrainGradientParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="contain", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`. +- Source: `packages/shaders/src/shaders/grain-gradient.ts`, `packages/shaders-react/src/shaders/grain-gradient.tsx`, `docs/src/shader-defs/grain-gradient-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colors` | `string[]` | no | `["#7300ff","#eba8ff","#00bfff","#2a00ff"]` | — | Colors used by the shader; implementation capacity is 7. | +| `softness` | `number` | no | `0.5` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | +| `intensity` | `number` | no | `0.5` | editor range: 0…1 | Distortion between color bands | +| `noise` | `number` | no | `0.25` | editor range: 0…1 | Grainy noise overlay | +| `shape` | `GrainGradientShape` | no | `"corners"` | options: "wave", "dots", "truchet", "corners", "ripple", "blob", "sphere" | Shape type | diff --git a/skills/paper-shaders/references/shaders/halftone-cmyk.md b/skills/paper-shaders/references/shaders/halftone-cmyk.md new file mode 100644 index 00000000..36e2d705 --- /dev/null +++ b/skills/paper-shaders/references/shaders/halftone-cmyk.md @@ -0,0 +1,36 @@ +# Halftone CMYK + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +CMYK halftone printing effect applied to images with customizable dot patterns and ink colors for each channel (Cyan, Magenta, Yellow, Black). + +- React: `HalftoneCmyk` and `halftoneCmykPresets` from `@paper-design/shaders-react`. +- Vanilla: `halftoneCmykFragmentShader` and `HalftoneCmykParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0, frame=0, fit="cover", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`. +- Source: `packages/shaders/src/shaders/halftone-cmyk.ts`, `packages/shaders-react/src/shaders/halftone-cmyk.tsx`, `docs/src/shader-defs/halftone-cmyk-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `image` | `HTMLImageElement \| string` | no | `""` | — | The image to use for the effect | +| `colorBack` | `string` | no | `"#fbfaf5"` | — | Background (paper) color | +| `colorC` | `string` | no | `"#00b4ff"` | — | Cyan ink color (alpha controls layer transparency, not dot size) | +| `colorM` | `string` | no | `"#fc519f"` | — | Magenta ink color (alpha controls layer transparency, not dot size) | +| `colorY` | `string` | no | `"#ffd800"` | — | Yellow ink color (alpha controls layer transparency, not dot size) | +| `colorK` | `string` | no | `"#231f20"` | — | Black ink color (alpha controls layer transparency, not dot size) | +| `size` | `number` | no | `0.2` | editor range: 0…1 | Grid size (relative to image box) | +| `contrast` | `number` | no | `1` | editor range: 0…2 | Input image contrast | +| `softness` | `number` | no | `1` | editor range: 0…1 | Dots edge softness | +| `grainSize` | `number` | no | `0.5` | editor range: 0…1 | Size of grain overlay texture (relative to image box) | +| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain affecting dot size | +| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Strength of the black-and-white grain overlay on the final output | +| `gridNoise` | `number` | no | `0.2` | editor range: 0…1 | Displaces both dot positions and color sampling points; naturally makes the background more visible | +| `floodC` | `number` | no | `0.15` | editor range: 0…1; shader source documents -1…1 | Flat cyan dot-size adjustment applied uniformly | +| `floodM` | `number` | no | `0` | editor range: 0…1; shader source documents -1…1 | Flat magenta dot-size adjustment applied uniformly | +| `floodY` | `number` | no | `0` | editor range: 0…1; shader source documents -1…1 | Flat yellow dot-size adjustment applied uniformly | +| `floodK` | `number` | no | `0` | editor range: 0…1; shader source documents -1…1 | Flat black dot-size adjustment applied uniformly | +| `gainC` | `number` | no | `0.3` | editor range: -1…1 | Proportional cyan dot-size gain that enhances existing dots | +| `gainM` | `number` | no | `0` | editor range: -1…1 | Proportional magenta dot-size gain that enhances existing dots | +| `gainY` | `number` | no | `0.2` | editor range: -1…1 | Proportional yellow dot-size gain that enhances existing dots | +| `gainK` | `number` | no | `0` | editor range: -1…1 | Proportional black dot-size gain that enhances existing dots | +| `type` | `HalftoneCmykType` | no | `"ink"` | options: "dots", "ink", "sharp" | Dot type style (the difference between dots and ink is visible only with low softness) | diff --git a/skills/paper-shaders/references/shaders/halftone-dots.md b/skills/paper-shaders/references/shaders/halftone-dots.md new file mode 100644 index 00000000..9b1b7bcb --- /dev/null +++ b/skills/paper-shaders/references/shaders/halftone-dots.md @@ -0,0 +1,26 @@ +# Halftone Dots + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +A halftone-dot image filter featuring customizable grids, color palettes, and dot styles. + +- React: `HalftoneDots` and `halftoneDotsPresets` from `@paper-design/shaders-react`. +- Vanilla: `halftoneDotsFragmentShader` and `HalftoneDotsParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0, frame=0, fit="cover", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/halftone-dots.ts`, `packages/shaders-react/src/shaders/halftone-dots.tsx`, `docs/src/shader-defs/halftone-dots-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `image` | `HTMLImageElement \| string` | no | `""` | — | The image to use for the effect | +| `colorFront` | `string` | no | `"#2b2b2b"` | — | Foreground color | +| `colorBack` | `string` | no | `"#f2f1e8"` | — | Background color | +| `size` | `number` | no | `0.5` | editor range: 0…1 | Grid size relative to the image box | +| `grid` | `HalftoneDotsGrid` | no | `"hex"` | options: "square", "hex" | Dots grid type | +| `radius` | `number` | no | `1.25` | editor range: 0…2 | Maximum dot size (relative to the grid cell) | +| `contrast` | `number` | no | `0.4` | editor range: 0…1 | Contrast applied to the sampled image | +| `originalColors` | `boolean` | no | `false` | options: "true", "false" | Use the sampled image’s original colors instead of colorFront | +| `inverted` | `boolean` | no | `false` | options: "true", "false" | Inverts the image luminance, doesn’t affect the color scheme; not effective at zero contrast | +| `grainMixer` | `number` | no | `0.2` | editor range: 0…1 | Strength of grain distortion applied to shape edges | +| `grainOverlay` | `number` | no | `0.2` | editor range: 0…1 | Post-processing b/w grain overlay | +| `grainSize` | `number` | no | `0.5` | editor range: 0…1 | The scale applied to both grain distortion and grain overlay | +| `type` | `HalftoneDotsType` | no | `"gooey"` | options: "classic", "gooey", "holes", "soft" | Dot style | diff --git a/skills/paper-shaders/references/shaders/heatmap.md b/skills/paper-shaders/references/shaders/heatmap.md new file mode 100644 index 00000000..e214657d --- /dev/null +++ b/skills/paper-shaders/references/shaders/heatmap.md @@ -0,0 +1,23 @@ +# Heatmap + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +A glowing gradient of colors flowing through an input shape. The effect creates a smoothly animated wave of intensity across the image. + +- React: `Heatmap` and `heatmapPresets` from `@paper-design/shaders-react`. +- Vanilla: `heatmapFragmentShader` and `HeatmapParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="contain", scale=0.75, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: preprocess image input with `toProcessedHeatmap`; pass `["u_image"]` as the `ShaderMount` mipmaps argument. +- Source: `packages/shaders/src/shaders/heatmap.ts`, `packages/shaders-react/src/shaders/heatmap.tsx`, `docs/src/shader-defs/heatmap-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `image` | `HTMLImageElement \| string` | yes | `""` | — | The image to use for the effect | +| `contour` | `number` | no | `0.5` | editor range: 0…1 | The heat intensity near the edges of the input shape | +| `angle` | `number` | no | `0` | editor range: 0…360 | The direction of the heatwaves (angle relative to the shape) | +| `noise` | `number` | no | `0` | editor range: 0…1 | Grain applied across the entire graphic | +| `innerGlow` | `number` | no | `0.5` | editor range: 0…1 | The size of the heated area inside the input shape | +| `outerGlow` | `number` | no | `0.5` | editor range: 0…1 | Size of the heated area outside the input shape | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colors` | `string[]` | no | `["#11206a","#1f3ba2","#2f63e7","#6bd7ff","#ffe679","#ff991e","#ff4c00"]` | — | Colors used by the shader; implementation capacity is 10. | +| `suspendWhenProcessingImage` | `boolean` | no | `false` | — | React-only. Suspends the component when the image is being processed. | diff --git a/skills/paper-shaders/references/shaders/image-dithering.md b/skills/paper-shaders/references/shaders/image-dithering.md new file mode 100644 index 00000000..c30f1ddf --- /dev/null +++ b/skills/paper-shaders/references/shaders/image-dithering.md @@ -0,0 +1,23 @@ +# Image Dithering + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +A dithering image filter with support for 4 dithering modes and multiple color palettes (2-color, 3-color, and multicolor options, using either predefined colors or colors sampled from the original image). + +- React: `ImageDithering` and `imageDitheringPresets` from `@paper-design/shaders-react`. +- Vanilla: `imageDitheringFragmentShader` and `ImageDitheringParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0, frame=0, fit="cover", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/image-dithering.ts`, `packages/shaders-react/src/shaders/image-dithering.tsx`, `docs/src/shader-defs/image-dithering-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `image` | `HTMLImageElement \| string` | yes | `""` | — | The image to use for the effect | +| `colorFront` | `string` | no | `"#94ffaf"` | — | Foreground color | +| `colorBack` | `string` | no | `"#000c38"` | — | Background color | +| `colorHighlight` | `string` | no | `"#eaff94"` | — | The secondary foreground color (set it same as colorFront to get a classic 2-color dithering) | +| `type` | `DitheringType` | no | `"8x8"` | options: "random", "2x2", "4x4", "8x8" | Dithering type | +| `size` | `number` | no | `2` | editor range: 0.5…20 | Pixel size of dithering grid; linked to the screen space, not to the image box | +| `colorSteps` | `number` | no | `2` | editor range: 1…7; step: 1 | Number of colors to use (applies to both color modes) | +| `originalColors` | `boolean` | no | `false` | options: "true", "false" | Use the original colors of the image | +| `inverted` | `boolean` | no | `false` | — | Inverts image luminance without changing the color scheme. | +| `pxSize` | `number` | no | — | — | React-only. @deprecated use `size` instead | diff --git a/skills/paper-shaders/references/shaders/liquid-metal.md b/skills/paper-shaders/references/shaders/liquid-metal.md new file mode 100644 index 00000000..ce2866dc --- /dev/null +++ b/skills/paper-shaders/references/shaders/liquid-metal.md @@ -0,0 +1,26 @@ +# Liquid Metal + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Futuristic liquid metal material applied to uploaded logo or abstract shape. Fluid motion imitation applied over user image with animated stripe pattern getting distorted along shape edges. + +- React: `LiquidMetal` and `liquidMetalPresets` from `@paper-design/shaders-react`. +- Vanilla: `liquidMetalFragmentShader` and `LiquidMetalParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="contain", scale=0.6, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: preprocess image input with `toProcessedLiquidMetal`; pass `["u_image"]` as the `ShaderMount` mipmaps argument. +- Source: `packages/shaders/src/shaders/liquid-metal.ts`, `packages/shaders-react/src/shaders/liquid-metal.tsx`, `docs/src/shader-defs/liquid-metal-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#AAAAAC"` | — | Background color | +| `colorTint` | `string` | no | `"#ffffff"` | — | Overlay color (color burn blending used) | +| `image` | `HTMLImageElement \| string \| undefined` | no | `""` | — | An optional image used as an effect mask. A transparent background is required. If no image is provided, the shader defaults to one of the predefined shapes. | +| `repetition` | `number` | no | `2` | editor range: 1…10 | Density of pattern stripes | +| `shiftRed` | `number` | no | `0.3` | editor range: -1…1 | R-channel dispersion | +| `shiftBlue` | `number` | no | `0.3` | editor range: -1…1 | B-channel dispersion | +| `contour` | `number` | no | `0.4` | editor range: 0…1 | Strength of the distortion on the shape edges | +| `softness` | `number` | no | `0.1` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | +| `distortion` | `number` | no | `0.07` | editor range: 0…1 | Noise distortion over the stripes pattern | +| `angle` | `number` | no | `70` | editor range: 0…360 | The direction of pattern animation (angle relative to the shape) | +| `shape` | `LiquidMetalShape` | no | `"diamond"` | options: "none", "circle", "daisy", "diamond", "metaballs" | The predefined shape used as an effect mask when no image is provided. | +| `suspendWhenProcessingImage` | `boolean` | no | `false` | — | React-only. Suspends the component when the image is being processed. | diff --git a/skills/paper-shaders/references/shaders/mesh-gradient.md b/skills/paper-shaders/references/shaders/mesh-gradient.md new file mode 100644 index 00000000..2b9c3ef9 --- /dev/null +++ b/skills/paper-shaders/references/shaders/mesh-gradient.md @@ -0,0 +1,18 @@ +# Mesh Gradient + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +A flowing composition of color spots, moving along distinct trajectories and transformed by organic distortion. + +- React: `MeshGradient` and `meshGradientPresets` from `@paper-design/shaders-react`. +- Vanilla: `meshGradientFragmentShader` and `MeshGradientParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="contain", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/mesh-gradient.ts`, `packages/shaders-react/src/shaders/mesh-gradient.tsx`, `docs/src/shader-defs/mesh-gradient-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colors` | `string[]` | no | `["#e0eaff","#241d9a","#f75092","#9f50d3"]` | — | Colors used by the shader; implementation capacity is 10. | +| `distortion` | `number` | no | `0.8` | editor range: 0…1 | The power of organic noise distortion | +| `swirl` | `number` | no | `0.1` | editor range: 0…1 | The power of vortex distortion | +| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to the shapes’ edges | +| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing b/w grain overlay | diff --git a/skills/paper-shaders/references/shaders/metaballs.md b/skills/paper-shaders/references/shaders/metaballs.md new file mode 100644 index 00000000..ac714179 --- /dev/null +++ b/skills/paper-shaders/references/shaders/metaballs.md @@ -0,0 +1,18 @@ +# Metaballs + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Up to 20 colored gooey balls moving around the center and merging into smooth organic shapes. + +- React: `Metaballs` and `metaballsPresets` from `@paper-design/shaders-react`. +- Vanilla: `metaballsFragmentShader` and `MetaballsParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="contain", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`. +- Source: `packages/shaders/src/shaders/metaballs.ts`, `packages/shaders-react/src/shaders/metaballs.tsx`, `docs/src/shader-defs/metaballs-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colors` | `string[]` | no | `["#6e33cc","#ff5500","#ffc105","#ffc800","#f585ff"]` | — | Colors used by the shader; implementation capacity is 8. | +| `count` | `number` | no | `10` | editor range: 1…20 | Number of balls | +| `size` | `number` | no | `0.83` | editor range: 0…1 | The size of the balls | diff --git a/skills/paper-shaders/references/shaders/neuro-noise.md b/skills/paper-shaders/references/shaders/neuro-noise.md new file mode 100644 index 00000000..c383ac7c --- /dev/null +++ b/skills/paper-shaders/references/shaders/neuro-noise.md @@ -0,0 +1,18 @@ +# Neuro Noise + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +A glowing, web-like structure of fluid lines and soft intersections. Great for creating atmospheric, organic-yet-futuristic visuals. + +- React: `NeuroNoise` and `neuroNoisePresets` from `@paper-design/shaders-react`. +- Vanilla: `neuroNoiseFragmentShader` and `NeuroNoiseParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="none", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/neuro-noise.ts`, `packages/shaders-react/src/shaders/neuro-noise.tsx`, `docs/src/shader-defs/neuro-noise-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorFront` | `string` | no | `"#ffffff"` | — | Graphics highlight color | +| `colorMid` | `string` | no | `"#47a6ff"` | — | Graphics main color | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `brightness` | `number` | no | `0.05` | editor range: 0…1 | Luminosity of the crossing points | +| `contrast` | `number` | no | `0.3` | editor range: 0…1 | Sharpness of the bright–dark transition | diff --git a/skills/paper-shaders/references/shaders/paper-texture.md b/skills/paper-shaders/references/shaders/paper-texture.md new file mode 100644 index 00000000..afa71fc0 --- /dev/null +++ b/skills/paper-shaders/references/shaders/paper-texture.md @@ -0,0 +1,32 @@ +# Paper Texture + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +A static texture built from multiple noise layers, usable for realistic paper and cardboard surfaces. Can be used as an image filter or as a standalone texture. + +- React: `PaperTexture` and `paperTexturePresets` from `@paper-design/shaders-react`. +- Vanilla: `paperTextureFragmentShader` and `PaperTextureParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0, frame=0, fit="cover", scale=0.6, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`; pass `["u_image"]` as the `ShaderMount` mipmaps argument. +- Source: `packages/shaders/src/shaders/paper-texture.ts`, `packages/shaders-react/src/shaders/paper-texture.tsx`, `docs/src/shader-defs/paper-texture-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `image` | `HTMLImageElement \| string` | no | `""` | — | The image to use for the effect | +| `colorFront` | `string` | no | `"#9fadbc"` | — | Foreground color | +| `colorBack` | `string` | no | `"#ffffff"` | — | Background color | +| `contrast` | `number` | no | `0.3` | editor range: 0…1 | Blending behavior (sharper vs. smoother color transitions) | +| `roughness` | `number` | no | `0.4` | editor range: 0…1 | Pixel noise, related to canvas (not scalable) | +| `fiber` | `number` | no | `0.3` | editor range: 0…1 | Curly-shaped noise | +| `fiberSize` | `number` | no | `0.2` | editor range: 0…1 | Curly-shaped noise scale | +| `crumples` | `number` | no | `0.3` | editor range: 0…1 | Cell-based crumple pattern | +| `foldCount` | `number` | no | `5` | editor range: 1…15; step: 1 | Number of folds (15 max) | +| `folds` | `number` | no | `0.65` | editor range: 0…1 | Depth of the folds | +| `fade` | `number` | no | `0` | editor range: 0…1 | Big-scale noise mask applied to the pattern | +| `crumpleSize` | `number` | no | `0.35` | editor range: 0…1 | Cell-based crumple pattern scale | +| `drops` | `number` | no | `0.2` | editor range: 0…1 | The visibility of speckle pattern | +| `seed` | `number` | no | `5.8` | editor range: 0…1000 | Seed applied to folds, crumples and dots | +| `fiberScale` | `number` | no | — | — | React-only. @deprecated use `fiberSize` instead | +| `crumplesScale` | `number` | no | — | — | React-only. @deprecated use `crumpleSize` instead | +| `foldsNumber` | `number` | no | — | — | React-only. @deprecated use `foldCount` instead | +| `blur` | `number` | no | — | — | React-only. @deprecated use `fade` instead | diff --git a/skills/paper-shaders/references/shaders/perlin-noise.md b/skills/paper-shaders/references/shaders/perlin-noise.md new file mode 100644 index 00000000..c0604554 --- /dev/null +++ b/skills/paper-shaders/references/shaders/perlin-noise.md @@ -0,0 +1,20 @@ +# Perlin Noise + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Classic animated 3D Perlin noise with exposed controls. Original algorithm: https://www.shadertoy.com/view/NlSGDz + +- React: `PerlinNoise` and `perlinNoisePresets` from `@paper-design/shaders-react`. +- Vanilla: `perlinNoiseFragmentShader` and `PerlinNoiseParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0.5, frame=0, fit="none", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/perlin-noise.ts`, `packages/shaders-react/src/shaders/perlin-noise.tsx`, `docs/src/shader-defs/perlin-noise-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorFront` | `string` | no | `"#fccff7"` | — | Foreground color | +| `colorBack` | `string` | no | `"#632ad5"` | — | Background color | +| `proportion` | `number` | no | `0.35` | editor range: 0…1 | Blend point between 2 colors (0.5 = equal distribution) | +| `softness` | `number` | no | `0.1` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | +| `octaveCount` | `number` | no | `1` | editor range: 1…8; step: 1 | Perlin noise octaves number (more octaves for more detailed patterns) | +| `persistence` | `number` | no | `1` | editor range: 0.3…1 | Roughness, falloff between octaves | +| `lacunarity` | `number` | no | `1.5` | editor range: 1.5…10 | Frequency step, typically around 2. Defines how compressed the pattern is | diff --git a/skills/paper-shaders/references/shaders/pulsing-border.md b/skills/paper-shaders/references/shaders/pulsing-border.md new file mode 100644 index 00000000..4c870d1f --- /dev/null +++ b/skills/paper-shaders/references/shaders/pulsing-border.md @@ -0,0 +1,32 @@ +# Pulsing Border + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Luminous trails of color merging into a glowing gradient contour. + +- React: `PulsingBorder` and `pulsingBorderPresets` from `@paper-design/shaders-react`. +- Vanilla: `pulsingBorderFragmentShader` and `PulsingBorderParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="contain", scale=0.6, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`. +- Source: `packages/shaders/src/shaders/pulsing-border.ts`, `packages/shaders-react/src/shaders/pulsing-border.tsx`, `docs/src/shader-defs/pulsing-border-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colors` | `string[]` | no | `["#0dc1fd","#d915ef","#ff3f2ecc"]` | — | Colors used by the shader; implementation capacity is 5. | +| `roundness` | `number` | no | `0.25` | editor range: 0…1 | The border radius | +| `thickness` | `number` | no | `0.1` | editor range: 0…1 | The border base width | +| `margin` | `number` | no | `0` | editor range: 0…1 | Distance from canvas edges to the effect | +| `marginLeft` | `number` | no | `0` | editor range: 0…1 | Distance from the left edge to the effect | +| `marginRight` | `number` | no | `0` | editor range: 0…1 | Distance from the right edge to the effect | +| `marginTop` | `number` | no | `0` | editor range: 0…1 | Distance from the top edge to the effect | +| `marginBottom` | `number` | no | `0` | editor range: 0…1 | Distance from the bottom edge to the effect | +| `aspectRatio` | `PulsingBorderAspectRatio` | no | `"auto"` | options: "auto", "square" | Aspect ratio of the effect | +| `softness` | `number` | no | `0.75` | editor range: 0…1 | Border edge sharpness (0 = hard edge, 1 = smooth gradient) | +| `intensity` | `number` | no | `0.2` | editor range: 0…1 | Thickness of individual color spots | +| `bloom` | `number` | no | `0.25` | editor range: 0…1 | The power of glow (0 = normal color blending, 1 = fully additive blending) | +| `spots` | `number` | no | `5` | editor range: 1…20; step: 1; shader loop capacity: 4 | Requested spots per color. The shader renders at most 4, so the default value of 5 has the same effective spot count as 4. | +| `spotSize` | `number` | no | `0.5` | editor range: 0…1 | Angular size of spots | +| `pulse` | `number` | no | `0.25` | editor range: 0…1 | Optional pulsing animation | +| `smoke` | `number` | no | `0.3` | editor range: 0…1 | Optional noisy shape extending the border shape | +| `smokeSize` | `number` | no | `0.6` | editor range: 0…1 | The size of the smoke effect (effective with smoke > 0) | diff --git a/skills/paper-shaders/references/shaders/simplex-noise.md b/skills/paper-shaders/references/shaders/simplex-noise.md new file mode 100644 index 00000000..f3aa09e3 --- /dev/null +++ b/skills/paper-shaders/references/shaders/simplex-noise.md @@ -0,0 +1,16 @@ +# Simplex Noise + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +A multi-color gradient mapped into smooth, animated curves built as a combination of 2 Simplex noises. + +- React: `SimplexNoise` and `simplexNoisePresets` from `@paper-design/shaders-react`. +- Vanilla: `simplexNoiseFragmentShader` and `SimplexNoiseParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0.5, frame=0, fit="none", scale=0.6, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/simplex-noise.ts`, `packages/shaders-react/src/shaders/simplex-noise.tsx`, `docs/src/shader-defs/simplex-noise-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colors` | `string[]` | no | `["#4449CF","#FFD1E0","#F94446","#FFD36B","#FFFFFF"]` | — | Colors used by the shader; implementation capacity is 10. | +| `stepsPerColor` | `number` | no | `2` | editor range: 1…10; step: 1 | Number of extra colors between base colors (1 = N color palette, 2 = 2×N color palette, 3 = 3×N color palette, etc) | +| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | diff --git a/skills/paper-shaders/references/shaders/smoke-ring.md b/skills/paper-shaders/references/shaders/smoke-ring.md new file mode 100644 index 00000000..2cda0560 --- /dev/null +++ b/skills/paper-shaders/references/shaders/smoke-ring.md @@ -0,0 +1,21 @@ +# Smoke Ring + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Radial multi-colored gradient shaped with layered noise for a natural, smoky aesthetic. + +- React: `SmokeRing` and `smokeRingPresets` from `@paper-design/shaders-react`. +- Vanilla: `smokeRingFragmentShader` and `SmokeRingParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0.5, frame=0, fit="contain", scale=0.8, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`. +- Source: `packages/shaders/src/shaders/smoke-ring.ts`, `packages/shaders-react/src/shaders/smoke-ring.tsx`, `docs/src/shader-defs/smoke-ring-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colors` | `string[]` | no | `["#ffffff"]` | — | Colors used by the shader; implementation capacity is 10. | +| `noiseScale` | `number` | no | `3` | editor range: 0.01…5 | The noise frequency | +| `thickness` | `number` | no | `0.65` | editor range: 0.01…1 | The thickness of the ring shape | +| `radius` | `number` | no | `0.25` | editor range: 0…1 | The radius of the ring shape | +| `innerShape` | `number` | no | `0.7` | editor range: 0…4 | The ring inner fill | +| `noiseIterations` | `number` | no | `8` | editor range: 1…8; step: 1 | A number of noise layers, more layers gives more details | diff --git a/skills/paper-shaders/references/shaders/spiral.md b/skills/paper-shaders/references/shaders/spiral.md new file mode 100644 index 00000000..7d337a44 --- /dev/null +++ b/skills/paper-shaders/references/shaders/spiral.md @@ -0,0 +1,23 @@ +# Spiral + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +A single-colored animated spiral that morphs across a wide range of shapes - from crisp, thin-lined geometry to flowing whirlpool forms and wavy, abstract rings. + +- React: `Spiral` and `spiralPresets` from `@paper-design/shaders-react`. +- Vanilla: `spiralFragmentShader` and `SpiralParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="none", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/spiral.ts`, `packages/shaders-react/src/shaders/spiral.tsx`, `docs/src/shader-defs/spiral-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#001429"` | — | Background color | +| `colorFront` | `string` | no | `"#79D1FF"` | — | Foreground (ink) color | +| `density` | `number` | no | `1` | editor range: 0…1 | Spacing falloff simulating perspective (0 = flat spiral) | +| `distortion` | `number` | no | `0` | editor range: 0…1 | Power of shape distortion applied along the spiral | +| `strokeWidth` | `number` | no | `0.5` | editor range: 0…1 | Thickness of spiral curve | +| `strokeTaper` | `number` | no | `0` | editor range: 0…1 | How much the stroke loses width away from the center (0 = full visibility) | +| `strokeCap` | `number` | no | `0` | editor range: 0…1 | Extra stroke width at the center (no effect with strokeWidth = 0.5) | +| `noise` | `number` | no | `0` | editor range: 0…1 | Noise distortion applied over the canvas (no effect with noiseFrequency = 0) | +| `noiseFrequency` | `number` | no | `0` | editor range: 0…1 | Noise frequency (no effect with noise = 0) | +| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | diff --git a/skills/paper-shaders/references/shaders/static-mesh-gradient.md b/skills/paper-shaders/references/shaders/static-mesh-gradient.md new file mode 100644 index 00000000..90d72778 --- /dev/null +++ b/skills/paper-shaders/references/shaders/static-mesh-gradient.md @@ -0,0 +1,22 @@ +# Static Mesh Gradient + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Multi-point mesh gradient with up to 10 color spots, enhanced by two-direction warping, adjustable blend sharpness, and grain controls. + +- React: `StaticMeshGradient` and `staticMeshGradientPresets` from `@paper-design/shaders-react`. +- Vanilla: `staticMeshGradientFragmentShader` and `StaticMeshGradientParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0, frame=0, fit="contain", scale=1, rotation=270, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/static-mesh-gradient.ts`, `packages/shaders-react/src/shaders/static-mesh-gradient.tsx`, `docs/src/shader-defs/static-mesh-gradient-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colors` | `string[]` | no | `["#ffad0a","#6200ff","#e2a3ff","#ff99fd"]` | — | Colors used by the shader; implementation capacity is 10. | +| `positions` | `number` | no | `2` | editor range: 0…100 | Color spots placement | +| `waveX` | `number` | no | `1` | editor range: 0…1 | Strength of sine wave distortion along X axis | +| `waveXShift` | `number` | no | `0.6` | editor range: 0…1 | Phase offset applied to the X-axis wave | +| `waveY` | `number` | no | `1` | editor range: 0…1 | Strength of sine wave distortion along Y axis | +| `waveYShift` | `number` | no | `0.21` | editor range: 0…1 | Phase offset applied to the Y-axis wave | +| `mixing` | `number` | no | `0.93` | editor range: 0…1 | Blending behavior: 0 gives hard stripes, 0.5 is smooth, and 1 gives a gradual blend | +| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to the shapes’ edges | +| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing b/w grain overlay | diff --git a/skills/paper-shaders/references/shaders/static-radial-gradient.md b/skills/paper-shaders/references/shaders/static-radial-gradient.md new file mode 100644 index 00000000..ea518efe --- /dev/null +++ b/skills/paper-shaders/references/shaders/static-radial-gradient.md @@ -0,0 +1,25 @@ +# Static Radial Gradient + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Radial gradient with up to 10 blended colors, featuring advanced mixing modes, focal point controls, shape distortion, and grain effects. + +- React: `StaticRadialGradient` and `staticRadialGradientPresets` from `@paper-design/shaders-react`. +- Vanilla: `staticRadialGradientFragmentShader` and `StaticRadialGradientParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0, frame=0, fit="contain", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/static-radial-gradient.ts`, `packages/shaders-react/src/shaders/static-radial-gradient.tsx`, `docs/src/shader-defs/static-radial-gradient-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colors` | `string[]` | no | `["#00bbff","#00ffe1","#ffffff"]` | — | Colors used by the shader; implementation capacity is 10. | +| `radius` | `number` | no | `0.8` | editor range: 0…3 | The size of the shape | +| `focalDistance` | `number` | no | `0.99` | editor range: 0…3 | Distance of the focal point from center | +| `focalAngle` | `number` | no | `0` | editor range: 0…360 | Angle of the focal point in degrees (effective with focalDistance > 0) | +| `falloff` | `number` | no | `0.24` | editor range: -1…1 | Gradient decay (0 for linear gradient) | +| `mixing` | `number` | no | `0.5` | editor range: 0…1 | Blending behavior: 0 gives hard stripes and 1 gives a smooth gradient | +| `distortion` | `number` | no | `0` | editor range: 0…1 | Strength of radial distortion | +| `distortionShift` | `number` | no | `0` | editor range: -1…1 | Radial distortion offset (effective with distortion > 0) | +| `distortionFreq` | `number` | no | `12` | editor range: 0…20; step: 1 | Radial distortion frequency (effective with distortion > 0) | +| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to the shapes’ edges | +| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing b/w grain overlay | diff --git a/skills/paper-shaders/references/shaders/swirl.md b/skills/paper-shaders/references/shaders/swirl.md new file mode 100644 index 00000000..3c9eba25 --- /dev/null +++ b/skills/paper-shaders/references/shaders/swirl.md @@ -0,0 +1,22 @@ +# Swirl + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Animated bands of color twisting and bending, producing spirals, arcs, and flowing circular patterns. + +- React: `Swirl` and `swirlPresets` from `@paper-design/shaders-react`. +- Vanilla: `swirlFragmentShader` and `SwirlParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0.32, frame=0, fit="contain", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Source: `packages/shaders/src/shaders/swirl.ts`, `packages/shaders-react/src/shaders/swirl.tsx`, `docs/src/shader-defs/swirl-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorBack` | `string` | no | `"#330000"` | — | Background color | +| `colors` | `string[]` | no | `["#ffd1d1","#ff8a8a","#660000"]` | — | Colors used by the shader; implementation capacity is 10. | +| `bandCount` | `number` | no | `4` | editor range: 0…15; step: 1 | Number of color bands (0 for concentric ripples) | +| `twist` | `number` | no | `0.1` | editor range: 0…1 | Vortex power (0 = straight sectoral shapes) | +| `center` | `number` | no | `0.2` | editor range: 0…1 | How far from the center the swirl colors begin to appear | +| `proportion` | `number` | no | `0.5` | editor range: 0…1 | Blend point between colors (0.5 = equal distribution) | +| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | +| `noiseFrequency` | `number` | no | `0.4` | editor range: 0…1 | Noise frequency (no effect with noise = 0) | +| `noise` | `number` | no | `0.2` | editor range: 0…1 | Strength of noise distortion (no effect with noiseFrequency = 0) | diff --git a/skills/paper-shaders/references/shaders/voronoi.md b/skills/paper-shaders/references/shaders/voronoi.md new file mode 100644 index 00000000..bb4011fa --- /dev/null +++ b/skills/paper-shaders/references/shaders/voronoi.md @@ -0,0 +1,21 @@ +# Voronoi + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Anti-aliased animated Voronoi pattern with smooth and customizable edges. + +- React: `Voronoi` and `voronoiPresets` from `@paper-design/shaders-react`. +- Vanilla: `voronoiFragmentShader` and `VoronoiParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=0.5, frame=0, fit="none", scale=0.5, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`. +- Source: `packages/shaders/src/shaders/voronoi.ts`, `packages/shaders-react/src/shaders/voronoi.tsx`, `docs/src/shader-defs/voronoi-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colors` | `string[]` | no | `["#ff8247","#ffe53d"]` | — | Colors used by the shader; implementation capacity is 5. | +| `stepsPerColor` | `number` | no | `3` | editor range: 1…3; step: 1 | Number of extra colors between base colors (1 = N color palette, 2 = 2×N color palette, 3 = 3×N color palette, etc) | +| `colorGap` | `string` | no | `"#2e0000"` | — | Color used for cell borders/gaps | +| `colorGlow` | `string` | no | `"#ffffff"` | — | Color tint for the radial inner shadow effect inside cells (effective with glow > 0) | +| `distortion` | `number` | no | `0.4` | editor range: 0…0.5 | Strength of noise-driven displacement of cell centers | +| `gap` | `number` | no | `0.04` | editor range: 0…0.1 | Width of the border/gap between cells | +| `glow` | `number` | no | `0` | editor range: 0…1 | Strength of the radial inner shadow inside cells | diff --git a/skills/paper-shaders/references/shaders/warp.md b/skills/paper-shaders/references/shaders/warp.md new file mode 100644 index 00000000..2c656e2b --- /dev/null +++ b/skills/paper-shaders/references/shaders/warp.md @@ -0,0 +1,23 @@ +# Warp + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Animated color fields warped by noise and swirls, applied over base patterns (checks, stripes, or split edge). Blends up to 10 colors with adjustable distribution, softness, distortion, and swirl. Great for fluid, smoky, or marbled effects. + +- React: `Warp` and `warpPresets` from `@paper-design/shaders-react`. +- Vanilla: `warpFragmentShader` and `WarpParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="none", scale=1, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`. +- Source: `packages/shaders/src/shaders/warp.ts`, `packages/shaders-react/src/shaders/warp.tsx`, `docs/src/shader-defs/warp-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colors` | `string[]` | no | `["#121212","#9470ff","#121212","#8838ff"]` | — | Colors used by the shader; implementation capacity is 10. | +| `rotation` | `number` | no | `0` | editor range: 0…360 | Overall rotation angle of the graphics. | +| `proportion` | `number` | no | `0.45` | editor range: 0…1 | Blend point between 2 colors (0.5 = equal distribution) | +| `softness` | `number` | no | `1` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | +| `shape` | `WarpPattern` | no | `"checks"` | options: "checks", "stripes", "edge" | Base pattern type | +| `shapeScale` | `number` | no | `0.1` | editor range: 0…1 | Zoom level of the base pattern | +| `distortion` | `number` | no | `0.25` | editor range: 0…1 | Strength of noise-based distortion | +| `swirl` | `number` | no | `0.8` | editor range: 0…1 | Strength of the swirl distortion | +| `swirlIterations` | `number` | no | `10` | editor range: 0…20; hard loop capacity: 20 | Exclusive integer loop bound for layered swirl passes. For an integer `N` in the editor range, the shader executes indices 1 through `N - 1`; the default 10 therefore runs 9 passes, and 20 runs 19. Values of 21 or more reach the hard 20-pass loop capacity. | diff --git a/skills/paper-shaders/references/shaders/water.md b/skills/paper-shaders/references/shaders/water.md new file mode 100644 index 00000000..79866477 --- /dev/null +++ b/skills/paper-shaders/references/shaders/water.md @@ -0,0 +1,24 @@ +# Water + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Water-like surface distortion with natural caustic realism. Works as an image filter or standalone animated texture. + +- React: `Water` and `waterPresets` from `@paper-design/shaders-react`. +- Vanilla: `waterFragmentShader` and `WaterParams` from `@paper-design/shaders`. +- Common controls: sizing and motion. Defaults: speed=1, frame=0, fit="contain", scale=0.8, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- Vanilla requirements: pass `["u_image"]` as the `ShaderMount` mipmaps argument. +- Source: `packages/shaders/src/shaders/water.ts`, `packages/shaders-react/src/shaders/water.tsx`, `docs/src/shader-defs/water-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `image` | `HTMLImageElement \| string` | no | `""` | — | The image to use for the effect | +| `colorBack` | `string` | no | `"#909090"` | — | Background color | +| `colorHighlight` | `string` | no | `"#ffffff"` | — | Highlight color | +| `highlights` | `number` | no | `0.07` | editor range: 0…1 | A coloring added over the image/background, following the caustic shape | +| `layering` | `number` | no | `0.5` | editor range: 0…1 | The power of 2nd layer of caustic distortion | +| `edges` | `number` | no | `0.8` | editor range: 0…1 | Caustic distortion power on the image edges | +| `caustic` | `number` | no | `0.1` | editor range: 0…1 | Power of caustic distortion | +| `waves` | `number` | no | `0.3` | editor range: 0…1 | Additional distortion based on simplex noise, independent from caustic | +| `size` | `number` | no | `1` | editor range: 0.01…7 | Pattern scale relative to the image | +| `effectScale` | `number` | no | — | — | React-only. @deprecated use `size` instead | diff --git a/skills/paper-shaders/references/shaders/waves.md b/skills/paper-shaders/references/shaders/waves.md new file mode 100644 index 00000000..431caf3c --- /dev/null +++ b/skills/paper-shaders/references/shaders/waves.md @@ -0,0 +1,23 @@ +# Waves + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +Static line pattern configurable into textures ranging from sharp zigzags to smooth flowing waves. + +- React: `Waves` and `wavesPresets` from `@paper-design/shaders-react`. +- Vanilla: `wavesFragmentShader` and `WavesParams` from `@paper-design/shaders`. +- Common controls: sizing. Defaults: fit="none", scale=0.6, rotation=0, offsetX=0, offsetY=0, originX=0.5, originY=0.5, worldWidth=0, worldHeight=0. +- React performance override: `maxPixelCount` defaults to `6016 * 3384`. +- Source: `packages/shaders/src/shaders/waves.ts`, `packages/shaders-react/src/shaders/waves.tsx`, `docs/src/shader-defs/waves-def.ts`. + +| Prop | Type | Required | React default | Constraints | Effect | +| --- | --- | --- | --- | --- | --- | +| `colorFront` | `string` | no | `"#ffbb00"` | — | Foreground color | +| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `rotation` | `number` | no | `0` | editor range: 0…360 | Overall rotation angle of the graphics. | +| `shape` | `number` | no | `0` | editor range: 0…3 | Line shape control: zigzag at 0, sine at 1, one irregular waveform at 2, and a second irregular waveform at 3. Intermediate values morph gradually between adjacent shapes. | +| `frequency` | `number` | no | `0.5` | editor range: 0…2 | Wave frequency | +| `amplitude` | `number` | no | `0.5` | editor range: 0…1 | Wave amplitude | +| `spacing` | `number` | no | `1.2` | editor range: 0…2 | The space between every two wavy lines | +| `proportion` | `number` | no | `0.1` | editor range: 0…1 | Blend point between front and back colors (0.5 = equal distribution) | +| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | diff --git a/skills/paper-shaders/references/usage.md b/skills/paper-shaders/references/usage.md new file mode 100644 index 00000000..ba5c329d --- /dev/null +++ b/skills/paper-shaders/references/usage.md @@ -0,0 +1,285 @@ +# Paper Shaders usage + +This reference is derived from: + +- `packages/shaders/src/index.ts` +- `packages/shaders/src/shader-mount.ts` +- `packages/shaders/src/shader-sizing.ts` +- `packages/shaders/src/get-shader-color-from-string.ts` +- `packages/shaders-react/src/index.ts` +- `packages/shaders-react/src/shader-mount.tsx` +- `packages/shaders-react/src/shaders/*.tsx` + +Read the matching file under [Shader references](../SKILL.md#shader-references) for shader-specific props, defaults, ranges, enum options, exports, capacities, and special vanilla requirements. + +## Contents + +- [Packages](#packages) +- [React](#react) + - [Presets](#presets) + - [Low-level React mount](#low-level-react-mount) +- [Vanilla](#vanilla) + - [Convert React props to vanilla uniforms](#convert-react-props-to-vanilla-uniforms) + - [Noise textures](#noise-textures) + - [Images](#images) + - [Preprocessed image shaders](#preprocessed-image-shaders) + - [Color syntax](#color-syntax) +- [Common sizing and motion](#common-sizing-and-motion) +- [Verification](#verification) + +## Packages + +- React: `@paper-design/shaders-react` +- Vanilla JavaScript or TypeScript: `@paper-design/shaders` + +The repository README asks consumers to pin the dependency because breaking changes may ship under `0.0.x` versioning. Match the project's existing package manager and version policy. + +## React + +Use the named component exported by `@paper-design/shaders-react`. + +```tsx +import { MeshGradient } from '@paper-design/shaders-react'; + +export function HeroShader() { + return ( + + ); +} +``` + +Every named shader component accepts its shader-specific params plus the common component controls: + +- ordinary `div` props except `color` and `ref` +- `ref?: React.Ref` +- `width?: string | number` +- `height?: string | number` +- `minPixelRatio?: number` +- `maxPixelCount?: number` +- `webGlContextAttributes?: WebGLContextAttributes` + +`width` and `height` become inline styles. Other layout styles can be passed through `style`. Ensure the element resolves to a non-zero width and height. + +### Presets + +Each named React component has a corresponding exported preset array, such as `meshGradientPresets`. Preset `params` contain the shader params plus all sizing defaults and, when the shader supports motion, `speed` and `frame`. Image presets intentionally omit `image`; React-only component controls are also outside preset `params`. + +```tsx +import { MeshGradient, meshGradientPresets } from '@paper-design/shaders-react'; + +const preset = meshGradientPresets[0].params; + +; +``` + +Use the catalog's exact preset export name. + +### Low-level React mount + +`ShaderMount` from `@paper-design/shaders-react` accepts: + +- `fragmentShader` +- `uniforms` +- `speed` and `frame` +- `mipmaps` +- `minPixelRatio` and `maxPixelCount` +- `webGlContextAttributes` +- `width`, `height`, and ordinary supported `div` props + +String uniform values are treated as image URLs, not arbitrary string uniforms. Prefer named components for package shaders because they construct and convert uniforms correctly. + +## Vanilla + +The vanilla package exports fragment shader source and `ShaderMount`; it does not provide named convenience mount functions. Construct the complete uniform object yourself. +Create and dispose the mount in a browser/client lifecycle: the class requires DOM, `navigator`, canvas, and WebGL APIs. + +```ts +import { + ShaderFitOptions, + ShaderMount, + getShaderColorFromString, + meshGradientFragmentShader, + type MeshGradientUniforms, +} from '@paper-design/shaders'; + +const host = document.querySelector('#shader'); +if (!host) throw new Error('Missing #shader element'); + +const colors = ['#e0eaff', '#241d9a', '#f75092', '#9f50d3']; + +const uniforms: MeshGradientUniforms = { + u_colors: colors.map(getShaderColorFromString), + u_colorsCount: colors.length, + u_distortion: 0.8, + u_swirl: 0.1, + u_grainMixer: 0, + u_grainOverlay: 0, + u_fit: ShaderFitOptions.contain, + u_scale: 1, + u_rotation: 0, + u_offsetX: 0, + u_offsetY: 0, + u_originX: 0.5, + u_originY: 0.5, + u_worldWidth: 0, + u_worldHeight: 0, +}; + +const mount = new ShaderMount( + host, + meshGradientFragmentShader, + uniforms, + undefined, + 1, + 0 +); + +mount.setUniforms({ u_distortion: 0.5 }); +mount.setSpeed(0.5); + +// Call during teardown: +mount.dispose(); +``` + +The constructor arguments, in order, are: + +1. parent `HTMLElement` +2. fragment shader string +3. initial uniform object +4. optional `WebGLContextAttributes` +5. speed, default `0` +6. frame, default `0` +7. minimum pixel ratio, default `2` +8. maximum pixel count, default `1920 * 1080 * 4` +9. uniform names that require mipmaps, default `[]` + +The parent receives a prepended canvas, `data-paper-shader`, and `paperShaderMount`. `dispose()` removes the canvas and WebGL resources. + +`ShaderMount` supports partial updates with `setUniforms`, and also exposes `getCurrentFrame`, `setFrame`, `setSpeed`, `setMinPixelRatio`, and `setMaxPixelCount`. + +### Convert React props to vanilla uniforms + +Follow the named React component's `uniforms` object. Apply these source-defined rules: + +- Convert each color string with `getShaderColorFromString`. +- Convert `colors` with `.map(getShaderColorFromString)` and also set `u_colorsCount`. +- Convert `fit` with `ShaderFitOptions[fit]`. +- Convert enum props with their exported mapping object, such as `WarpPatterns[shape]`. +- Pass `speed` and `frame` to `ShaderMount`; they are not shader-specific uniforms. +- Map common sizing props to `u_fit`, `u_scale`, `u_rotation`, `u_offsetX`, `u_offsetY`, `u_originX`, `u_originY`, `u_worldWidth`, and `u_worldHeight`. +- Pass booleans as booleans. `ShaderMount` converts them to integer uniforms. +- Pass textures as fully loaded `HTMLImageElement` instances. + +Do not assume every prop becomes `u_${prop}`. These package mappings differ: + +- `Dithering.size` and `ImageDithering.size` → `u_pxSize` +- `DotGrid.size` → `u_dotSize` +- `FlutedGlass.margin` and `PulsingBorder.margin` → defaults for all four side-specific margin uniforms; an explicitly supplied side value wins +- `width`, `height`, `minPixelRatio`, `maxPixelCount`, `webGlContextAttributes`, and `ref` configure the mount/container and are not uniforms +- `suspendWhenProcessingImage` and deprecated compatibility props are React-only + +Always inspect the catalog's source paths when constructing a vanilla shader. The uniform interfaces in `packages/shaders/src/shaders/*.ts` list the complete required uniform object. + +### Noise textures + +For shaders whose catalog says to set `u_noiseTexture`, import `getShaderNoiseTexture` and include: + +```ts +const noiseTexture = getShaderNoiseTexture(); +if (!noiseTexture) throw new Error('Noise textures require a browser'); +await noiseTexture.decode(); + +// Include in the initial uniforms: +u_noiseTexture: noiseTexture +``` + +The source returns `undefined` outside the browser and a newly created `HTMLImageElement` in the browser. Wait for it to load before constructing the vanilla `ShaderMount`; the mount rejects incomplete images. The React mount performs this wait itself. + +### Images + +The low-level vanilla mount accepts `HTMLImageElement`, not URL strings. The image must be fully loaded and have a non-zero `naturalWidth`; otherwise mounting the texture throws. + +The React mount accepts an `HTMLImageElement` or a string that is either: + +- an absolute path beginning with `/` +- a URL accepted by `new URL(value)` +- an empty string, which becomes a transparent pixel + +For an external URL, the React loader sets `crossOrigin = "anonymous"`. When both natural dimensions are below 1024, it sets the image dimensions so the shorter side is 1024 before upload. + +When a texture is supplied as `u_image`, `ShaderMount` automatically looks up and fills `u_imageAspectRatio`. + +Pass `["u_image"]` as the final vanilla constructor argument when the catalog requires mipmaps. + +### Preprocessed image shaders + +`Heatmap`, `LiquidMetal`, and `GemSmoke` preprocess image inputs in their React components. Reproduce this in vanilla: + +- `toProcessedHeatmap(fileOrUrl)` returns `{ blob }` +- `toProcessedLiquidMetal(fileOrUrl)` returns `{ imageData, pngBlob }` +- `toProcessedGemSmoke(fileOrUrl)` returns `{ imageData, pngBlob }` + +Each function accepts `File | string` and requires browser document/canvas APIs. Load the returned blob into an `HTMLImageElement`, pass that loaded image as `u_image`, and enable mipmaps for `u_image`. + +For `LiquidMetal` and `GemSmoke`, also set `u_isImage` from whether an original image was supplied and convert `shape` with `LiquidMetalShapes` or `GemSmokeShapes`. When no original image is supplied, load the exported `emptyPixel` into an `HTMLImageElement`, use that placeholder as the initial `u_image`, and set `u_isImage` to `false`; the shader still declares and samples `u_image`. + +### Color syntax + +`getShaderColorFromString` supports: + +- 3-, 4-, 6-, and 8-digit hex +- comma-form `rgb(...)` and `rgba(...)` +- comma-form `hsl(...)` and `hsla(...)` +- already-normalized RGB or RGBA number tuples when calling the utility directly + +It does not parse CSS named colors. Invalid values fall back to `[0.5, 0.5, 0.5, 1]`. + +## Common sizing and motion + +All named shader components accept: + +- `fit?: "none" | "contain" | "cover"` +- `scale?: number` +- `rotation?: number` +- `originX?: number` +- `originY?: number` +- `offsetX?: number` +- `offsetY?: number` +- `worldWidth?: number` +- `worldHeight?: number` + +Defaults come from one of two source objects, then may be overridden by the component's default preset: + +- `defaultObjectSizing`: `fit="contain"` and otherwise `scale=1`, `rotation=0`, offsets `0`, origins `0.5`, and world dimensions `0` +- `defaultPatternSizing`: the same values except `fit="none"` + +The catalog lists the effective defaults for every shader. + +Shaders whose params extend `ShaderMotionParams` also accept: + +- `speed?: number`: `0` stops the animation loop; negative values play backward +- `frame?: number`: animation position in milliseconds; the shader receives seconds as `u_time` + +The mount pauses animated rendering when the document is hidden. It also pauses when the element leaves the viewport when `IntersectionObserver` is available in the element's window. + +`minPixelRatio` defaults to `2`. `maxPixelCount` defaults to `1920 * 1080 * 4` physical pixels. The `Waves` React component overrides its `maxPixelCount` default to `6016 * 3384`. +The `DotGrid` React component uses the same `6016 * 3384` override. + +## Verification + +- Type-check against the installed package version. +- Confirm the mount has a non-zero layout size. +- Confirm all initial vanilla uniforms are present. +- Confirm color arrays are non-empty and do not exceed the implementation capacity in the catalog. +- Confirm image inputs load and satisfy CORS rules. +- Confirm preprocessors run only in a browser environment. +- Call `dispose()` for vanilla mounts during teardown. From 3eb72119e915db220882e47502cf4bb8665cfc2a Mon Sep 17 00:00:00 2001 From: Pouya Saadeghi Date: Fri, 31 Jul 2026 17:08:11 +0300 Subject: [PATCH 2/3] Auto generate skill files based on source code --- .github/workflows/generated-skills.yml | 39 ++ bun.lock | 5 + package.json | 3 + .../skill-generator/content/fragments.json | 13 + packages/skill-generator/content/orders.json | 16 + .../content/templates/shader.md | 12 + .../content/templates/skill.md | 47 +++ .../content/templates/usage.md | 271 ++++++++++++++ packages/skill-generator/content/text.json | 14 + packages/skill-generator/package.json | 10 + packages/skill-generator/src/ast.ts | 196 ++++++++++ packages/skill-generator/src/catalog.ts | 97 +++++ packages/skill-generator/src/content.ts | 46 +++ .../skill-generator/src/generate-skills.ts | 176 +++++++++ packages/skill-generator/src/model.ts | 124 +++++++ packages/skill-generator/src/render.ts | 156 ++++++++ .../src/shader-comment.test.ts | 36 ++ .../skill-generator/src/shader-comment.ts | 74 ++++ packages/skill-generator/src/source-model.ts | 350 ++++++++++++++++++ packages/skill-generator/tsconfig.json | 15 + publish.js | 24 ++ .../references/shaders/color-panels.md | 16 +- .../references/shaders/dithering.md | 8 +- .../references/shaders/dot-grid.md | 20 +- .../references/shaders/dot-orbit.md | 10 +- .../references/shaders/fluted-glass.md | 28 +- .../references/shaders/gem-smoke.md | 18 +- .../references/shaders/god-rays.md | 10 +- .../references/shaders/grain-gradient.md | 8 +- .../references/shaders/halftone-cmyk.md | 42 +-- .../references/shaders/halftone-dots.md | 20 +- .../references/shaders/heatmap.md | 12 +- .../references/shaders/image-dithering.md | 18 +- .../references/shaders/liquid-metal.md | 12 +- .../references/shaders/mesh-gradient.md | 10 +- .../references/shaders/metaballs.md | 6 +- .../references/shaders/neuro-noise.md | 8 +- .../references/shaders/paper-texture.md | 18 +- .../references/shaders/perlin-noise.md | 12 +- .../references/shaders/pulsing-border.md | 22 +- .../references/shaders/simplex-noise.md | 6 +- .../references/shaders/smoke-ring.md | 14 +- .../references/shaders/spiral.md | 16 +- .../shaders/static-mesh-gradient.md | 10 +- .../shaders/static-radial-gradient.md | 20 +- .../paper-shaders/references/shaders/swirl.md | 16 +- .../references/shaders/voronoi.md | 8 +- .../paper-shaders/references/shaders/warp.md | 12 +- .../paper-shaders/references/shaders/water.md | 10 +- .../paper-shaders/references/shaders/waves.md | 14 +- skills/paper-shaders/references/usage.md | 6 +- 51 files changed, 1939 insertions(+), 215 deletions(-) create mode 100644 .github/workflows/generated-skills.yml create mode 100644 packages/skill-generator/content/fragments.json create mode 100644 packages/skill-generator/content/orders.json create mode 100644 packages/skill-generator/content/templates/shader.md create mode 100644 packages/skill-generator/content/templates/skill.md create mode 100644 packages/skill-generator/content/templates/usage.md create mode 100644 packages/skill-generator/content/text.json create mode 100644 packages/skill-generator/package.json create mode 100644 packages/skill-generator/src/ast.ts create mode 100644 packages/skill-generator/src/catalog.ts create mode 100644 packages/skill-generator/src/content.ts create mode 100644 packages/skill-generator/src/generate-skills.ts create mode 100644 packages/skill-generator/src/model.ts create mode 100644 packages/skill-generator/src/render.ts create mode 100644 packages/skill-generator/src/shader-comment.test.ts create mode 100644 packages/skill-generator/src/shader-comment.ts create mode 100644 packages/skill-generator/src/source-model.ts create mode 100644 packages/skill-generator/tsconfig.json diff --git a/.github/workflows/generated-skills.yml b/.github/workflows/generated-skills.yml new file mode 100644 index 00000000..cec08952 --- /dev/null +++ b/.github/workflows/generated-skills.yml @@ -0,0 +1,39 @@ +name: Generated Skills + +on: + pull_request: + branches: [main] + paths: + - 'docs/src/shader-defs/**' + - 'packages/shaders/**' + - 'packages/shaders-react/**' + - 'packages/skill-generator/**' + - 'skills/**' + - '.github/workflows/generated-skills.yml' + - '.bun-version' + - 'bun.lock' + - 'package.json' + - 'publish.js' + +permissions: + contents: read + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Use Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version-file: .bun-version + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Type-check generator + run: bun run --cwd packages/skill-generator type-check + + - name: Check generated skills + run: bun run check-skills diff --git a/bun.lock b/bun.lock index cfd0bf0b..b204fa41 100644 --- a/bun.lock +++ b/bun.lock @@ -66,6 +66,9 @@ "@types/react", ], }, + "packages/skill-generator": { + "name": "@paper-design/skill-generator", + }, }, "patchedDependencies": { "leva@0.9.34": "patches/leva@0.9.34.patch", @@ -319,6 +322,8 @@ "@paper-design/shaders-react": ["@paper-design/shaders-react@workspace:packages/shaders-react"], + "@paper-design/skill-generator": ["@paper-design/skill-generator@workspace:packages/skill-generator"], + "@radix-ui/popper": ["@radix-ui/popper@0.1.0", "", { "dependencies": { "@babel/runtime": "^7.13.10", "csstype": "^3.0.4" } }, "sha512-uzYeElL3w7SeNMuQpXiFlBhTT+JyaNMCwDfjKkrzugEcYrf5n52PHqncNdQPUtR42hJh8V9FsqyEDbDxkeNjJQ=="], "@radix-ui/primitive": ["@radix-ui/primitive@0.1.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-tqxZKybwN5Fa3VzZry4G6mXAAb9aAqKmPtnVbZpL0vsBwvOHTBwsjHVPXylocYLwEtBY9SCe665bYnNB515uoA=="], diff --git a/package.json b/package.json index 456b083d..491cc655 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "license": "Apache-2.0", "workspaces": [ "docs", + "packages/skill-generator", "packages/shaders", "packages/shaders-react" ], @@ -13,6 +14,8 @@ "publish-all": "run-s clean build && bun run publish.js", "publish-canary": "run-s clean build && bun run publish.js --canary", "publish-all-test": "run-s clean build && bun run publish.js --dry-run", + "generate-skills": "bun run --cwd packages/skill-generator generate", + "check-skills": "bun run --cwd packages/skill-generator check", "dev:docs": "bun run --filter docs dev", "dev:packages": "nodemon --watch 'packages/*/src/**/*' --ext ts,tsx --exec 'bun run build' --on-change-only", "dev": "run-s clean build && run-p dev:*", diff --git a/packages/skill-generator/content/fragments.json b/packages/skill-generator/content/fragments.json new file mode 100644 index 00000000..accaec72 --- /dev/null +++ b/packages/skill-generator/content/fragments.json @@ -0,0 +1,13 @@ +{ + "shaderLink": "- [{{name}}](references/shaders/{{slug}}.md)", + "note": "- {{note}}", + "default": "{{name}}={{value}}", + "inlineCode": "`{{value}}`", + "tableHeader": [ + "| Prop | Type | Required | React default | Constraints | Effect |", + "| --- | --- | --- | --- | --- | --- |" + ], + "tableRow": "| `{{name}}` | `{{type}}` | {{required}} | {{defaultValue}} | {{constraints}} | {{description}} |", + "usageProperty": "- `{{name}}{{optional}}: {{type}}`", + "usageMotionProperty": "- `{{name}}{{optional}}: {{type}}`: {{description}}" +} diff --git a/packages/skill-generator/content/orders.json b/packages/skill-generator/content/orders.json new file mode 100644 index 00000000..a0405434 --- /dev/null +++ b/packages/skill-generator/content/orders.json @@ -0,0 +1,16 @@ +{ + "usageComponentProperties": ["ref", "width", "height", "minPixelRatio", "maxPixelCount", "webGlContextAttributes"], + "commonDefaults": [ + "speed", + "frame", + "fit", + "scale", + "rotation", + "offsetX", + "offsetY", + "originX", + "originY", + "worldWidth", + "worldHeight" + ] +} diff --git a/packages/skill-generator/content/templates/shader.md b/packages/skill-generator/content/templates/shader.md new file mode 100644 index 00000000..4088034b --- /dev/null +++ b/packages/skill-generator/content/templates/shader.md @@ -0,0 +1,12 @@ +# {{name}} + +Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. + +{{description}} + +- React: `{{component}}` and `{{preset}}` from `@paper-design/shaders-react`. +- Vanilla: `{{fragmentShader}}` and `{{paramsType}}` from `@paper-design/shaders`. +- Common controls: {{controls}}. Defaults: {{defaults}}.{{notes}} +- {{source}} + +{{table}} diff --git a/packages/skill-generator/content/templates/skill.md b/packages/skill-generator/content/templates/skill.md new file mode 100644 index 00000000..adfafff7 --- /dev/null +++ b/packages/skill-generator/content/templates/skill.md @@ -0,0 +1,47 @@ +--- +name: paper-shaders +description: Implement, convert, customize, debug, or review Paper Shaders effects in React or vanilla JavaScript using `@paper-design/shaders-react` and `@paper-design/shaders`. +--- + +# Paper Shaders + +Implement Paper Shaders from the package source contract. Do not infer prop names, defaults, enum values, uniform mappings, color limits, or image behavior. + +## Workflow + +1. Inspect the target project's package manager and installed Paper Shaders package/version. Preserve the existing framework and dependency style. +2. Read [references/usage.md](references/usage.md) before writing integration code. +3. Read only the relevant file under [Shader references](#shader-references). Use its exact component export, fragment-shader export, props, defaults, enum options, and implementation capacity. +4. For React, prefer the named shader component. Use the low-level React `ShaderMount` only for a custom fragment shader or an explicitly requested uniform-level integration. +5. For vanilla JavaScript or TypeScript, reproduce the named React component's uniform construction. Apply every conversion and special requirement listed in `usage.md` and the matching shader reference. +6. Give the mount element an explicit rendered size. Keep shader props separate from layout styles and ordinary DOM props. +7. Verify with the project's type-check/build and, when rendering is available, inspect the result at the intended dimensions. + +## Source authority + +- Treat `packages/shaders/src/shaders/*.ts` and `packages/shaders-react/src/shaders/*.tsx` as authoritative for runtime behavior, types, defaults, enum mappings, and uniforms. +- Treat `packages/shaders/src/shader-mount.ts`, `packages/shaders/src/shader-sizing.ts`, and `packages/shaders-react/src/shader-mount.tsx` as authoritative for mounting, images, sizing, motion, and performance controls. +- Treat numeric ranges and steps in `docs/src/shader-defs/*-def.ts` as editor guidance, not runtime validation. Components pass values through without clamping. +- Resolve source contradictions in favor of the shader implementation and its exported types/constants. Never repeat a stale prose claim when an array size, type, constant, or component mapping contradicts it. +- Recheck current source when working against a different repository revision. Do not assume this reference overrides changed code. + +## Implementation rules + +- Import only public exports from the package entry point. +- Use `@paper-design/shaders-react` for named React components and presets. +- Use `@paper-design/shaders` for `ShaderMount`, fragment shaders, uniform types, enum maps, color conversion, noise textures, and image preprocessors. +- Supply all required initial vanilla uniforms. `ShaderMount` records uniform locations from the constructor's initial uniform object; later partial updates cannot introduce an unregistered uniform. +- Dispose vanilla mounts during teardown. +- Keep color arrays non-empty and do not exceed the shader reference's implementation capacity for colors or other fixed-size loops. +- Do not pass CSS named colors. The source color parser accepts hex, `rgb`/`rgba`, and `hsl`/`hsla` syntax. +- Do not use deprecated React aliases in new code. + +## References + +- [Usage and integration](references/usage.md): React, vanilla, common controls, uniform conversion, images, lifecycle, and special cases. + +## Shader references + +Read only the file for the shader being used: + +{{shaderLinks}} diff --git a/packages/skill-generator/content/templates/usage.md b/packages/skill-generator/content/templates/usage.md new file mode 100644 index 00000000..c943dab3 --- /dev/null +++ b/packages/skill-generator/content/templates/usage.md @@ -0,0 +1,271 @@ +# Paper Shaders usage + +This reference is derived from: + +- `packages/shaders/src/index.ts` +- `packages/shaders/src/shader-mount.ts` +- `packages/shaders/src/shader-sizing.ts` +- `packages/shaders/src/get-shader-color-from-string.ts` +- `packages/shaders-react/src/index.ts` +- `packages/shaders-react/src/shader-mount.tsx` +- `packages/shaders-react/src/shaders/*.tsx` + +Read the matching file under [Shader references](../SKILL.md#shader-references) for shader-specific props, defaults, ranges, enum options, exports, capacities, and special vanilla requirements. + +## Contents + +- [Packages](#packages) +- [React](#react) + - [Presets](#presets) + - [Low-level React mount](#low-level-react-mount) +- [Vanilla](#vanilla) + - [Convert React props to vanilla uniforms](#convert-react-props-to-vanilla-uniforms) + - [Noise textures](#noise-textures) + - [Images](#images) + - [Preprocessed image shaders](#preprocessed-image-shaders) + - [Color syntax](#color-syntax) +- [Common sizing and motion](#common-sizing-and-motion) +- [Verification](#verification) + +## Packages + +- React: `@paper-design/shaders-react` +- Vanilla JavaScript or TypeScript: `@paper-design/shaders` + +The repository README asks consumers to pin the dependency because breaking changes may ship under `0.0.x` versioning. Match the project's existing package manager and version policy. + +## React + +Use the named component exported by `@paper-design/shaders-react`. + +```tsx +import { MeshGradient } from '@paper-design/shaders-react'; + +export function HeroShader() { + return ( + + ); +} +``` + +Every named shader component accepts its shader-specific params plus the common component controls: + +- ordinary `div` props except `color` and `ref` +{{componentControls}} + +`width` and `height` become inline styles. Other layout styles can be passed through `style`. Ensure the element resolves to a non-zero width and height. + +### Presets + +Each named React component has a corresponding exported preset array, such as `meshGradientPresets`. Preset `params` contain the shader params plus all sizing defaults and, when the shader supports motion, `speed` and `frame`. Image presets intentionally omit `image`; React-only component controls are also outside preset `params`. + +```tsx +import { MeshGradient, meshGradientPresets } from '@paper-design/shaders-react'; + +const preset = meshGradientPresets[0].params; + +; +``` + +Use the catalog's exact preset export name. + +### Low-level React mount + +`ShaderMount` from `@paper-design/shaders-react` accepts: + +- `fragmentShader` +- `uniforms` +- `speed` and `frame` +- `mipmaps` +- `minPixelRatio` and `maxPixelCount` +- `webGlContextAttributes` +- `width`, `height`, and ordinary supported `div` props + +String uniform values are treated as image URLs, not arbitrary string uniforms. Prefer named components for package shaders because they construct and convert uniforms correctly. + +## Vanilla + +The vanilla package exports fragment shader source and `ShaderMount`; it does not provide named convenience mount functions. Construct the complete uniform object yourself. +Create and dispose the mount in a browser/client lifecycle: the class requires DOM, `navigator`, canvas, and WebGL APIs. + +```ts +import { + ShaderFitOptions, + ShaderMount, + getShaderColorFromString, + meshGradientFragmentShader, + type MeshGradientUniforms, +} from '@paper-design/shaders'; + +const host = document.querySelector('#shader'); +if (!host) throw new Error('Missing #shader element'); + +const colors = ['#e0eaff', '#241d9a', '#f75092', '#9f50d3']; + +const uniforms: MeshGradientUniforms = { + u_colors: colors.map(getShaderColorFromString), + u_colorsCount: colors.length, + u_distortion: 0.8, + u_swirl: 0.1, + u_grainMixer: 0, + u_grainOverlay: 0, + u_fit: ShaderFitOptions.contain, + u_scale: 1, + u_rotation: 0, + u_offsetX: 0, + u_offsetY: 0, + u_originX: 0.5, + u_originY: 0.5, + u_worldWidth: 0, + u_worldHeight: 0, +}; + +const mount = new ShaderMount( + host, + meshGradientFragmentShader, + uniforms, + undefined, + 1, + 0 +); + +mount.setUniforms({ u_distortion: 0.5 }); +mount.setSpeed(0.5); + +// Call during teardown: +mount.dispose(); +``` + +The constructor arguments, in order, are: + +1. parent `HTMLElement` +2. fragment shader string +3. initial uniform object +4. optional `WebGLContextAttributes` +5. speed, default `0` +6. frame, default `0` +7. minimum pixel ratio, default `2` +8. maximum pixel count, default `1920 * 1080 * 4` +9. uniform names that require mipmaps, default `[]` + +The parent receives a prepended canvas, `data-paper-shader`, and `paperShaderMount`. `dispose()` removes the canvas and WebGL resources. + +`ShaderMount` supports partial updates with `setUniforms`, and also exposes `getCurrentFrame`, `setFrame`, `setSpeed`, `setMinPixelRatio`, and `setMaxPixelCount`. + +### Convert React props to vanilla uniforms + +Follow the named React component's `uniforms` object. Apply these source-defined rules: + +- Convert each color string with `getShaderColorFromString`. +- Convert `colors` with `.map(getShaderColorFromString)` and also set `u_colorsCount`. +- Convert `fit` with `ShaderFitOptions[fit]`. +- Convert enum props with their exported mapping object, such as `WarpPatterns[shape]`. +- Pass `speed` and `frame` to `ShaderMount`; they are not shader-specific uniforms. +- Map common sizing props to `u_fit`, `u_scale`, `u_rotation`, `u_offsetX`, `u_offsetY`, `u_originX`, `u_originY`, `u_worldWidth`, and `u_worldHeight`. +- Pass booleans as booleans. `ShaderMount` converts them to integer uniforms. +- Pass textures as fully loaded `HTMLImageElement` instances. + +Do not assume every prop becomes `u_${prop}`. These package mappings differ: + +- `Dithering.size` and `ImageDithering.size` → `u_pxSize` +- `DotGrid.size` → `u_dotSize` +- `FlutedGlass.margin` and `PulsingBorder.margin` → defaults for all four side-specific margin uniforms; an explicitly supplied side value wins +- `width`, `height`, `minPixelRatio`, `maxPixelCount`, `webGlContextAttributes`, and `ref` configure the mount/container and are not uniforms +- `suspendWhenProcessingImage` and deprecated compatibility props are React-only + +Always inspect the catalog's source paths when constructing a vanilla shader. The uniform interfaces in `packages/shaders/src/shaders/*.ts` list the complete required uniform object. + +### Noise textures + +For shaders whose catalog says to set `u_noiseTexture`, import `getShaderNoiseTexture` and include: + +```ts +const noiseTexture = getShaderNoiseTexture(); +if (!noiseTexture) throw new Error('Noise textures require a browser'); +await noiseTexture.decode(); + +// Include in the initial uniforms: +u_noiseTexture: noiseTexture +``` + +The source returns `undefined` outside the browser and a newly created `HTMLImageElement` in the browser. Wait for it to load before constructing the vanilla `ShaderMount`; the mount rejects incomplete images. The React mount performs this wait itself. + +### Images + +The low-level vanilla mount accepts `HTMLImageElement`, not URL strings. The image must be fully loaded and have a non-zero `naturalWidth`; otherwise mounting the texture throws. + +The React mount accepts an `HTMLImageElement` or a string that is either: + +- an absolute path beginning with `/` +- a URL accepted by `new URL(value)` +- an empty string, which becomes a transparent pixel + +For an external URL, the React loader sets `crossOrigin = "anonymous"`. When both natural dimensions are below 1024, it sets the image dimensions so the shorter side is 1024 before upload. + +When a texture is supplied as `u_image`, `ShaderMount` automatically looks up and fills `u_imageAspectRatio`. + +Pass `["u_image"]` as the final vanilla constructor argument when the catalog requires mipmaps. + +### Preprocessed image shaders + +`Heatmap`, `LiquidMetal`, and `GemSmoke` preprocess image inputs in their React components. Reproduce this in vanilla: + +- `toProcessedHeatmap(fileOrUrl)` returns `Promise<{ blob: Blob }>` +- `toProcessedLiquidMetal(fileOrUrl)` returns `Promise<{ imageData: ImageData; pngBlob: Blob }>` +- `toProcessedGemSmoke(fileOrUrl)` returns `Promise<{ imageData: ImageData; pngBlob: Blob }>` + +Each function accepts `File | string` and requires browser document/canvas APIs. Load the returned blob into an `HTMLImageElement`, pass that loaded image as `u_image`, and enable mipmaps for `u_image`. + +For `LiquidMetal` and `GemSmoke`, also set `u_isImage` from whether an original image was supplied and convert `shape` with `LiquidMetalShapes` or `GemSmokeShapes`. When no original image is supplied, load the exported `emptyPixel` into an `HTMLImageElement`, use that placeholder as the initial `u_image`, and set `u_isImage` to `false`; the shader still declares and samples `u_image`. + +### Color syntax + +`getShaderColorFromString` supports: + +- 3-, 4-, 6-, and 8-digit hex +- comma-form `rgb(...)` and `rgba(...)` +- comma-form `hsl(...)` and `hsla(...)` +- already-normalized RGB or RGBA number tuples when calling the utility directly + +It does not parse CSS named colors. Invalid values fall back to `[0.5, 0.5, 0.5, 1]`. + +## Common sizing and motion + +All named shader components accept: + +{{sizingProperties}} + +Defaults come from one of two source objects, then may be overridden by the component's default preset: + +- `defaultObjectSizing`: `fit="contain"` and otherwise `scale=1`, `rotation=0`, offsets `0`, origins `0.5`, and world dimensions `0` +- `defaultPatternSizing`: the same values except `fit="none"` + +The catalog lists the effective defaults for every shader. + +Shaders whose params extend `ShaderMotionParams` also accept: + +{{motionProperties}} + +The mount pauses animated rendering when the document is hidden. It also pauses when the element leaves the viewport when `IntersectionObserver` is available in the element's window. + +`minPixelRatio` defaults to `2`. `maxPixelCount` defaults to `1920 * 1080 * 4` physical pixels. The `Waves` React component overrides its `maxPixelCount` default to `6016 * 3384`. +The `DotGrid` React component uses the same `6016 * 3384` override. + +## Verification + +- Type-check against the installed package version. +- Confirm the mount has a non-zero layout size. +- Confirm all initial vanilla uniforms are present. +- Confirm color arrays are non-empty and do not exceed the implementation capacity in the catalog. +- Confirm image inputs load and satisfy CORS rules. +- Confirm preprocessors run only in a browser environment. +- Call `dispose()` for vanilla mounts during teardown. diff --git a/packages/skill-generator/content/text.json b/packages/skill-generator/content/text.json new file mode 100644 index 00000000..8c85811c --- /dev/null +++ b/packages/skill-generator/content/text.json @@ -0,0 +1,14 @@ +{ + "phrases": { + "noiseRequirement": "Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`.", + "mipmapRequirement": "Vanilla requirements: pass `[\"u_image\"]` as the `ShaderMount` mipmaps argument.", + "combinedNoiseAndMipmapRequirement": "Vanilla requirements: create `u_noiseTexture` with `getShaderNoiseTexture()` and wait for the image to load before constructing `ShaderMount`; pass `[\"u_image\"]` as the `ShaderMount` mipmaps argument.", + "preprocessorRequirement": "Vanilla requirements: preprocess image input with `{{preprocessor}}`; pass `[\"u_image\"]` as the `ShaderMount` mipmaps argument.", + "maxPixelCount": "React performance override: `maxPixelCount` defaults to `{{expression}}`.", + "enumMismatch": "Enum source note: the documentation UI definition contains stale {{documented}}; the exported `{{mapping}}` mapping and component type use {{source}}." + }, + "usageMotionDescriptions": { + "speed": "`0` stops the animation loop; negative values play backward", + "frame": "animation position in milliseconds; the shader receives seconds as `u_time`" + } +} diff --git a/packages/skill-generator/package.json b/packages/skill-generator/package.json new file mode 100644 index 00000000..9e43978b --- /dev/null +++ b/packages/skill-generator/package.json @@ -0,0 +1,10 @@ +{ + "name": "@paper-design/skill-generator", + "private": true, + "type": "module", + "scripts": { + "generate": "bun run src/generate-skills.ts", + "check": "bun run src/generate-skills.ts --check", + "type-check": "tsc --project tsconfig.json" + } +} diff --git a/packages/skill-generator/src/ast.ts b/packages/skill-generator/src/ast.ts new file mode 100644 index 00000000..750a02b3 --- /dev/null +++ b/packages/skill-generator/src/ast.ts @@ -0,0 +1,196 @@ +import ts from 'typescript'; +import type { StaticRecord, StaticValue } from './model.js'; + +export const unwrapExpression = (expression: ts.Expression): ts.Expression => { + if ( + ts.isAsExpression(expression) || + ts.isSatisfiesExpression(expression) || + ts.isTypeAssertionExpression(expression) || + ts.isNonNullExpression(expression) || + ts.isParenthesizedExpression(expression) + ) { + return unwrapExpression(expression.expression); + } + + return expression; +}; + +export const resolveSymbol = (checker: ts.TypeChecker, node: ts.Node): ts.Symbol | undefined => { + const symbol = checker.getSymbolAtLocation(node); + return symbol && symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol; +}; + +export const variableInitializer = (symbol: ts.Symbol | undefined): ts.Expression | undefined => + symbol?.declarations + ?.filter(ts.isVariableDeclaration) + .map((declaration) => declaration.initializer) + .find((initializer): initializer is ts.Expression => Boolean(initializer)); + +export const propertyName = (name: ts.PropertyName | ts.BindingName | undefined): string | undefined => { + if (!name) return undefined; + if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) { + return name.text; + } + + return undefined; +}; + +export const objectProperty = (object: ts.ObjectLiteralExpression, name: string): ts.PropertyAssignment | undefined => + object.properties.filter(ts.isPropertyAssignment).find((property) => propertyName(property.name) === name); + +export const exportedVariable = (sourceFile: ts.SourceFile, name: string): ts.VariableDeclaration | undefined => + sourceFile.statements + .filter(ts.isVariableStatement) + .filter((statement) => statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) + .flatMap((statement) => [...statement.declarationList.declarations]) + .find((declaration) => propertyName(declaration.name) === name); + +export const interfaceDeclaration = (sourceFile: ts.SourceFile, name: string): ts.InterfaceDeclaration | undefined => + sourceFile.statements.filter(ts.isInterfaceDeclaration).find((declaration) => declaration.name.text === name); + +export const typeAliasDeclaration = (sourceFile: ts.SourceFile, name: string): ts.TypeAliasDeclaration | undefined => + sourceFile.statements.filter(ts.isTypeAliasDeclaration).find((declaration) => declaration.name.text === name); + +export const staticExpressionValue = ( + checker: ts.TypeChecker, + input: ts.Expression, + visited: ReadonlySet = new Set() +): StaticValue | undefined => { + const expression = unwrapExpression(input); + if (visited.has(expression)) return undefined; + const nextVisited = new Set(visited).add(expression); + const literal = literalExpressionValue(expression); + if (literal !== undefined) return literal; + + if (ts.isPrefixUnaryExpression(expression)) return unaryExpressionValue(checker, expression, nextVisited); + if (ts.isArrayLiteralExpression(expression)) return arrayExpressionValue(checker, expression, nextVisited); + if (ts.isObjectLiteralExpression(expression)) return objectExpressionValue(checker, expression, nextVisited); + if (ts.isIdentifier(expression)) return identifierExpressionValue(checker, expression, nextVisited); + if (ts.isPropertyAccessExpression(expression)) return propertyAccessExpressionValue(checker, expression, nextVisited); + if (ts.isBinaryExpression(expression)) return binaryExpressionValue(checker, expression, nextVisited); + + return undefined; +}; + +export const sourceStaticValue = (input: ts.Expression): StaticValue | undefined => { + const expression = unwrapExpression(input); + const literal = literalExpressionValue(expression); + if (literal !== undefined) return literal; + if (ts.isPrefixUnaryExpression(expression)) { + const value = sourceStaticValue(expression.operand); + return typeof value === 'number' && expression.operator === ts.SyntaxKind.MinusToken ? -value : value; + } + if (ts.isArrayLiteralExpression(expression)) { + const values = expression.elements.map((element) => + ts.isExpression(element) ? sourceStaticValue(element) : undefined + ); + return values.every((value) => value !== undefined) ? (values as StaticValue[]) : undefined; + } + return undefined; +}; + +export const isStaticRecord = (value: StaticValue | undefined): value is StaticRecord => + typeof value === 'object' && value !== null && !Array.isArray(value); + +const literalExpressionValue = (expression: ts.Expression): StaticValue | undefined => { + if (ts.isStringLiteralLike(expression)) return expression.text; + if (ts.isNumericLiteral(expression)) return Number(expression.text); + if (expression.kind === ts.SyntaxKind.TrueKeyword) return true; + if (expression.kind === ts.SyntaxKind.FalseKeyword) return false; + if (expression.kind === ts.SyntaxKind.NullKeyword) return null; + return undefined; +}; + +const unaryExpressionValue = ( + checker: ts.TypeChecker, + expression: ts.PrefixUnaryExpression, + visited: ReadonlySet +): StaticValue | undefined => { + const value = staticExpressionValue(checker, expression.operand, visited); + if (typeof value !== 'number') return undefined; + if (expression.operator === ts.SyntaxKind.MinusToken) return -value; + if (expression.operator === ts.SyntaxKind.PlusToken) return value; + return undefined; +}; + +const arrayExpressionValue = ( + checker: ts.TypeChecker, + expression: ts.ArrayLiteralExpression, + visited: ReadonlySet +): StaticValue[] | undefined => { + const values = expression.elements.map((element) => + ts.isSpreadElement(element) ? undefined : staticExpressionValue(checker, element, visited) + ); + return values.every((value) => value !== undefined) ? (values as StaticValue[]) : undefined; +}; + +const objectExpressionValue = ( + checker: ts.TypeChecker, + expression: ts.ObjectLiteralExpression, + visited: ReadonlySet +): StaticRecord | undefined => + expression.properties.reduce( + (result, property) => mergeObjectProperty(checker, result, property, visited), + {} + ); + +const mergeObjectProperty = ( + checker: ts.TypeChecker, + record: StaticRecord | undefined, + property: ts.ObjectLiteralElementLike, + visited: ReadonlySet +): StaticRecord | undefined => { + if (!record) return undefined; + if (ts.isSpreadAssignment(property)) { + const spread = staticExpressionValue(checker, property.expression, visited); + return isStaticRecord(spread) ? { ...record, ...spread } : undefined; + } + if (ts.isPropertyAssignment(property)) { + const name = propertyName(property.name); + const value = staticExpressionValue(checker, property.initializer, visited); + return name && value !== undefined ? { ...record, [name]: value } : undefined; + } + if (ts.isShorthandPropertyAssignment(property)) { + const value = staticExpressionValue(checker, property.name, visited); + return value !== undefined ? { ...record, [property.name.text]: value } : undefined; + } + return record; +}; + +const identifierExpressionValue = ( + checker: ts.TypeChecker, + expression: ts.Identifier, + visited: ReadonlySet +): StaticValue | undefined => { + const initializer = variableInitializer(resolveSymbol(checker, expression)); + return initializer ? staticExpressionValue(checker, initializer, visited) : undefined; +}; + +const propertyAccessExpressionValue = ( + checker: ts.TypeChecker, + expression: ts.PropertyAccessExpression, + visited: ReadonlySet +): StaticValue | undefined => { + const object = staticExpressionValue(checker, expression.expression, visited); + return isStaticRecord(object) ? object[expression.name.text] : undefined; +}; + +const binaryExpressionValue = ( + checker: ts.TypeChecker, + expression: ts.BinaryExpression, + visited: ReadonlySet +): number | undefined => { + const left = staticExpressionValue(checker, expression.left, visited); + const right = staticExpressionValue(checker, expression.right, visited); + return typeof left === 'number' && typeof right === 'number' + ? numericBinaryValue(expression.operatorToken.kind, left, right) + : undefined; +}; + +const numericBinaryValue = (operator: ts.SyntaxKind, left: number, right: number): number | undefined => { + if (operator === ts.SyntaxKind.PlusToken) return left + right; + if (operator === ts.SyntaxKind.MinusToken) return left - right; + if (operator === ts.SyntaxKind.AsteriskToken) return left * right; + if (operator === ts.SyntaxKind.SlashToken) return left / right; + return undefined; +}; diff --git a/packages/skill-generator/src/catalog.ts b/packages/skill-generator/src/catalog.ts new file mode 100644 index 00000000..026c8172 --- /dev/null +++ b/packages/skill-generator/src/catalog.ts @@ -0,0 +1,97 @@ +import type { + DocumentationProperty, + DocumentationShader, + ShaderProperty, + ShaderComment, + ShaderCommentProperty, + SourceProperty, + StaticRecord, +} from './model.js'; + +export const combineProperties = ( + source: SourceProperty[], + documentation: DocumentationShader, + sourceComment: ShaderComment, + uniformNames: Record, + defaults: StaticRecord, + metadata: StaticRecord +): ShaderProperty[] => + source.map((property) => { + const documented = documentation.properties.find((candidate) => candidate.name === property.name); + const commented = sourceComment.properties.find( + (candidate) => candidate.name === (uniformNames[property.name] ?? property.name) + ); + return { + ...property, + ...(defaults[property.name] !== undefined ? { defaultValue: defaults[property.name] } : {}), + description: propertyDescription(property, commented, documented), + constraints: propertyConstraints(property, commented, documented, metadata), + }; + }); + +const propertyDescription = ( + property: SourceProperty, + sourceComment: ShaderCommentProperty | undefined, + documentation: DocumentationProperty | undefined +): string => { + if (property.deprecated) { + return deprecatedDescription(property.deprecated); + } + if (property.description) { + return reactOnlyDescription(property.description); + } + return sourceComment?.description ?? documentation?.description ?? reactOnlyDescription(); +}; + +const propertyConstraints = ( + property: SourceProperty, + sourceComment: ShaderCommentProperty | undefined, + documentation: DocumentationProperty | undefined, + metadata: StaticRecord +): string[] => { + if (property.deprecated) return []; + const range = + typeof documentation?.min === 'number' && typeof documentation.max === 'number' + ? editorRange(documentation.min, documentation.max) + : undefined; + const step = typeof documentation?.step === 'number' ? constraintStep(documentation.step) : undefined; + const options = property.options.length > 0 ? constraintOptions(property.options) : undefined; + const sourceRange = + sourceComment?.min !== undefined && + sourceComment.max !== undefined && + (sourceComment.min !== documentation?.min || sourceComment.max !== documentation.max) + ? documentedSourceRange(sourceComment.min, sourceComment.max) + : undefined; + const capacity = propertyCapacity(property.name, metadata); + const implementationCapacity = + capacity !== undefined && capacity !== sourceComment?.max && capacity !== documentation?.max + ? capacityConstraint(capacity) + : undefined; + return [range, step, options, sourceRange, implementationCapacity].filter((value): value is string => Boolean(value)); +}; + +export const deprecatedDescription = (comment: string): string => `React-only. @deprecated ${comment}`; + +export const reactOnlyDescription = (description?: string): string => + description ? `React-only. ${description}` : 'React-only.'; + +export const editorRange = (min: number, max: number): string => `editor range: ${min}…${max}`; + +export const constraintStep = (step: number): string => `step: ${step}`; + +export const constraintOptions = (options: string[]): string => + `options: ${options.map((option) => JSON.stringify(option)).join(', ')}`; + +export const documentedSourceRange = (min: number, max: number): string => + `shader source documents ${min}…${max}`; + +export const capacityConstraint = (capacity: number): string => `implementation capacity: ${capacity}`; + +export const propertyCapacity = (propertyName: string, metadata: StaticRecord): number | undefined => { + const name = `max${propertyName.charAt(0).toUpperCase()}${propertyName.slice(1)}`; + const value = metadata[name]; + return typeof value === 'number' ? value : undefined; +}; + +export const replaceTokens = (template: string, values: Record): string => + Object.entries(values).reduce((result, [name, value]) => result.replaceAll(`{{${name}}}`, value), template); diff --git a/packages/skill-generator/src/content.ts b/packages/skill-generator/src/content.ts new file mode 100644 index 00000000..66886e9a --- /dev/null +++ b/packages/skill-generator/src/content.ts @@ -0,0 +1,46 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import type { ContentOrders, RenderFragments, SkillContent, SkillTemplates, WrittenText } from './model.js'; + +const templateNames = ['skill', 'usage', 'shader'] as const satisfies readonly (keyof SkillTemplates)[]; + +export const loadSkillContent = (directory: string): SkillContent => { + const writtenText = readJson(path.join(directory, 'text.json')); + return { + templates: requireKeys(loadMarkdownDirectory(path.join(directory, 'templates')), templateNames, 'templates'), + fragments: readJson(path.join(directory, 'fragments.json')), + orders: readJson(path.join(directory, 'orders.json')), + text: writtenText.phrases, + usageMotionDescriptions: writtenText.usageMotionDescriptions, + }; +}; + +export const loadMarkdownDirectory = (directory: string): Record => + Object.fromEntries( + filesWithExtension(directory, '.md').map((fileName) => [ + path.basename(fileName, '.md'), + withoutFinalLineBreak(fs.readFileSync(path.join(directory, fileName), 'utf8')), + ]) + ); + +export const readJson = (filePath: string): Value => JSON.parse(fs.readFileSync(filePath, 'utf8')) as Value; + +export const filesWithExtension = (directory: string, extension: string): string[] => + fs + .readdirSync(directory) + .filter((fileName) => path.extname(fileName) === extension) + .sort(); + +export const withoutFinalLineBreak = (value: string): string => value.replace(/\r?\n$/, ''); + +export const requireKeys = ( + record: Record, + keys: readonly Key[], + group: string +): Record => { + const missing = keys.filter((key) => !(key in record)); + if (missing.length > 0) { + throw new Error(`Missing ${group}: ${missing.join(', ')}`); + } + return record as Record; +}; diff --git a/packages/skill-generator/src/generate-skills.ts b/packages/skill-generator/src/generate-skills.ts new file mode 100644 index 00000000..8368b36c --- /dev/null +++ b/packages/skill-generator/src/generate-skills.ts @@ -0,0 +1,176 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import ts from 'typescript'; +import { combineProperties } from './catalog.js'; +import { loadSkillContent } from './content.js'; +import type { Shader } from './model.js'; +import { renderShader, renderSkill, renderUsage } from './render.js'; +import { parseShaderComment } from './shader-comment.js'; +import { + bindingDefaultValues, + bindingDefaultExpression, + callsIdentifier, + calledIdentifierStartingWith, + defaultPresetParams, + discoverShaderSources, + enumMismatches, + interfaceExtends, + jsxArrayAttributeIncludes, + parseCommonDocumentationProperties, + parseDocumentationShader, + shaderMetadata, + shaderModulePaths, + sourceProperties, + uniformPropertyNames, +} from './source-model.js'; + +const root = path.resolve(import.meta.dir, '../../..'); +const content = loadSkillContent(path.join(root, 'packages/skill-generator/content')); +const check = process.argv.includes('--check'); +const configPath = path.join(root, 'packages/shaders-react/tsconfig.json'); +const configFile = ts.readConfigFile(configPath, ts.sys.readFile); +if (configFile.error) { + throw new Error(ts.flattenDiagnosticMessageText(configFile.error.messageText, '\n')); +} +const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath)); +const compilerOptions: ts.CompilerOptions = { + ...parsedConfig.options, + baseUrl: root, + paths: { + '@paper-design/shaders': ['packages/shaders/src/index.ts'], + '@paper-design/shaders/*': ['packages/shaders/src/*'], + }, +}; +const program = ts.createProgram(parsedConfig.fileNames, compilerOptions); +const checker = program.getTypeChecker(); +const coreIndexPath = path.join(root, 'packages/shaders/src/index.ts'); +const reactIndexPath = path.join(root, 'packages/shaders-react/src/index.ts'); +const coreIndex = program.getSourceFile(coreIndexPath); +const reactIndex = program.getSourceFile(reactIndexPath); +if (!coreIndex || !reactIndex) { + throw new Error('Could not load Paper Shaders package entry points'); +} +const commonDocumentationPath = path.join(root, 'docs/src/shader-defs/common-param-def.ts'); +const commonDocumentationFile = ts.createSourceFile( + commonDocumentationPath, + fs.readFileSync(commonDocumentationPath, 'utf8'), + ts.ScriptTarget.Latest, + true, + ts.ScriptKind.TS +); +const commonDocumentation = parseCommonDocumentationProperties(commonDocumentationFile); +const sizingSource = program.getSourceFile(path.join(root, 'packages/shaders/src/shader-sizing.ts')); +const motionSource = program.getSourceFile(path.join(root, 'packages/shaders/src/shader-mount.ts')); +const componentSource = program.getSourceFile(path.join(root, 'packages/shaders-react/src/shader-mount.tsx')); +if (!sizingSource || !motionSource || !componentSource) { + throw new Error('Could not load common Paper Shaders types'); +} +const sizingProperties = sourceProperties(checker, sizingSource, 'ShaderSizingParams'); +const motionProperties = sourceProperties(checker, motionSource, 'ShaderMotionParams'); +const componentProperties = sourceProperties(checker, componentSource, 'ShaderComponentProps'); + +const sources = discoverShaderSources(root, coreIndex, reactIndex); +if (sources.length !== shaderModulePaths(reactIndex).length || sources.length === 0) { + throw new Error('Could not resolve every exported shader module'); +} +const shaders = sources.map((source): Shader => { + const coreFile = program.getSourceFile(source.corePath); + const reactFile = program.getSourceFile(source.reactPath); + const documentationText = fs.readFileSync(source.documentationPath, 'utf8'); + const documentationFile = ts.createSourceFile( + source.documentationPath, + documentationText, + ts.ScriptTarget.Latest, + true, + ts.ScriptKind.TS + ); + const documentation = parseDocumentationShader(documentationFile); + if (!coreFile || !reactFile || !documentation) { + throw new Error(`Could not load source model for ${source.slug}`); + } + documentation.properties.push(...commonDocumentation); + + const coreProperties = sourceProperties(checker, coreFile, source.exports.paramsType); + const reactProperties = sourceProperties(checker, reactFile, `${source.exports.component}Props`); + const sourceComment = parseShaderComment(coreFile.getFullText(), source.exports.fragmentShader); + if (coreProperties.length === 0) { + throw new Error(`Could not resolve properties for ${source.exports.paramsType}`); + } + if (!sourceComment) { + throw new Error(`Could not resolve shader comment for ${source.exports.paramsType}`); + } + const propertiesFromSource = [...coreProperties, ...reactProperties]; + const defaults = { + ...defaultPresetParams(checker, reactFile), + ...bindingDefaultValues(checker, reactFile), + }; + const metadata = shaderMetadata(checker, coreFile); + const properties = combineProperties( + propertiesFromSource, + documentation, + sourceComment, + uniformPropertyNames( + reactFile, + propertiesFromSource.map((property) => property.name) + ), + defaults, + metadata + ); + const shader: Shader = { + ...source, + name: documentation.name, + description: sourceComment.description, + properties, + defaults, + hasMotion: interfaceExtends(coreFile, source.exports.paramsType, 'ShaderMotionParams'), + usesNoiseTexture: callsIdentifier(reactFile, 'getShaderNoiseTexture'), + usesImageMipmaps: jsxArrayAttributeIncludes(reactFile, 'mipmaps', 'u_image'), + imagePreprocessor: calledIdentifierStartingWith(reactFile, 'toProcessed'), + maxPixelCountExpression: bindingDefaultExpression(reactFile, 'maxPixelCount'), + enumMismatches: enumMismatches(checker, coreFile, coreProperties, documentation), + }; + return shader; +}); +const sortedShaders = [...shaders].sort((left, right) => left.name.localeCompare(right.name)); +const outputRoot = path.join(root, 'skills/paper-shaders'); +const files = new Map([ + [path.join(outputRoot, 'SKILL.md'), renderSkill(content, sortedShaders)], + [ + path.join(outputRoot, 'references/usage.md'), + renderUsage(content, componentProperties, sizingProperties, motionProperties), + ], + ...sortedShaders.map((shader): [string, string] => [ + path.join(outputRoot, 'references/shaders', `${shader.slug}.md`), + renderShader(root, content, shader), + ]), +]); +const staleFiles = fs.existsSync(path.join(outputRoot, 'references/shaders')) + ? fs + .readdirSync(path.join(outputRoot, 'references/shaders')) + .filter((name) => name.endsWith('.md')) + .map((name) => path.join(outputRoot, 'references/shaders', name)) + .filter((filePath) => !files.has(filePath)) + : []; +const changedFiles = [...files].filter( + ([filePath, value]) => !fs.existsSync(filePath) || fs.readFileSync(filePath, 'utf8') !== value +); + +if (check && (changedFiles.length > 0 || staleFiles.length > 0)) { + const names = [ + ...changedFiles.map(([filePath]) => path.relative(root, filePath)), + ...staleFiles.map((filePath) => path.relative(root, filePath)), + ]; + throw new Error(`Generated skills are out of date:\n${names.join('\n')}`); +} + +if (!check) { + for (const [filePath, value] of changedFiles) { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + fs.writeFileSync(filePath, value); + } + for (const filePath of staleFiles) { + fs.unlinkSync(filePath); + } +} + +console.log(check ? `Checked ${files.size} generated skill files` : `Generated ${files.size} skill files`); diff --git a/packages/skill-generator/src/model.ts b/packages/skill-generator/src/model.ts new file mode 100644 index 00000000..f166bb09 --- /dev/null +++ b/packages/skill-generator/src/model.ts @@ -0,0 +1,124 @@ +export type StaticValue = string | number | boolean | null | StaticValue[] | { [key: string]: StaticValue }; + +export type StaticRecord = { [key: string]: StaticValue }; + +export type SourceProperty = { + name: string; + type: string; + required: boolean; + options: string[]; + deprecated?: string; + description?: string; +}; + +export type DocumentationProperty = { + name: string; + description: string; + min?: number; + max?: number; + step?: number; + options: string[]; +}; + +export type DocumentationShader = { + name: string; + description: string; + properties: DocumentationProperty[]; +}; + +export type ShaderCommentProperty = { + name: string; + description: string; + min?: number; + max?: number; +}; + +export type ShaderComment = { + description: string; + properties: ShaderCommentProperty[]; +}; + +export type ShaderExports = { + component: string; + paramsType: string; + preset: string; + fragmentShader: string; +}; + +export type ShaderSource = { + slug: string; + corePath: string; + reactPath: string; + documentationPath: string; + exports: ShaderExports; +}; + +export type ShaderProperty = SourceProperty & { + defaultValue?: StaticValue; + description: string; + constraints: string[]; +}; + +export type Shader = ShaderSource & { + name: string; + description: string; + properties: ShaderProperty[]; + defaults: StaticRecord; + hasMotion: boolean; + usesNoiseTexture: boolean; + usesImageMipmaps: boolean; + imagePreprocessor?: string; + maxPixelCountExpression?: string; + enumMismatches: EnumMismatch[]; +}; + +export type EnumMismatch = { + prop: string; + sourceOptions: string[]; + documentationOptions: string[]; + mapping?: string; +}; + +export type SkillTemplates = { + skill: string; + usage: string; + shader: string; +}; + +export type RenderFragments = { + shaderLink: string; + note: string; + default: string; + inlineCode: string; + tableHeader: string[]; + tableRow: string; + usageProperty: string; + usageMotionProperty: string; +}; + +export type ContentOrders = { + usageComponentProperties: string[]; + commonDefaults: string[]; +}; + +export type SkillText = { + noiseRequirement: string; + mipmapRequirement: string; + combinedNoiseAndMipmapRequirement: string; + preprocessorRequirement: string; + maxPixelCount: string; + enumMismatch: string; +}; + +export type WrittenText = { + phrases: SkillText; + usageMotionDescriptions: Record; +}; + +export type SkillContent = { + templates: SkillTemplates; + fragments: RenderFragments; + orders: ContentOrders; + usageMotionDescriptions: Record; + text: SkillText; +}; diff --git a/packages/skill-generator/src/render.ts b/packages/skill-generator/src/render.ts new file mode 100644 index 00000000..8c5d39d5 --- /dev/null +++ b/packages/skill-generator/src/render.ts @@ -0,0 +1,156 @@ +import path from 'node:path'; +import { replaceTokens } from './catalog.js'; +import type { EnumMismatch, Shader, ShaderProperty, SkillContent, SourceProperty, StaticValue } from './model.js'; + +export const renderSkill = (content: SkillContent, shaders: Shader[]): string => + renderTemplate(content.templates.skill, { + shaderLinks: shaders + .map((shader) => + replaceTokens(content.fragments.shaderLink, { + name: shader.name, + slug: shader.slug, + }) + ) + .join('\n'), + }); + +export const renderUsage = ( + content: SkillContent, + componentProperties: SourceProperty[], + sizingProperties: SourceProperty[], + motionProperties: SourceProperty[] +): string => + renderTemplate(content.templates.usage, { + componentControls: orderedProperties(componentProperties, content.orders.usageComponentProperties) + .map((property) => renderUsageProperty(content, property)) + .join('\n'), + sizingProperties: sizingProperties.map((property) => renderUsageProperty(content, property)).join('\n'), + motionProperties: motionProperties + .map((property) => + replaceTokens(content.fragments.usageMotionProperty, { + name: property.name, + optional: optionalMarker(property.required), + type: displayType(property), + description: content.usageMotionDescriptions[property.name] ?? '', + }) + ) + .join('\n'), + }); + +export const renderShader = (root: string, content: SkillContent, shader: Shader): string => { + const requirements = renderRequirements(content, shader); + const notes = [...requirements, ...shader.enumMismatches.map((mismatch) => renderEnumMismatch(content, mismatch))]; + const defaults = content.orders.commonDefaults + .map((name) => [name, shader.defaults[name]] as const) + .filter((entry): entry is readonly [string, StaticValue] => entry[1] !== undefined) + .map(([name, value]) => + replaceTokens(content.fragments.default, { + name, + value: formatValue(value), + }) + ) + .join(', '); + const table = [ + ...content.fragments.tableHeader, + ...shader.properties.map((property) => renderProperty(content, property)), + ].join('\n'); + + return renderTemplate(content.templates.shader, { + name: shader.name, + description: shader.description, + component: shader.exports.component, + preset: shader.exports.preset, + fragmentShader: shader.exports.fragmentShader, + paramsType: shader.exports.paramsType, + controls: controlsLabel(shader.hasMotion), + defaults, + notes: + notes.length > 0 ? `\n${notes.map((note) => replaceTokens(content.fragments.note, { note })).join('\n')}` : '', + source: sourceList( + relativePath(root, shader.corePath), + relativePath(root, shader.reactPath), + relativePath(root, shader.documentationPath) + ), + table, + }); +}; + +export const renderProperty = (content: SkillContent, property: ShaderProperty): string => + replaceTokens(content.fragments.tableRow, { + name: property.name, + type: escapeTable(property.type), + required: requiredLabel(property.required), + defaultValue: + property.defaultValue === undefined + ? emptyTableCell() + : replaceTokens(content.fragments.inlineCode, { + value: escapeTable(formatValue(property.defaultValue)), + }), + constraints: property.constraints.length > 0 ? escapeTable(property.constraints.join('; ')) : emptyTableCell(), + description: escapeTable(property.description), + }); + +export const renderTemplate = (template: string, values: Record): string => + `${replaceTokens(template, values)}\n`; + +export const formatValue = (value: StaticValue): string => JSON.stringify(value); + +export const renderUsageProperty = (content: SkillContent, property: SourceProperty): string => + replaceTokens(content.fragments.usageProperty, { + name: property.name, + optional: optionalMarker(property.required), + type: displayType(property), + }); + +export const optionalMarker = (required: boolean): string => (required ? '' : '?'); + +export const controlsLabel = (hasMotion: boolean): string => (hasMotion ? 'sizing and motion' : 'sizing'); + +export const sourceList = (corePath: string, reactPath: string, documentationPath: string): string => + `Source: \`${corePath}\`, \`${reactPath}\`, \`${documentationPath}\`.`; + +export const requiredLabel = (required: boolean): string => (required ? 'yes' : 'no'); + +export const emptyTableCell = (): string => '—'; + +const renderRequirements = (content: SkillContent, shader: Shader): string[] => { + if (shader.usesNoiseTexture && shader.usesImageMipmaps) { + return [content.text.combinedNoiseAndMipmapRequirement]; + } + const requirements = [ + shader.usesNoiseTexture ? content.text.noiseRequirement : undefined, + shader.usesImageMipmaps + ? shader.imagePreprocessor + ? replaceTokens(content.text.preprocessorRequirement, { + preprocessor: shader.imagePreprocessor, + }) + : content.text.mipmapRequirement + : undefined, + shader.maxPixelCountExpression + ? replaceTokens(content.text.maxPixelCount, { + expression: shader.maxPixelCountExpression, + }) + : undefined, + ]; + return requirements.filter((value): value is string => Boolean(value)); +}; + +const renderEnumMismatch = (content: SkillContent, mismatch: EnumMismatch): string => + replaceTokens(content.text.enumMismatch, { + documented: mismatch.documentationOptions + .map((value) => replaceTokens(content.fragments.inlineCode, { value })) + .join(', '), + source: mismatch.sourceOptions.map((value) => replaceTokens(content.fragments.inlineCode, { value })).join(', '), + mapping: mismatch.mapping ?? '', + }); + +const relativePath = (root: string, filePath: string): string => + path.relative(root, filePath).split(path.sep).join('/'); + +const escapeTable = (value: string): string => value.replaceAll('|', '\\|'); + +const displayType = (property: SourceProperty): string => + property.options.length > 0 ? property.options.map((option) => JSON.stringify(option)).join(' | ') : property.type; + +const orderedProperties = (properties: SourceProperty[], order: string[]): SourceProperty[] => + [...properties].sort((left, right) => order.indexOf(left.name) - order.indexOf(right.name)); diff --git a/packages/skill-generator/src/shader-comment.test.ts b/packages/skill-generator/src/shader-comment.test.ts new file mode 100644 index 00000000..e2526a18 --- /dev/null +++ b/packages/skill-generator/src/shader-comment.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, test } from 'bun:test'; +import { descriptionAndRange, parseShaderComment } from './shader-comment.js'; + +describe('parseShaderComment', () => { + test('reads the shader description and uniform documentation', () => { + const source = ` + /** + * Example shader description + * split over two lines. + * + * Fragment shader uniforms: + * - u_amount (float): Effect strength (0 to 1) + * - u_mode (float): Mode (0 = first, 1 = second) + */ + export const exampleFragmentShader = ''; + `; + + expect(parseShaderComment(source, 'exampleFragmentShader')).toEqual({ + description: 'Example shader description split over two lines.', + properties: [ + { name: 'amount', description: 'Effect strength', min: 0, max: 1 }, + { name: 'mode', description: 'Mode (0 = first, 1 = second)' }, + ], + }); + }); +}); + +describe('descriptionAndRange', () => { + test('preserves descriptive parentheses before a trailing range', () => { + expect(descriptionAndRange('Proportional gain (enhances existing dots, -1 to 1)')).toEqual({ + description: 'Proportional gain (enhances existing dots)', + min: -1, + max: 1, + }); + }); +}); diff --git a/packages/skill-generator/src/shader-comment.ts b/packages/skill-generator/src/shader-comment.ts new file mode 100644 index 00000000..7053470d --- /dev/null +++ b/packages/skill-generator/src/shader-comment.ts @@ -0,0 +1,74 @@ +import type { ShaderComment, ShaderCommentProperty } from './model.js'; + +const jsDocPattern = /\/\*\*([\s\S]*?)\*\//g; +const uniformPattern = /^- u_([A-Za-z0-9]+) \([^)]+\): (.+)$/; +const numberPattern = '-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)'; +const trailingRangePattern = new RegExp(`(${numberPattern}) to (${numberPattern})\\)$`); + +export const parseShaderComment = (source: string, fragmentShaderName: string): ShaderComment | undefined => { + const comment = precedingJsDoc(source, fragmentShaderName); + if (!comment) return undefined; + const lines = jsDocLines(comment); + const description = firstParagraph(lines); + return description + ? { + description, + properties: lines + .map(parseUniformLine) + .filter((property): property is ShaderCommentProperty => Boolean(property)), + } + : undefined; +}; + +export const precedingJsDoc = (source: string, declarationName: string): string | undefined => { + const declarationIndex = source.indexOf(declarationName); + if (declarationIndex < 0) return undefined; + return [...source.slice(0, declarationIndex).matchAll(jsDocPattern)].at(-1)?.[1]; +}; + +export const jsDocLines = (comment: string): string[] => + comment.split(/\r?\n/).map((line) => line.replace(/^\s*\*\s?/, '').trimEnd()); + +export const firstParagraph = (lines: string[]): string => { + const start = lines.findIndex((line) => line.trim().length > 0); + if (start < 0) return ''; + const paragraph = lines.slice(start); + const end = paragraph.findIndex((line) => line.trim().length === 0); + return paragraph + .slice(0, end < 0 ? paragraph.length : end) + .map((line) => line.trim()) + .join(' '); +}; + +export const parseUniformLine = (line: string): ShaderCommentProperty | undefined => { + const match = line.match(uniformPattern); + if (!match?.[1] || !match[2]) return undefined; + const value = descriptionAndRange(match[2]); + return { + name: match[1], + description: value.description, + ...(value.min === undefined ? {} : { min: value.min }), + ...(value.max === undefined ? {} : { max: value.max }), + }; +}; + +export const descriptionAndRange = (value: string): { description: string; min?: number; max?: number } => { + const match = value.match(trailingRangePattern); + if (!match?.[1] || !match[2] || match.index === undefined) { + return { description: value }; + } + return { + description: closeUnbalancedParenthesis(removeRangeSeparator(value.slice(0, match.index))), + min: Number(match[1]), + max: Number(match[2]), + }; +}; + +export const removeRangeSeparator = (value: string): string => + value.trimEnd().replace(/\($/, '').replace(/,\s*$/, '').trimEnd(); + +export const closeUnbalancedParenthesis = (value: string): string => + characterCount(value, '(') > characterCount(value, ')') ? `${value})` : value; + +export const characterCount = (value: string, character: string): number => + [...value].filter((candidate) => candidate === character).length; diff --git a/packages/skill-generator/src/source-model.ts b/packages/skill-generator/src/source-model.ts new file mode 100644 index 00000000..97a2ac43 --- /dev/null +++ b/packages/skill-generator/src/source-model.ts @@ -0,0 +1,350 @@ +import path from 'node:path'; +import ts from 'typescript'; +import { + exportedVariable, + interfaceDeclaration, + isStaticRecord, + objectProperty, + propertyName, + resolveSymbol, + sourceStaticValue, + staticExpressionValue, + typeAliasDeclaration, + unwrapExpression, +} from './ast.js'; +import type { + DocumentationProperty, + DocumentationShader, + EnumMismatch, + ShaderExports, + ShaderSource, + SourceProperty, + StaticRecord, +} from './model.js'; + +export const discoverShaderSources = ( + root: string, + coreIndex: ts.SourceFile, + reactIndex: ts.SourceFile +): ShaderSource[] => + shaderModulePaths(reactIndex) + .map((modulePath) => { + const slug = path.basename(modulePath, '.js'); + const exports = shaderExports( + exportNamesForModule(reactIndex, modulePath), + exportNamesForModule(coreIndex, modulePath) + ); + return exports + ? { + slug, + corePath: path.join(root, 'packages/shaders/src/shaders', `${slug}.ts`), + reactPath: path.join(root, 'packages/shaders-react/src/shaders', `${slug}.tsx`), + documentationPath: path.join(root, 'docs/src/shader-defs', `${slug}-def.ts`), + exports, + } + : undefined; + }) + .filter((source): source is ShaderSource => Boolean(source)); + +export const shaderModulePaths = (sourceFile: ts.SourceFile): string[] => [ + ...new Set( + sourceFile.statements + .filter(ts.isExportDeclaration) + .map((declaration) => shaderModulePath(declaration)) + .filter((modulePath): modulePath is string => Boolean(modulePath)) + ), +]; + +export const sourceProperties = ( + checker: ts.TypeChecker, + sourceFile: ts.SourceFile, + interfaceName: string +): SourceProperty[] => { + const declaration = interfaceDeclaration(sourceFile, interfaceName); + if (!declaration) return []; + + return declaration.members + .filter(ts.isPropertySignature) + .map((member) => { + const name = propertyName(member.name); + return name && member.type + ? { + name, + type: member.type.getText(sourceFile), + required: !member.questionToken, + options: orderedStringOptions(checker, sourceFile, member.type), + ...(deprecatedText(member) ? { deprecated: deprecatedText(member) } : {}), + ...(jsDocText(member) ? { description: jsDocText(member) } : {}), + } + : undefined; + }) + .filter((property): property is SourceProperty => Boolean(property)); +}; + +export const interfaceExtends = (sourceFile: ts.SourceFile, interfaceName: string, baseName: string): boolean => + Boolean( + interfaceDeclaration(sourceFile, interfaceName)?.heritageClauses?.some((clause) => + clause.types.some((type) => type.expression.getText(sourceFile) === baseName) + ) + ); + +export const defaultPresetParams = (checker: ts.TypeChecker, sourceFile: ts.SourceFile): StaticRecord => { + const initializer = exportedVariable(sourceFile, 'defaultPreset')?.initializer; + const preset = initializer ? staticExpressionValue(checker, initializer) : undefined; + const params = isStaticRecord(preset) ? preset.params : undefined; + return isStaticRecord(params) ? params : {}; +}; + +export const bindingDefaultValues = (checker: ts.TypeChecker, sourceFile: ts.SourceFile): StaticRecord => + descendants(sourceFile) + .filter(ts.isBindingElement) + .reduce((defaults, element) => { + const name = propertyName(element.name); + const value = element.initializer ? staticExpressionValue(checker, element.initializer) : undefined; + return name && value !== undefined ? { ...defaults, [name]: value } : defaults; + }, {}); + +export const shaderMetadata = (checker: ts.TypeChecker, sourceFile: ts.SourceFile): StaticRecord => { + const declaration = sourceFile.statements + .filter(ts.isVariableStatement) + .flatMap((statement) => [...statement.declarationList.declarations]) + .find((item) => propertyName(item.name)?.endsWith('Meta')); + const value = declaration?.initializer ? staticExpressionValue(checker, declaration.initializer) : undefined; + return isStaticRecord(value) ? value : {}; +}; + +export const parseDocumentationShader = (sourceFile: ts.SourceFile): DocumentationShader | undefined => { + const declaration = sourceFile.statements + .filter(ts.isVariableStatement) + .flatMap((statement) => [...statement.declarationList.declarations]) + .find((item) => propertyName(item.name)?.endsWith('Def')); + const object = declaration?.initializer ? unwrapExpression(declaration.initializer) : undefined; + if (!object || !ts.isObjectLiteralExpression(object)) return undefined; + + const name = staticPropertyValue(object, 'name'); + const description = staticPropertyValue(object, 'description'); + const params = objectProperty(object, 'params')?.initializer; + if (typeof name !== 'string' || typeof description !== 'string' || !params || !ts.isArrayLiteralExpression(params)) { + return undefined; + } + + return { + name, + description, + properties: params.elements + .filter(ts.isObjectLiteralExpression) + .map(parseDocumentationProperty) + .filter((property): property is DocumentationProperty => Boolean(property)), + }; +}; + +export const parseCommonDocumentationProperties = (sourceFile: ts.SourceFile): DocumentationProperty[] => { + const initializer = exportedVariable(sourceFile, 'commonParams')?.initializer; + const object = initializer ? unwrapExpression(initializer) : undefined; + if (!object || !ts.isObjectLiteralExpression(object)) return []; + return object.properties + .filter(ts.isPropertyAssignment) + .map((property) => unwrapExpression(property.initializer)) + .filter(ts.isObjectLiteralExpression) + .map(parseDocumentationProperty) + .filter((property): property is DocumentationProperty => Boolean(property)); +}; + +export const callsIdentifier = (sourceFile: ts.SourceFile, identifier: string): boolean => + descendants(sourceFile) + .filter(ts.isCallExpression) + .some((call) => ts.isIdentifier(call.expression) && call.expression.text === identifier); + +export const calledIdentifierStartingWith = (sourceFile: ts.SourceFile, prefix: string): string | undefined => + descendants(sourceFile) + .filter(ts.isCallExpression) + .map((call) => call.expression) + .filter(ts.isIdentifier) + .map((identifier) => identifier.text) + .find((name) => name.startsWith(prefix)); + +export const jsxArrayAttributeIncludes = ( + sourceFile: ts.SourceFile, + attributeName: string, + expectedValue: string +): boolean => + descendants(sourceFile) + .filter(ts.isJsxAttribute) + .filter((attribute) => attribute.name.getText(sourceFile) === attributeName) + .map((attribute) => attribute.initializer) + .filter((initializer): initializer is ts.JsxExpression => Boolean(initializer && ts.isJsxExpression(initializer))) + .map((expression) => expression.expression) + .filter((expression): expression is ts.ArrayLiteralExpression => + Boolean(expression && ts.isArrayLiteralExpression(expression)) + ) + .some((array) => + array.elements.some((element) => ts.isStringLiteralLike(element) && element.text === expectedValue) + ); + +export const bindingDefaultExpression = (sourceFile: ts.SourceFile, bindingName: string): string | undefined => + descendants(sourceFile) + .filter(ts.isBindingElement) + .find((element) => propertyName(element.name) === bindingName) + ?.initializer?.getText(sourceFile); + +export const uniformPropertyNames = ( + sourceFile: ts.SourceFile, + propertyNames: readonly string[] +): Record => { + const names = new Set(propertyNames); + const declaration = descendants(sourceFile) + .filter(ts.isVariableDeclaration) + .find((candidate) => propertyName(candidate.name) === 'uniforms'); + const initializer = declaration?.initializer ? unwrapExpression(declaration.initializer) : undefined; + if (!initializer || !ts.isObjectLiteralExpression(initializer)) return {}; + + return initializer.properties.filter(ts.isPropertyAssignment).reduce>((result, property) => { + const uniform = propertyName(property.name); + const sourceProperty = referencedIdentifier(property.initializer, names); + return uniform?.startsWith('u_') && sourceProperty && !(sourceProperty in result) + ? { ...result, [sourceProperty]: uniform.slice(2) } + : result; + }, {}); +}; + +export const enumMismatches = ( + checker: ts.TypeChecker, + sourceFile: ts.SourceFile, + properties: SourceProperty[], + documentation: DocumentationShader +): EnumMismatch[] => + properties.flatMap((property) => { + const documented = documentation.properties.find((candidate) => candidate.name === property.name); + const differs = + documented && + documented.options.length > 0 && + property.options.length > 0 && + (documented.options.some((option) => !property.options.includes(option)) || + property.options.some((option) => !documented.options.includes(option))); + return differs + ? [ + { + prop: property.name, + sourceOptions: property.options.filter((option) => !documented.options.includes(option)), + documentationOptions: documented.options.filter((option) => !property.options.includes(option)), + mapping: enumMappingName(sourceFile, checker, property.type), + }, + ] + : []; + }); + +const shaderModulePath = (declaration: ts.ExportDeclaration): string | undefined => { + const modulePath = declaration.moduleSpecifier; + return modulePath && ts.isStringLiteral(modulePath) && modulePath.text.startsWith('./shaders/') + ? modulePath.text + : undefined; +}; + +const exportNamesForModule = (sourceFile: ts.SourceFile, modulePath: string): string[] => + sourceFile.statements + .filter(ts.isExportDeclaration) + .filter( + (declaration) => + declaration.moduleSpecifier && + ts.isStringLiteral(declaration.moduleSpecifier) && + declaration.moduleSpecifier.text === modulePath + ) + .flatMap(namedExports); + +const shaderExports = (reactNames: string[], coreNames: string[]): ShaderExports | undefined => { + const component = reactNames.find((name) => /^[A-Z]/.test(name)); + const preset = reactNames.find((name) => name.endsWith('Presets')); + const paramsType = coreNames.find((name) => name.endsWith('Params')); + const fragmentShader = coreNames.find((name) => name.endsWith('FragmentShader')); + return component && preset && paramsType && fragmentShader + ? { component, preset, paramsType, fragmentShader } + : undefined; +}; + +const namedExports = (declaration: ts.ExportDeclaration): string[] => + declaration?.exportClause && ts.isNamedExports(declaration.exportClause) + ? declaration.exportClause.elements.map((element) => element.name.text) + : []; + +const stringLiteralOptions = (type: ts.Type): string[] => + type.isUnion() + ? type.types.filter((item): item is ts.StringLiteralType => item.isStringLiteral()).map((item) => item.value) + : type.isStringLiteral() + ? [type.value] + : []; + +const orderedStringOptions = (checker: ts.TypeChecker, sourceFile: ts.SourceFile, typeNode: ts.TypeNode): string[] => { + const mapping = mappingNameForType(sourceFile, typeNode.getText(sourceFile)); + const initializer = mapping ? exportedVariable(sourceFile, mapping)?.initializer : undefined; + const object = initializer ? unwrapExpression(initializer) : undefined; + return object && ts.isObjectLiteralExpression(object) + ? object.properties + .map((property) => ('name' in property ? propertyName(property.name) : undefined)) + .filter((name): name is string => Boolean(name)) + : stringLiteralOptions(checker.getTypeAtLocation(typeNode)); +}; + +const deprecatedText = (member: ts.PropertySignature): string | undefined => + ts + .getJSDocTags(member) + .filter((tag) => tag.tagName.text === 'deprecated') + .map((tag) => (typeof tag.comment === 'string' ? tag.comment : tag.comment?.map((part) => part.text).join(''))) + .find((comment): comment is string => Boolean(comment)); + +const jsDocText = (member: ts.PropertySignature): string | undefined => + ts + .getJSDocCommentsAndTags(member) + .filter(ts.isJSDoc) + .map((doc) => (typeof doc.comment === 'string' ? doc.comment : doc.comment?.map((part) => part.text).join(''))) + .find((comment): comment is string => Boolean(comment)); + +const parseDocumentationProperty = (object: ts.ObjectLiteralExpression): DocumentationProperty | undefined => { + const name = staticPropertyValue(object, 'name'); + const description = staticPropertyValue(object, 'description'); + if (typeof name !== 'string' || typeof description !== 'string') return undefined; + const min = staticPropertyValue(object, 'min'); + const max = staticPropertyValue(object, 'max'); + const step = staticPropertyValue(object, 'step'); + const options = staticPropertyValue(object, 'options'); + return { + name, + description, + ...(typeof min === 'number' ? { min } : {}), + ...(typeof max === 'number' ? { max } : {}), + ...(typeof step === 'number' ? { step } : {}), + options: Array.isArray(options) ? options.filter((option): option is string => typeof option === 'string') : [], + }; +}; + +const staticPropertyValue = (object: ts.ObjectLiteralExpression, name: string) => { + const expression = objectProperty(object, name)?.initializer; + return expression ? sourceStaticValue(expression) : undefined; +}; + +const descendants = (root: ts.Node): ts.Node[] => { + const children: ts.Node[] = []; + root.forEachChild((child) => { + children.push(child, ...descendants(child)); + }); + return children; +}; + +const referencedIdentifier = (expression: ts.Expression, names: ReadonlySet): string | undefined => + [expression, ...descendants(expression)] + .filter(ts.isIdentifier) + .map((identifier) => identifier.text) + .find((name) => names.has(name)); + +const enumMappingName = (sourceFile: ts.SourceFile, checker: ts.TypeChecker, typeName: string): string | undefined => { + const mapping = mappingNameForType(sourceFile, typeName); + if (mapping) return mapping; + const alias = typeAliasDeclaration(sourceFile, typeName); + const type = alias ? checker.getTypeAtLocation(alias.type) : undefined; + return type?.aliasSymbol?.name ?? (alias ? resolveSymbol(checker, alias.name)?.name : undefined); +}; + +const mappingNameForType = (sourceFile: ts.SourceFile, typeName: string): string | undefined => { + const alias = typeAliasDeclaration(sourceFile, typeName); + const match = alias?.type.getText(sourceFile).match(/keyof typeof (\w+)/); + return match?.[1]; +}; diff --git a/packages/skill-generator/tsconfig.json b/packages/skill-generator/tsconfig.json new file mode 100644 index 00000000..9ae82d08 --- /dev/null +++ b/packages/skill-generator/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "skipLibCheck": true, + "noUncheckedIndexedAccess": true, + "resolveJsonModule": true, + "verbatimModuleSyntax": true, + "noEmit": true + }, + "include": ["src/**/*.ts"] +} diff --git a/publish.js b/publish.js index 24744a29..a2a2df25 100644 --- a/publish.js +++ b/publish.js @@ -3,12 +3,15 @@ import { spawn } from 'child_process'; import { readFileSync, writeFileSync } from 'fs'; import { $ } from 'bun'; +await $`bun run generate-skills`; + // This publish process automatically replaces workspace:* with the actual version number of other packages currently in the repo // The packages to publish – this will run in order and await each package before moving on const packages = ['shaders', 'shaders-react']; const isDryRun = process.argv.includes('--dry-run'); +const publishedPackages = []; // Extract the tag value from the command line arguments const isCanaryRelease = process.argv.includes('--canary'); @@ -92,6 +95,7 @@ async function publish(pkg) { console.log(`Skipping ${pkg}: Publication failed or package is already up to date`); } else { console.log(`Published ${pkg}`); + publishedPackages.push(pkg); } // Restore the original package.json to put back workspace:* dependencies @@ -105,11 +109,31 @@ async function publish(pkg) { }); } +async function commitGeneratedSkills() { + const changes = await $`git status --porcelain -- skills`.text(); + if (!changes.trim()) { + console.log('Generated skills are already up to date in Git'); + return; + } + + const publishedVersions = [ + ...new Set(publishedPackages.map((pkg) => packageVersionMap[`@paper-design/${pkg}`])), + ].join(', '); + + await $`git add skills`; + await $`git commit -m ${`Generate skills for ${publishedVersions}`}`; + await $`git push`; + console.log('Committed and pushed generated skills'); +} + async function publishAll() { try { for (const pkg of packages) { await publish(pkg); } + if (!isDryRun && publishedPackages.length > 0) { + await commitGeneratedSkills(); + } console.log('All packages processed!'); } catch (error) { console.error('An unexpected error occurred:', error); diff --git a/skills/paper-shaders/references/shaders/color-panels.md b/skills/paper-shaders/references/shaders/color-panels.md index 15c33e5f..c8c2d2c4 100644 --- a/skills/paper-shaders/references/shaders/color-panels.md +++ b/skills/paper-shaders/references/shaders/color-panels.md @@ -2,7 +2,7 @@ Use the listed implementation types, options, capacities, exports, and defaults. Values labeled “editor range” come from the documentation UI definitions; the component does not clamp props to that range. -Pseudo-3D semi-transparent panels rotating around a central axis. +Pseudo-3D semi-transparent panels rotating around a central axis - React: `ColorPanels` and `colorPanelsPresets` from `@paper-design/shaders-react`. - Vanilla: `colorPanelsFragmentShader` and `ColorPanelsParams` from `@paper-design/shaders`. @@ -11,14 +11,14 @@ Pseudo-3D semi-transparent panels rotating around a central axis. | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#ff9d00","#fd4f30","#809bff","#6d2eff","#333aff","#f15cff","#ffd557"]` | — | Colors used by the shader; implementation capacity is 7. | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colors` | `string[]` | no | `["#ff9d00","#fd4f30","#809bff","#6d2eff","#333aff","#f15cff","#ffd557"]` | — | Up to 7 RGBA colors used to color the panels | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | | `angle1` | `number` | no | `0` | editor range: -1…1 | Skew angle applied to all panes | | `angle2` | `number` | no | `0` | editor range: -1…1 | Skew angle applied to all panes | -| `length` | `number` | no | `1.1` | editor range: 0…3 | Panel length (relative to total height) | -| `edges` | `boolean` | no | `false` | options: "true", "false" | Color highlight on the panels edges | -| `blur` | `number` | no | `0` | editor range: 0…0.5 | Side blur (0 for sharp edges) | +| `length` | `number` | no | `1.1` | editor range: 0…3 | Panel length relative to total height | +| `edges` | `boolean` | no | `false` | — | Color highlight on the panels edges | +| `blur` | `number` | no | `0` | editor range: 0…0.5 | Side blur, 0 for sharp edges | | `fadeIn` | `number` | no | `1` | editor range: 0…1 | Transparency near central axis | | `fadeOut` | `number` | no | `0.3` | editor range: 0…1 | Transparency near viewer | -| `density` | `number` | no | `3` | editor range: 0.25…7 | Controls the angular spacing between panels | -| `gradient` | `number` | no | `0` | editor range: 0…1 | Color mixing within a panel (0 = solid panel color, 1 = gradient of two colors) | +| `density` | `number` | no | `3` | editor range: 0.25…7 | Angle between every 2 panels | +| `gradient` | `number` | no | `0` | editor range: 0…1 | Color mixing within a panel, 0 = solid, 1 = gradient | diff --git a/skills/paper-shaders/references/shaders/dithering.md b/skills/paper-shaders/references/shaders/dithering.md index 65d6a2e4..270e2be6 100644 --- a/skills/paper-shaders/references/shaders/dithering.md +++ b/skills/paper-shaders/references/shaders/dithering.md @@ -11,9 +11,9 @@ Animated 2-color dithering over multiple pattern sources (noise, warp, dots, wav | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `colorFront` | `string` | no | `"#00b2ff"` | — | The foreground (ink) color | -| `shape` | `DitheringShape` | no | `"sphere"` | options: "simplex", "warp", "dots", "wave", "ripple", "swirl", "sphere" | Shape pattern type | -| `type` | `DitheringType` | no | `"4x4"` | options: "random", "2x2", "4x4", "8x8" | Dithering type | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `colorFront` | `string` | no | `"#00b2ff"` | — | Foreground (ink) color in RGBA | +| `shape` | `DitheringShape` | no | `"sphere"` | options: "simplex", "warp", "dots", "wave", "ripple", "swirl", "sphere" | Shape pattern type (1 = simplex, 2 = warp, 3 = dots, 4 = wave, 5 = ripple, 6 = swirl, 7 = sphere) | +| `type` | `DitheringType` | no | `"4x4"` | options: "random", "2x2", "4x4", "8x8" | Dithering type (1 = random, 2 = 2x2 Bayer, 3 = 4x4 Bayer, 4 = 8x8 Bayer) | | `size` | `number` | no | `2` | editor range: 1…20; shader source documents 0.5…20 | Pixel size of dithering grid | | `pxSize` | `number` | no | — | — | React-only. @deprecated use `size` instead | diff --git a/skills/paper-shaders/references/shaders/dot-grid.md b/skills/paper-shaders/references/shaders/dot-grid.md index 37fc46d2..4e259aa1 100644 --- a/skills/paper-shaders/references/shaders/dot-grid.md +++ b/skills/paper-shaders/references/shaders/dot-grid.md @@ -12,13 +12,13 @@ Static grid pattern made of circles, diamonds, squares or triangles. | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `colorFill` | `string` | no | `"#ffffff"` | — | Shape fill color | -| `colorStroke` | `string` | no | `"#ffaa00"` | — | Shape stroke color | -| `size` | `number` | no | `2` | editor range: 1…100 | Base size of each shape, pixels | -| `gapX` | `number` | no | `32` | editor range: 2…500 | Pattern horizontal spacing, pixels | -| `gapY` | `number` | no | `32` | editor range: 2…500 | Pattern vertical spacing, pixels | -| `strokeWidth` | `number` | no | `0` | editor range: 0…50 | The outline stroke width, pixels | -| `sizeRange` | `number` | no | `0` | editor range: 0…1 | Random variation in shape size (0 = uniform size, higher = random value up to base size) | -| `opacityRange` | `number` | no | `0` | editor range: 0…1 | Random variation in shape opacity (0 = all shapes opaque, higher = semi-transparent dots) | -| `shape` | `DotGridShape` | no | `"circle"` | options: "circle", "diamond", "square", "triangle" | The shape type | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `colorFill` | `string` | no | `"#ffffff"` | — | Shape fill color in RGBA | +| `colorStroke` | `string` | no | `"#ffaa00"` | — | Shape stroke color in RGBA | +| `size` | `number` | no | `2` | editor range: 1…100 | Base size of each shape in pixels | +| `gapX` | `number` | no | `32` | editor range: 2…500 | Pattern horizontal spacing in pixels | +| `gapY` | `number` | no | `32` | editor range: 2…500 | Pattern vertical spacing in pixels | +| `strokeWidth` | `number` | no | `0` | editor range: 0…50 | Outline stroke width in pixels | +| `sizeRange` | `number` | no | `0` | editor range: 0…1 | Random variation in shape size, 0 = uniform, higher = random up to base size | +| `opacityRange` | `number` | no | `0` | editor range: 0…1 | Random variation in shape opacity, 0 = opaque, higher = semi-transparent | +| `shape` | `DotGridShape` | no | `"circle"` | options: "circle", "diamond", "square", "triangle" | Shape type (0 = circle, 1 = diamond, 2 = square, 3 = triangle) | diff --git a/skills/paper-shaders/references/shaders/dot-orbit.md b/skills/paper-shaders/references/shaders/dot-orbit.md index 915f8729..2238a328 100644 --- a/skills/paper-shaders/references/shaders/dot-orbit.md +++ b/skills/paper-shaders/references/shaders/dot-orbit.md @@ -12,9 +12,9 @@ Animated multi-color dots pattern with each dot orbiting around its cell center. | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `colors` | `string[]` | no | `["#ffc96b","#ff6200","#ff2f00","#421100","#1a0000"]` | — | Colors used by the shader; implementation capacity is 10. | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `colors` | `string[]` | no | `["#ffc96b","#ff6200","#ff2f00","#421100","#1a0000"]` | — | Up to 10 base colors in RGBA | | `size` | `number` | no | `1` | editor range: 0…1 | Dot radius relative to cell size | -| `sizeRange` | `number` | no | `0` | editor range: 0…1 | Random variation in shape size (0 = uniform size, higher = random value up to base size) | -| `spreading` | `number` | no | `1` | editor range: 0…1 | Maximum orbit distance | -| `stepsPerColor` | `number` | no | `4` | editor range: 1…4; step: 1 | Number of extra colors between base colors (1 = N color palette, 2 = 2×N color palette, 3 = 3×N color palette, etc) | +| `sizeRange` | `number` | no | `0` | editor range: 0…1 | Random variation in shape size, 0 = uniform, higher = random up to base size | +| `spreading` | `number` | no | `1` | editor range: 0…1 | Maximum orbit distance around cell center | +| `stepsPerColor` | `number` | no | `4` | editor range: 1…4; step: 1 | Number of extra colors between base colors, 1 = N colors, 2 = 2×N, etc. | diff --git a/skills/paper-shaders/references/shaders/fluted-glass.md b/skills/paper-shaders/references/shaders/fluted-glass.md index b55f189c..dc5aa15c 100644 --- a/skills/paper-shaders/references/shaders/fluted-glass.md +++ b/skills/paper-shaders/references/shaders/fluted-glass.md @@ -13,16 +13,16 @@ Fluted glass image filter that transforms an image into streaked, ribbed distort | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `image` | `HTMLImageElement \| string` | no | `""` | — | The image to use for the effect | -| `colorBack` | `string` | no | `"#00000000"` | — | Background color | -| `colorShadow` | `string` | no | `"#000000"` | — | Shadows color | -| `colorHighlight` | `string` | no | `"#ffffff"` | — | Highlights color | -| `shadows` | `number` | no | `0.25` | editor range: 0…1 | A color gradient added over both image and background, following the distortion shape | -| `size` | `number` | no | `0.5` | editor range: 0…1; step: 0.001 | The size of the distortion shape grid | -| `angle` | `number` | no | `0` | editor range: 0…180 | Direction of the grid relative to the image | -| `distortion` | `number` | no | `0.5` | editor range: 0…1 | The power of distortion applied within each stripe | +| `image` | `HTMLImageElement \| string` | no | `""` | — | Source image texture | +| `colorBack` | `string` | no | `"#00000000"` | — | Background color in RGBA | +| `colorShadow` | `string` | no | `"#000000"` | — | Shadows color in RGBA | +| `colorHighlight` | `string` | no | `"#ffffff"` | — | Highlights color in RGBA | +| `shadows` | `number` | no | `0.25` | editor range: 0…1 | Color gradient added over image and background, following distortion shape | +| `size` | `number` | no | `0.5` | editor range: 0…1; step: 0.001 | Size of the distortion shape grid | +| `angle` | `number` | no | `0` | editor range: 0…180 | Direction of the grid relative to the image in degrees | +| `distortion` | `number` | no | `0.5` | editor range: 0…1 | Power of distortion applied within each stripe | | `shift` | `number` | no | `0` | editor range: -1…1 | Texture shift in direction opposite to the grid | -| `blur` | `number` | no | `0` | editor range: 0…1 | One-directional blur over the image and extra blur around the edges | +| `blur` | `number` | no | `0` | editor range: 0…1 | One-directional blur over the image and extra blur around edges | | `edges` | `number` | no | `0.25` | editor range: 0…1 | Glass distortion and softness on the image edges | | `margin` | `number` | no | `0` | editor range: 0…1 | Distance from image edges to the effect | | `marginLeft` | `number` | no | `0` | editor range: 0…1 | Distance from the left edge to the effect | @@ -30,9 +30,9 @@ Fluted glass image filter that transforms an image into streaked, ribbed distort | `marginTop` | `number` | no | `0` | editor range: 0…1 | Distance from the top edge to the effect | | `marginBottom` | `number` | no | `0` | editor range: 0…1 | Distance from the bottom edge to the effect | | `stretch` | `number` | no | `0` | editor range: 0…1 | Extra distortion along the grid lines | -| `distortionShape` | `GlassDistortionShape` | no | `"prism"` | options: "prism", "lens", "contour", "cascade", "flat" | The shape of the distortion | -| `highlights` | `number` | no | `0.1` | editor range: 0…1 | Thin strokes along the distortion shape; useful for antialiasing on a small grid | -| `shape` | `GlassGridShape` | no | `"lines"` | options: "lines", "linesIrregular", "wave", "zigzag", "pattern" | The shape of the grid | -| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to the shapes’ edges | -| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing b/w grain overlay | +| `distortionShape` | `GlassDistortionShape` | no | `"prism"` | options: "prism", "lens", "contour", "cascade", "flat" | Shape of distortion (1 = prism, 2 = lens, 3 = contour, 4 = cascade, 5 = flat) | +| `highlights` | `number` | no | `0.1` | editor range: 0…1 | Thin strokes along distortion shape, useful for antialiasing on small grid | +| `shape` | `GlassGridShape` | no | `"lines"` | options: "lines", "linesIrregular", "wave", "zigzag", "pattern" | Grid shape (1 = lines, 2 = linesIrregular, 3 = wave, 4 = zigzag, 5 = pattern) | +| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to shape edges | +| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing black/white grain overlay | | `count` | `number` | no | — | — | React-only. @deprecated use `size` instead | diff --git a/skills/paper-shaders/references/shaders/gem-smoke.md b/skills/paper-shaders/references/shaders/gem-smoke.md index 0416f283..676e3969 100644 --- a/skills/paper-shaders/references/shaders/gem-smoke.md +++ b/skills/paper-shaders/references/shaders/gem-smoke.md @@ -12,16 +12,16 @@ Animated color fields placed over uploaded logo shape; gives the illusion of smo | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#333333","#e7e6df"]` | — | Colors used by the shader; implementation capacity is 6. | -| `colorBack` | `string` | no | `"#f0efea"` | — | Background color | +| `colors` | `string[]` | no | `["#333333","#e7e6df"]` | — | Up to 6 smoke colors in RGBA | +| `colorBack` | `string` | no | `"#f0efea"` | — | Background color in RGBA | | `image` | `HTMLImageElement \| string \| undefined` | no | `""` | — | An optional image used as an effect mask. A transparent background is required. If no image is provided, the shader defaults to one of the predefined shapes. | -| `innerDistortion` | `number` | no | `0.8` | editor range: 0…1 | The power of smoke distortion inside the input shape (shape defined by alpha channel) | -| `outerDistortion` | `number` | no | `0.6` | editor range: 0…1 | The power of smoke distortion outside the input shape (shape defined by alpha channel) | -| `outerGlow` | `number` | no | `0.55` | editor range: 0…1 | The visibility of smoke shape out of the input shape (shape defined by alpha channel) | -| `innerGlow` | `number` | no | `1` | editor range: 0…1 | The visibility of smoke shape inside the input shape (shape defined by alpha channel) | -| `colorInner` | `string` | no | `"#fafaf5"` | — | Additional color inside the input shape, mixing with smoke | +| `innerDistortion` | `number` | no | `0.8` | editor range: 0…1 | Power of smoke distortion inside the input shape | +| `outerDistortion` | `number` | no | `0.6` | editor range: 0…1 | Power of smoke distortion outside the input shape | +| `outerGlow` | `number` | no | `0.55` | editor range: 0…1 | Visibility of smoke shape outside the input shape | +| `innerGlow` | `number` | no | `1` | editor range: 0…1 | Visibility of smoke shape inside the input shape | +| `colorInner` | `string` | no | `"#fafaf5"` | — | Additional color inside the input shape, mixing with smoke (RGBA) | | `offset` | `number` | no | `0` | editor range: -1…1 | Vertical offset of smoke inside the shape | -| `angle` | `number` | no | `0` | editor range: 0…360 | Smoke direction | -| `size` | `number` | no | `0.8` | editor range: 0…1 | The size of smoke shape relative to the image box | +| `angle` | `number` | no | `0` | editor range: 0…360 | Smoke direction in degrees | +| `size` | `number` | no | `0.8` | editor range: 0…1 | Size of smoke shape relative to the image box | | `shape` | `GemSmokeShape` | no | `"diamond"` | options: "none", "circle", "daisy", "diamond", "metaballs" | The predefined shape used as an effect mask when no image is provided. | | `suspendWhenProcessingImage` | `boolean` | no | `false` | — | React-only. Suspends the component when the image is being processed. | diff --git a/skills/paper-shaders/references/shaders/god-rays.md b/skills/paper-shaders/references/shaders/god-rays.md index 156b0802..aa82be2f 100644 --- a/skills/paper-shaders/references/shaders/god-rays.md +++ b/skills/paper-shaders/references/shaders/god-rays.md @@ -12,12 +12,12 @@ Animated rays of light radiating from the center, blended with up to 5 colors. | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `colorBloom` | `string` | no | `"#0000ff"` | — | Color overlay blended with the rays | -| `colors` | `string[]` | no | `["#a600ff6e","#6200fff0","#ffffff","#33fff5"]` | — | Colors used by the shader; implementation capacity is 5. | -| `spotty` | `number` | no | `0.3` | editor range: 0…1 | The length of the rays | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `colorBloom` | `string` | no | `"#0000ff"` | — | Color overlay blended with the rays in RGBA | +| `colors` | `string[]` | no | `["#a600ff6e","#6200fff0","#ffffff","#33fff5"]` | — | Up to 5 ray colors in RGBA | +| `spotty` | `number` | no | `0.3` | editor range: 0…1 | The length of the rays, higher = more spots/shorter rays | | `midSize` | `number` | no | `0.2` | editor range: 0…1 | Size of the circular glow shape in the center | | `midIntensity` | `number` | no | `0.4` | editor range: 0…1 | Brightness/intensity of the central glow | | `density` | `number` | no | `0.3` | editor range: 0…1 | The number of rays | | `intensity` | `number` | no | `0.8` | editor range: 0…1 | Visibility/strength of the rays | -| `bloom` | `number` | no | `0.4` | editor range: 0…1 | Strength of the bloom/overlay effect | +| `bloom` | `number` | no | `0.4` | editor range: 0…1 | Strength of the bloom/overlay effect, 0 = alpha blend, 1 = additive blend | diff --git a/skills/paper-shaders/references/shaders/grain-gradient.md b/skills/paper-shaders/references/shaders/grain-gradient.md index fd313104..a736e6e8 100644 --- a/skills/paper-shaders/references/shaders/grain-gradient.md +++ b/skills/paper-shaders/references/shaders/grain-gradient.md @@ -12,9 +12,9 @@ Multi-color gradients with grainy, noise-textured distortion available in 7 anim | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `colors` | `string[]` | no | `["#7300ff","#eba8ff","#00bfff","#2a00ff"]` | — | Colors used by the shader; implementation capacity is 7. | -| `softness` | `number` | no | `0.5` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `colors` | `string[]` | no | `["#7300ff","#eba8ff","#00bfff","#2a00ff"]` | — | Up to 7 gradient colors in RGBA | +| `softness` | `number` | no | `0.5` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient | | `intensity` | `number` | no | `0.5` | editor range: 0…1 | Distortion between color bands | | `noise` | `number` | no | `0.25` | editor range: 0…1 | Grainy noise overlay | -| `shape` | `GrainGradientShape` | no | `"corners"` | options: "wave", "dots", "truchet", "corners", "ripple", "blob", "sphere" | Shape type | +| `shape` | `GrainGradientShape` | no | `"corners"` | options: "wave", "dots", "truchet", "corners", "ripple", "blob", "sphere" | Shape type (1 = wave, 2 = dots, 3 = truchet, 4 = corners, 5 = ripple, 6 = blob, 7 = sphere) | diff --git a/skills/paper-shaders/references/shaders/halftone-cmyk.md b/skills/paper-shaders/references/shaders/halftone-cmyk.md index 36e2d705..a799d744 100644 --- a/skills/paper-shaders/references/shaders/halftone-cmyk.md +++ b/skills/paper-shaders/references/shaders/halftone-cmyk.md @@ -12,25 +12,25 @@ CMYK halftone printing effect applied to images with customizable dot patterns a | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `image` | `HTMLImageElement \| string` | no | `""` | — | The image to use for the effect | -| `colorBack` | `string` | no | `"#fbfaf5"` | — | Background (paper) color | -| `colorC` | `string` | no | `"#00b4ff"` | — | Cyan ink color (alpha controls layer transparency, not dot size) | -| `colorM` | `string` | no | `"#fc519f"` | — | Magenta ink color (alpha controls layer transparency, not dot size) | -| `colorY` | `string` | no | `"#ffd800"` | — | Yellow ink color (alpha controls layer transparency, not dot size) | -| `colorK` | `string` | no | `"#231f20"` | — | Black ink color (alpha controls layer transparency, not dot size) | -| `size` | `number` | no | `0.2` | editor range: 0…1 | Grid size (relative to image box) | -| `contrast` | `number` | no | `1` | editor range: 0…2 | Input image contrast | -| `softness` | `number` | no | `1` | editor range: 0…1 | Dots edge softness | -| `grainSize` | `number` | no | `0.5` | editor range: 0…1 | Size of grain overlay texture (relative to image box) | +| `image` | `HTMLImageElement \| string` | no | `""` | — | Source image texture | +| `colorBack` | `string` | no | `"#fbfaf5"` | — | Background (paper) color in RGBA | +| `colorC` | `string` | no | `"#00b4ff"` | — | Cyan ink color in RGBA | +| `colorM` | `string` | no | `"#fc519f"` | — | Magenta ink color in RGBA | +| `colorY` | `string` | no | `"#ffd800"` | — | Yellow ink color in RGBA | +| `colorK` | `string` | no | `"#231f20"` | — | Black ink color in RGBA | +| `size` | `number` | no | `0.2` | editor range: 0…1 | Halftone cell size | +| `contrast` | `number` | no | `1` | editor range: 0…2 | Image contrast adjustment | +| `softness` | `number` | no | `1` | editor range: 0…1 | Edge softness of dots | +| `grainSize` | `number` | no | `0.5` | editor range: 0…1 | Size of grain overlay texture | | `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain affecting dot size | -| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Strength of the black-and-white grain overlay on the final output | -| `gridNoise` | `number` | no | `0.2` | editor range: 0…1 | Displaces both dot positions and color sampling points; naturally makes the background more visible | -| `floodC` | `number` | no | `0.15` | editor range: 0…1; shader source documents -1…1 | Flat cyan dot-size adjustment applied uniformly | -| `floodM` | `number` | no | `0` | editor range: 0…1; shader source documents -1…1 | Flat magenta dot-size adjustment applied uniformly | -| `floodY` | `number` | no | `0` | editor range: 0…1; shader source documents -1…1 | Flat yellow dot-size adjustment applied uniformly | -| `floodK` | `number` | no | `0` | editor range: 0…1; shader source documents -1…1 | Flat black dot-size adjustment applied uniformly | -| `gainC` | `number` | no | `0.3` | editor range: -1…1 | Proportional cyan dot-size gain that enhances existing dots | -| `gainM` | `number` | no | `0` | editor range: -1…1 | Proportional magenta dot-size gain that enhances existing dots | -| `gainY` | `number` | no | `0.2` | editor range: -1…1 | Proportional yellow dot-size gain that enhances existing dots | -| `gainK` | `number` | no | `0` | editor range: -1…1 | Proportional black dot-size gain that enhances existing dots | -| `type` | `HalftoneCmykType` | no | `"ink"` | options: "dots", "ink", "sharp" | Dot type style (the difference between dots and ink is visible only with low softness) | +| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Strength of grain overlay on final output | +| `gridNoise` | `number` | no | `0.2` | editor range: 0…1 | Strength of smooth noise applied to both dot positions and color sampling | +| `floodC` | `number` | no | `0.15` | editor range: 0…1; shader source documents -1…1 | Flat cyan dot size adjustment applied uniformly | +| `floodM` | `number` | no | `0` | editor range: 0…1; shader source documents -1…1 | Flat magenta dot size adjustment applied uniformly | +| `floodY` | `number` | no | `0` | editor range: 0…1; shader source documents -1…1 | Flat yellow dot size adjustment applied uniformly | +| `floodK` | `number` | no | `0` | editor range: 0…1; shader source documents -1…1 | Flat black dot size adjustment applied uniformly | +| `gainC` | `number` | no | `0.3` | editor range: -1…1 | Proportional cyan dot size gain (enhances existing dots) | +| `gainM` | `number` | no | `0` | editor range: -1…1 | Proportional magenta dot size gain (enhances existing dots) | +| `gainY` | `number` | no | `0.2` | editor range: -1…1 | Proportional yellow dot size gain (enhances existing dots) | +| `gainK` | `number` | no | `0` | editor range: -1…1 | Proportional black dot size gain (enhances existing dots) | +| `type` | `HalftoneCmykType` | no | `"ink"` | options: "dots", "ink", "sharp" | Dot shape style (0 = dots, 1 = ink, 2 = sharp) | diff --git a/skills/paper-shaders/references/shaders/halftone-dots.md b/skills/paper-shaders/references/shaders/halftone-dots.md index 9b1b7bcb..4e92b382 100644 --- a/skills/paper-shaders/references/shaders/halftone-dots.md +++ b/skills/paper-shaders/references/shaders/halftone-dots.md @@ -11,16 +11,16 @@ A halftone-dot image filter featuring customizable grids, color palettes, and do | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `image` | `HTMLImageElement \| string` | no | `""` | — | The image to use for the effect | -| `colorFront` | `string` | no | `"#2b2b2b"` | — | Foreground color | -| `colorBack` | `string` | no | `"#f2f1e8"` | — | Background color | +| `image` | `HTMLImageElement \| string` | no | `""` | — | Source image texture | +| `colorFront` | `string` | no | `"#2b2b2b"` | — | Foreground color in RGBA | +| `colorBack` | `string` | no | `"#f2f1e8"` | — | Background color in RGBA | | `size` | `number` | no | `0.5` | editor range: 0…1 | Grid size relative to the image box | -| `grid` | `HalftoneDotsGrid` | no | `"hex"` | options: "square", "hex" | Dots grid type | -| `radius` | `number` | no | `1.25` | editor range: 0…2 | Maximum dot size (relative to the grid cell) | +| `grid` | `HalftoneDotsGrid` | no | `"hex"` | options: "square", "hex" | Grid type (0 = square, 1 = hex) | +| `radius` | `number` | no | `1.25` | editor range: 0…2 | Maximum dot size relative to grid cell | | `contrast` | `number` | no | `0.4` | editor range: 0…1 | Contrast applied to the sampled image | -| `originalColors` | `boolean` | no | `false` | options: "true", "false" | Use the sampled image’s original colors instead of colorFront | -| `inverted` | `boolean` | no | `false` | options: "true", "false" | Inverts the image luminance, doesn’t affect the color scheme; not effective at zero contrast | +| `originalColors` | `boolean` | no | `false` | — | Use sampled image's original colors instead of colorFront | +| `inverted` | `boolean` | no | `false` | — | Inverts the image luminance, doesn't affect the color scheme; not effective at zero contrast | | `grainMixer` | `number` | no | `0.2` | editor range: 0…1 | Strength of grain distortion applied to shape edges | -| `grainOverlay` | `number` | no | `0.2` | editor range: 0…1 | Post-processing b/w grain overlay | -| `grainSize` | `number` | no | `0.5` | editor range: 0…1 | The scale applied to both grain distortion and grain overlay | -| `type` | `HalftoneDotsType` | no | `"gooey"` | options: "classic", "gooey", "holes", "soft" | Dot style | +| `grainOverlay` | `number` | no | `0.2` | editor range: 0…1 | Post-processing black/white grain overlay | +| `grainSize` | `number` | no | `0.5` | editor range: 0…1 | Scale applied to both grain distortion and grain overlay | +| `type` | `HalftoneDotsType` | no | `"gooey"` | options: "classic", "gooey", "holes", "soft" | Dot style (0 = classic, 1 = gooey, 2 = holes, 3 = soft) | diff --git a/skills/paper-shaders/references/shaders/heatmap.md b/skills/paper-shaders/references/shaders/heatmap.md index e214657d..49d38c07 100644 --- a/skills/paper-shaders/references/shaders/heatmap.md +++ b/skills/paper-shaders/references/shaders/heatmap.md @@ -12,12 +12,12 @@ A glowing gradient of colors flowing through an input shape. The effect creates | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `image` | `HTMLImageElement \| string` | yes | `""` | — | The image to use for the effect | -| `contour` | `number` | no | `0.5` | editor range: 0…1 | The heat intensity near the edges of the input shape | -| `angle` | `number` | no | `0` | editor range: 0…360 | The direction of the heatwaves (angle relative to the shape) | +| `image` | `HTMLImageElement \| string` | yes | `""` | — | Pre-processed source image texture (R = contour, G = outer blur, B = inner blur) | +| `contour` | `number` | no | `0.5` | editor range: 0…1 | Heat intensity near the edges of the input shape | +| `angle` | `number` | no | `0` | editor range: 0…360 | Direction of the heatwaves in degrees | | `noise` | `number` | no | `0` | editor range: 0…1 | Grain applied across the entire graphic | -| `innerGlow` | `number` | no | `0.5` | editor range: 0…1 | The size of the heated area inside the input shape | +| `innerGlow` | `number` | no | `0.5` | editor range: 0…1 | Size of the heated area inside the input shape | | `outerGlow` | `number` | no | `0.5` | editor range: 0…1 | Size of the heated area outside the input shape | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `colors` | `string[]` | no | `["#11206a","#1f3ba2","#2f63e7","#6bd7ff","#ffe679","#ff991e","#ff4c00"]` | — | Colors used by the shader; implementation capacity is 10. | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `colors` | `string[]` | no | `["#11206a","#1f3ba2","#2f63e7","#6bd7ff","#ffe679","#ff991e","#ff4c00"]` | — | Up to 10 heatmap colors in RGBA | | `suspendWhenProcessingImage` | `boolean` | no | `false` | — | React-only. Suspends the component when the image is being processed. | diff --git a/skills/paper-shaders/references/shaders/image-dithering.md b/skills/paper-shaders/references/shaders/image-dithering.md index c30f1ddf..c191310b 100644 --- a/skills/paper-shaders/references/shaders/image-dithering.md +++ b/skills/paper-shaders/references/shaders/image-dithering.md @@ -11,13 +11,13 @@ A dithering image filter with support for 4 dithering modes and multiple color p | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `image` | `HTMLImageElement \| string` | yes | `""` | — | The image to use for the effect | -| `colorFront` | `string` | no | `"#94ffaf"` | — | Foreground color | -| `colorBack` | `string` | no | `"#000c38"` | — | Background color | -| `colorHighlight` | `string` | no | `"#eaff94"` | — | The secondary foreground color (set it same as colorFront to get a classic 2-color dithering) | -| `type` | `DitheringType` | no | `"8x8"` | options: "random", "2x2", "4x4", "8x8" | Dithering type | -| `size` | `number` | no | `2` | editor range: 0.5…20 | Pixel size of dithering grid; linked to the screen space, not to the image box | -| `colorSteps` | `number` | no | `2` | editor range: 1…7; step: 1 | Number of colors to use (applies to both color modes) | -| `originalColors` | `boolean` | no | `false` | options: "true", "false" | Use the original colors of the image | -| `inverted` | `boolean` | no | `false` | — | Inverts image luminance without changing the color scheme. | +| `image` | `HTMLImageElement \| string` | yes | `""` | — | Source image texture | +| `colorFront` | `string` | no | `"#94ffaf"` | — | Foreground color in RGBA | +| `colorBack` | `string` | no | `"#000c38"` | — | Background color in RGBA | +| `colorHighlight` | `string` | no | `"#eaff94"` | — | Secondary foreground color in RGBA (set same as colorFront for classic 2-color dithering) | +| `type` | `DitheringType` | no | `"8x8"` | options: "random", "2x2", "4x4", "8x8" | Dithering type (1 = random, 2 = 2x2 Bayer, 3 = 4x4 Bayer, 4 = 8x8 Bayer) | +| `size` | `number` | no | `2` | editor range: 0.5…20 | Pixel size of dithering grid | +| `colorSteps` | `number` | no | `2` | editor range: 1…7; step: 1 | Number of colors to use, applies to both color modes | +| `originalColors` | `boolean` | no | `false` | — | Use the original colors of the image instead of the color palette | +| `inverted` | `boolean` | no | `false` | — | Inverts the image luminance, doesn't affect the color scheme; not effective at zero contrast | | `pxSize` | `number` | no | — | — | React-only. @deprecated use `size` instead | diff --git a/skills/paper-shaders/references/shaders/liquid-metal.md b/skills/paper-shaders/references/shaders/liquid-metal.md index ce2866dc..8f4e59e4 100644 --- a/skills/paper-shaders/references/shaders/liquid-metal.md +++ b/skills/paper-shaders/references/shaders/liquid-metal.md @@ -12,15 +12,15 @@ Futuristic liquid metal material applied to uploaded logo or abstract shape. Flu | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#AAAAAC"` | — | Background color | -| `colorTint` | `string` | no | `"#ffffff"` | — | Overlay color (color burn blending used) | -| `image` | `HTMLImageElement \| string \| undefined` | no | `""` | — | An optional image used as an effect mask. A transparent background is required. If no image is provided, the shader defaults to one of the predefined shapes. | +| `colorBack` | `string` | no | `"#AAAAAC"` | — | Background color in RGBA | +| `colorTint` | `string` | no | `"#ffffff"` | — | Overlay color in RGBA (color burn blending used) | +| `image` | `HTMLImageElement \| string \| undefined` | no | `""` | — | Whether an image is being used as the effect mask | | `repetition` | `number` | no | `2` | editor range: 1…10 | Density of pattern stripes | | `shiftRed` | `number` | no | `0.3` | editor range: -1…1 | R-channel dispersion | | `shiftBlue` | `number` | no | `0.3` | editor range: -1…1 | B-channel dispersion | | `contour` | `number` | no | `0.4` | editor range: 0…1 | Strength of the distortion on the shape edges | -| `softness` | `number` | no | `0.1` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | +| `softness` | `number` | no | `0.1` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient | | `distortion` | `number` | no | `0.07` | editor range: 0…1 | Noise distortion over the stripes pattern | -| `angle` | `number` | no | `70` | editor range: 0…360 | The direction of pattern animation (angle relative to the shape) | -| `shape` | `LiquidMetalShape` | no | `"diamond"` | options: "none", "circle", "daisy", "diamond", "metaballs" | The predefined shape used as an effect mask when no image is provided. | +| `angle` | `number` | no | `70` | editor range: 0…360 | Direction of pattern animation in degrees | +| `shape` | `LiquidMetalShape` | no | `"diamond"` | options: "none", "circle", "daisy", "diamond", "metaballs" | Predefined shape when no image provided (0 = none, 1 = circle, 2 = daisy, 3 = diamond, 4 = metaballs) | | `suspendWhenProcessingImage` | `boolean` | no | `false` | — | React-only. Suspends the component when the image is being processed. | diff --git a/skills/paper-shaders/references/shaders/mesh-gradient.md b/skills/paper-shaders/references/shaders/mesh-gradient.md index 2b9c3ef9..e8fa7264 100644 --- a/skills/paper-shaders/references/shaders/mesh-gradient.md +++ b/skills/paper-shaders/references/shaders/mesh-gradient.md @@ -11,8 +11,8 @@ A flowing composition of color spots, moving along distinct trajectories and tra | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#e0eaff","#241d9a","#f75092","#9f50d3"]` | — | Colors used by the shader; implementation capacity is 10. | -| `distortion` | `number` | no | `0.8` | editor range: 0…1 | The power of organic noise distortion | -| `swirl` | `number` | no | `0.1` | editor range: 0…1 | The power of vortex distortion | -| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to the shapes’ edges | -| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing b/w grain overlay | +| `colors` | `string[]` | no | `["#e0eaff","#241d9a","#f75092","#9f50d3"]` | — | Up to 10 color spots in RGBA | +| `distortion` | `number` | no | `0.8` | editor range: 0…1 | Power of organic noise distortion | +| `swirl` | `number` | no | `0.1` | editor range: 0…1 | Power of vortex distortion | +| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to shape edges | +| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing black/white grain overlay | diff --git a/skills/paper-shaders/references/shaders/metaballs.md b/skills/paper-shaders/references/shaders/metaballs.md index ac714179..89f1bb7e 100644 --- a/skills/paper-shaders/references/shaders/metaballs.md +++ b/skills/paper-shaders/references/shaders/metaballs.md @@ -12,7 +12,7 @@ Up to 20 colored gooey balls moving around the center and merging into smooth or | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `colors` | `string[]` | no | `["#6e33cc","#ff5500","#ffc105","#ffc800","#f585ff"]` | — | Colors used by the shader; implementation capacity is 8. | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `colors` | `string[]` | no | `["#6e33cc","#ff5500","#ffc105","#ffc800","#f585ff"]` | — | Up to 8 base colors in RGBA | | `count` | `number` | no | `10` | editor range: 1…20 | Number of balls | -| `size` | `number` | no | `0.83` | editor range: 0…1 | The size of the balls | +| `size` | `number` | no | `0.83` | editor range: 0…1 | Size of the balls | diff --git a/skills/paper-shaders/references/shaders/neuro-noise.md b/skills/paper-shaders/references/shaders/neuro-noise.md index c383ac7c..a1326edd 100644 --- a/skills/paper-shaders/references/shaders/neuro-noise.md +++ b/skills/paper-shaders/references/shaders/neuro-noise.md @@ -11,8 +11,8 @@ A glowing, web-like structure of fluid lines and soft intersections. Great for c | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorFront` | `string` | no | `"#ffffff"` | — | Graphics highlight color | -| `colorMid` | `string` | no | `"#47a6ff"` | — | Graphics main color | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | +| `colorFront` | `string` | no | `"#ffffff"` | — | Graphics highlight color in RGBA | +| `colorMid` | `string` | no | `"#47a6ff"` | — | Graphics main color in RGBA | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | | `brightness` | `number` | no | `0.05` | editor range: 0…1 | Luminosity of the crossing points | -| `contrast` | `number` | no | `0.3` | editor range: 0…1 | Sharpness of the bright–dark transition | +| `contrast` | `number` | no | `0.3` | editor range: 0…1 | Sharpness of the bright-dark transition | diff --git a/skills/paper-shaders/references/shaders/paper-texture.md b/skills/paper-shaders/references/shaders/paper-texture.md index afa71fc0..b07fb450 100644 --- a/skills/paper-shaders/references/shaders/paper-texture.md +++ b/skills/paper-shaders/references/shaders/paper-texture.md @@ -12,19 +12,19 @@ A static texture built from multiple noise layers, usable for realistic paper an | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `image` | `HTMLImageElement \| string` | no | `""` | — | The image to use for the effect | -| `colorFront` | `string` | no | `"#9fadbc"` | — | Foreground color | -| `colorBack` | `string` | no | `"#ffffff"` | — | Background color | -| `contrast` | `number` | no | `0.3` | editor range: 0…1 | Blending behavior (sharper vs. smoother color transitions) | -| `roughness` | `number` | no | `0.4` | editor range: 0…1 | Pixel noise, related to canvas (not scalable) | -| `fiber` | `number` | no | `0.3` | editor range: 0…1 | Curly-shaped noise | +| `image` | `HTMLImageElement \| string` | no | `""` | — | Optional source image texture | +| `colorFront` | `string` | no | `"#9fadbc"` | — | Foreground color in RGBA | +| `colorBack` | `string` | no | `"#ffffff"` | — | Background color in RGBA | +| `contrast` | `number` | no | `0.3` | editor range: 0…1 | Blending behavior, sharper vs smoother color transitions | +| `roughness` | `number` | no | `0.4` | editor range: 0…1 | Pixel noise, related to canvas and not scalable | +| `fiber` | `number` | no | `0.3` | editor range: 0…1 | Curly-shaped noise intensity | | `fiberSize` | `number` | no | `0.2` | editor range: 0…1 | Curly-shaped noise scale | -| `crumples` | `number` | no | `0.3` | editor range: 0…1 | Cell-based crumple pattern | -| `foldCount` | `number` | no | `5` | editor range: 1…15; step: 1 | Number of folds (15 max) | +| `crumples` | `number` | no | `0.3` | editor range: 0…1 | Cell-based crumple pattern intensity | +| `foldCount` | `number` | no | `5` | editor range: 1…15; step: 1 | Number of folds | | `folds` | `number` | no | `0.65` | editor range: 0…1 | Depth of the folds | | `fade` | `number` | no | `0` | editor range: 0…1 | Big-scale noise mask applied to the pattern | | `crumpleSize` | `number` | no | `0.35` | editor range: 0…1 | Cell-based crumple pattern scale | -| `drops` | `number` | no | `0.2` | editor range: 0…1 | The visibility of speckle pattern | +| `drops` | `number` | no | `0.2` | editor range: 0…1 | Visibility of speckle pattern | | `seed` | `number` | no | `5.8` | editor range: 0…1000 | Seed applied to folds, crumples and dots | | `fiberScale` | `number` | no | — | — | React-only. @deprecated use `fiberSize` instead | | `crumplesScale` | `number` | no | — | — | React-only. @deprecated use `crumpleSize` instead | diff --git a/skills/paper-shaders/references/shaders/perlin-noise.md b/skills/paper-shaders/references/shaders/perlin-noise.md index c0604554..06618c8f 100644 --- a/skills/paper-shaders/references/shaders/perlin-noise.md +++ b/skills/paper-shaders/references/shaders/perlin-noise.md @@ -11,10 +11,10 @@ Classic animated 3D Perlin noise with exposed controls. Original algorithm: http | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorFront` | `string` | no | `"#fccff7"` | — | Foreground color | -| `colorBack` | `string` | no | `"#632ad5"` | — | Background color | -| `proportion` | `number` | no | `0.35` | editor range: 0…1 | Blend point between 2 colors (0.5 = equal distribution) | -| `softness` | `number` | no | `0.1` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | -| `octaveCount` | `number` | no | `1` | editor range: 1…8; step: 1 | Perlin noise octaves number (more octaves for more detailed patterns) | +| `colorFront` | `string` | no | `"#fccff7"` | — | Foreground color in RGBA | +| `colorBack` | `string` | no | `"#632ad5"` | — | Background color in RGBA | +| `proportion` | `number` | no | `0.35` | editor range: 0…1 | Blend point between 2 colors, 0.5 = equal distribution | +| `softness` | `number` | no | `0.1` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient | +| `octaveCount` | `number` | no | `1` | editor range: 1…8; step: 1 | Perlin noise octaves number, more octaves for more detailed patterns | | `persistence` | `number` | no | `1` | editor range: 0.3…1 | Roughness, falloff between octaves | -| `lacunarity` | `number` | no | `1.5` | editor range: 1.5…10 | Frequency step, typically around 2. Defines how compressed the pattern is | +| `lacunarity` | `number` | no | `1.5` | editor range: 1.5…10 | Frequency step, defines how compressed the pattern is | diff --git a/skills/paper-shaders/references/shaders/pulsing-border.md b/skills/paper-shaders/references/shaders/pulsing-border.md index 4c870d1f..2fc1c1fc 100644 --- a/skills/paper-shaders/references/shaders/pulsing-border.md +++ b/skills/paper-shaders/references/shaders/pulsing-border.md @@ -12,21 +12,21 @@ Luminous trails of color merging into a glowing gradient contour. | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `colors` | `string[]` | no | `["#0dc1fd","#d915ef","#ff3f2ecc"]` | — | Colors used by the shader; implementation capacity is 5. | -| `roundness` | `number` | no | `0.25` | editor range: 0…1 | The border radius | -| `thickness` | `number` | no | `0.1` | editor range: 0…1 | The border base width | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `colors` | `string[]` | no | `["#0dc1fd","#d915ef","#ff3f2ecc"]` | — | Up to 5 spot colors in RGBA | +| `roundness` | `number` | no | `0.25` | editor range: 0…1 | Border radius | +| `thickness` | `number` | no | `0.1` | editor range: 0…1 | Border base width | | `margin` | `number` | no | `0` | editor range: 0…1 | Distance from canvas edges to the effect | | `marginLeft` | `number` | no | `0` | editor range: 0…1 | Distance from the left edge to the effect | | `marginRight` | `number` | no | `0` | editor range: 0…1 | Distance from the right edge to the effect | | `marginTop` | `number` | no | `0` | editor range: 0…1 | Distance from the top edge to the effect | | `marginBottom` | `number` | no | `0` | editor range: 0…1 | Distance from the bottom edge to the effect | -| `aspectRatio` | `PulsingBorderAspectRatio` | no | `"auto"` | options: "auto", "square" | Aspect ratio of the effect | -| `softness` | `number` | no | `0.75` | editor range: 0…1 | Border edge sharpness (0 = hard edge, 1 = smooth gradient) | +| `aspectRatio` | `PulsingBorderAspectRatio` | no | `"auto"` | options: "auto", "square" | Aspect ratio mode (0 = auto, 1 = square) | +| `softness` | `number` | no | `0.75` | editor range: 0…1 | Border edge sharpness, 0 = hard edge, 1 = smooth gradient | | `intensity` | `number` | no | `0.2` | editor range: 0…1 | Thickness of individual color spots | -| `bloom` | `number` | no | `0.25` | editor range: 0…1 | The power of glow (0 = normal color blending, 1 = fully additive blending) | -| `spots` | `number` | no | `5` | editor range: 1…20; step: 1; shader loop capacity: 4 | Requested spots per color. The shader renders at most 4, so the default value of 5 has the same effective spot count as 4. | +| `bloom` | `number` | no | `0.25` | editor range: 0…1 | Power of glow, 0 = normal blending, 1 = additive blending | +| `spots` | `number` | no | `5` | editor range: 1…20; step: 1; implementation capacity: 4 | Number of spots added for each color | | `spotSize` | `number` | no | `0.5` | editor range: 0…1 | Angular size of spots | -| `pulse` | `number` | no | `0.25` | editor range: 0…1 | Optional pulsing animation | -| `smoke` | `number` | no | `0.3` | editor range: 0…1 | Optional noisy shape extending the border shape | -| `smokeSize` | `number` | no | `0.6` | editor range: 0…1 | The size of the smoke effect (effective with smoke > 0) | +| `pulse` | `number` | no | `0.25` | editor range: 0…1 | Optional pulsing animation intensity | +| `smoke` | `number` | no | `0.3` | editor range: 0…1 | Optional noisy shape extending the border | +| `smokeSize` | `number` | no | `0.6` | editor range: 0…1 | Size of the smoke effect | diff --git a/skills/paper-shaders/references/shaders/simplex-noise.md b/skills/paper-shaders/references/shaders/simplex-noise.md index f3aa09e3..818231b2 100644 --- a/skills/paper-shaders/references/shaders/simplex-noise.md +++ b/skills/paper-shaders/references/shaders/simplex-noise.md @@ -11,6 +11,6 @@ A multi-color gradient mapped into smooth, animated curves built as a combinatio | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#4449CF","#FFD1E0","#F94446","#FFD36B","#FFFFFF"]` | — | Colors used by the shader; implementation capacity is 10. | -| `stepsPerColor` | `number` | no | `2` | editor range: 1…10; step: 1 | Number of extra colors between base colors (1 = N color palette, 2 = 2×N color palette, 3 = 3×N color palette, etc) | -| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | +| `colors` | `string[]` | no | `["#4449CF","#FFD1E0","#F94446","#FFD36B","#FFFFFF"]` | — | Up to 10 base colors in RGBA | +| `stepsPerColor` | `number` | no | `2` | editor range: 1…10; step: 1 | Number of extra colors between base colors, 1 = N colors, 2 = 2×N, etc. | +| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient | diff --git a/skills/paper-shaders/references/shaders/smoke-ring.md b/skills/paper-shaders/references/shaders/smoke-ring.md index 2cda0560..bef78352 100644 --- a/skills/paper-shaders/references/shaders/smoke-ring.md +++ b/skills/paper-shaders/references/shaders/smoke-ring.md @@ -12,10 +12,10 @@ Radial multi-colored gradient shaped with layered noise for a natural, smoky aes | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `colors` | `string[]` | no | `["#ffffff"]` | — | Colors used by the shader; implementation capacity is 10. | -| `noiseScale` | `number` | no | `3` | editor range: 0.01…5 | The noise frequency | -| `thickness` | `number` | no | `0.65` | editor range: 0.01…1 | The thickness of the ring shape | -| `radius` | `number` | no | `0.25` | editor range: 0…1 | The radius of the ring shape | -| `innerShape` | `number` | no | `0.7` | editor range: 0…4 | The ring inner fill | -| `noiseIterations` | `number` | no | `8` | editor range: 1…8; step: 1 | A number of noise layers, more layers gives more details | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `colors` | `string[]` | no | `["#ffffff"]` | — | Up to 10 gradient colors in RGBA | +| `noiseScale` | `number` | no | `3` | editor range: 0.01…5 | Noise frequency | +| `thickness` | `number` | no | `0.65` | editor range: 0.01…1 | Thickness of the ring shape | +| `radius` | `number` | no | `0.25` | editor range: 0…1 | Radius of the ring shape | +| `innerShape` | `number` | no | `0.7` | editor range: 0…4 | Ring inner fill amount | +| `noiseIterations` | `number` | no | `8` | editor range: 1…8; step: 1 | Number of noise layers, more layers gives more details | diff --git a/skills/paper-shaders/references/shaders/spiral.md b/skills/paper-shaders/references/shaders/spiral.md index 7d337a44..aac027e1 100644 --- a/skills/paper-shaders/references/shaders/spiral.md +++ b/skills/paper-shaders/references/shaders/spiral.md @@ -11,13 +11,13 @@ A single-colored animated spiral that morphs across a wide range of shapes - fro | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#001429"` | — | Background color | -| `colorFront` | `string` | no | `"#79D1FF"` | — | Foreground (ink) color | -| `density` | `number` | no | `1` | editor range: 0…1 | Spacing falloff simulating perspective (0 = flat spiral) | +| `colorBack` | `string` | no | `"#001429"` | — | Background color in RGBA | +| `colorFront` | `string` | no | `"#79D1FF"` | — | Foreground (ink) color in RGBA | +| `density` | `number` | no | `1` | editor range: 0…1 | Spacing falloff simulating perspective, 0 = flat spiral | | `distortion` | `number` | no | `0` | editor range: 0…1 | Power of shape distortion applied along the spiral | | `strokeWidth` | `number` | no | `0.5` | editor range: 0…1 | Thickness of spiral curve | -| `strokeTaper` | `number` | no | `0` | editor range: 0…1 | How much the stroke loses width away from the center (0 = full visibility) | -| `strokeCap` | `number` | no | `0` | editor range: 0…1 | Extra stroke width at the center (no effect with strokeWidth = 0.5) | -| `noise` | `number` | no | `0` | editor range: 0…1 | Noise distortion applied over the canvas (no effect with noiseFrequency = 0) | -| `noiseFrequency` | `number` | no | `0` | editor range: 0…1 | Noise frequency (no effect with noise = 0) | -| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | +| `strokeTaper` | `number` | no | `0` | editor range: 0…1 | How much stroke loses width away from center, 0 = full visibility | +| `strokeCap` | `number` | no | `0` | editor range: 0…1 | Extra stroke width at the center, no effect with strokeWidth = 0.5 | +| `noise` | `number` | no | `0` | editor range: 0…1 | Noise distortion applied over the canvas, no effect with noiseFrequency = 0 | +| `noiseFrequency` | `number` | no | `0` | editor range: 0…1 | Noise frequency, no effect with noise = 0 | +| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient | diff --git a/skills/paper-shaders/references/shaders/static-mesh-gradient.md b/skills/paper-shaders/references/shaders/static-mesh-gradient.md index 90d72778..0a316336 100644 --- a/skills/paper-shaders/references/shaders/static-mesh-gradient.md +++ b/skills/paper-shaders/references/shaders/static-mesh-gradient.md @@ -11,12 +11,12 @@ Multi-point mesh gradient with up to 10 color spots, enhanced by two-direction w | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#ffad0a","#6200ff","#e2a3ff","#ff99fd"]` | — | Colors used by the shader; implementation capacity is 10. | -| `positions` | `number` | no | `2` | editor range: 0…100 | Color spots placement | +| `colors` | `string[]` | no | `["#ffad0a","#6200ff","#e2a3ff","#ff99fd"]` | — | Up to 10 gradient colors in RGBA | +| `positions` | `number` | no | `2` | editor range: 0…100 | Color spots placement seed | | `waveX` | `number` | no | `1` | editor range: 0…1 | Strength of sine wave distortion along X axis | | `waveXShift` | `number` | no | `0.6` | editor range: 0…1 | Phase offset applied to the X-axis wave | | `waveY` | `number` | no | `1` | editor range: 0…1 | Strength of sine wave distortion along Y axis | | `waveYShift` | `number` | no | `0.21` | editor range: 0…1 | Phase offset applied to the Y-axis wave | -| `mixing` | `number` | no | `0.93` | editor range: 0…1 | Blending behavior: 0 gives hard stripes, 0.5 is smooth, and 1 gives a gradual blend | -| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to the shapes’ edges | -| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing b/w grain overlay | +| `mixing` | `number` | no | `0.93` | editor range: 0…1 | Blending behavior, 0 = hard stripes, 0.5 = smooth, 1 = gradual blend | +| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to shape edges | +| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing black/white grain overlay | diff --git a/skills/paper-shaders/references/shaders/static-radial-gradient.md b/skills/paper-shaders/references/shaders/static-radial-gradient.md index ea518efe..4b316685 100644 --- a/skills/paper-shaders/references/shaders/static-radial-gradient.md +++ b/skills/paper-shaders/references/shaders/static-radial-gradient.md @@ -11,15 +11,15 @@ Radial gradient with up to 10 blended colors, featuring advanced mixing modes, f | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `colors` | `string[]` | no | `["#00bbff","#00ffe1","#ffffff"]` | — | Colors used by the shader; implementation capacity is 10. | -| `radius` | `number` | no | `0.8` | editor range: 0…3 | The size of the shape | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `colors` | `string[]` | no | `["#00bbff","#00ffe1","#ffffff"]` | — | Up to 10 gradient colors in RGBA | +| `radius` | `number` | no | `0.8` | editor range: 0…3 | Size of the shape | | `focalDistance` | `number` | no | `0.99` | editor range: 0…3 | Distance of the focal point from center | -| `focalAngle` | `number` | no | `0` | editor range: 0…360 | Angle of the focal point in degrees (effective with focalDistance > 0) | -| `falloff` | `number` | no | `0.24` | editor range: -1…1 | Gradient decay (0 for linear gradient) | -| `mixing` | `number` | no | `0.5` | editor range: 0…1 | Blending behavior: 0 gives hard stripes and 1 gives a smooth gradient | +| `focalAngle` | `number` | no | `0` | editor range: 0…360 | Angle of the focal point in degrees, effective with focalDistance > 0 | +| `falloff` | `number` | no | `0.24` | editor range: -1…1 | Gradient decay, 0 = linear gradient | +| `mixing` | `number` | no | `0.5` | editor range: 0…1 | Blending behavior, 0 = hard stripes, 1 = smooth gradient | | `distortion` | `number` | no | `0` | editor range: 0…1 | Strength of radial distortion | -| `distortionShift` | `number` | no | `0` | editor range: -1…1 | Radial distortion offset (effective with distortion > 0) | -| `distortionFreq` | `number` | no | `12` | editor range: 0…20; step: 1 | Radial distortion frequency (effective with distortion > 0) | -| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to the shapes’ edges | -| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing b/w grain overlay | +| `distortionShift` | `number` | no | `0` | editor range: -1…1 | Radial distortion offset, effective with distortion > 0 | +| `distortionFreq` | `number` | no | `12` | editor range: 0…20; step: 1 | Radial distortion frequency, effective with distortion > 0 | +| `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to shape edges | +| `grainOverlay` | `number` | no | `0` | editor range: 0…1 | Post-processing black/white grain overlay | diff --git a/skills/paper-shaders/references/shaders/swirl.md b/skills/paper-shaders/references/shaders/swirl.md index 3c9eba25..89beb5c1 100644 --- a/skills/paper-shaders/references/shaders/swirl.md +++ b/skills/paper-shaders/references/shaders/swirl.md @@ -11,12 +11,12 @@ Animated bands of color twisting and bending, producing spirals, arcs, and flowi | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorBack` | `string` | no | `"#330000"` | — | Background color | -| `colors` | `string[]` | no | `["#ffd1d1","#ff8a8a","#660000"]` | — | Colors used by the shader; implementation capacity is 10. | -| `bandCount` | `number` | no | `4` | editor range: 0…15; step: 1 | Number of color bands (0 for concentric ripples) | -| `twist` | `number` | no | `0.1` | editor range: 0…1 | Vortex power (0 = straight sectoral shapes) | +| `colorBack` | `string` | no | `"#330000"` | — | Background color in RGBA | +| `colors` | `string[]` | no | `["#ffd1d1","#ff8a8a","#660000"]` | — | Up to 10 stripe colors in RGBA | +| `bandCount` | `number` | no | `4` | editor range: 0…15; step: 1 | Number of color bands, 0 = concentric ripples | +| `twist` | `number` | no | `0.1` | editor range: 0…1 | Vortex power, 0 = straight sectoral shapes | | `center` | `number` | no | `0.2` | editor range: 0…1 | How far from the center the swirl colors begin to appear | -| `proportion` | `number` | no | `0.5` | editor range: 0…1 | Blend point between colors (0.5 = equal distribution) | -| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | -| `noiseFrequency` | `number` | no | `0.4` | editor range: 0…1 | Noise frequency (no effect with noise = 0) | -| `noise` | `number` | no | `0.2` | editor range: 0…1 | Strength of noise distortion (no effect with noiseFrequency = 0) | +| `proportion` | `number` | no | `0.5` | editor range: 0…1 | Blend point between colors, 0.5 = equal distribution | +| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient | +| `noiseFrequency` | `number` | no | `0.4` | editor range: 0…1 | Noise frequency, no effect with noise = 0 | +| `noise` | `number` | no | `0.2` | editor range: 0…1 | Strength of noise distortion, no effect with noiseFrequency = 0 | diff --git a/skills/paper-shaders/references/shaders/voronoi.md b/skills/paper-shaders/references/shaders/voronoi.md index bb4011fa..b4dd092a 100644 --- a/skills/paper-shaders/references/shaders/voronoi.md +++ b/skills/paper-shaders/references/shaders/voronoi.md @@ -12,10 +12,10 @@ Anti-aliased animated Voronoi pattern with smooth and customizable edges. | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#ff8247","#ffe53d"]` | — | Colors used by the shader; implementation capacity is 5. | -| `stepsPerColor` | `number` | no | `3` | editor range: 1…3; step: 1 | Number of extra colors between base colors (1 = N color palette, 2 = 2×N color palette, 3 = 3×N color palette, etc) | -| `colorGap` | `string` | no | `"#2e0000"` | — | Color used for cell borders/gaps | -| `colorGlow` | `string` | no | `"#ffffff"` | — | Color tint for the radial inner shadow effect inside cells (effective with glow > 0) | +| `colors` | `string[]` | no | `["#ff8247","#ffe53d"]` | — | Up to 5 base cell colors in RGBA | +| `stepsPerColor` | `number` | no | `3` | editor range: 1…3; step: 1 | Number of extra colors between base colors, 1 = N colors, 2 = 2×N, etc. | +| `colorGap` | `string` | no | `"#2e0000"` | — | Color used for cell borders/gaps in RGBA | +| `colorGlow` | `string` | no | `"#ffffff"` | — | Color tint for radial inner shadow inside cells in RGBA, effective with glow > 0 | | `distortion` | `number` | no | `0.4` | editor range: 0…0.5 | Strength of noise-driven displacement of cell centers | | `gap` | `number` | no | `0.04` | editor range: 0…0.1 | Width of the border/gap between cells | | `glow` | `number` | no | `0` | editor range: 0…1 | Strength of the radial inner shadow inside cells | diff --git a/skills/paper-shaders/references/shaders/warp.md b/skills/paper-shaders/references/shaders/warp.md index 2c656e2b..aff9e640 100644 --- a/skills/paper-shaders/references/shaders/warp.md +++ b/skills/paper-shaders/references/shaders/warp.md @@ -12,12 +12,12 @@ Animated color fields warped by noise and swirls, applied over base patterns (ch | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#121212","#9470ff","#121212","#8838ff"]` | — | Colors used by the shader; implementation capacity is 10. | -| `rotation` | `number` | no | `0` | editor range: 0…360 | Overall rotation angle of the graphics. | -| `proportion` | `number` | no | `0.45` | editor range: 0…1 | Blend point between 2 colors (0.5 = equal distribution) | -| `softness` | `number` | no | `1` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | -| `shape` | `WarpPattern` | no | `"checks"` | options: "checks", "stripes", "edge" | Base pattern type | +| `colors` | `string[]` | no | `["#121212","#9470ff","#121212","#8838ff"]` | — | Up to 10 gradient colors in RGBA | +| `rotation` | `number` | no | `0` | editor range: 0…360 | Overall rotation angle of the graphics in degrees | +| `proportion` | `number` | no | `0.45` | editor range: 0…1 | Blend point between colors, 0.5 = equal distribution | +| `softness` | `number` | no | `1` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient | +| `shape` | `WarpPattern` | no | `"checks"` | options: "checks", "stripes", "edge" | Base pattern type (0 = checks, 1 = stripes, 2 = edge) | | `shapeScale` | `number` | no | `0.1` | editor range: 0…1 | Zoom level of the base pattern | | `distortion` | `number` | no | `0.25` | editor range: 0…1 | Strength of noise-based distortion | | `swirl` | `number` | no | `0.8` | editor range: 0…1 | Strength of the swirl distortion | -| `swirlIterations` | `number` | no | `10` | editor range: 0…20; hard loop capacity: 20 | Exclusive integer loop bound for layered swirl passes. For an integer `N` in the editor range, the shader executes indices 1 through `N - 1`; the default 10 therefore runs 9 passes, and 20 runs 19. Values of 21 or more reach the hard 20-pass loop capacity. | +| `swirlIterations` | `number` | no | `10` | editor range: 0…20 | Number of layered swirl passes, effective with swirl > 0 | diff --git a/skills/paper-shaders/references/shaders/water.md b/skills/paper-shaders/references/shaders/water.md index 79866477..730a85c9 100644 --- a/skills/paper-shaders/references/shaders/water.md +++ b/skills/paper-shaders/references/shaders/water.md @@ -12,11 +12,11 @@ Water-like surface distortion with natural caustic realism. Works as an image fi | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `image` | `HTMLImageElement \| string` | no | `""` | — | The image to use for the effect | -| `colorBack` | `string` | no | `"#909090"` | — | Background color | -| `colorHighlight` | `string` | no | `"#ffffff"` | — | Highlight color | -| `highlights` | `number` | no | `0.07` | editor range: 0…1 | A coloring added over the image/background, following the caustic shape | -| `layering` | `number` | no | `0.5` | editor range: 0…1 | The power of 2nd layer of caustic distortion | +| `image` | `HTMLImageElement \| string` | no | `""` | — | Optional source image texture | +| `colorBack` | `string` | no | `"#909090"` | — | Background color in RGBA | +| `colorHighlight` | `string` | no | `"#ffffff"` | — | Highlight color in RGBA | +| `highlights` | `number` | no | `0.07` | editor range: 0…1 | Coloring added over image/background following caustic shape | +| `layering` | `number` | no | `0.5` | editor range: 0…1 | Power of 2nd layer of caustic distortion | | `edges` | `number` | no | `0.8` | editor range: 0…1 | Caustic distortion power on the image edges | | `caustic` | `number` | no | `0.1` | editor range: 0…1 | Power of caustic distortion | | `waves` | `number` | no | `0.3` | editor range: 0…1 | Additional distortion based on simplex noise, independent from caustic | diff --git a/skills/paper-shaders/references/shaders/waves.md b/skills/paper-shaders/references/shaders/waves.md index 431caf3c..a1120724 100644 --- a/skills/paper-shaders/references/shaders/waves.md +++ b/skills/paper-shaders/references/shaders/waves.md @@ -12,12 +12,12 @@ Static line pattern configurable into textures ranging from sharp zigzags to smo | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colorFront` | `string` | no | `"#ffbb00"` | — | Foreground color | -| `colorBack` | `string` | no | `"#000000"` | — | Background color | -| `rotation` | `number` | no | `0` | editor range: 0…360 | Overall rotation angle of the graphics. | -| `shape` | `number` | no | `0` | editor range: 0…3 | Line shape control: zigzag at 0, sine at 1, one irregular waveform at 2, and a second irregular waveform at 3. Intermediate values morph gradually between adjacent shapes. | +| `colorFront` | `string` | no | `"#ffbb00"` | — | Foreground color in RGBA | +| `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | +| `rotation` | `number` | no | `0` | editor range: 0…360 | Overall rotation angle of the graphics in degrees | +| `shape` | `number` | no | `0` | editor range: 0…3 | Line shape, 0 = zigzag, 1 = sine, 2-3 = irregular waves, fractional values morph between shapes | | `frequency` | `number` | no | `0.5` | editor range: 0…2 | Wave frequency | | `amplitude` | `number` | no | `0.5` | editor range: 0…1 | Wave amplitude | -| `spacing` | `number` | no | `1.2` | editor range: 0…2 | The space between every two wavy lines | -| `proportion` | `number` | no | `0.1` | editor range: 0…1 | Blend point between front and back colors (0.5 = equal distribution) | -| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness (0 = hard edge, 1 = smooth gradient) | +| `spacing` | `number` | no | `1.2` | editor range: 0…2 | Space between every two wavy lines | +| `proportion` | `number` | no | `0.1` | editor range: 0…1 | Blend point between front and back colors, 0.5 = equal distribution | +| `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient | diff --git a/skills/paper-shaders/references/usage.md b/skills/paper-shaders/references/usage.md index ba5c329d..5a893a66 100644 --- a/skills/paper-shaders/references/usage.md +++ b/skills/paper-shaders/references/usage.md @@ -224,9 +224,9 @@ Pass `["u_image"]` as the final vanilla constructor argument when the catalog re `Heatmap`, `LiquidMetal`, and `GemSmoke` preprocess image inputs in their React components. Reproduce this in vanilla: -- `toProcessedHeatmap(fileOrUrl)` returns `{ blob }` -- `toProcessedLiquidMetal(fileOrUrl)` returns `{ imageData, pngBlob }` -- `toProcessedGemSmoke(fileOrUrl)` returns `{ imageData, pngBlob }` +- `toProcessedHeatmap(fileOrUrl)` returns `Promise<{ blob: Blob }>` +- `toProcessedLiquidMetal(fileOrUrl)` returns `Promise<{ imageData: ImageData; pngBlob: Blob }>` +- `toProcessedGemSmoke(fileOrUrl)` returns `Promise<{ imageData: ImageData; pngBlob: Blob }>` Each function accepts `File | string` and requires browser document/canvas APIs. Load the returned blob into an `HTMLImageElement`, pass that loaded image as `u_image`, and enable mipmaps for `u_image`. From 5c5b6e3e74ab517f47ab31ef658f25ded251d1ba Mon Sep 17 00:00:00 2001 From: Pouya Saadeghi Date: Mon, 3 Aug 2026 20:59:14 +0300 Subject: [PATCH 3/3] skills-generator: fix prop mapping and color capacities --- packages/skill-generator/src/catalog.test.ts | 12 ++++++++++ packages/skill-generator/src/catalog.ts | 10 ++++---- .../skill-generator/src/source-model.test.ts | 23 +++++++++++++++++++ packages/skill-generator/src/source-model.ts | 9 ++++---- .../references/shaders/color-panels.md | 2 +- .../references/shaders/dot-orbit.md | 2 +- .../references/shaders/gem-smoke.md | 4 ++-- .../references/shaders/god-rays.md | 2 +- .../references/shaders/grain-gradient.md | 2 +- .../references/shaders/heatmap.md | 2 +- .../references/shaders/liquid-metal.md | 2 +- .../references/shaders/mesh-gradient.md | 2 +- .../references/shaders/metaballs.md | 2 +- .../references/shaders/pulsing-border.md | 2 +- .../references/shaders/simplex-noise.md | 2 +- .../references/shaders/smoke-ring.md | 2 +- .../shaders/static-mesh-gradient.md | 2 +- .../shaders/static-radial-gradient.md | 2 +- .../paper-shaders/references/shaders/swirl.md | 2 +- .../references/shaders/voronoi.md | 2 +- .../paper-shaders/references/shaders/warp.md | 2 +- 21 files changed, 62 insertions(+), 28 deletions(-) create mode 100644 packages/skill-generator/src/catalog.test.ts create mode 100644 packages/skill-generator/src/source-model.test.ts diff --git a/packages/skill-generator/src/catalog.test.ts b/packages/skill-generator/src/catalog.test.ts new file mode 100644 index 00000000..e68c8a66 --- /dev/null +++ b/packages/skill-generator/src/catalog.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, test } from 'bun:test'; +import { propertyCapacity } from './catalog.js'; + +describe('propertyCapacity', () => { + test('resolves a plural property from singular count metadata', () => { + expect(propertyCapacity('colors', { maxColorCount: 10 })).toBe(10); + }); + + test('preserves direct metadata matches', () => { + expect(propertyCapacity('spots', { maxSpots: 4 })).toBe(4); + }); +}); diff --git a/packages/skill-generator/src/catalog.ts b/packages/skill-generator/src/catalog.ts index 026c8172..6d0214ff 100644 --- a/packages/skill-generator/src/catalog.ts +++ b/packages/skill-generator/src/catalog.ts @@ -82,15 +82,15 @@ export const constraintStep = (step: number): string => `step: ${step}`; export const constraintOptions = (options: string[]): string => `options: ${options.map((option) => JSON.stringify(option)).join(', ')}`; -export const documentedSourceRange = (min: number, max: number): string => - `shader source documents ${min}…${max}`; +export const documentedSourceRange = (min: number, max: number): string => `shader source documents ${min}…${max}`; export const capacityConstraint = (capacity: number): string => `implementation capacity: ${capacity}`; export const propertyCapacity = (propertyName: string, metadata: StaticRecord): number | undefined => { - const name = `max${propertyName.charAt(0).toUpperCase()}${propertyName.slice(1)}`; - const value = metadata[name]; - return typeof value === 'number' ? value : undefined; + const property = `${propertyName.charAt(0).toUpperCase()}${propertyName.slice(1)}`; + const singularProperty = propertyName.endsWith('s') ? property.slice(0, -1) : property; + const names = [`max${property}`, `max${singularProperty}Count`]; + return names.map((name) => metadata[name]).find((value): value is number => typeof value === 'number'); }; export const replaceTokens = (template: string, values: Record): string => diff --git a/packages/skill-generator/src/source-model.test.ts b/packages/skill-generator/src/source-model.test.ts new file mode 100644 index 00000000..30fe6b98 --- /dev/null +++ b/packages/skill-generator/src/source-model.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, test } from 'bun:test'; +import ts from 'typescript'; +import { uniformPropertyNames } from './source-model.js'; + +describe('uniformPropertyNames', () => { + test('ignores identifiers nested inside uniform initializer expressions', () => { + const sourceFile = ts.createSourceFile( + 'shader.tsx', + ` + const uniforms = { + u_isImage: Boolean(image), + u_image: processedImage, + u_direct: directProperty, + }; + `, + ts.ScriptTarget.Latest, + true, + ts.ScriptKind.TSX + ); + + expect(uniformPropertyNames(sourceFile, ['image', 'directProperty'])).toEqual({ directProperty: 'direct' }); + }); +}); diff --git a/packages/skill-generator/src/source-model.ts b/packages/skill-generator/src/source-model.ts index 97a2ac43..ecf79ce5 100644 --- a/packages/skill-generator/src/source-model.ts +++ b/packages/skill-generator/src/source-model.ts @@ -329,11 +329,10 @@ const descendants = (root: ts.Node): ts.Node[] => { return children; }; -const referencedIdentifier = (expression: ts.Expression, names: ReadonlySet): string | undefined => - [expression, ...descendants(expression)] - .filter(ts.isIdentifier) - .map((identifier) => identifier.text) - .find((name) => names.has(name)); +const referencedIdentifier = (expression: ts.Expression, names: ReadonlySet): string | undefined => { + const identifier = unwrapExpression(expression); + return ts.isIdentifier(identifier) && names.has(identifier.text) ? identifier.text : undefined; +}; const enumMappingName = (sourceFile: ts.SourceFile, checker: ts.TypeChecker, typeName: string): string | undefined => { const mapping = mappingNameForType(sourceFile, typeName); diff --git a/skills/paper-shaders/references/shaders/color-panels.md b/skills/paper-shaders/references/shaders/color-panels.md index c8c2d2c4..f209cfd1 100644 --- a/skills/paper-shaders/references/shaders/color-panels.md +++ b/skills/paper-shaders/references/shaders/color-panels.md @@ -11,7 +11,7 @@ Pseudo-3D semi-transparent panels rotating around a central axis | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#ff9d00","#fd4f30","#809bff","#6d2eff","#333aff","#f15cff","#ffd557"]` | — | Up to 7 RGBA colors used to color the panels | +| `colors` | `string[]` | no | `["#ff9d00","#fd4f30","#809bff","#6d2eff","#333aff","#f15cff","#ffd557"]` | implementation capacity: 7 | Up to 7 RGBA colors used to color the panels | | `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | | `angle1` | `number` | no | `0` | editor range: -1…1 | Skew angle applied to all panes | | `angle2` | `number` | no | `0` | editor range: -1…1 | Skew angle applied to all panes | diff --git a/skills/paper-shaders/references/shaders/dot-orbit.md b/skills/paper-shaders/references/shaders/dot-orbit.md index 2238a328..ee3ccf4f 100644 --- a/skills/paper-shaders/references/shaders/dot-orbit.md +++ b/skills/paper-shaders/references/shaders/dot-orbit.md @@ -13,7 +13,7 @@ Animated multi-color dots pattern with each dot orbiting around its cell center. | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | | `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | -| `colors` | `string[]` | no | `["#ffc96b","#ff6200","#ff2f00","#421100","#1a0000"]` | — | Up to 10 base colors in RGBA | +| `colors` | `string[]` | no | `["#ffc96b","#ff6200","#ff2f00","#421100","#1a0000"]` | implementation capacity: 10 | Up to 10 base colors in RGBA | | `size` | `number` | no | `1` | editor range: 0…1 | Dot radius relative to cell size | | `sizeRange` | `number` | no | `0` | editor range: 0…1 | Random variation in shape size, 0 = uniform, higher = random up to base size | | `spreading` | `number` | no | `1` | editor range: 0…1 | Maximum orbit distance around cell center | diff --git a/skills/paper-shaders/references/shaders/gem-smoke.md b/skills/paper-shaders/references/shaders/gem-smoke.md index 676e3969..0db75877 100644 --- a/skills/paper-shaders/references/shaders/gem-smoke.md +++ b/skills/paper-shaders/references/shaders/gem-smoke.md @@ -12,9 +12,9 @@ Animated color fields placed over uploaded logo shape; gives the illusion of smo | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#333333","#e7e6df"]` | — | Up to 6 smoke colors in RGBA | +| `colors` | `string[]` | no | `["#333333","#e7e6df"]` | implementation capacity: 6 | Up to 6 smoke colors in RGBA | | `colorBack` | `string` | no | `"#f0efea"` | — | Background color in RGBA | -| `image` | `HTMLImageElement \| string \| undefined` | no | `""` | — | An optional image used as an effect mask. A transparent background is required. If no image is provided, the shader defaults to one of the predefined shapes. | +| `image` | `HTMLImageElement \| string \| undefined` | no | `""` | — | Pre-processed source image texture (R = edge gradient, G = alpha) | | `innerDistortion` | `number` | no | `0.8` | editor range: 0…1 | Power of smoke distortion inside the input shape | | `outerDistortion` | `number` | no | `0.6` | editor range: 0…1 | Power of smoke distortion outside the input shape | | `outerGlow` | `number` | no | `0.55` | editor range: 0…1 | Visibility of smoke shape outside the input shape | diff --git a/skills/paper-shaders/references/shaders/god-rays.md b/skills/paper-shaders/references/shaders/god-rays.md index aa82be2f..29a77b8f 100644 --- a/skills/paper-shaders/references/shaders/god-rays.md +++ b/skills/paper-shaders/references/shaders/god-rays.md @@ -14,7 +14,7 @@ Animated rays of light radiating from the center, blended with up to 5 colors. | --- | --- | --- | --- | --- | --- | | `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | | `colorBloom` | `string` | no | `"#0000ff"` | — | Color overlay blended with the rays in RGBA | -| `colors` | `string[]` | no | `["#a600ff6e","#6200fff0","#ffffff","#33fff5"]` | — | Up to 5 ray colors in RGBA | +| `colors` | `string[]` | no | `["#a600ff6e","#6200fff0","#ffffff","#33fff5"]` | implementation capacity: 5 | Up to 5 ray colors in RGBA | | `spotty` | `number` | no | `0.3` | editor range: 0…1 | The length of the rays, higher = more spots/shorter rays | | `midSize` | `number` | no | `0.2` | editor range: 0…1 | Size of the circular glow shape in the center | | `midIntensity` | `number` | no | `0.4` | editor range: 0…1 | Brightness/intensity of the central glow | diff --git a/skills/paper-shaders/references/shaders/grain-gradient.md b/skills/paper-shaders/references/shaders/grain-gradient.md index a736e6e8..6bb32d58 100644 --- a/skills/paper-shaders/references/shaders/grain-gradient.md +++ b/skills/paper-shaders/references/shaders/grain-gradient.md @@ -13,7 +13,7 @@ Multi-color gradients with grainy, noise-textured distortion available in 7 anim | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | | `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | -| `colors` | `string[]` | no | `["#7300ff","#eba8ff","#00bfff","#2a00ff"]` | — | Up to 7 gradient colors in RGBA | +| `colors` | `string[]` | no | `["#7300ff","#eba8ff","#00bfff","#2a00ff"]` | implementation capacity: 7 | Up to 7 gradient colors in RGBA | | `softness` | `number` | no | `0.5` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient | | `intensity` | `number` | no | `0.5` | editor range: 0…1 | Distortion between color bands | | `noise` | `number` | no | `0.25` | editor range: 0…1 | Grainy noise overlay | diff --git a/skills/paper-shaders/references/shaders/heatmap.md b/skills/paper-shaders/references/shaders/heatmap.md index 49d38c07..4fbff812 100644 --- a/skills/paper-shaders/references/shaders/heatmap.md +++ b/skills/paper-shaders/references/shaders/heatmap.md @@ -19,5 +19,5 @@ A glowing gradient of colors flowing through an input shape. The effect creates | `innerGlow` | `number` | no | `0.5` | editor range: 0…1 | Size of the heated area inside the input shape | | `outerGlow` | `number` | no | `0.5` | editor range: 0…1 | Size of the heated area outside the input shape | | `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | -| `colors` | `string[]` | no | `["#11206a","#1f3ba2","#2f63e7","#6bd7ff","#ffe679","#ff991e","#ff4c00"]` | — | Up to 10 heatmap colors in RGBA | +| `colors` | `string[]` | no | `["#11206a","#1f3ba2","#2f63e7","#6bd7ff","#ffe679","#ff991e","#ff4c00"]` | implementation capacity: 10 | Up to 10 heatmap colors in RGBA | | `suspendWhenProcessingImage` | `boolean` | no | `false` | — | React-only. Suspends the component when the image is being processed. | diff --git a/skills/paper-shaders/references/shaders/liquid-metal.md b/skills/paper-shaders/references/shaders/liquid-metal.md index 8f4e59e4..aa1f8932 100644 --- a/skills/paper-shaders/references/shaders/liquid-metal.md +++ b/skills/paper-shaders/references/shaders/liquid-metal.md @@ -14,7 +14,7 @@ Futuristic liquid metal material applied to uploaded logo or abstract shape. Flu | --- | --- | --- | --- | --- | --- | | `colorBack` | `string` | no | `"#AAAAAC"` | — | Background color in RGBA | | `colorTint` | `string` | no | `"#ffffff"` | — | Overlay color in RGBA (color burn blending used) | -| `image` | `HTMLImageElement \| string \| undefined` | no | `""` | — | Whether an image is being used as the effect mask | +| `image` | `HTMLImageElement \| string \| undefined` | no | `""` | — | Pre-processed source image texture (R = edge gradient, G = opacity) | | `repetition` | `number` | no | `2` | editor range: 1…10 | Density of pattern stripes | | `shiftRed` | `number` | no | `0.3` | editor range: -1…1 | R-channel dispersion | | `shiftBlue` | `number` | no | `0.3` | editor range: -1…1 | B-channel dispersion | diff --git a/skills/paper-shaders/references/shaders/mesh-gradient.md b/skills/paper-shaders/references/shaders/mesh-gradient.md index e8fa7264..b4f7cc99 100644 --- a/skills/paper-shaders/references/shaders/mesh-gradient.md +++ b/skills/paper-shaders/references/shaders/mesh-gradient.md @@ -11,7 +11,7 @@ A flowing composition of color spots, moving along distinct trajectories and tra | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#e0eaff","#241d9a","#f75092","#9f50d3"]` | — | Up to 10 color spots in RGBA | +| `colors` | `string[]` | no | `["#e0eaff","#241d9a","#f75092","#9f50d3"]` | implementation capacity: 10 | Up to 10 color spots in RGBA | | `distortion` | `number` | no | `0.8` | editor range: 0…1 | Power of organic noise distortion | | `swirl` | `number` | no | `0.1` | editor range: 0…1 | Power of vortex distortion | | `grainMixer` | `number` | no | `0` | editor range: 0…1 | Strength of grain distortion applied to shape edges | diff --git a/skills/paper-shaders/references/shaders/metaballs.md b/skills/paper-shaders/references/shaders/metaballs.md index 89f1bb7e..42e0d47c 100644 --- a/skills/paper-shaders/references/shaders/metaballs.md +++ b/skills/paper-shaders/references/shaders/metaballs.md @@ -13,6 +13,6 @@ Up to 20 colored gooey balls moving around the center and merging into smooth or | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | | `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | -| `colors` | `string[]` | no | `["#6e33cc","#ff5500","#ffc105","#ffc800","#f585ff"]` | — | Up to 8 base colors in RGBA | +| `colors` | `string[]` | no | `["#6e33cc","#ff5500","#ffc105","#ffc800","#f585ff"]` | implementation capacity: 8 | Up to 8 base colors in RGBA | | `count` | `number` | no | `10` | editor range: 1…20 | Number of balls | | `size` | `number` | no | `0.83` | editor range: 0…1 | Size of the balls | diff --git a/skills/paper-shaders/references/shaders/pulsing-border.md b/skills/paper-shaders/references/shaders/pulsing-border.md index 2fc1c1fc..3b3d7560 100644 --- a/skills/paper-shaders/references/shaders/pulsing-border.md +++ b/skills/paper-shaders/references/shaders/pulsing-border.md @@ -13,7 +13,7 @@ Luminous trails of color merging into a glowing gradient contour. | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | | `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | -| `colors` | `string[]` | no | `["#0dc1fd","#d915ef","#ff3f2ecc"]` | — | Up to 5 spot colors in RGBA | +| `colors` | `string[]` | no | `["#0dc1fd","#d915ef","#ff3f2ecc"]` | implementation capacity: 5 | Up to 5 spot colors in RGBA | | `roundness` | `number` | no | `0.25` | editor range: 0…1 | Border radius | | `thickness` | `number` | no | `0.1` | editor range: 0…1 | Border base width | | `margin` | `number` | no | `0` | editor range: 0…1 | Distance from canvas edges to the effect | diff --git a/skills/paper-shaders/references/shaders/simplex-noise.md b/skills/paper-shaders/references/shaders/simplex-noise.md index 818231b2..8bde9b55 100644 --- a/skills/paper-shaders/references/shaders/simplex-noise.md +++ b/skills/paper-shaders/references/shaders/simplex-noise.md @@ -11,6 +11,6 @@ A multi-color gradient mapped into smooth, animated curves built as a combinatio | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#4449CF","#FFD1E0","#F94446","#FFD36B","#FFFFFF"]` | — | Up to 10 base colors in RGBA | +| `colors` | `string[]` | no | `["#4449CF","#FFD1E0","#F94446","#FFD36B","#FFFFFF"]` | implementation capacity: 10 | Up to 10 base colors in RGBA | | `stepsPerColor` | `number` | no | `2` | editor range: 1…10; step: 1 | Number of extra colors between base colors, 1 = N colors, 2 = 2×N, etc. | | `softness` | `number` | no | `0` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient | diff --git a/skills/paper-shaders/references/shaders/smoke-ring.md b/skills/paper-shaders/references/shaders/smoke-ring.md index bef78352..4dbe9afd 100644 --- a/skills/paper-shaders/references/shaders/smoke-ring.md +++ b/skills/paper-shaders/references/shaders/smoke-ring.md @@ -13,7 +13,7 @@ Radial multi-colored gradient shaped with layered noise for a natural, smoky aes | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | | `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | -| `colors` | `string[]` | no | `["#ffffff"]` | — | Up to 10 gradient colors in RGBA | +| `colors` | `string[]` | no | `["#ffffff"]` | implementation capacity: 10 | Up to 10 gradient colors in RGBA | | `noiseScale` | `number` | no | `3` | editor range: 0.01…5 | Noise frequency | | `thickness` | `number` | no | `0.65` | editor range: 0.01…1 | Thickness of the ring shape | | `radius` | `number` | no | `0.25` | editor range: 0…1 | Radius of the ring shape | diff --git a/skills/paper-shaders/references/shaders/static-mesh-gradient.md b/skills/paper-shaders/references/shaders/static-mesh-gradient.md index 0a316336..d1e70f87 100644 --- a/skills/paper-shaders/references/shaders/static-mesh-gradient.md +++ b/skills/paper-shaders/references/shaders/static-mesh-gradient.md @@ -11,7 +11,7 @@ Multi-point mesh gradient with up to 10 color spots, enhanced by two-direction w | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#ffad0a","#6200ff","#e2a3ff","#ff99fd"]` | — | Up to 10 gradient colors in RGBA | +| `colors` | `string[]` | no | `["#ffad0a","#6200ff","#e2a3ff","#ff99fd"]` | implementation capacity: 10 | Up to 10 gradient colors in RGBA | | `positions` | `number` | no | `2` | editor range: 0…100 | Color spots placement seed | | `waveX` | `number` | no | `1` | editor range: 0…1 | Strength of sine wave distortion along X axis | | `waveXShift` | `number` | no | `0.6` | editor range: 0…1 | Phase offset applied to the X-axis wave | diff --git a/skills/paper-shaders/references/shaders/static-radial-gradient.md b/skills/paper-shaders/references/shaders/static-radial-gradient.md index 4b316685..c5c58e67 100644 --- a/skills/paper-shaders/references/shaders/static-radial-gradient.md +++ b/skills/paper-shaders/references/shaders/static-radial-gradient.md @@ -12,7 +12,7 @@ Radial gradient with up to 10 blended colors, featuring advanced mixing modes, f | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | | `colorBack` | `string` | no | `"#000000"` | — | Background color in RGBA | -| `colors` | `string[]` | no | `["#00bbff","#00ffe1","#ffffff"]` | — | Up to 10 gradient colors in RGBA | +| `colors` | `string[]` | no | `["#00bbff","#00ffe1","#ffffff"]` | implementation capacity: 10 | Up to 10 gradient colors in RGBA | | `radius` | `number` | no | `0.8` | editor range: 0…3 | Size of the shape | | `focalDistance` | `number` | no | `0.99` | editor range: 0…3 | Distance of the focal point from center | | `focalAngle` | `number` | no | `0` | editor range: 0…360 | Angle of the focal point in degrees, effective with focalDistance > 0 | diff --git a/skills/paper-shaders/references/shaders/swirl.md b/skills/paper-shaders/references/shaders/swirl.md index 89beb5c1..10dc116f 100644 --- a/skills/paper-shaders/references/shaders/swirl.md +++ b/skills/paper-shaders/references/shaders/swirl.md @@ -12,7 +12,7 @@ Animated bands of color twisting and bending, producing spirals, arcs, and flowi | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | | `colorBack` | `string` | no | `"#330000"` | — | Background color in RGBA | -| `colors` | `string[]` | no | `["#ffd1d1","#ff8a8a","#660000"]` | — | Up to 10 stripe colors in RGBA | +| `colors` | `string[]` | no | `["#ffd1d1","#ff8a8a","#660000"]` | implementation capacity: 10 | Up to 10 stripe colors in RGBA | | `bandCount` | `number` | no | `4` | editor range: 0…15; step: 1 | Number of color bands, 0 = concentric ripples | | `twist` | `number` | no | `0.1` | editor range: 0…1 | Vortex power, 0 = straight sectoral shapes | | `center` | `number` | no | `0.2` | editor range: 0…1 | How far from the center the swirl colors begin to appear | diff --git a/skills/paper-shaders/references/shaders/voronoi.md b/skills/paper-shaders/references/shaders/voronoi.md index b4dd092a..29d2f4ab 100644 --- a/skills/paper-shaders/references/shaders/voronoi.md +++ b/skills/paper-shaders/references/shaders/voronoi.md @@ -12,7 +12,7 @@ Anti-aliased animated Voronoi pattern with smooth and customizable edges. | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#ff8247","#ffe53d"]` | — | Up to 5 base cell colors in RGBA | +| `colors` | `string[]` | no | `["#ff8247","#ffe53d"]` | implementation capacity: 5 | Up to 5 base cell colors in RGBA | | `stepsPerColor` | `number` | no | `3` | editor range: 1…3; step: 1 | Number of extra colors between base colors, 1 = N colors, 2 = 2×N, etc. | | `colorGap` | `string` | no | `"#2e0000"` | — | Color used for cell borders/gaps in RGBA | | `colorGlow` | `string` | no | `"#ffffff"` | — | Color tint for radial inner shadow inside cells in RGBA, effective with glow > 0 | diff --git a/skills/paper-shaders/references/shaders/warp.md b/skills/paper-shaders/references/shaders/warp.md index aff9e640..a4666e30 100644 --- a/skills/paper-shaders/references/shaders/warp.md +++ b/skills/paper-shaders/references/shaders/warp.md @@ -12,7 +12,7 @@ Animated color fields warped by noise and swirls, applied over base patterns (ch | Prop | Type | Required | React default | Constraints | Effect | | --- | --- | --- | --- | --- | --- | -| `colors` | `string[]` | no | `["#121212","#9470ff","#121212","#8838ff"]` | — | Up to 10 gradient colors in RGBA | +| `colors` | `string[]` | no | `["#121212","#9470ff","#121212","#8838ff"]` | implementation capacity: 10 | Up to 10 gradient colors in RGBA | | `rotation` | `number` | no | `0` | editor range: 0…360 | Overall rotation angle of the graphics in degrees | | `proportion` | `number` | no | `0.45` | editor range: 0…1 | Blend point between colors, 0.5 = equal distribution | | `softness` | `number` | no | `1` | editor range: 0…1 | Color transition sharpness, 0 = hard edge, 1 = smooth gradient |