This document provides repo-specific guidance for AI coding agents working on LUT Chainer.
# Build web + CLI bundles
npm run build
# Watch mode
npm run dev
# Type check only
npm run typecheckUse a local HTTP server when validating the web app:
npm run build
npm run serve
# http://localhost:8000Do not rely on file://index.html. Example preset loading expects HTTP.
LUT Chainer is a browser-based shader editor for authoring LUT step chains.
Main capabilities:
- edit LUT step chains and blend modes
- preview the result in real time with WebGL and CPU fallback
- tweak material and light settings
- export generated GLSL / HLSL
- save and load
.lutchainarchives
app/browser/ Browser app orchestration, DOM wiring, Svelte components
platforms/webgl/ DOM-free WebGL runtime implementations
platforms/browser/Browser API adapters (storage, download, file helpers)
features/ Domain models and pure business logic
shared/ Pure cross-cutting helpers and archive code
app/cli/ Node CLI entrypoints and commands
platforms/node/ Node runtime adapters
Allowed direction:
app/browser -> features, shared, platforms/browser, platforms/webgl
app/cli -> features, shared, platforms/node
platforms/* -> features, shared
features -> shared
shared -> (no app/platform imports)
Forbidden direction:
features/**must not import fromapp/**orplatforms/**shared/**must not import fromapp/**orplatforms/**platforms/webgl/**must not usedocument,window,navigator, or Svelteapp/cli/**must not import fromapp/browser/**or browser-only modules
- Put DOM orchestration, event wiring, and Svelte components in
src/app/browser/ - Put WebGL renderer classes and texture/program helpers in
src/platforms/webgl/ - Put browser storage/download/file adapters in
src/platforms/browser/ - Keep
src/features/pure enough that it can be reused by browser, CLI, and Blender-related tooling - Keep
src/shared/limited to pure helpers such as math, geometry, and archive parsing/serialization
Use these naming patterns consistently:
| Pattern | Role |
|---|---|
*-model.ts |
immutable models, enums, constants |
*-state.ts |
mutable state + getter/setter access |
*-runtime.ts |
pure computation logic |
*-controller.ts |
stateful controllers created via factory |
*-bindings.ts |
event binding and interaction wiring |
*-view.ts |
UI-facing derived state and helpers |
*-system.ts |
lifecycle-managed runtime subsystem |
svelte-*.svelte |
Svelte component modules |
main-*.ts |
browser bootstrap/orchestration modules |
types.ts |
type-only barrel exports |
Bad:
features/**importing DOM code, Svelte, orplatforms/webglplatforms/webgl/**importing browser DOM helpersapp/cli/**importing browser-only modules
Good:
// features/step/step-runtime.ts
export function composeColorFromSteps(steps: readonly StepRuntimeModel[]): Color { ... }
// app/browser/ui/main-ui-setup.ts
import { composeColorFromSteps } from '../../../features/step/step-runtime.ts';Do not mutate shared state objects directly. Use controller/state APIs or injected callbacks.
Good:
const controller = createPipelineCommandController({
getSteps: () => getPipelineSteps(),
setSteps: steps => setPipelineSteps(steps),
status: (message, kind) => showStatus(message, kind),
t,
});Important defaults:
strict: truetarget: ES2020
Implications:
- avoid implicit
any - avoid
String.prototype.replaceAll() - prefer explicit callback parameter types when inference is weak
CPU fallback and WebGL preview must match. Preserve texel-center sampling.
Good:
const uPixel = u * width - 0.5;
const vPixel = v * height - 0.5;Do not use /src/... or ../src/.... Use correct relative paths from the current file.
Do not interpolate dynamic IDs directly into CSS selectors if they may contain special characters.
Prefer:
const element = Array.from(document.querySelectorAll('[data-lut-id]'))
.find(node => node.getAttribute('data-lut-id') === lutId);If code touches:
documentwindownavigatorlocalStorageFile,Blob, object URLs
it belongs in app/browser or platforms/browser, not features or shared.
If code touches:
WebGLRenderingContext- texture/program/buffer management
it belongs in platforms/webgl, unless it is pure shader/data logic with no runtime dependency.
src/
├─ app/
│ ├─ browser/
│ └─ cli/
├─ features/
│ ├─ lut-editor/
│ ├─ pipeline/
│ ├─ shader/
│ └─ step/
├─ platforms/
│ ├─ browser/
│ ├─ node/
│ └─ webgl/
├─ shared/
│ ├─ lutchain/
│ └─ utils/
└─ types/
- confirm the target layer is correct before adding a file
- prefer moving browser/runtime code out of
featuresrather than importing upward - keep
sharedpure - keep
platforms/webglreusable without DOM globals - run
npm run typecheckafter TypeScript changes
.agents/rules/coding-style.md.agents/rules/common-mistakes.md.agents/rules/blender-addon.md