# Architecture `dsh-pixel-studio` is a DSH **bundle** containing one host plugin and one browser client half. The bundle declaration is what lets the DSH Plugin Manager install it; the bundled patch supplies the loader row. ```text package.json main → lib/index.js exports["./client"] → lib/client.js dsh.bundle → { patch: "./cordis.patch.yml" } dsh.client → { platform: "web", inject: ["@deepseek-ai/dsh-client-ui-tool"] } cordis.patch.yml - insert: [{ id: pixel-studio, name: dsh-pixel-studio }] ``` Installing the package adds `dsh-pixel-studio` to the profile's `dsh.profile.bundles`; the loader applies the patch layer and resolves the row back to this package's `main` and `./client` exports. ## Host half The host plugin registers: - a `pixel-studio:craft` system-prompt section; - three tools, `pixel_art`, `pixel_animation` and `pixel_shader`. Grid/animation execution is deterministic: 1. Validate dimensions, grids, palettes, frame timings and output limits. 2. Resolve the palette, legend or template roles. 3. Compose layers with source-over alpha. 4. Apply effects (outline, shadow, background). 5. Generate motion frames or use explicit frames. 6. Encode PNG, GIF, spritesheet, atlas JSON, OYPX and a manifest. 7. Build compact inline canvas metadata. 8. Return file paths, craft report and inline canvas in the structured output. ### Procedural shader path `pixel_shader` follows the design of oy-pixel's `pixel_run_shader`, but uses a Lua **subset** rather than an embedded Lua VM. The pipeline is: ```text code string → lexer/parser (lib/pixel/lua) → bounded tree-walking interpreter → ShaderCanvas framebuffer → frames + palette → shared writeArtifacts() pipeline ``` Design contract mirrored from oy-pixel: - canvas operations: `pset`, `pget`, `line`, `rect`, `rectfill`, `circle`, `circfill`, `ellipse`, `ellipsefill`, `flood`, `replace`, `outline`, `clear`, `stamp`; - palette indexing with 1-based `pal(i)`; - colour helpers `hex`, `mix`, `hsv`, `alpha`; - deterministic `rand`/`noise` with a per-frame seed; - animation uniforms `time`, `phase`, `frame_index`, `frame_count`; - shape clipping, error line numbers, budgets for instructions (20M), wall clock (5s per frame, 10s total) and changed pixels; - direct `#RRGGBB[AA]` strings, with `nil` erasing a pixel. Supported language: `local`, assignment, calls, `if/elseif/else`, numeric `for`, `while`, `break`, table constructors and `[key]` indexing. It does not support user functions, metatables, generic-for, `require` or host access. `frameCount > 1` samples the same script per frame with `time = index / fps` and `phase = index / frameCount`. Full API: [SHADER.md](SHADER.md). The presentation metadata is a separate projection of that result: ```text { canvas: { available: true, width, height, colors: ["#RRGGBBAA", ...], frames: [{ rows: ["...", ...], durationMs, label }] }, imagePath, gifPath, sheetPath, oypxPath, ... } ``` ### Why a custom inline format? - One or two symbols per pixel is smaller than JSON pixel objects or base64 RGBA, including shader gradients with ~100 colours. - The host can produce it without canvas. - The client can decode it into `ImageData` synchronously. - The format is bounded: ≤131,072 pixels and ≤4,096 colours. Up to 64 colours use one symbol per pixel (`encoding: "i1"`); larger palettes use two base-64 symbols (`encoding: "i2"`), which keeps procedural gradients in the native canvas. Beyond either budget, `available: false` and the client renders a compact open-files fallback. ## Client half `lib/client.js` is a hand-written client module: ```js window.__ModuleLoader__.load({ id: "dsh-pixel-studio", factory(require) { ... return { apply, inject }; } }); ``` It requires only the `react` runtime supplied by DSH. During materialization it injects scoped CSS. `apply(ctx)` registers a keyed `tool.call.toolview` entry for all three tool names. The view: - decodes each frame into an offscreen canvas via `createImageData`; - draws the current frame with `imageSmoothingEnabled = false`; - optionally draws the previous frame at low alpha (onion skin); - advances frames with a `setTimeout` chain, paused for reduced motion; - exposes zoom, grid, background and file-open actions; - collapses without losing the active frame. No image request, data URL, iframe or network access is needed. ## Security boundaries - The host owns all file writes. The client receives only display data and file paths. - Inline canvas data is generated by the host and bounded; the client never evaluates HTML from artwork names or palette values (React escapes text). - The client uses `openFile` callbacks from DSH rather than constructing raw resource URLs. - Shader code is parsed and executed by a local tree-walking interpreter with no host globals, modules or filesystem access. It cannot call `require`, `eval`, `Function`, or Node APIs, and every loop is budgeted. - No third-party runtime dependencies are bundled. The GIF/PNG/OYPX encoders are local implementations. ## Release pipeline `.github/workflows/release.yml` runs on `v*` tags: 1. `npm ci`, `npm test`; 2. `npm pack`; 3. copy the versioned tarball to the version-free `dsh-pixel-studio.tgz`; 4. create the GitHub Release with `GITHUB_TOKEN` and attach the tarball. The version-free asset supports the stable `releases/latest/download/dsh-pixel-studio.tgz` URL used by storefronts. `screenshots.json` lists four repository images for storefront previews. ## Compatibility - DSH 0.2.x, client platform `web`. - Node 20+ for host execution and tests. - OYPX target: version 2, direct `#RRGGBBAA` row RLE, current oy-pixel parser. - GIF: GIF89a, global 256-colour table, transparent index 0, disposal 2. ## Test strategy `node:test` covers: - colour parsing and palette ramps; - grid, layer and transform behavior; - PNG/GIF signatures and byte-level structure; - OYPX row-width and direct-colour tokens; - inline canvas encoding and budget fallback; - craft checks; - shader execution: tables, `stamp`, animation phase movement, error lines and pset bounds; - DSH tool/section registration; - client bundle registration and a real-React render check (optional local verification, not part of the public test suite).