← ChatCutCONTENT HISTORY

Update to ChatCut

Snapshot Sep 30, 2026 · 23:14 UTC · version 1.10.14

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "shader-gen",
  "description": "AI shader generator for WebGL video effects, transitions, masks, and color grading (LUT / 调色 / 电影感 / film look). Use when the user wants a video effect (滤镜 / 特效), a transition (转场 / crossfade / wipe / cube / 3d), a mask (蒙版 / 遮罩 / reveal), a zoom / push-in (推近 / 推镜头), or a color grade — try the built-in effects (zoom, builtin LUTs) before generating a new shader.",
  "included_files": [
    {
      "relative_path": "examples/cube-rotate.md",
      "size_in_bytes": 2205
    },
    {
      "relative_path": "examples/door-open.md",
      "size_in_bytes": 4222
    },
    {
      "relative_path": "examples/page-curl.md",
      "size_in_bytes": 4158
    },
    {
      "relative_path": "references/design-principles.md",
      "size_in_bytes": 12553
    },
    {
      "relative_path": "references/property-changes.md",
      "size_in_bytes": 4544
    }
  ],
  "skill_md_contents": "---\nname: shader-gen\ndescription: AI shader generator for WebGL video effects, transitions, masks, and color grading (LUT / 调色 / 电影感 / film look). Use when the user wants a video effect (滤镜 / 特效), a transition (转场 / crossfade / wipe / cube / 3d), a mask (蒙版 / 遮罩 / reveal), a zoom / push-in (推近 / 推镜头), or a color grade — try the built-in effects (zoom, builtin LUTs) before generating a new shader.\nuser-invocable: true\n---\n\n# Shader Generator\n\nSubmit-only: creates a backend generation job, returns `jobId`. Use the `track_progress` tool for job lifecycle after submission.\n\n**Always use `generate.ts` for new shaders.** Manual authoring is only for editing existing asset code — never as a fallback when generation fails.\n\n## Catalog-first rule — try existing assets before generation\n\nBefore generating a shader, call `browse_library` unless the user names an exact asset id that is already visible in `browse_assets`.\n\n`browse_library` is the source of truth for built-in effects, built-in transitions, and project effect/transition assets. Built-ins are stable global asset ids, not per-project DB assets, so they may not appear in `browse_assets`.\n\nApply catalog entries with `edit_item`, do **not** call `submit_shader`.\n\nGood catalog searches:\n\n```text\nbrowse_library(query: \"zoom\")\nbrowse_library(category: \"transitions\", query: \"dissolve\")\nbrowse_library(category: \"audio-fx\")\n```\n\nGenerate only when no catalog entry matches the user's intent closely enough.\n\n### `builtin:zoom` uses the same track-bound placement as every effect\n\nEffects always have timeline geometry. For a whole-clip effect, pass `targetItemId`; ChatCut resolves it to a clip-anchored range covering that clip. For an explicit timeline range, pass `trackId` + `trackBoundFrom` + `trackBoundDurationInFrames`.\n\n```text\n# Zoom on the entire video clip\nedit_item(json: '{\"adds\":[{\"type\":\"effect\",\"assetId\":\"builtin:zoom\",\"targetItemId\":\"<clip-id>\",\"propertyOverrides\":{\"magnification\":1.5,\"shape\":\"hold\"}}]}')\n\n# Zoom on a sub-range of the clip (e.g. frames 90–150 only, a punch zoom on a beat)\nedit_item(json: '{\"adds\":[{\"type\":\"effect\",\"assetId\":\"builtin:zoom\",\"mode\":\"track-bound\",\"trackId\":\"<trackId>\",\"trackBoundFrom\":90,\"trackBoundDurationInFrames\":60,\"propertyOverrides\":{\"magnification\":2,\"shape\":\"punch\"}}]}')\n```\n\nUse `preview_timeline({views:[\"timeline\"],tracks:[\"V1\"]})` to obtain track item ids and timeline-frame ranges. Use `inspect_item({itemId:\"...\"})` when exact item detail is needed.\n\n| Key             | Type   | Range / values                             | Default | Notes                                          |\n| --------------- | ------ | ------------------------------------------ | ------- | ---------------------------------------------- |\n| `magnification` | number | 1–4                                        | `1.5`   | Zoom factor; 1 = no zoom, 2 = 2× in            |\n| `focalPointX`   | number | 0–1                                        | `0.5`   | Horizontal focal point (0 = left, 1 = right)   |\n| `focalPointY`   | number | 0–1                                        | `0.5`   | Vertical focal point (0 = top, 1 = bottom)     |\n| `shape`         | select | `punch` / `hold` / `slow-push` / `instant` | `hold`  | Animation curve                                |\n| `focalMode`     | select | `auto` / `manual`                          | `auto`  | `auto` picks subject; `manual` uses focalPoint |\n| `easeInFrames`  | number | 0–60                                       | `8`     | Frames to ramp in                              |\n| `easeOutFrames` | number | 0–60                                       | `8`     | Frames to ramp out                             |\n\nOmit `propertyOverrides` entirely for default zoom. Send only the keys you want to change — patch semantics.\n\n### Clip-anchored vs adjustment-track\n\nEffect items have two placements, both with a concrete time range:\n\n- **Clip-anchored**: pass `targetItemId` for the whole clip, or include an explicit range that intersects the clip. The stored range is local to that clip and follows it when it moves.\n- **Adjustment-track**: pass `trackId` + `trackBoundFrom` + `trackBoundDurationInFrames` for a range over empty track space. The range is absolute on the timeline.\n\nUse `targetItemId` for whole-clip effects. Use explicit track geometry only when the requested range differs from a clip's full duration.\n\n### Built-in LUT properties\n\n```text\nedit_item(json: '{\"adds\":[{\"type\":\"effect\",\"targetItemId\":\"<clip-id>\",\"assetId\":\"builtin:slog3-s709\",\"propertyOverrides\":{\"intensity\":1}}]}')\n```\n\n| Key         | Type   | Range | Default | Notes                          |\n| ----------- | ------ | ----- | ------- | ------------------------------ |\n| `intensity` | number | 0–1   | `1`     | LUT strength; 1 = full applied |\n\nTo swap: delete the effect and re-add with a different `assetId`. To remove: delete the effect item.\n\nUser-uploaded `.cube` LUT assets take this exact same shape — only `assetId` differs. See \"Applying an Existing LUT Asset\" below.\n\n## Supported Targets\n\nEffects and transitions apply to `video`, `image`, and `gif` items.\n\n## Type Routing\n\nBefore generating anything, check two non-generation paths first:\n\n1. **Catalog entry** — use `browse_library` for built-in and project effects/transitions.\n2. **User-uploaded `.cube` LUT asset** that already exists in the project library — bind it instead of generating, see \"Applying an Existing LUT Asset\" below. It shows up in `browse_assets` as `type: effect` with a `lut`-typed entry in `editableProperties`.\n\n| User wants                                                           | `--type`     |\n| -------------------------------------------------------------------- | ------------ |\n| Video appearance (color, blur, glow, grain, distortion)              | `effect`     |\n| Color grade / look (teal-orange, cinematic, vintage, LUT-style)      | `effect`     |\n| Visibility control (mask, reveal, wipe, shape cutout, gradient fade) | `effect`     |\n| Blend between clips (crossfade, dissolve, slide, 3D cube/page flip)  | `transition` |\n\n\"LUT-style\" in the table means **generating a fresh GLSL color grade that resembles a LUT** — only when the user wants something new. If they want to apply a `.cube` file already in the library, don't generate; bind the existing asset instead.\n\nNo separate LUT or mask generator for the generation path — those are all `effect`.\n\n## Applying an Existing LUT Asset\n\n**Default target is the timeline.** \"Apply this LUT\" means an `edit_item` effect on the clip. Only reach for `edit_asset sourceLut` when the user asks for the source itself everywhere it appears — every clip cut from that asset, or a log-to-Rec.709 normalization of the footage — because that changes every instance of the asset on every timeline.\n\n`.cube` files uploaded by the user become **effect assets with `category: \"lut\"`**. Applying one to a clip is **not** generation — it is the same `edit_item` effect shape as a built-in LUT, with that asset's own id as `assetId`:\n\n```text\nedit_item(json: '{\"adds\":[{\"type\":\"effect\",\"targetItemId\":\"<clip-id>\",\"assetId\":\"<lut-effect-asset-id>\",\"propertyOverrides\":{\"intensity\":1}}]}')\n```\n\nKey points:\n\n- `assetId` is the LUT effect asset's real id. There is no literal `\"lut\"` assetId, and no LUT binding nested inside `propertyOverrides`.\n- `propertyOverrides` carries only `intensity` (0–1, default 1). The `.cube` binding lives on the asset, not on the effect item.\n- Find the id with `browse_assets type:\"effect\"`: a LUT asset is the one whose `editableProperties` contain a `lut`-typed key. Built-in LUTs come from `browse_library category:\"luts\"`.\n- `targetItemType` defaults to `video`; also supports `image`, `gif`.\n- To swap: delete the effect and re-add with the other LUT's `assetId`. To remove: delete the effect item.\n- To grade a whole source clip everywhere it appears instead of one timeline item, use `edit_asset` update with `{\"sourceLut\":{\"assetId\":\"<lut-effect-asset-id>\"}}` on the video/image/gif asset; `{\"sourceLut\":null}` removes it.\n- If the `.cube` is not in the project yet, you cannot get it in: this surface has no LUT import path, and `.cube` is not an accepted chat attachment either, so never ask the user to send you the file. Ask them to drag it into the editor's media pool instead — the editor registers it as a LUT asset and it then shows up in `browse_assets`. Do not tell them ChatCut cannot handle `.cube`; the editor can.\n\nDo not call `submit_shader` for this path.\n\nAfter applying, confirm with `preview_timeline` that the effect is listed on the target clip's track. Do not report success from the `edit_item` response alone.\n\n## Usage\n\nBefore calling `submit_shader`, restate the user's intent in one concrete sentence, then proceed immediately. After `track_progress` returns, state what was produced in one line — do NOT ask \"Keep it or regenerate it?\".\n\n```ts\nsubmit_shader({\n  type: \"effect\",\n  prompt: \"Chromatic aberration with RGB split\",\n  name: \"Chromatic Aberration\",\n});\n\nsubmit_shader({\n  type: \"transition\",\n  prompt: \"Smooth crossfade with soft edge\",\n  name: \"Crossfade\",\n});\n\nsubmit_shader({\n  type: \"effect\",\n  prompt: \"Cinematic teal-orange color grade\",\n});\n\nsubmit_shader({\n  type: \"effect\",\n  prompt: \"Stronger version\",\n  referenceAssetIds: [\"effect_asset_id\"],\n});\n```\n\n## Strategy\n\n- Submit, then stop. Tell user the job was created.\n- Use the `track_progress` tool for status/wait after submission.\n- Generation always produces a library asset — never refuse because the timeline isn't ready.\n- **Apply is separate and optional.** Only apply when user explicitly asks (\"加到视频\", \"apply\", \"用到第一段\"). When ambiguous, default to library-only.\n\n## Editing Existing Properties\n\nAny time you're about to edit shader `asset.properties`, applied effect/transition `item.propertyOverrides`, or promote a hardcoded shader value, read [`references/property-changes.md`](references/property-changes.md) first.\n\nIt reinforces that shader `properties` is an array, but the allowed shader property types are only `number`, `boolean`, `color`, `select`, and `vec2`. Motion Graphic properties are also arrays, but use a different type set.\n\n## Parameters\n\n| Param               | Description                                                                                                                                                    | Default |\n| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |\n| `type`              | `\"effect\"` or `\"transition\"` (req'd)                                                                                                                           | —       |\n| `prompt`            | Description of the shader (req'd)                                                                                                                              | —       |\n| `name`              | Asset name shown in library                                                                                                                                    | —       |\n| `referenceAssetIds` | Asset ids. Image id → model LOOKS AT it for visual inspiration. Effect/transition id → reuse its code as style anchor (≤1 per submit, kind must match `type`). | —       |\n\n## Output\n\nReturns `{ success, job: { jobId, status }, manage: { status, wait, watch } }`.\n\n## Applying to Timeline\n\nOnly when user explicitly requests. Refresh the affected timeline with `view:\"timeline\"`, passing `track` to narrow the read when appropriate.\n\n### Effect\n\n```text\nedit_item(json: '{\"adds\":[{\"type\":\"effect\",\"targetItemId\":\"<id>\",\"assetId\":\"<id>\",\"enabled\":true,\"propertyOverrides\":{}}]}')\n```\n\n### Transition\n\nRequires two adjacent same-track endpoints. `edit_item` validates live seam feasibility and refuses durations that would require freeze frames or overlapping neighboring transitions. If the add fails, retry with the suggested `durationInFrames`, trim the clips to expose handles, delete/shorten neighboring transitions, or keep a hard cut.\n\n```text\nedit_item(json: '{\"adds\":[{\"type\":\"transition\",\"assetId\":\"<id>\",\"outgoingItemId\":\"<id1>\",\"incomingItemId\":\"<id2>\",\"durationInFrames\":30}]}')\n```\n\n## Validation & Verification\n\n### Backend Validation\n\nWhen generating via `generate.ts`, the backend handles validation automatically (transpile, AST security, class structure, retry on failure).\n\n### Manual Code Verification\n\n**NEVER write shader code from scratch.** Always use `generate.ts` for new shaders. This section is ONLY for modifying existing shader code that was already generated.\n\nWhen writing shader code manually, read `${CLAUDE_SKILL_DIR}/references/design-principles.md` first. If the change touches editable properties, also read `${CLAUDE_SKILL_DIR}/references/property-changes.md`.\n\nTypical workflow:\n\n1. `inspect_asset` with the shader `assetId` and `includeCode: true` — read the current source.\n2. Edit the source in your own context.\n3. `edit_asset` with `action=update`, the same `assetId`, and the full replacement source inline in `json.code`. Validation runs automatically on update — if code is invalid, the update is rejected with error details.\n"
}

SHA-256: f0275739d6c758c6fa1483174115c54fa479d8e8921a8217c70e8b4aa19af5b2