@weasel-js/core 1.2.0 → 1.3.0-pre.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +1508 -0
  2. package/dist/{DrawCommand-uNkv5TjO.d.ts → DrawCommand-BnZyQaDc.d.ts} +6 -189
  3. package/dist/chunk-2KKYDDDD.js +121 -0
  4. package/dist/chunk-2KKYDDDD.js.map +1 -0
  5. package/dist/{chunk-J7LUY47N.js → chunk-3FB44DBC.js} +3 -3
  6. package/dist/{chunk-J7LUY47N.js.map → chunk-3FB44DBC.js.map} +1 -1
  7. package/dist/{chunk-CKADTCHK.js → chunk-3ZV53ZW2.js} +51 -14
  8. package/dist/chunk-3ZV53ZW2.js.map +1 -0
  9. package/dist/{chunk-ADWOB5K2.js → chunk-67KE7SDP.js} +4 -4
  10. package/dist/{chunk-ADWOB5K2.js.map → chunk-67KE7SDP.js.map} +1 -1
  11. package/dist/{chunk-CSMF654J.js → chunk-AQS4JGRA.js} +50 -7
  12. package/dist/chunk-AQS4JGRA.js.map +1 -0
  13. package/dist/{chunk-HGF5X6MZ.js → chunk-BJN5ZRIJ.js} +2 -2
  14. package/dist/chunk-BJN5ZRIJ.js.map +1 -0
  15. package/dist/{chunk-4Z6J4IVH.js → chunk-F74NVEHT.js} +14 -46
  16. package/dist/chunk-F74NVEHT.js.map +1 -0
  17. package/dist/{chunk-7KYLIC3E.js → chunk-IFOVTYAK.js} +16611 -15749
  18. package/dist/chunk-IFOVTYAK.js.map +1 -0
  19. package/dist/{chunk-UGFFCMQP.js → chunk-MXFSHJOM.js} +4 -4
  20. package/dist/{chunk-UGFFCMQP.js.map → chunk-MXFSHJOM.js.map} +1 -1
  21. package/dist/{chunk-BHVYVFGV.js → chunk-ZB7UYJVG.js} +3 -3
  22. package/dist/{chunk-BHVYVFGV.js.map → chunk-ZB7UYJVG.js.map} +1 -1
  23. package/dist/clipboard.d.ts +3 -3
  24. package/dist/clipboard.js +3 -3
  25. package/dist/clone.d.ts +2 -2
  26. package/dist/clone.js +3 -3
  27. package/dist/{geometry-C56YbnfD.d.ts → geometry-Dtt_k6Dq.d.ts} +1 -1
  28. package/dist/{grid-DqOi0Vgi.d.ts → grid-nnXU4VjN.d.ts} +1 -1
  29. package/dist/index.d.ts +1510 -1292
  30. package/dist/index.js +10 -10
  31. package/dist/insert.d.ts +3 -3
  32. package/dist/insert.js +1 -1
  33. package/dist/move.d.ts +4 -4
  34. package/dist/move.js +3 -3
  35. package/dist/{options-C6HYCKP7.d.ts → options-CdFl510T.d.ts} +1 -1
  36. package/dist/{path-B6MMiodD.d.ts → path-JEV2c5If.d.ts} +2 -13
  37. package/dist/patterns-builtin.d.ts +1 -1
  38. package/dist/patterns-builtin.js +1 -1
  39. package/dist/{pointSnapToGrid-BBtn5bus.d.ts → pointSnapToGrid-Dmthv94u.d.ts} +2 -2
  40. package/dist/{registry-l3JX0Z6b.d.ts → registry-BU1_3L_C.d.ts} +629 -34
  41. package/dist/renderer.d.ts +38 -37
  42. package/dist/renderer.js +10 -10
  43. package/dist/resize.d.ts +4 -4
  44. package/dist/resize.js +2 -2
  45. package/dist/routing.d.ts +35 -9
  46. package/dist/routing.js +1 -1
  47. package/dist/{stroke-DaxnQ-u7.d.ts → stroke-5pBwOm9s.d.ts} +41 -4
  48. package/dist/{types-C2R2bjxf.d.ts → types-DIQAisSG.d.ts} +99 -5
  49. package/dist/{types-DbysOO-2.d.ts → types-XBcDEp3Y.d.ts} +14 -9
  50. package/dist/{types-BhrifbZ1.d.ts → types-miXHGrZM.d.ts} +1 -1
  51. package/package.json +8 -6
  52. package/dist/chunk-4Z6J4IVH.js.map +0 -1
  53. package/dist/chunk-7KYLIC3E.js.map +0 -1
  54. package/dist/chunk-CKADTCHK.js.map +0 -1
  55. package/dist/chunk-CSMF654J.js.map +0 -1
  56. package/dist/chunk-GVCNT7UH.js +0 -47
  57. package/dist/chunk-GVCNT7UH.js.map +0 -1
  58. package/dist/chunk-HGF5X6MZ.js.map +0 -1
  59. package/dist/paint-types-1fUdZUaH.d.ts +0 -266
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/interactions/actions/depRegistry.tsx","../src/interactions/actions/buildDeps.ts","../src/interactions/actions/registry.tsx","../src/tools/routing/reflection/registry.ts","../src/tools/routing/reflection/conflicts.ts"],"names":["createContext","useRef","useMemo","jsx","useContext","useEffect"],"mappings":"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","file":"chunk-CKADTCHK.js","sourcesContent":["/**\n * Dep registry for registry unification.\n *\n * Holds named \"live source\" thunks. Actions declare `requires: ['selection']`;\n * the dispatcher calls `registry.get('selection')` at invocation time to\n * build a typed Deps bag.\n *\n * `DepSchema` — the map of dep name → value type — is the single source of\n * truth for what `requires`/`register`/`get` accept. Its concrete fields live\n * in `./depSchema` (co-located with the dep value types). It is declared there\n * as a plain `export interface` rather than via a cross-module\n * `declare module './depRegistry'` augmentation: that augmentation does NOT\n * merge once rollup-plugin-dts flattens both files into one `.d.ts` chunk,\n * which silently emptied `DepSchema` for consumers. Consumers still extend it\n * via `declare module '@weasel-js/core'` augmentation.\n */\nimport {\n createContext, useContext, useEffect, useMemo, useRef,\n type ReactNode,\n} from 'react';\nimport type { DepSchema, DepName } from './depSchema';\n\nexport type { DepSchema, DepName };\n\n/** Holds the live sources an action's declared dependencies resolve to.\n * Sources are thunks, read at invocation time, so an action never captures\n * stale state. */\nexport interface DepRegistry {\n register<K extends DepName>(name: K, source: () => DepSchema[K]): () => void;\n get<K extends DepName>(name: K): DepSchema[K] | undefined;\n}\n\nconst DepRegistryContext = createContext<DepRegistry | null>(null);\n\n/** Provides the dep registry for a canvas. `<SceneCanvas>` mounts one; a\n * consumer registering its own dep sources must be inside it. */\nexport function DepRegistryProvider({ children }: { children: ReactNode }) {\n const sourcesRef = useRef(new Map<DepName, () => unknown>() as Map<string, () => unknown>);\n\n const registry = useMemo<DepRegistry>(() => ({\n register: <K extends DepName>(name: K, source: () => DepSchema[K]) => {\n sourcesRef.current.set(name as string, source as () => unknown);\n return () => { sourcesRef.current.delete(name as string); };\n },\n get: <K extends DepName>(name: K) =>\n sourcesRef.current.get(name as string)?.() as DepSchema[K] | undefined,\n }), []);\n\n return <DepRegistryContext.Provider value={registry}>{children}</DepRegistryContext.Provider>;\n}\n\n/** The dep registry in scope. Throws outside a `<DepRegistryProvider>`. */\nexport function useDepRegistry(): DepRegistry {\n const r = useContext(DepRegistryContext);\n if (r === null) {\n throw new Error('useDepRegistry: no DepRegistryProvider in scope. Wrap your tree with <DepRegistryProvider> (typically inside <SceneCanvas>).');\n }\n return r;\n}\n\n/**\n * Like `useDepRegistry`, but returns `null` when no `<DepRegistryProvider>` is\n * in scope instead of throwing. Used by `useStandardActions` to preserve its\n * silent-no-op contract when neither provider is present.\n */\nexport function useOptionalDepRegistry(): DepRegistry | null {\n return useContext(DepRegistryContext);\n}\n\n/** Register a live source for `name` for the lifetime of the calling\n * component. The `source` thunk is called at dispatch time and should\n * return the latest value. */\nexport function useDepSource<K extends DepName>(name: K, source: () => DepSchema[K]) {\n const registry = useDepRegistry();\n const sourceRef = useRef(source);\n sourceRef.current = source;\n useEffect(() => {\n return registry.register(name, () => sourceRef.current());\n }, [name, registry]);\n}\n","/**\n * Shared dep-bag builder — the single implementation behind both the gesture\n * dispatcher's per-invocation `buildDeps` and `ActionsRegistry.trigger`'s\n * `requires` branch. Reads the action's declared `requires`, resolves each\n * name against the `DepRegistry`, and (dev builds only) wraps the bag in a\n * Proxy that warns on undeclared reads.\n */\nimport type { Action } from './registry';\nimport type { DepRegistry } from './depRegistry';\nimport type { ActionDeps } from './invoker';\n\n/** True in dev builds; false in production. Tree-shakes the Proxy out of\n * prod entirely. (Mirrors the dispatcher's own `DEV` const.) */\nconst DEV: boolean = (() => {\n try {\n return Boolean((import.meta as unknown as { env?: { DEV?: boolean } }).env?.DEV);\n } catch {\n return false;\n }\n})();\n\n/** Build the deps bag for an action from its declared `requires`. */\nexport function buildDepsFromRequires(action: Action, depRegistry: DepRegistry): ActionDeps {\n const requires = action.requires ?? [];\n const entries = requires.map((name) => [name, depRegistry.get(name)]);\n const deps = Object.fromEntries(entries) as ActionDeps;\n\n // Dev-only: wrap deps in a Proxy that warns when the invoker reads a\n // key not declared in `requires`. Catches silent-failure-by-typo or\n // forgotten-requires (the #1 bug class in this codebase pre-Phase-14e).\n // Production builds: bypass the Proxy entirely (zero overhead).\n if (DEV) {\n const declared = new Set<string>(requires);\n return new Proxy(deps as Record<string, unknown>, {\n get(target, prop, receiver) {\n if (typeof prop === 'string' && !declared.has(prop) && prop !== 'then') {\n const message =\n `[weasel:dispatcher] action \"${action.id}\" read deps.${prop} but did not declare it in \\`requires\\`. ` +\n `Add \\`requires: [...'${prop}']\\` to the descriptor or the dep will be undefined at runtime.`;\n // Warn first so the console trail survives even when the throw is\n // swallowed by a caller, then throw so the gesture fails visibly\n // instead of silently running without the dep.\n console.warn(message);\n throw new Error(message);\n }\n return Reflect.get(target, prop, receiver);\n },\n }) as ActionDeps;\n }\n return deps;\n}\n","/**\n * @experimental\n * Actions Registry — owns the registered `Action` descriptors and the\n * imperative `trigger` path. Keystrokes reach actions through the gesture\n * dispatcher matching their `defaultBinding`, not through a listener here.\n * Spec: docs/superpowers/specs/2026-05-09-actions-registry-design.md\n */\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport type { GestureSpec, PhaseSpec } from '../gestures/spec';\nimport type { ActionDeps, BindingOpts, Invoker } from './invoker';\nimport type { GestureBinding } from './binding';\nimport { useOptionalDepRegistry, type DepRegistry, type DepName } from './depRegistry';\nimport { buildDepsFromRequires } from './buildDeps';\nimport { RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, type PhaseAtom } from '../../tools/routing/routeGrammar';\nimport type { Dispatcher, UiOngoingControl } from '../dispatcher/dispatcher';\nexport type { UiOngoingControl } from '../dispatcher/dispatcher';\n\n/**\n * @experimental\n * A single entry in `Action.defaultBinding[]`. Either a bare `GestureSpec`\n * (no per-binding opts) or an object form that pairs a spec with\n * `BindingOpts` for parametric actions (e.g. `{ params: { axis: 'x' } }`).\n * Use the object form when two bindings for the same action differ only in\n * a runtime parameter — the dispatcher extracts `opts.params` and passes\n * them to `ImmediateInvoker.run` as its second argument.\n */\nexport type BoundGesture = GestureSpec | { spec: GestureSpec; opts: BindingOpts };\n\n/**\n * @experimental\n * Flatten an action's `defaultBinding` into `GestureBinding`s. A bare\n * `GestureSpec` has `kind` at top level; the object form has `spec`.\n */\nexport function actionBindings(action: Action): GestureBinding[] {\n const gs = action.defaultBinding;\n if (!gs) return [];\n const raw: BoundGesture[] = Array.isArray(gs) ? gs : [gs as BoundGesture];\n return raw.map((entry) => {\n const isBoundObj = !('kind' in entry);\n const spec = isBoundObj ? (entry as { spec: GestureSpec }).spec : (entry as GestureSpec);\n const opts = isBoundObj ? (entry as { opts: BindingOpts }).opts : undefined;\n return { spec, actionId: action.id, ...(opts !== undefined ? { opts } : {}) };\n });\n}\n\n/**\n * @experimental\n * Single registered action. v1: one binding per action.\n */\nexport interface Action {\n id: string;\n label: string;\n /** The gesture-spec form of the binding, read by the gesture dispatcher.\n * May be a single `GestureSpec`, a bare `GestureSpec[]` (any-of semantics),\n * or a `BoundGesture[]` where each entry is either a bare `GestureSpec` or\n * `{ spec, opts }` — use the object form for parametric actions where two\n * bindings for the same action differ only by `opts.params` (e.g. `flip`\n * with `axis: 'x'` vs `'y'`). The dispatcher extracts `opts.params` and\n * passes them to `ImmediateInvoker.run` as its second argument. */\n defaultBinding?: GestureSpec | BoundGesture[];\n /** Names of the deps this action's invoker reads (keys of `DepSchema`).\n * The dispatcher (and `trigger`, when `requires` is present) resolves\n * each name against the `DepRegistry` at invocation time and passes the\n * resulting bag to the invoker. Dev builds warn when the invoker reads a\n * dep it didn't declare here — see `buildDepsFromRequires`. */\n requires?: readonly DepName[];\n /** Inline-SVG icon for palette / toolbar surfaces. Mirrors\n * `ToolPresentation.icon` so a generic `<ActionBar>` can render from\n * action metadata the same way `<ToolPalette>` renders from tool\n * metadata. May be a static `ReactNode` or a function (rare; useful\n * for state-aware icons like a \"lock\" toggle). */\n icon?: ReactNode | (() => ReactNode);\n /** Grouping key for palette/menu surfaces. Free-form string; the kit\n * ships defaults for `'align'` (six edges/centers), `'distribute'`\n * (two axes), and recommends `'pathfinder'` for boolean ops. */\n group?: string;\n /** Display override for the keyboard shortcut. When omitted, palette\n * surfaces derive a label from `defaultBinding` via their own\n * formatter. */\n shortcut?: string;\n /** Pluggable invocation strategy. The gesture dispatcher routes matched\n * bindings through `invoker.start` / `invoker.run` depending on timing.\n * All kit-standard descriptors ship one; consumer-supplied actions\n * without an invoker can still register but won't be triggered. */\n invoker?: Invoker;\n /** When set to `'hotkey'`, this action's `defaultBinding` rides the hotkey\n * `BindingScope` instead of the ambient scope — meaning it beats any\n * active-tool binding on the same input shape. Use for tool-switch\n * shortcuts and global held-key triggers. Default: ambient. */\n scope?: 'hotkey';\n /**\n * @experimental\n * Optional predicate the command palette consults when rendering. Return\n * `true` when the action is currently triggerable. Return a reason string\n * (e.g. `'Selection required'`) when disabled — the palette greys out\n * the row, skips it in keyboard nav, ignores clicks, and shows the\n * reason next to the label. Keystroke dispatch (the registered binding)\n * is unaffected; the action's own `run` should self-guard.\n *\n * **Contract:** must be pure (no side effects), fast (< 4ms in dev), and\n * must not throw. If a call throws or exceeds the budget in dev mode,\n * `evaluateEnabled` logs a one-time warning per action id; throws are\n * caught and treated as disabled with reason `'(predicate threw)'`.\n *\n * Snapshot-on-open semantics: the palette evaluates `enabled` once when\n * opened and does NOT re-evaluate on selection changes while open. Live\n * reactive updates are deferred — palette is short-lived.\n *\n * The reason set is a closed enum — to add a new reason, edit\n * `ActionDisabledReason` and the consumer's display map.\n *\n * The optional `deps` argument is the same bag passed to\n * `ImmediateInvoker.run`; callers (`evaluateEnabled` / the ActionBar) may\n * synthesize it from the surrounding `DepRegistry` so predicates can\n * inspect selection / scene / etc. Predicates that don't need deps just\n * ignore the arg.\n */\n enabled?: (deps?: ActionDeps) => true | ActionDisabledReason;\n /**\n * Declarative eligibility rule, evaluated against the current\n * `RuleCtx` by the dispatcher before invoking `start()`. Omitted =\n * always eligible.\n *\n * Accepts either a fluent `Condition` (callable with `.rule`) or a\n * raw `Rule` tree; the dispatcher normalizes via `.rule` unwrap.\n *\n * Prefer `capability:`-based rules (e.g. `{ capability: 'transforms-selection' }`)\n * over `mode:` rules — capability rules survive new modes being added\n * that allow the same capability.\n */\n eligible?:\n | import('../../features/chrome-caps').Rule\n | import('../../features/chrome-caps').Condition;\n /**\n * CSS cursor shown while the pointer hovers a spot where this action\n * would win the drag. The hover-cursor pump (in `useGestureDispatcher`)\n * runs `Dispatcher.resolveOnly` on each idle pointermove — the same\n * match walk a real pointerdown takes — and applies the winning\n * action's `cursor`, so the hint and the actual click target stay in\n * sync by construction. Omitted = no override (the active tool's\n * `Tool.cursor` shows). Affordance hits are resolved earlier in the\n * pump via `AffordanceRegion.cursor` and never reach this field.\n *\n * Static string only. Prediction runs `enabled()` but cannot run the\n * invoker, so an action that matches yet bails at `start()` (empty\n * handle) may still show its cursor — keep `enabled` accurate for\n * actions that declare one.\n */\n cursor?: string;\n /**\n * CSS cursor shown while THIS action's ongoing handle is in flight —\n * grabbing while panning, `move` while dragging a selection, `crosshair`\n * while pulling a marquee.\n *\n * Separate from `cursor` because the two answer different questions:\n * `cursor` is a prediction (\"a drag from here would pan\"), this is a state\n * (\"you are panning\"). An action can declare either, both, or neither;\n * with only `cursor` set, the hover hint holds for the duration of the\n * gesture.\n *\n * This is where mid-gesture cursors live now. They used to come from the\n * tool side — `ViewportToolDef.engaged.cursor` for a phase-gated string,\n * or a function-form `Tool.cursor` reading the gesture scratch out of the\n * tool-routing dispatcher. Both belonged to a pipeline whose whole job was\n * being taken over by bindings, and neither could describe a cursor for an\n * action a tool doesn't own.\n */\n activeCursor?: string;\n}\n\n/**\n * @experimental\n * Closed enum of reasons an action might report itself as disabled. The\n * consumer (palette, menu, etc.) maps these symbolic values to display\n * strings via its own label map — see `demo/CommandPalette.tsx` for the\n * canonical mapping.\n */\nexport const ActionDisabledReason = {\n SelectionRequired: 'selection-required',\n SceneEmpty: 'scene-empty',\n NotApplicable: 'not-applicable',\n /** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */\n PredicateThrew: 'predicate-threw',\n} as const;\n/** Why an action is unavailable right now. */\nexport type ActionDisabledReason =\n (typeof ActionDisabledReason)[keyof typeof ActionDisabledReason];\n\n/**\n * @experimental\n * Result of evaluating an Action's `enabled` predicate.\n */\nexport interface ActionEnabledResult {\n enabled: boolean;\n reason?: ActionDisabledReason;\n}\n\nconst enabledSlowWarned = new Set<string>();\nconst enabledThrewWarned = new Set<string>();\nconst ENABLED_BUDGET_MS = 4;\n/**\n * @experimental\n * Safely evaluate an action's `enabled` predicate. Returns `{enabled: true}`\n * when no predicate is supplied. Catches throws and treats them as disabled\n * with reason `'(predicate threw)'`. In dev mode, warns once per action id\n * when a single call exceeds 4ms (single frame at 240fps — generous; real\n * predicates should be sub-millisecond).\n */\nexport function evaluateEnabled(\n action: Action,\n deps?: ActionDeps,\n): ActionEnabledResult {\n if (!action.enabled) return { enabled: true };\n const isDev = typeof process !== 'undefined' ? process.env.NODE_ENV !== 'production' : true;\n const now = (typeof performance !== 'undefined' && typeof performance.now === 'function')\n ? () => performance.now()\n : () => Date.now();\n const start = isDev ? now() : 0;\n try {\n const result = action.enabled(deps);\n if (isDev) {\n const elapsed = now() - start;\n if (elapsed > ENABLED_BUDGET_MS && !enabledSlowWarned.has(action.id)) {\n enabledSlowWarned.add(action.id);\n console.warn(\n `weasel actions: enabled() for action \"${action.id}\" took ${elapsed.toFixed(2)}ms ` +\n `(budget ${ENABLED_BUDGET_MS}ms). The predicate must be pure and fast — see Action.enabled JSDoc.`,\n );\n }\n }\n if (result === true) return { enabled: true };\n return { enabled: false, reason: result };\n } catch (e) {\n if (isDev && !enabledThrewWarned.has(action.id)) {\n enabledThrewWarned.add(action.id);\n console.warn(\n `weasel actions: enabled() for action \"${action.id}\" threw; treating as disabled. ` +\n `The predicate must not throw — see Action.enabled JSDoc.`,\n e,\n );\n }\n return { enabled: false, reason: ActionDisabledReason.PredicateThrew };\n }\n}\n\n/** @internal Test helper: reset warn-once dedup so tests can repro-fire warnings. */\nexport function _resetEnabledWarnsForTests(): void {\n enabledSlowWarned.clear();\n enabledThrewWarned.clear();\n}\n\n/**\n * @experimental\n * Partial override or full descriptor passed via `<SceneCanvas actions={...}>`.\n * `null` disables a default at this id.\n */\nexport type ActionEntry = null | Partial<Action> | Action;\n\n/**\n * @experimental\n * Shape of the `actions` prop on `<SceneCanvas>`. `null` disables all defaults.\n */\nexport type ActionsProp = null | Record<string, ActionEntry>;\n\n/**\n * @experimental\n * Imperative API exposed by `useActionsRegistry()`.\n */\nexport interface ActionsRegistry {\n register(action: Action): () => void;\n unregister(id: string): void;\n list(): readonly Action[];\n /** Fire an immediate-invoker action by id. The optional `params` arg is\n * forwarded to `ImmediateInvoker.run` as its second argument — use it for\n * parametric actions (e.g. `trigger('tool.activate', { toolId: 'rect' })`).\n * Ongoing-invoker actions are not reachable from `trigger`. */\n trigger(id: string, params?: Record<string, unknown>): boolean;\n /**\n * Subscribe to registry mutations. The callback fires after any\n * `register`/`unregister` that changes the version. Returns an\n * unsubscribe function. Designed for `useSyncExternalStore`-driven\n * surfaces (e.g. `<ActionBar>` in `@weasel-js/ui`) that need to\n * re-render when the action set changes.\n */\n subscribe(listener: () => void): () => void;\n\n /**\n * Start an ongoing action driven by UI (color picker, opacity slider).\n * Returns a control object with `update(params)` and `end(reason)`.\n *\n * Returns `null` if no dispatcher is wired into this registry, the\n * action is unknown, or its invoker is not ongoing.\n *\n * See `Dispatcher.beginUiOngoing` for full semantics including\n * auto-commit when a prior UI handle for the same action is in flight.\n */\n begin(id: string, params?: Record<string, unknown>): UiOngoingControl | null;\n\n /** Wire a dispatcher into the registry so `begin()` can delegate to it.\n * Call with `null` to detach. Idempotent. */\n setDispatcher(d: Dispatcher | null): void;\n\n /** Wire a `DepRegistry` into the registry so `trigger()` / `begin()` can\n * resolve action deps even when this provider is mounted ABOVE the dep\n * registry (e.g. a consumer's root `<ActionsProvider>` reused by\n * SceneCanvas's `ActionsProviderIfRoot`). Takes precedence over the dep\n * registry read from context at the provider's own level. Call with\n * `null` to detach. */\n setDepRegistry(r: DepRegistry | null): void;\n}\n\n// ─── Registration-time validation ─────────────────────────────────────────\n\n/** Validate that `id` is usable as an action id in the route grammar.\n * Rejects ids that start with a reserved sigil (would shadow future\n * grammar extensions) and ids that collide with phase keywords (would\n * parse as the bare-phase shorthand). Mirrors `defineTool`'s tool-id\n * validation. */\nfunction validateActionId(id: string): void {\n if (id.length === 0) {\n throw new Error(`weasel: action id may not be empty`);\n }\n if (RESERVED_ID_PREFIXES.has(id[0]!)) {\n throw new Error(\n `weasel: action id \"${id}\" starts with reserved sigil \"${id[0]}\" ` +\n `(reserved set: ${[...RESERVED_ID_PREFIXES].join(' ')})`,\n );\n }\n if (RESERVED_ID_NAMES.has(id)) {\n throw new Error(\n `weasel: action id \"${id}\" collides with a reserved phase keyword ` +\n `(reserved: ${[...RESERVED_ID_NAMES].join(', ')})`,\n );\n }\n}\n\n/** Reject any '&'-channel phase atom in an Action's defaultBinding —\n * actions have no owning tool, so '&' can't resolve. Tool-side bindings\n * (Tool.bindings) are the right place for '&' atoms. */\nfunction validateActionDefaultBinding(action: Action): void {\n const gs = action.defaultBinding;\n if (!gs) return;\n const entries: BoundGesture[] = Array.isArray(gs) ? gs : [gs as BoundGesture];\n for (const entry of entries) {\n const spec = ('kind' in entry ? entry : entry.spec) as GestureSpec;\n const phase: PhaseSpec | undefined = spec.phase;\n if (phase === undefined) continue;\n const atoms: readonly PhaseAtom[] = Array.isArray(phase)\n ? phase\n : [{ channel: '&', phase: phase }];\n for (const atom of atoms) {\n if (atom.channel === '&') {\n throw new Error(\n `weasel: action \"${action.id}\" defaultBinding uses '&' channel ` +\n `which has no owning tool to resolve to. Use a named tool channel ` +\n `(e.g. '[rect:engaged]') or '*' instead, or move this binding to ` +\n `the tool's own Tool.bindings.`,\n );\n }\n }\n }\n}\n\nconst ActionsContext = createContext<ActionsRegistry | null>(null);\n\n/**\n * @experimental\n * Mounts an `ActionsRegistry` for its lifetime. Children call\n * `useActionsRegistry()` or `useAction()` to participate. Mounts no input\n * listener of its own — the gesture dispatcher owns input.\n */\nexport function ActionsProvider({ children }: { children: ReactNode }): ReactElement {\n const actionsRef = useRef<Map<string, Action>>(new Map());\n const versionRef = useRef(0);\n const cachedRef = useRef<readonly Action[]>([]);\n const cachedVerRef = useRef(-1);\n const listenersRef = useRef<Set<() => void>>(new Set());\n\n // Trigger() consults the optional dep registry so actions can be fired\n // imperatively from ActionBar / palette callers — the registered\n // `invoker.run` receives a deps bag built fresh from the registry.\n const depReg = useOptionalDepRegistry();\n const depRegRef = useRef<DepRegistry | null>(depReg);\n depRegRef.current = depReg;\n\n // Dep registry wired via setDepRegistry — set by SceneCanvas's registrar\n // when this provider sits above the dep-registry scope (consumer root\n // <ActionsProvider>). Preferred over the context read above.\n const wiredDepRegRef = useRef<DepRegistry | null>(null);\n\n // Dispatcher ref — wired from SceneCanvas via setDispatcher so that\n // begin() can delegate to beginUiOngoing.\n const dispatcherRef = useRef<Dispatcher | null>(null);\n\n // The legacy keystroke loop that walked every action's\n // `defaultBinding: KeyBinding` and matched against keydown is gone, along\n // with the per-action consumer hooks (`useEscape`, `useDelete`, ...) that\n // carried their own `useKeybinding` listener. Every kit-standard descriptor\n // now routes through the gesture dispatcher via `defaultBinding`.\n\n const registry = useMemo<ActionsRegistry>(() => {\n const snapshot = (): readonly Action[] => {\n const v = versionRef.current;\n if (cachedVerRef.current === v) return cachedRef.current;\n const out = Object.freeze(Array.from(actionsRef.current.values()));\n cachedRef.current = out;\n cachedVerRef.current = v;\n return out;\n };\n const notify = (): void => {\n for (const l of listenersRef.current) {\n try {\n l();\n } catch (err) {\n console.error('weasel ActionsRegistry: subscriber threw', err);\n }\n }\n };\n return {\n register: (action: Action) => {\n validateActionId(action.id);\n validateActionDefaultBinding(action);\n actionsRef.current.set(action.id, action);\n versionRef.current++;\n notify();\n return () => {\n const cur = actionsRef.current.get(action.id);\n // Only unregister if the current entry is still us (last-writer-wins\n // means a later registrant should not be clobbered by our cleanup).\n if (cur === action) {\n actionsRef.current.delete(action.id);\n versionRef.current++;\n notify();\n }\n };\n },\n unregister: (id: string) => {\n if (actionsRef.current.delete(id)) {\n versionRef.current++;\n notify();\n }\n },\n list: () => snapshot(),\n trigger: (id: string, params?: Record<string, unknown>) => {\n const a = actionsRef.current.get(id);\n if (!a) return false;\n try {\n if (a.invoker && a.invoker.timing === 'immediate') {\n const r = wiredDepRegRef.current ?? depRegRef.current;\n // Prefer the action's declared `requires` (same contract the\n // dispatcher uses — shared `buildDepsFromRequires`, including\n // the dev-mode undeclared-read guard); legacy fixed bag\n // otherwise.\n const deps = !r\n ? {}\n : a.requires\n ? buildDepsFromRequires(a, r)\n : {\n selection: r.get('selection' as DepName),\n scene: r.get('scene' as DepName),\n history: r.get('history' as DepName),\n view: r.get('view' as DepName),\n pointer: r.get('pointer' as DepName),\n activeTool: r.get('activeTool' as DepName),\n booleansAdapter: r.get('booleansAdapter' as DepName),\n };\n a.invoker.run(deps as never, params);\n }\n } catch (err) {\n console.error(`weasel ActionsRegistry: action \"${id}\" threw`, err);\n }\n return true;\n },\n subscribe: (listener: () => void) => {\n listenersRef.current.add(listener);\n return () => {\n listenersRef.current.delete(listener);\n };\n },\n setDispatcher: (d: Dispatcher | null) => {\n dispatcherRef.current = d;\n },\n setDepRegistry: (r: DepRegistry | null) => {\n wiredDepRegRef.current = r;\n },\n begin: (id: string, params?: Record<string, unknown>) => {\n const disp = dispatcherRef.current;\n if (!disp) return null;\n const r = wiredDepRegRef.current ?? depRegRef.current;\n const a = actionsRef.current.get(id);\n // Same resolution order as `trigger`: the action's declared `requires`\n // when it has one, the legacy fixed bag otherwise. The fixed bag has no\n // `applyOps`, so the paint actions — which declare it and are the only\n // callers of `begin` — used to fall back to the scene's own history\n // instead of the consumer's.\n const deps = !r\n ? {}\n : a?.requires\n ? buildDepsFromRequires(a, r)\n : {\n selection: r.get('selection' as DepName),\n scene: r.get('scene' as DepName),\n history: r.get('history' as DepName),\n view: r.get('view' as DepName),\n pointer: r.get('pointer' as DepName),\n activeTool: r.get('activeTool' as DepName),\n booleansAdapter: r.get('booleansAdapter' as DepName),\n };\n return disp.beginUiOngoing(id, deps as never, params);\n },\n };\n }, []);\n\n return <ActionsContext.Provider value={registry}>{children}</ActionsContext.Provider>;\n}\n\n/**\n * @experimental\n * Returns the parent `ActionsRegistry`, or `null` when no provider is in scope.\n */\nexport function useActionsRegistry(): ActionsRegistry | null {\n return useContext(ActionsContext);\n}\n\n/**\n * @experimental\n * Register an `Action` for the lifetime of the calling component. No-op when\n * no `ActionsProvider` is in scope. Re-registers on `action` reference change\n * (consumers should memoize stable identities to avoid churn).\n */\nexport function useAction(action: Action): void {\n const reg = useActionsRegistry();\n useEffect(() => {\n if (!reg) return;\n return reg.register(action);\n }, [reg, action]);\n}\n","import type { Tool } from '../../types';\nimport type { GestureSpec, ModSpec, TargetSpec, PhaseSpec } from '@weasel-js/gestures';\nimport type { ParsedModifiers, ModifierKey } from '../routeGrammar';\nimport { getGestureDescriptor, isKnownGestureName, type GestureName } from '../gestures';\n\n/**\n * One row in the route registry — a single `GestureBinding` on one tool,\n * flattened into the route grammar's vocabulary so the inspector, the\n * conflict checker, and `describeRoute` can all read the same shape.\n *\n * Multiple rows can share (gesture, arg, target, modifiers) if different tools\n * declare them; consumers walk the list and group client-side.\n */\nexport interface RegistryEntry {\n toolId: string;\n /** Which phase the binding's `phase` spec restricts it to. `'any'` when it\n * declares none, declares `'*'`, or declares an atom list whose atoms\n * don't agree on one phase — such a binding fires in either phase, and\n * reporting it as `'initial'` made it collide with genuinely-initial\n * bindings in `findConflicts`' bucket key. */\n phase: 'initial' | 'engaged' | 'any';\n /** Structured v3 modifier requirements. Empty object = \"no modifiers\n * held\" (the strict default). */\n modifiers: ParsedModifiers;\n gesture: GestureName;\n /** Resolved arg value for arg-bearing gestures (wheel direction,\n * key name, multiTouchTap fingers). Undefined for gestures whose\n * descriptor has no `arg`. */\n arg: string | undefined;\n /** Target class for hit-testing gestures. `undefined` when the descriptor\n * has `hasTarget: false` or the spec declares no target;\n * {@link PREDICATE_TARGET} when the spec uses a `kindOf` predicate, which\n * the route grammar has no notation for. */\n target: string | undefined;\n /** The action this binding fires. */\n actionId: string;\n /** The `GestureSpec` this row was flattened from, by reference. Reflection\n * consumers that need something the grammar doesn't capture — the\n * specificity tuple, a `kindOf` predicate identity — read it here rather\n * than re-walking `Tool.bindings`. */\n spec: GestureSpec;\n}\n\n/**\n * Stand-in target for a `{ kindOf }` predicate spec.\n *\n * The route grammar can name target *classes* (`empty`, `selected-body`,\n * `kind:rect`) but not arbitrary predicates, so three distinct select-tool\n * bindings all render as this one token. Narrowing it would mean giving the\n * grammar a way to describe a function, which is a real design question and\n * not one this reflection layer should answer by inventing syntax.\n */\nexport const PREDICATE_TARGET = 'predicate';\n\n/** `GestureSpec.kind` → route-grammar gesture name. `multiTouch` has no\n * route-grammar gesture (only its tap synthesis does), so specs of that kind\n * are skipped. */\nconst SPEC_KIND_TO_GESTURE: Record<GestureSpec['kind'], GestureName | undefined> = {\n key: 'keyDown',\n 'key-held': 'keyHeld',\n wheel: 'wheel',\n click: 'click',\n doubleClick: 'dblTap',\n contextMenu: 'contextMenu',\n longPress: 'longPress',\n drag: 'drag',\n pointerDown: 'pointerDown',\n multiTouch: undefined,\n multiTouchTap: 'multiTouchTap',\n drop: 'drop',\n paste: 'paste',\n};\n\n/**\n * Flatten every tool's `bindings` into route-registry rows.\n *\n * This was `buildActionRegistry`, and it walked `ToolDef.initial` /\n * `.engaged` phase tables — the grammar that no longer exists. It was also\n * blind to `Tool.bindings` the entire time the two lived side by side, which\n * is why the inspector under-reported select by more than half. The rename\n * fixes a second thing: it never had anything to do with the Actions\n * Registry, and reading `buildActionRegistry` next to `ActionsRegistry`\n * suggested otherwise.\n */\nexport function buildRouteRegistry(\n tools: readonly Tool<unknown>[],\n): RegistryEntry[] {\n const out: RegistryEntry[] = [];\n for (const tool of tools) {\n for (const binding of tool.bindings ?? []) {\n const entry = entryFor(tool.id, binding.spec, binding.actionId);\n if (entry) out.push(entry);\n }\n }\n return out;\n}\n\nfunction entryFor(\n toolId: string,\n spec: GestureSpec,\n actionId: string,\n): RegistryEntry | null {\n const gesture = SPEC_KIND_TO_GESTURE[spec.kind];\n if (!gesture || !isKnownGestureName(gesture)) return null;\n const descriptor = getGestureDescriptor(gesture);\n return {\n toolId,\n actionId,\n gesture,\n phase: phaseOf('phase' in spec ? spec.phase : undefined),\n modifiers: parseModSpec('mods' in spec ? spec.mods : undefined),\n arg: descriptor.arg ? argOf(spec, descriptor.arg.default) : undefined,\n target: descriptor.hasTarget\n ? targetOf('target' in spec ? spec.target : undefined)\n : undefined,\n spec,\n };\n}\n\n/** Collapse a `PhaseSpec` to the single phase a binding is restricted to, or\n * `'any'` when it isn't restricted to one.\n *\n * `PhaseAtom.channel` says which channel's phase state the binding reads,\n * not which phase it fires in, so it plays no part in this collapse — only\n * the set of distinct `atom.phase` values matters. */\nfunction phaseOf(phase: PhaseSpec | undefined): 'initial' | 'engaged' | 'any' {\n if (phase === undefined || phase === '*') return 'any';\n if (phase === 'initial' || phase === 'engaged') return phase;\n const distinct = new Set(phase.map((atom) => atom.phase));\n if (distinct.size !== 1) return 'any';\n const only = [...distinct][0];\n return only === 'initial' || only === 'engaged' ? only : 'any';\n}\n\n/** `ModSpec` (per-key tri-state) → `ParsedModifiers`. `false` and absent both\n * mean \"must not be held\", which the parsed form spells as an absent key. */\nfunction parseModSpec(mods: ModSpec | undefined): ParsedModifiers {\n if (!mods) return {};\n const out: ParsedModifiers = {};\n for (const [name, req] of Object.entries(mods)) {\n if (req === true) out[name as ModifierKey] = 'required';\n else if (req === 'optional') out[name as ModifierKey] = 'optional';\n }\n return out;\n}\n\nfunction targetOf(target: TargetSpec | undefined): string | undefined {\n if (target === undefined) return undefined;\n return typeof target === 'string' ? target : PREDICATE_TARGET;\n}\n\nfunction argOf(spec: GestureSpec, fallback: string | undefined): string | undefined {\n if ('key' in spec) {\n return Array.isArray(spec.key) ? spec.key.join('|') : spec.key;\n }\n if ('fingers' in spec) return String(spec.fingers);\n if ('direction' in spec) return spec.direction ?? fallback;\n if ('types' in spec) return spec.types?.length ? spec.types.join('|') : fallback;\n return fallback;\n}\n\n// Re-export for downstream consumers.\nexport { getGestureDescriptor };\nexport type { GestureName };\n","import type { Tool } from '../../types';\nimport { actionBindings, type Action } from '../../../interactions/actions/registry';\nimport type { ParsedModifiers } from '../routeGrammar';\nimport { canonicalModifiers, formatRoute } from '../routeGrammar';\nimport { buildRouteRegistry, PREDICATE_TARGET, type RegistryEntry, type GestureName } from './registry';\n\n/** Two or more tools declare the same exact (phase, gesture, arg, target,\n * modifiers) tuple — the dispatcher's slot precedence picks one\n * arbitrarily (well, deterministically by slot order, but the author\n * probably didn't intend the duplication). */\nexport interface Conflict {\n phase: 'initial' | 'engaged' | 'any';\n gesture: GestureName;\n arg: string | undefined;\n target: string | undefined;\n modifiers: ParsedModifiers;\n /** All tool ids that registered the same tuple. At least 2 by\n * construction. Order matches the input tools[] order. */\n toolIds: string[];\n}\n\n/** Detect exact-tuple overlaps across a tool registration set.\n *\n * Intentionally NOT flagged:\n * - Broad vs. narrow targets (e.g. an untargeted `click` alongside\n * `click` on `empty`) — the dispatcher's specificity ordering resolves\n * those cleanly, and the broad one is usually the intended fallback.\n * - Different modifier requirements on the same target — they fire on\n * different inputs.\n * - A binding whose action declines via `enabled()` so a lower-priority\n * one can take the gesture. Detecting that intent would mean evaluating\n * the action; consumers can suppress known-intentional compositions in\n * their UI layer.\n *\n * - Two `{ kindOf }` predicate targets. The route grammar renders every\n * predicate as the single token {@link PREDICATE_TARGET}, so bucketing on\n * the rendered target alone reported select's `resize` / `rotate` / `move`\n * drags — three genuinely different predicates — as a three-way conflict.\n * Predicate entries bucket by function identity instead, which means two\n * *separately written but equivalent* predicates go unflagged. That's the\n * right way to be wrong here: this check has to be silent when nothing is\n * wrong or nobody will keep it on.\n *\n * Note that two bindings sharing a tuple on the SAME tool are now possible\n * (bindings are an array, where phase tables were objects with unique\n * keys) — so a conflict may name one tool twice.\n *\n * This is the raw same-tuple detector. Feeding it a whole tool *registry*\n * over-reports, because registry tools take turns in the active slot and\n * can't collide with each other — see {@link findScopedConflicts}.\n */\nexport function findConflicts(\n tools: readonly Tool<unknown>[],\n): Conflict[] {\n const entries = buildRouteRegistry(tools);\n const groups = new Map<string, RegistryEntry[]>();\n for (const entry of entries) {\n const key = `${entry.phase}|${entry.gesture}|${entry.arg ?? ''}|${targetKey(entry)}|${canonicalModifiers(entry.modifiers)}`;\n const bucket = groups.get(key);\n if (bucket) bucket.push(entry);\n else groups.set(key, [entry]);\n }\n const conflicts: Conflict[] = [];\n for (const bucket of groups.values()) {\n if (bucket.length < 2) continue;\n const first = bucket[0];\n conflicts.push({\n phase: first.phase,\n gesture: first.gesture,\n arg: first.arg,\n target: first.target,\n modifiers: first.modifiers,\n toolIds: bucket.map((e) => e.toolId),\n });\n }\n return conflicts;\n}\n\n/** Bucket key for an entry's target slot. Predicate targets all render as\n * the same grammar token, so they're keyed by the predicate's identity —\n * two different functions are two different targets. */\nfunction targetKey(entry: RegistryEntry): string {\n if (entry.target !== PREDICATE_TARGET) return entry.target ?? '';\n const spec = entry.spec as { target?: unknown };\n const pred = spec.target;\n if (typeof pred !== 'object' || pred === null) return PREDICATE_TARGET;\n return `${PREDICATE_TARGET}#${predicateId(pred)}`;\n}\n\nconst PREDICATE_IDS = new WeakMap<object, number>();\nlet nextPredicateId = 0;\n\nfunction predicateId(pred: object): number {\n let id = PREDICATE_IDS.get(pred);\n if (id === undefined) {\n id = nextPredicateId++;\n PREDICATE_IDS.set(pred, id);\n }\n return id;\n}\n\n/**\n * The tool scopes as the dispatcher sees them — which is what decides whether\n * two same-tuple bindings can actually collide.\n */\nexport interface ToolScopes {\n /** Tools eligible for the active slot, keyed by id or as a flat list. */\n registry: readonly Tool<unknown>[] | Readonly<Record<string, Tool<unknown>>>;\n /** Always-on tools. Every one of these is live at once. */\n ambient?: readonly Tool<unknown>[];\n /** Registered actions. Their `defaultBinding`s assemble at ambient scope\n * (hotkey scope when `Action.scope` says so), alongside the tools above. */\n actions?: readonly Action[];\n}\n\n/** An action's `defaultBinding`s in the shape the route registry reads, so a\n * tool-vs-action collision buckets with the tool-vs-tool ones. */\nfunction actionAsTool(action: Action): Tool<unknown> {\n return { id: action.id, eligibility: {}, bindings: actionBindings(action) };\n}\n\n/**\n * Detect the same-tuple overlaps that are *reachable* — the ones where the\n * dispatcher really does fall back on declaration order.\n *\n * `matchSorted` walks scopes in strict priority (hotkey > active > ambient)\n * and only sorts by specificity *within* a scope. So a cross-scope tie isn't\n * a tie at all: an ambient tool losing a tuple to the active tool is the\n * documented design, not an accident. Likewise two registry tools sharing a\n * tuple — `rect` and `ellipse` both binding a bare `drag` — can never both be\n * in the active slot, so they never compete.\n *\n * What's left, and what this reports:\n * - a single tool colliding with **itself** (possible since bindings became\n * an array), which is ambiguous in whichever slot it occupies;\n * - two **ambient** tools, all of which are live simultaneously and are\n * ordered only by registration;\n * - two **hotkey-capable** tools, which can stack.\n *\n * Registered actions join the same two buckets: their `defaultBinding`s\n * assemble at ambient scope (hotkey scope when `Action.scope` says so), so an\n * ambient tool and an action claiming one tuple really do fall back on\n * declaration order.\n */\nexport function findScopedConflicts(scopes: ToolScopes): Conflict[] {\n const registry = Array.isArray(scopes.registry)\n ? (scopes.registry as readonly Tool<unknown>[])\n : Object.values(scopes.registry as Readonly<Record<string, Tool<unknown>>>);\n const actions = scopes.actions ?? [];\n const ambientActions = actions.filter((a) => a.scope !== 'hotkey').map(actionAsTool);\n const hotkeyActions = actions.filter((a) => a.scope === 'hotkey').map(actionAsTool);\n const ambient = [...(scopes.ambient ?? []), ...ambientActions];\n\n const out: Conflict[] = [];\n const seen = new Set<string>();\n const add = (conflicts: readonly Conflict[]): void => {\n for (const c of conflicts) {\n const key = `${c.phase}|${c.gesture}|${c.arg ?? ''}|${c.target ?? ''}`\n + `|${canonicalModifiers(c.modifiers)}|${c.toolIds.join(',')}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(c);\n }\n };\n\n // Self-collisions: every tool, whichever slot it lands in.\n for (const tool of [...registry, ...(scopes.ambient ?? [])]) add(findConflicts([tool]));\n // Ambient tools and ambient-scope actions are all live together.\n if (ambient.length > 1) add(findConflicts(ambient));\n // Hotkey-capable tools can stack on each other. `hotkey` is declared on the\n // authored `ToolDef`, not carried onto the runtime `Tool` — `Tool.def` is\n // the reflection handle for exactly this kind of read, and it's typed\n // `unknown` so consumers cast at the use site.\n const hotkey = [\n ...registry.filter((t) => (t.def as { hotkey?: unknown } | undefined)?.hotkey !== undefined),\n ...hotkeyActions,\n ];\n if (hotkey.length > 1) add(findConflicts(hotkey));\n\n return out;\n}\n\n/**\n * Render a conflict as one line of human-readable text.\n *\n * The tuple is printed through {@link formatRoute}, so the message names the\n * collision in the same grammar the author wrote the binding in — modulo the\n * phase slot, which prints as a bare `'&'`-channel atom because the bucket key\n * collapses channel-bearing phase specs (`sel:engaged` and `&:engaged` collide\n * with each other, and the collapse is what made them collide).\n */\nexport function formatConflict(conflict: Conflict): string {\n const route = formatRoute({\n phases: [{ channel: '&', phase: conflict.phase === 'any' ? '*' : conflict.phase }],\n gesture: conflict.gesture,\n arg: conflict.arg,\n target: conflict.target,\n modifiers: conflict.modifiers,\n });\n return `${route} — declared by ${conflict.toolIds.join(', ')}`;\n}\n\n/**\n * Detect reachable route conflicts in an assembled tool set and report each one.\n *\n * This is the wiring `findConflicts` spent its first life without: the kit\n * could detect the one class of genuine routing ambiguity it has and never\n * looked. Call it once wherever a tool set is assembled (`useTools` does),\n * behind a `process.env.NODE_ENV !== 'production'` guard.\n *\n * **Warn, never throw.** A conflict between a consumer's tool and a kit tool\n * is a design question — sometimes deliberate, since the loser can still take\n * the gesture by declining through `enabled()` — and exploding in a running\n * app is the wrong way to raise it. A conflict between two *kit* tools is\n * always a bug, but the place to fail on that is the kit's own test suite\n * (`canvas/SceneCanvas.routeConflicts.test.tsx`), not a consumer's console.\n *\n * @returns The conflicts found, so callers can dedupe repeat reports.\n */\nexport function reportRouteConflicts(\n scopes: ToolScopes,\n warn: (message: string) => void = (m) => console.warn(m),\n): Conflict[] {\n const conflicts = findScopedConflicts(scopes);\n for (const c of conflicts) {\n warn(\n `[weasel] route conflict: two bindings declare the same (phase, gesture, arg, target, modifiers) tuple `\n + `in the same scope, so declaration order alone decides which one fires.\\n ${formatConflict(c)}`,\n );\n }\n return conflicts;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/ops/transform.ts","../src/core/ops/reparent.ts","../src/interactions/scratchKey.ts","../src/interactions/gestures/shared/snap.ts"],"names":[],"mappings":"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","file":"chunk-CSMF654J.js","sourcesContent":["import type { Op } from './types';\nimport { registerOpFactory } from './registry';\n\ninterface TransformAdapter<TPose> {\n setPose(id: string, pose: TPose): void;\n}\n\n/** @internal */\ninterface TransformArgs<TPose> {\n id: string;\n from: TPose;\n to: TPose;\n label?: string;\n coalesceKey?: string;\n}\n\n/** Op: set an object's pose, inverting back to `from`. `coalesceKey` defaults to\n * `transform:${id}` so successive pose writes to the same node (drag-move,\n * resize, repeated nudges) merge into one undo entry within the history's\n * coalesce window. Pass an explicit key (or a unique one) to opt out. */\nexport function createTransformOp<TPose>(args: TransformArgs<TPose>): Op {\n const { id, from, to, label, coalesceKey = `transform:${id}` } = args;\n return {\n name: 'transform',\n args: { id, from, to, label, coalesceKey },\n label,\n coalesceKey,\n apply(adapter) {\n // Always call setPose so consumers that inspect op behavior (tests,\n // overlays) see a consistent \"this op writes (id, to)\" signal. When\n // from and to are structurally identical, additionally report a\n // no-op to history so the entry can be skipped from the undo stack.\n // setPose(id, to) is idempotent in this branch — adapters write the\n // same pose they already had.\n (adapter as TransformAdapter<TPose>).setPose(id, to);\n if (poseShallowEqual(from, to)) return false;\n return undefined;\n },\n invert() {\n return createTransformOp<TPose>({ id, from: to, to: from, label, coalesceKey });\n },\n };\n}\n\nregisterOpFactory<TransformArgs<unknown>>('transform', (args) => createTransformOp(args));\n\nfunction poseShallowEqual<TPose>(a: TPose, b: TPose): boolean {\n if (a === b) return true;\n if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;\n const ka = Object.keys(a as object);\n const kb = Object.keys(b as object);\n if (ka.length !== kb.length) return false;\n for (const k of ka) {\n if ((a as Record<string, unknown>)[k] !== (b as Record<string, unknown>)[k]) return false;\n }\n return true;\n}\n","import type { Op } from './types';\nimport { registerOpFactory } from './registry';\n\ninterface ReparentAdapter {\n setParent(id: string, parentId: string | null): void;\n}\n\n/** @internal */\ninterface ReparentArgs {\n id: string;\n fromParentId: string | null;\n toParentId: string | null;\n label?: string;\n coalesceKey?: string;\n}\n\n/**\n * Op: change `id`'s parent, inverting back to the prior parent.\n *\n * `coalesceKey` defaults to `reparent:${id}` so successive reparents of the\n * same id batch-merge cleanly.\n */\nexport function createReparentOp(args: ReparentArgs): Op {\n const { id, fromParentId, toParentId, label = 'Reparent', coalesceKey = `reparent:${id}` } = args;\n return {\n name: 'reparent',\n args: { id, fromParentId, toParentId, label, coalesceKey },\n label,\n coalesceKey,\n apply(adapter) {\n (adapter as ReparentAdapter).setParent(id, toParentId);\n },\n invert() {\n return createReparentOp({\n id,\n fromParentId: toParentId,\n toParentId: fromParentId,\n label,\n coalesceKey,\n });\n },\n };\n}\n\nregisterOpFactory<ReparentArgs>('reparent', (args) => createReparentOp(args));\n","/**\n * Typed keys for the gesture/behavior scratch store.\n *\n * Gestures expose `ctx.scratch: Record<string, unknown>` as a per-gesture\n * mutable bag that behaviors use to stash state across `onDown` / `onMove`\n * / `onEnd` calls. The store is `unknown`-valued because behaviors are\n * tool-agnostic — a single behavior may run under different tools with\n * different scratch shapes. Without a typing convention every read site\n * has to `(ctx.scratch as <some shape>)[KEY]`, and a writer changing the\n * shape can silently break readers with no compile-time signal.\n *\n * `ScratchKey<T>` is a phantom-typed string that carries its payload type\n * via a branded interface. `getScratch` and `setScratch` use the brand to\n * narrow / constrain at the typing layer; at runtime the key is just its\n * underlying string and the store remains a plain `Record`.\n *\n * @example\n * ```ts\n * // Module-level (or behavior-level) constant, shared by writer + reader:\n * const LASSO_VERTICES = scratchKey<readonly { x: number; y: number }[]>('lasso.vertices');\n *\n * // Writer (a tool's onMove):\n * setScratch(ctx.scratch, LASSO_VERTICES, vertices);\n *\n * // Reader (a behavior's onEnd):\n * const vertices = getScratch(ctx.scratch, LASSO_VERTICES) ?? [];\n * // ^? readonly { x: number; y: number }[] | undefined\n * ```\n */\n\n/** Phantom-typed key for a scratch slot. The `__scratchKeyPayload` phantom\n * field carries the value type without occupying any runtime memory. */\nexport interface ScratchKey<T> {\n readonly name: string;\n /** Phantom — never read at runtime. Only present so TypeScript can\n * recover `T` at use sites. */\n readonly __scratchKeyPayload?: (_: T) => T;\n}\n\n/** Compatible scratch store shape. The kit's gesture context's `scratch`\n * field already satisfies this; consumers don't need to construct one. */\nexport type ScratchStore = Record<string, unknown>;\n\n/**\n * Make a typed scratch key. Use module-level constants so the same key\n * is shared by writer and reader.\n *\n * Namespace the name by behavior or feature to avoid collisions\n * (`'behavior.field'` is a good convention). Two keys with the same\n * `name` refer to the same slot at runtime — the type parameter is a\n * compile-time contract, not a uniqueness guarantee.\n */\nexport function scratchKey<T>(name: string): ScratchKey<T> {\n return { name };\n}\n\n/** Read a typed slot from the scratch store. Returns `undefined` if\n * nothing has been written under the key. */\nexport function getScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n): T | undefined {\n return store[key.name] as T | undefined;\n}\n\n/** Write a value to a typed slot. Overwrites whatever was there. */\nexport function setScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n value: T,\n): void {\n store[key.name] = value;\n}\n\n/** Remove a slot from the scratch store. Returns `true` if it was set,\n * `false` if it wasn't. */\nexport function deleteScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n): boolean {\n if (Object.prototype.hasOwnProperty.call(store, key.name)) {\n delete store[key.name];\n return true;\n }\n return false;\n}\n","import type { MoveBehavior, ModifierState, SnapStrategy } from '../types';\nimport {\n RECT_ORIGIN_PROJECTION,\n type OriginProjection,\n} from './strategies/grid';\n\ntype ModKey = keyof ModifierState;\n\n/**\n * Generic snap behavior factory: wraps a `SnapStrategy` and returns a\n * `MoveBehavior` whose `onMove` returns a uniform `GroupTransform`.\n *\n * Implementation:\n * 1. Compute the primary's proposed pose by applying the gesture's\n * `transform` (translate) to `origin.get(primaryId)`.\n * 2. Call `strategy.snap(proposed)`. If null, no-op.\n * 3. Derive the snapped delta via the `OriginProjection`:\n * dx = origin(snapped).x - origin(originPose).x\n * ...so the gesture applies the same delta to every dragged id.\n *\n * The pose-shape-aware delta extraction lives here — gestures stay pose-shape\n * agnostic. Default projection is `{x, y}`-bearing (rect / path / polygon).\n * Pass `origin` for exotic poses.\n */\nexport function snap<TPose>(\n strategy: SnapStrategy<TPose>,\n opts: { bypassKey?: ModKey; origin?: OriginProjection<TPose> } = {},\n): MoveBehavior<TPose> {\n const { bypassKey } = opts;\n const proj: OriginProjection<TPose> = opts.origin\n ?? (RECT_ORIGIN_PROJECTION as unknown as OriginProjection<TPose>);\n return {\n onMove(ctx, transform) {\n if (bypassKey && ctx.modifiers[bypassKey]) return;\n // The new contract: behaviors see a GroupTransform, not a pose. But\n // strategies still operate on poses. Reconstruct the proposed pose\n // by applying the (translate) transform to the primary's origin.\n if (transform.kind !== 'translate') return;\n const primaryId = ctx.draggedIds[0];\n if (primaryId === undefined) return;\n const originPose = ctx.origin.get(primaryId);\n if (originPose === undefined) return;\n const proposed = proj.translate(originPose, transform.dx, transform.dy);\n const snapped = strategy.snap(proposed, ctx);\n if (snapped === null) return;\n const o0 = proj.getOrigin(originPose);\n const o1 = proj.getOrigin(snapped);\n return {\n transform: { kind: 'translate', dx: o1.x - o0.x, dy: o1.y - o0.y },\n };\n },\n };\n}\n"]}
@@ -1,47 +0,0 @@
1
- // src/core/ops/registry.ts
2
- var FACTORIES = /* @__PURE__ */ new Map();
3
- function registerOpFactory(name, build) {
4
- FACTORIES.set(name, build);
5
- }
6
- function rebuildOp(name, args) {
7
- const f = FACTORIES.get(name);
8
- return f ? f(args) : null;
9
- }
10
-
11
- // src/core/ops/create.ts
12
- function createInsertOp(args) {
13
- const { node, label, index } = args;
14
- return {
15
- name: "insert",
16
- args: { node, label, index },
17
- label,
18
- apply(adapter) {
19
- adapter.insertNode(node, index);
20
- },
21
- invert() {
22
- return createDeleteOp({ node, label, index: index ?? -1 });
23
- }
24
- };
25
- }
26
- registerOpFactory("insert", (args) => createInsertOp(args));
27
-
28
- // src/core/ops/delete.ts
29
- function createDeleteOp(args) {
30
- const { node, label, index } = args;
31
- return {
32
- name: "delete",
33
- args: { node, label, index },
34
- label,
35
- apply(adapter) {
36
- adapter.removeNode(node.id);
37
- },
38
- invert() {
39
- return createInsertOp({ node, label, index });
40
- }
41
- };
42
- }
43
- registerOpFactory("delete", (args) => createDeleteOp(args));
44
-
45
- export { createDeleteOp, createInsertOp, rebuildOp, registerOpFactory };
46
- //# sourceMappingURL=chunk-GVCNT7UH.js.map
47
- //# sourceMappingURL=chunk-GVCNT7UH.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/ops/registry.ts","../src/core/ops/create.ts","../src/core/ops/delete.ts"],"names":[],"mappings":";AAeA,IAAM,SAAA,uBAAgB,GAAA,EAAqB;AAQpC,SAAS,iBAAA,CAAqB,MAAc,KAAA,EAA8B;AAC/E,EAAA,SAAA,CAAU,GAAA,CAAI,MAAM,KAAgB,CAAA;AACtC;AAKO,SAAS,SAAA,CAAU,MAAc,IAAA,EAA0B;AAChE,EAAA,MAAM,CAAA,GAAI,SAAA,CAAU,GAAA,CAAI,IAAI,CAAA;AAC5B,EAAA,OAAO,CAAA,GAAI,CAAA,CAAE,IAAI,CAAA,GAAI,IAAA;AACvB;;;ACNO,SAAS,eAA6C,IAAA,EAAmC;AAC9F,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,KAAA,EAAM,GAAI,IAAA;AAC/B,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,IACN,IAAA,EAAM,EAAE,IAAA,EAAM,KAAA,EAAO,KAAA,EAAM;AAAA,IAC3B,KAAA;AAAA,IACA,MAAM,OAAA,EAAS;AACb,MAAC,OAAA,CAAiC,UAAA,CAAW,IAAA,EAAM,KAAK,CAAA;AAAA,IAC1D,CAAA;AAAA,IACA,MAAA,GAAS;AAMP,MAAA,OAAO,eAAe,EAAE,IAAA,EAAM,OAAO,KAAA,EAAO,KAAA,IAAS,IAAI,CAAA;AAAA,IAC3D;AAAA,GACF;AACF;AAEA,iBAAA,CAA8C,QAAA,EAAU,CAAC,IAAA,KAAS,cAAA,CAAe,IAAI,CAAC,CAAA;;;ACzB/E,SAAS,eAA6C,IAAA,EAA6B;AACxF,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,KAAA,EAAM,GAAI,IAAA;AAC/B,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,IACN,IAAA,EAAM,EAAE,IAAA,EAAM,KAAA,EAAO,KAAA,EAAM;AAAA,IAC3B,KAAA;AAAA,IACA,MAAM,OAAA,EAAS;AACb,MAAC,OAAA,CAA0B,UAAA,CAAW,IAAA,CAAK,EAAE,CAAA;AAAA,IAC/C,CAAA;AAAA,IACA,MAAA,GAAS;AACP,MAAA,OAAO,cAAA,CAAe,EAAE,IAAA,EAAM,KAAA,EAAO,OAAO,CAAA;AAAA,IAC9C;AAAA,GACF;AACF;AAEA,iBAAA,CAA8C,QAAA,EAAU,CAAC,IAAA,KAAS,cAAA,CAAe,IAAI,CAAC,CAAA","file":"chunk-GVCNT7UH.js","sourcesContent":["// Op-factory registry — pairs a stable `name` (stamped onto each op by its\n// factory) with a `(args) => Op` rebuilder so a `{ name, args }` snapshot\n// can be reconstituted into a live `Op` whose `apply`/`invert` closures\n// behave identically to the original.\n//\n// The kit calls `registerOpFactory` at module-init time for every built-in\n// factory (see `./create.ts`, `./delete.ts`, `./transform.ts`, etc.).\n// Consumers wanting their custom ops to survive `History.serialize()` /\n// `History.restore()` register their factories at app bootstrap, before\n// `createHistory` is called.\n\nimport type { Op } from './types';\n\ntype Builder = (args: unknown) => Op;\n\nconst FACTORIES = new Map<string, Builder>();\n\n/** Register an op-factory builder under a stable name. The builder receives\n * the serialized `args` and must return an `Op` equivalent to one freshly\n * constructed at the original call site (`apply` and `invert` behave the\n * same). Idempotent on the same `(name, build)` pair; a second registration\n * with a different builder silently overwrites — kit modules import each\n * factory exactly once at init, so this never races in practice. */\nexport function registerOpFactory<A>(name: string, build: (args: A) => Op): void {\n FACTORIES.set(name, build as Builder);\n}\n\n/** Rebuild an op from its serialized `(name, args)`. Returns `null` when\n * `name` isn't registered — callers handle this (typically by substituting\n * a no-op placeholder so stack ordering survives across kit version skew). */\nexport function rebuildOp(name: string, args: unknown): Op | null {\n const f = FACTORIES.get(name);\n return f ? f(args) : null;\n}\n\n/** Test-only: drop all registrations. Not exported from the kit barrel — the\n * built-in factories self-register at module init, so production code never\n * needs to clear the table. Exposed for unit tests that want to assert the\n * unknown-name fallback path. */\nexport function _clearOpFactoriesForTest(): void {\n FACTORIES.clear();\n}\n\n/** Test-only: read-only snapshot of currently-registered names. */\nexport function _registeredOpNamesForTest(): string[] {\n return [...FACTORIES.keys()];\n}\n","import type { Op } from './types';\nimport { createDeleteOp } from './delete';\nimport { registerOpFactory } from './registry';\n\ninterface InsertAdapter<TNode> {\n /** When `index` is supplied, insert at that position; adapters that ignore\n * the second arg (or don't expose it) fall back to their default\n * placement (typically append-to-end). */\n insertNode(node: TNode, index?: number): void;\n}\n\n/** Type alias for ops produced by `createInsertOp`. Carries no extra type info today;\n * exists so consumers can name the op type when needed. */\nexport type InsertOp = Op;\n\n/** @internal */\ninterface InsertArgs<TNode extends { id: string }> {\n node: TNode;\n label?: string;\n /** Original z-index in the host array. Forwarded to\n * `adapter.insertNode(node, index)` when present so undo of a multi-delete\n * batch restores paint order instead of reversing it. Optional — adapters\n * that don't honor it still work. */\n index?: number;\n}\n\n/** Op: insert `node` into the scene; inverts to a delete of the same id. */\nexport function createInsertOp<TNode extends { id: string }>(args: InsertArgs<TNode>): InsertOp {\n const { node, label, index } = args;\n return {\n name: 'insert',\n args: { node, label, index },\n label,\n apply(adapter) {\n (adapter as InsertAdapter<TNode>).insertNode(node, index);\n },\n invert() {\n // Insert's `index` is optional (fresh-insert callsites have no\n // meaningful original position); the inverted Delete needs *some*\n // index. Default to -1 — the symmetry only matters when the Insert\n // itself was a re-insert from a prior Delete, in which case the\n // index round-trips correctly.\n return createDeleteOp({ node, label, index: index ?? -1 });\n },\n };\n}\n\nregisterOpFactory<InsertArgs<{ id: string }>>('insert', (args) => createInsertOp(args));\n","import type { Op } from './types';\nimport { createInsertOp } from './create';\nimport { registerOpFactory } from './registry';\n\ninterface DeleteAdapter {\n removeNode(id: string): void;\n}\n\n/** @internal */\ninterface DeleteArgs<TNode extends { id: string }> {\n node: TNode;\n label?: string;\n /** Z-index the node occupies in its host array at the moment of delete.\n * Forwarded through `invert()` to the re-insert so undo restores paint\n * order — without this, undo of a multi-delete batch fully reverses\n * the stack. Required: every delete callsite knows where the node sits\n * (it has to, to remove it), so there's no good reason to drop the\n * data on the floor. */\n index: number;\n}\n\n/** Op: remove `node` from the scene; inverts to a re-insert of the captured node. */\nexport function createDeleteOp<TNode extends { id: string }>(args: DeleteArgs<TNode>): Op {\n const { node, label, index } = args;\n return {\n name: 'delete',\n args: { node, label, index },\n label,\n apply(adapter) {\n (adapter as DeleteAdapter).removeNode(node.id);\n },\n invert() {\n return createInsertOp({ node, label, index });\n },\n };\n}\n\nregisterOpFactory<DeleteArgs<{ id: string }>>('delete', (args) => createDeleteOp(args));\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/renderer/textures/registerTexture.ts","../src/features/patterns/tileGeometry.ts","../src/features/patterns/index.ts","../src/features/patterns/patterns-builtin.ts"],"names":[],"mappings":"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","file":"chunk-HGF5X6MZ.js","sourcesContent":["/**\n * registerTexture — accepts an image source, assigns an opaque id, stores in\n * a module-level registry. Actual GL upload happens lazily at draw time in\n * drawShader() via GLTextureCache.upload (which is idempotent).\n *\n * Lifecycle: textures live for the renderer's lifetime. No unregister in v1.\n *\n * Convention §9: this registry stores image data only — no per-renderer state.\n * Each WeaselRenderer's GLTextureCache does its own dedup via has(id).\n */\n\nexport interface TextureHandle {\n readonly id: string;\n}\n\nexport interface TextureEntry {\n source: HTMLImageElement | ImageBitmap;\n}\n\nlet counter = 0;\nlet registry = new Map<string, TextureEntry>();\n\n/** @internal Test helper — do not call from product code. */\nexport function _resetTextureRegistryForTests(): void {\n registry = new Map();\n counter = 0;\n}\n\nexport function getTexture(id: string): TextureEntry | null {\n return registry.get(id) ?? null;\n}\n\n/**\n * Register an image for use as a shader texture uniform.\n *\n * Returns a TextureHandle whose `id` can be passed as a ShaderUniform value.\n * The handle is valid for the lifetime of the renderer it will be used with.\n *\n * @param image The image to register. HTMLImageElement must be fully loaded.\n * @returns Opaque TextureHandle.\n */\nexport function registerTexture(\n image: HTMLImageElement | ImageBitmap,\n): TextureHandle {\n const id = `tex_${++counter}`;\n registry.set(id, { source: image });\n return { id };\n}\n","/**\n * The shapes a built-in tile is made of, as data.\n *\n * One description, two renderers: `patterns-builtin` strokes/fills these to\n * an `OffscreenCanvas` to build the GL texture, and `@weasel-js/svg` maps\n * them to `<pattern>` children. Duplicating the tile drawing in the\n * serializer would let the raster and vector forms drift apart silently.\n */\n\nimport type { TilePatternSpec } from '../../core/paint-types';\n\n/** One primitive within a pattern tile, in tile-local coordinates. */\nexport type TileShape =\n | { kind: 'line'; x1: number; y1: number; x2: number; y2: number; width: number; color: string }\n | { kind: 'circle'; cx: number; cy: number; r: number; color: string }\n | { kind: 'ellipse'; cx: number; cy: number; rx: number; ry: number; rotation: number; color: string }\n | { kind: 'rect'; x: number; y: number; width: number; height: number; color: string };\n\n/** A built-in tile's shapes plus the edge length they're drawn within. */\nexport interface TileGeometry {\n size: number;\n shapes: TileShape[];\n}\n\n/** Defaults per tile kind, matching each factory's signature. */\nexport function tileSize(spec: TilePatternSpec): number {\n if (spec.size !== undefined) return spec.size;\n switch (spec.tile) {\n case 'hatch': return 5;\n case 'crosshatch': return 6;\n case 'dots': return 6;\n case 'chunks': return 48;\n default: return 8;\n }\n}\n\n/** Seeded PRNG (mulberry32) — `chunks` places its scatter with this, so the\n * same `seed` yields the same tile in both the raster and vector paths. */\nfunction mulberry32(seed: number): () => number {\n let s = seed | 0;\n return () => {\n s = (s + 0x6D2B79F5) | 0;\n let t = Math.imul(s ^ (s >>> 15), 1 | s);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/** Resolve a pattern spec to the shapes that make up one tile. Consumers that\n * want to draw a pattern swatch themselves — a picker, an SVG export — read\n * this rather than rasterizing a texture. */\nexport function tileGeometry(spec: TilePatternSpec): TileGeometry {\n const s = tileSize(spec);\n const { color } = spec;\n\n switch (spec.tile) {\n case 'hatch': {\n const width = spec.lineWidth ?? 1;\n return { size: s, shapes: forwardDiagonals(s, width, color) };\n }\n case 'crosshatch': {\n const width = spec.lineWidth ?? 0.8;\n return {\n size: s,\n shapes: [...forwardDiagonals(s, width, color), ...backwardDiagonals(s, width, color)],\n };\n }\n case 'dots': {\n const r = spec.radius ?? 1;\n return { size: s, shapes: [{ kind: 'circle', cx: s / 2, cy: s / 2, r, color }] };\n }\n case 'chunks': {\n const density = spec.density ?? 0.35;\n const chunkSize = spec.chunkSize ?? 3;\n const rand = mulberry32(spec.seed ?? 42);\n const count = Math.round((s * s * density) / (chunkSize * chunkSize));\n const shapes: TileShape[] = [];\n if (spec.bg !== undefined) {\n shapes.push({ kind: 'rect', x: 0, y: 0, width: s, height: s, color: spec.bg });\n }\n for (let i = 0; i < count; i++) {\n const cx = rand() * s;\n const cy = rand() * s;\n const w = chunkSize * (0.5 + rand());\n const h = chunkSize * (0.5 + rand());\n const rotation = rand() * Math.PI;\n shapes.push({ kind: 'ellipse', cx, cy, rx: w / 2, ry: h / 2, rotation, color });\n }\n return { size: s, shapes };\n }\n default:\n return { size: s, shapes: [] };\n }\n}\n\n/** The forward (`/`) diagonal, plus the two corner stubs that make it tile\n * seamlessly across the edges. */\nfunction forwardDiagonals(s: number, width: number, color: string): TileShape[] {\n return [\n { kind: 'line', x1: -1, y1: s + 1, x2: s + 1, y2: -1, width, color },\n { kind: 'line', x1: -1, y1: 1, x2: 1, y2: -1, width, color },\n { kind: 'line', x1: s - 1, y1: s + 1, x2: s + 1, y2: s - 1, width, color },\n ];\n}\n\n/** The backward (`\\`) diagonal and its two stubs. */\nfunction backwardDiagonals(s: number, width: number, color: string): TileShape[] {\n return [\n { kind: 'line', x1: -1, y1: -1, x2: s + 1, y2: s + 1, width, color },\n { kind: 'line', x1: s - 1, y1: -1, x2: s + 1, y2: 1, width, color },\n { kind: 'line', x1: -1, y1: s - 1, x2: 1, y2: s + 1, width, color },\n ];\n}\n\n/** Draw a tile's shapes to a 2D context — the raster half of the pair. */\nexport function drawTileShapes(oc: CanvasRenderingContext2D, geometry: TileGeometry): void {\n for (const shape of geometry.shapes) {\n if (shape.kind === 'line') {\n oc.strokeStyle = shape.color;\n oc.lineWidth = shape.width;\n oc.beginPath();\n oc.moveTo(shape.x1, shape.y1);\n oc.lineTo(shape.x2, shape.y2);\n oc.stroke();\n continue;\n }\n oc.fillStyle = shape.color;\n if (shape.kind === 'rect') {\n oc.fillRect(shape.x, shape.y, shape.width, shape.height);\n continue;\n }\n oc.beginPath();\n if (shape.kind === 'circle') {\n oc.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2);\n } else {\n oc.ellipse(shape.cx, shape.cy, shape.rx, shape.ry, shape.rotation, 0, Math.PI * 2);\n }\n oc.fill();\n }\n}\n","/**\n * Tile-pattern primitive — a small helper for building repeating texture\n * tiles from a draw callback. The result plugs into a `FillStyle` of\n * `{ fill: 'pattern', pattern }` (see `../../core/paint-types.ts`).\n *\n * GL-backed: the tile is rendered to an `OffscreenCanvas` (with a 2D fallback\n * to a regular `<canvas>` for environments without `OffscreenCanvas`),\n * converted to an `ImageBitmap`, and registered with `registerTexture()`.\n * The returned `TextureHandle` is what `FillStyle.pattern` accepts.\n *\n * For ready-to-use named patterns (hatch, crosshatch, dots, chunks),\n * see the `@weasel-js/core/patterns-builtin` subpath.\n */\n\nimport { registerTexture } from '../../renderer/textures/registerTexture';\nimport type { TextureHandle } from '../../renderer/textures/registerTexture';\n\n/** Options for `createTilePattern`. */\nexport interface TilePatternOpts {\n /** Edge length (in pixels) of the square offscreen tile. */\n size: number;\n /** FillStyle one tile at integer-pixel coordinates `(0, 0) .. (size, size)`. */\n draw: (oc: CanvasRenderingContext2D, size: number) => void;\n}\n\n/**\n * Render a single tile to an offscreen canvas and register it as a GL\n * texture, returning a `TextureHandle` you can wrap in a `FillStyle` of\n * `{ fill: 'pattern', pattern }`. Returns `null` only when an\n * `OffscreenCanvas`/2D context can't be acquired (very rare; same fallback\n * semantics as the prior `CanvasPattern`-based implementation).\n */\nexport function createTilePattern(opts: TilePatternOpts): TextureHandle | null {\n // Prefer OffscreenCanvas; fall back to <canvas> for environments without it\n // (tests in jsdom may need that fallback — try OffscreenCanvas first).\n let off: OffscreenCanvas | HTMLCanvasElement;\n try {\n off = new OffscreenCanvas(opts.size, opts.size);\n } catch {\n if (typeof document === 'undefined') return null;\n const c = document.createElement('canvas');\n c.width = opts.size;\n c.height = opts.size;\n off = c;\n }\n const oc = off.getContext('2d') as CanvasRenderingContext2D | null;\n if (!oc) return null;\n opts.draw(oc, opts.size);\n\n // Convert to ImageBitmap so registerTexture can store it.\n // OffscreenCanvas.transferToImageBitmap() is the fast path; the\n // synchronous path requires OffscreenCanvas. If we got here without it,\n // the env doesn't support OffscreenCanvas — fail gracefully.\n let bitmap: ImageBitmap;\n if (typeof (off as OffscreenCanvas).transferToImageBitmap === 'function') {\n bitmap = (off as OffscreenCanvas).transferToImageBitmap();\n } else {\n return null;\n }\n return registerTexture(bitmap);\n}\n","/**\n * Built-in pattern factories — generic line/dot/scatter textures. Each\n * returns a `TextureHandle` you can wrap in a `FillStyle` of\n * `{ fill: 'pattern', pattern }` and pass to anything that accepts a `FillStyle`\n * (`FillStyle`-aware layers, path fills, etc.).\n * Imported from a subpath so consumers that ship their own catalog can\n * avoid the bundle cost.\n *\n * All factories require an explicit `color` (and `bg` for `chunks`). No\n * defaults are baked in — the caller's design system is the source of truth\n * for palette decisions.\n *\n * Each is a thin wrapper over `tileGeometry`, which describes the tile as\n * shapes so `@weasel-js/svg` can emit the same tile as a `<pattern>`.\n * Prefer naming a tile in a `TilePatternSpec` over calling these directly —\n * a spec serializes, a `TextureHandle` does not.\n */\n\nexport { tileGeometry, tileSize, drawTileShapes } from './tileGeometry';\nexport type { TileShape, TileGeometry } from './tileGeometry';\n\nimport { createTilePattern } from '.';\nimport { tileGeometry, drawTileShapes, tileSize } from './tileGeometry';\nimport type { TilePatternSpec } from '../../core/paint-types';\nimport type { TextureHandle } from '../../renderer/textures/registerTexture';\n\nfunction build(spec: TilePatternSpec): TextureHandle | null {\n const geometry = tileGeometry(spec);\n return createTilePattern({\n size: tileSize(spec),\n draw: (oc) => drawTileShapes(oc, geometry),\n });\n}\n\n/** Diagonal hatch (forward slash). */\nexport interface HatchParams {\n color: string;\n size?: number;\n lineWidth?: number;\n}\n\n/** Build a diagonal-hatch pattern texture. */\nexport function hatch(params: HatchParams): TextureHandle | null {\n return build({ tile: 'hatch', ...params });\n}\n\n/** Diagonal hatch in both directions. */\nexport interface CrosshatchParams {\n color: string;\n size?: number;\n lineWidth?: number;\n}\n\n/** Build a crosshatch pattern texture. */\nexport function crosshatch(params: CrosshatchParams): TextureHandle | null {\n return build({ tile: 'crosshatch', ...params });\n}\n\n/** Regular grid of filled dots. */\nexport interface DotsParams {\n color: string;\n size?: number;\n radius?: number;\n}\n\n/** Build a dot-grid pattern texture. */\nexport function dots(params: DotsParams): TextureHandle | null {\n return build({ tile: 'dots', ...params });\n}\n\n/**\n * Random scatter of small ellipses on top of a solid background. Driven by a\n * seeded PRNG so the same `seed` produces a deterministic tile.\n *\n * Note: the `bg` field is optional — omit it for a transparent background\n * (the scatter shapes draw directly onto whatever sits beneath the overlay).\n */\nexport interface ChunksParams {\n color: string;\n bg?: string;\n size?: number;\n /** Coverage: `chunks * chunkSize²` per `size²` tile. Range ~[0, 1]. */\n density?: number;\n chunkSize?: number;\n seed?: number;\n}\n\n/** Build a seeded random-scatter pattern texture. */\nexport function chunks(params: ChunksParams): TextureHandle | null {\n return build({ tile: 'chunks', ...params });\n}\n"]}
@@ -1,266 +0,0 @@
1
- /**
2
- * registerTexture — accepts an image source, assigns an opaque id, stores in
3
- * a module-level registry. Actual GL upload happens lazily at draw time in
4
- * drawShader() via GLTextureCache.upload (which is idempotent).
5
- *
6
- * Lifecycle: textures live for the renderer's lifetime. No unregister in v1.
7
- *
8
- * Convention §9: this registry stores image data only — no per-renderer state.
9
- * Each WeaselRenderer's GLTextureCache does its own dedup via has(id).
10
- */
11
- interface TextureHandle {
12
- readonly id: string;
13
- }
14
- /**
15
- * Register an image for use as a shader texture uniform.
16
- *
17
- * Returns a TextureHandle whose `id` can be passed as a ShaderUniform value.
18
- * The handle is valid for the lifetime of the renderer it will be used with.
19
- *
20
- * @param image The image to register. HTMLImageElement must be fully loaded.
21
- * @returns Opaque TextureHandle.
22
- */
23
- declare function registerTexture(image: HTMLImageElement | ImageBitmap): TextureHandle;
24
-
25
- /**
26
- * FillStyle and Stroke types — the unified shape for "what color or texture
27
- * paints these pixels," modeled on SVG's paint-server concept.
28
- *
29
- * - `FillStyle` is a tagged union: solid color, pattern, or gradient. Used
30
- * wherever a kit option previously took `fillStyle: string`.
31
- * - `Stroke` pairs a `FillStyle` with structural stroke parameters (width, dash,
32
- * line cap/join, alignment).
33
- * - These types are consumed by the GL renderer's DrawCommand path fills
34
- * and strokes.
35
- *
36
- * The 2D `applyPaint` / `applyStroke` / `renderFilledRegion` helpers that
37
- * formerly lived alongside these types were deleted with the 2D backend in
38
- * Step 10. `alignedStrokeRect` survives as a pure geometry helper used by
39
- * path tessellation and the selection overlay.
40
- */
41
-
42
- /**
43
- * Color/texture strategy for fills (and, via `Stroke.paint`, strokes).
44
- *
45
- * `fill` is optional and defaults to `'solid'` — `{ color: '#abc' }` is
46
- * equivalent to `{ fill: 'solid', color: '#abc' }`. Pattern paints must set
47
- * `fill: 'pattern'` explicitly.
48
- *
49
- * The `'pattern'` variant's payload is either a `TilePatternSpec` — plain
50
- * data naming one of the built-in tiles, which survives serialization — or a
51
- * `TextureHandle` for a tile the consumer built itself via
52
- * `createTilePattern()`. A handle is a session-scoped registry key, so a
53
- * paint carrying one cannot be persisted or exported; prefer the spec.
54
- *
55
- * `units` on a pattern names the space the tile's origin and scale live in,
56
- * not the space of any geometry (a pattern has none). Under `'bounds'` the
57
- * tile anchors to the painted node's box, so the pattern travels with the
58
- * node and a resize reveals more tiles rather than stretching them;
59
- * `fillInPoseFrame` resolves that to a `'local'` paint with an explicit
60
- * `origin` before the renderer sees it.
61
- */
62
- type FillStyle = {
63
- fill?: 'solid';
64
- color: string;
65
- opacity?: number;
66
- } | {
67
- fill: 'pattern';
68
- pattern: TextureHandle | TilePatternSpec;
69
- units?: GradientUnits;
70
- origin?: {
71
- x: number;
72
- y: number;
73
- };
74
- opacity?: number;
75
- } | {
76
- fill: 'linear-gradient';
77
- from: {
78
- x: number;
79
- y: number;
80
- };
81
- to: {
82
- x: number;
83
- y: number;
84
- };
85
- stops: GradStop[];
86
- units?: GradientUnits;
87
- opacity?: number;
88
- } | {
89
- fill: 'radial-gradient';
90
- center: {
91
- x: number;
92
- y: number;
93
- };
94
- radius: number;
95
- stops: GradStop[];
96
- units?: GradientUnits;
97
- opacity?: number;
98
- } | {
99
- fill: 'conic-gradient';
100
- center: {
101
- x: number;
102
- y: number;
103
- };
104
- angle: number;
105
- stops: GradStop[];
106
- units?: GradientUnits;
107
- opacity?: number;
108
- };
109
- /**
110
- * Which coordinate space a gradient's geometry (`from`/`to`, `center`,
111
- * `radius`) is expressed in. SVG's `gradientUnits`, plus an option for
112
- * paints that are themselves viewport furniture.
113
- *
114
- * - `'bounds'`: fractions of the painted node's bounding box, `0..1` on each
115
- * axis — SVG `objectBoundingBox`. Resolved by the node painter, before the
116
- * renderer sees it, so the paint follows the node through moves, resizes
117
- * and rotation. What a gradient on a scene node wants.
118
- * - `'local'`: the coordinate space the geometry was handed to the renderer
119
- * in — the enclosing group's frame. For draw commands a consumer builds
120
- * itself, where "the coordinates I just wrote" is the useful frame.
121
- * - `'world'`: scene coordinates. The paint stays put under pan and zoom
122
- * while the geometry moves through it. SVG `userSpaceOnUse`. Requires the
123
- * renderer to have been handed a view matrix; falls back to `'screen'`
124
- * when it has not.
125
- * - `'screen'`: CSS pixels of the drawing surface. For overlays and
126
- * viewport-fixed washes that should not move with the content at all.
127
- *
128
- * Defaults to `'screen'`, which is the behavior every gradient had before
129
- * this field existed.
130
- */
131
- type GradientUnits = 'bounds' | 'local' | 'world' | 'screen';
132
- /**
133
- * A built-in tile, described as plain data. The serializable half of the
134
- * pattern paint: `resolvePatternSpec()` turns one into a `TextureHandle`
135
- * at paint time, memoized so identical specs share a texture.
136
- *
137
- * `size` is the tile's edge length, and doubles as its extent in paint
138
- * space — a bigger `size` rasterizes a bigger tile rather than magnifying
139
- * a small one, which is why there is no separate scale field.
140
- */
141
- interface TilePatternSpec {
142
- tile: 'hatch' | 'crosshatch' | 'dots' | 'chunks';
143
- color: string;
144
- /** `chunks` only — omit for a transparent tile background. */
145
- bg?: string;
146
- size?: number;
147
- /** `hatch` / `crosshatch`. */
148
- lineWidth?: number;
149
- /** `dots`. */
150
- radius?: number;
151
- /** `chunks`. */
152
- density?: number;
153
- chunkSize?: number;
154
- seed?: number;
155
- }
156
- /** A single color stop within a gradient. `offset` is in 0..1. */
157
- interface GradStop {
158
- offset: number;
159
- color: string;
160
- }
161
- /** The gradient members of `FillStyle`, as one type. What a gradient editor
162
- * edits, and what the gradient-specific helpers accept. */
163
- type GradientFill = Extract<FillStyle, {
164
- fill: 'linear-gradient' | 'radial-gradient' | 'conic-gradient';
165
- }>;
166
- /** `GradientFill['fill']` — the three gradient discriminants on their own. */
167
- type GradientKind = GradientFill['fill'];
168
- /**
169
- * Where a stroke sits relative to the geometric edge it strokes.
170
- *
171
- * - `'center'` (default): canvas-native — half the stroke width sits inside
172
- * the geometry, half outside.
173
- * - `'inner'`: the entire stroke lies inside the geometry. The outer edge of
174
- * the stroke coincides with the geometric edge.
175
- * - `'outer'`: the entire stroke lies outside the geometry. The inner edge
176
- * of the stroke coincides with the geometric edge.
177
- *
178
- * Mirrors the (proposed) SVG `stroke-alignment` property. Honoring `inner`
179
- * or `outer` is the renderer's responsibility — for axis-aligned rects, the
180
- * kit shifts coordinates by `width / 2`. For arbitrary paths, renderers
181
- * typically use a stencil mask of the stroked path against the geometry.
182
- */
183
- type StrokeAlign = 'center' | 'inner' | 'outer';
184
- /** Stroke style: a FillStyle plus structural line parameters. */
185
- interface Stroke {
186
- paint: FillStyle;
187
- /** World units, or `{ px }` for screen pixels — resolved against the
188
- * accumulated transform scale at draw time, so it holds its on-screen
189
- * thickness as the view zooms. */
190
- width?: number | {
191
- px: number;
192
- };
193
- /** Per `CanvasRenderingContext2D.setLineDash` — empty/omitted = solid. */
194
- dash?: number[];
195
- cap?: 'butt' | 'round' | 'square';
196
- join?: 'miter' | 'round' | 'bevel';
197
- /**
198
- * Miter join fallback threshold. When the miter length exceeds
199
- * `miterLimit * width / 2`, the join falls back to a bevel. Default 4,
200
- * matching SVG — which is also what the kit's own serializer implies when
201
- * it omits the attribute for an unset field. Canvas2D's 10 lets an acute
202
- * corner throw a spike four times the half-width.
203
- */
204
- miterLimit?: number;
205
- /** Where the stroke sits relative to the geometric edge. Default `'center'`. */
206
- align?: StrokeAlign;
207
- /**
208
- * Per-anchor RGBA, flat (length = 4 × countPathAnchors(path)). Each
209
- * value in 0..1. Arc-length interpolated across the tessellated ribbon
210
- * between consecutive anchors. When set, `paint` is still required —
211
- * its `opacity` (and color, as a placeholder) flow through the shader.
212
- */
213
- vertexColors?: number[];
214
- /**
215
- * Per-anchor stroke width (length = `countPathAnchors(path)`). When set,
216
- * the tessellator interpolates half-widths along each segment to produce
217
- * a tapered ribbon. `width` is used as the fallback for any anchor whose
218
- * entry is missing or non-finite. Pressure-driven pencil strokes use
219
- * this; pair with `pressureToWidth` to derive widths from stylus input.
220
- *
221
- * Joins between adjacent segments whose widths differ by more than
222
- * `varyingWidthJoinThreshold` (default 1.5×) are forced to bevel
223
- * regardless of the `join` setting — miter math is unstable when widths
224
- * vary across the corner; smooth round joins with mismatched widths
225
- * are a future enhancement.
226
- */
227
- vertexWidths?: number[];
228
- /**
229
- * Max width ratio (greater / lesser) at which a non-bevel join is
230
- * preserved when `vertexWidths` causes adjacent segments to differ.
231
- * Beyond this ratio the join falls back to bevel. Default 1.5. Ignored
232
- * when `vertexWidths` is absent.
233
- */
234
- varyingWidthJoinThreshold?: number;
235
- }
236
- /**
237
- * Inflate (positive) or deflate (negative) a rect to honor `align` when
238
- * stroking it. Returns the rect to pass to a stroked-rect renderer. `width`
239
- * is the stroke width (defaults to 1 to match canvas).
240
- *
241
- * Pure geometry helper — no rendering side effects. Used by path
242
- * tessellation and the selection overlay to produce a rect whose
243
- * center-aligned stroke visually coincides with the requested
244
- * inner/outer-aligned stroke of the original rect.
245
- */
246
- declare function alignedStrokeRect(rect: {
247
- x: number;
248
- y: number;
249
- width: number;
250
- height: number;
251
- }, align: StrokeAlign, width?: number): {
252
- x: number;
253
- y: number;
254
- width: number;
255
- height: number;
256
- };
257
- /** Region a fill is clipped to. */
258
- interface Region {
259
- x: number;
260
- y: number;
261
- w: number;
262
- h: number;
263
- shape: 'rectangle' | 'circle';
264
- }
265
-
266
- export { type FillStyle as F, type GradStop as G, type Region as R, type Stroke as S, type TilePatternSpec as T, type TextureHandle as a, type GradientKind as b, type GradientFill as c, type GradientUnits as d, type StrokeAlign as e, alignedStrokeRect as f, registerTexture as r };