@weasel-js/modes 1.0.2 → 1.0.3

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.
@@ -84,5 +84,5 @@ function byId(id) {
84
84
  }
85
85
 
86
86
  export { CROP, DEFAULT_MODES, FREE_TRANSFORM, ISOLATION, NORMAL, PATH_EDIT, TEXT_EDIT, byId };
87
- //# sourceMappingURL=chunk-KILROWYB.js.map
88
- //# sourceMappingURL=chunk-KILROWYB.js.map
87
+ //# sourceMappingURL=chunk-YAJYQMBG.js.map
88
+ //# sourceMappingURL=chunk-YAJYQMBG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/presets/default.ts"],"names":["m"],"mappings":"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","file":"chunk-YAJYQMBG.js","sourcesContent":["import type { ModeDefinition } from '../modeDefinition';\n\n/** The default mode: everything except anchor and text editing, no scoping,\n * no workspace tint. */\nexport const NORMAL: ModeDefinition = {\n id: 'normal',\n kind: 'soft',\n allows: [\n 'creates-selection',\n 'transforms-selection',\n 'creates-paths',\n 'creates-shapes',\n 'creates-text',\n 'samples-color',\n 'applies-fill',\n 'edits-page',\n ],\n scoping: false,\n};\n\n/** Editing the anchors of a path. Scoped to the target, entered by\n * double-clicking it, left with Escape. */\nexport const PATH_EDIT: ModeDefinition = {\n id: 'path-edit',\n kind: 'soft',\n allows: ['edits-anchors'],\n scoping: true,\n workspace: { tint: '#3b82f6', gradient: 'bottom-up', intensity: 0.12 },\n entry: { trigger: 'double-click-target', shortcut: 'Enter' },\n exit: { shortcut: 'Escape' },\n};\n\n/** Working inside one subtree with everything outside it dimmed and inert.\n * Allows the same authoring tools as `NORMAL`, minus page edits. */\nexport const ISOLATION: ModeDefinition = {\n id: 'isolation',\n kind: 'soft',\n allows: [\n 'creates-selection',\n 'transforms-selection',\n 'creates-paths',\n 'creates-shapes',\n 'creates-text',\n 'samples-color',\n 'applies-fill',\n ],\n scoping: true,\n workspace: { tint: '#8b5cf6', gradient: 'bottom-up', intensity: 0.12 },\n entry: { trigger: 'double-click-target' },\n exit: { shortcut: 'Escape' },\n};\n\n/** A transaction that transforms the selection: the whole session is one undo\n * step, committed with Enter or discarded with Escape. */\nexport const FREE_TRANSFORM: ModeDefinition = {\n id: 'free-transform',\n kind: 'strict',\n allows: ['transforms-selection'],\n scoping: false,\n workspace: { tint: '#f59e0b', gradient: 'bottom-up', intensity: 0.12 },\n entry: { shortcut: 'Meta+T' },\n commit: { shortcut: 'Enter' },\n cancel: { shortcut: 'Escape' },\n};\n\n/** Editing the text of one node. Entered by double-clicking it, left with\n * Escape. */\nexport const TEXT_EDIT: ModeDefinition = {\n id: 'text-edit',\n kind: 'soft',\n allows: ['edits-text'],\n scoping: false,\n workspace: { tint: '#10b981', gradient: 'bottom-up', intensity: 0.12 },\n entry: { trigger: 'double-click-target' },\n exit: { shortcut: 'Escape' },\n};\n\n/** A transaction that changes the page extents, committed with Enter or\n * discarded with Escape. */\nexport const CROP: ModeDefinition = {\n id: 'crop',\n kind: 'strict',\n allows: ['edits-page'],\n scoping: false,\n workspace: { tint: '#ef4444', gradient: 'bottom-up', intensity: 0.12 },\n entry: { shortcut: 'C' },\n commit: { shortcut: 'Enter' },\n cancel: { shortcut: 'Escape' },\n};\n\n/** The six modes this package ships, ready to hand to `createModeRegistry`. */\nexport const DEFAULT_MODES: readonly ModeDefinition[] = [\n NORMAL,\n PATH_EDIT,\n ISOLATION,\n FREE_TRANSFORM,\n TEXT_EDIT,\n CROP,\n];\n\n/** Look up one of the `DEFAULT_MODES` by id. Throws on an unknown id. For an\n * app's own mode set, use the registry's `byId` instead. */\nexport function byId(id: string): ModeDefinition {\n const m = DEFAULT_MODES.find((m) => m.id === id);\n if (!m) throw new Error(`Unknown mode id: ${id}`);\n return m;\n}\n"]}
@@ -2,17 +2,34 @@
2
2
  * Apps and other consumers can add their own tags; this list is what
3
3
  * `weasel-modes` itself uses. */
4
4
  declare const ALL_TAGS: readonly ["navigation", "creates-selection", "creates-paths", "creates-shapes", "creates-text", "edits-anchors", "edits-text", "transforms-selection", "samples-color", "applies-fill", "edits-page"];
5
+ /** One capability a tool or contribution declares, and a mode allows. Any
6
+ * string is accepted so apps can add tags of their own; `ALL_TAGS` is the
7
+ * set this package ships. */
5
8
  type CapabilityTag = (typeof ALL_TAGS)[number] | (string & {});
6
9
  /** Tags that are implicitly allowed in every mode — never listed per-mode.
7
10
  * A tool tagged with any of these is always eligible. */
8
11
  declare const IMPLICIT_TAGS: readonly CapabilityTag[];
12
+ /** Whether a string is one of the shipped `ALL_TAGS`. An app-defined tag is
13
+ * still a valid `CapabilityTag` and will answer `false` here. */
9
14
  declare function isCapabilityTag(value: string): value is CapabilityTag;
10
15
 
16
+ /** How a mode tints the workspace — the area around the page — so the user
17
+ * can see at a glance which mode is active. */
11
18
  interface WorkspaceVisual {
12
19
  tint?: string;
13
20
  gradient?: 'top-down' | 'bottom-up';
14
21
  intensity?: number;
15
22
  }
23
+ /**
24
+ * A mode: an app-level editing context that narrows which tools are usable and
25
+ * how the workspace looks. Tools live inside modes; a tool is never "in" one.
26
+ *
27
+ * `kind` picks the lifecycle. A `soft` mode (path-edit, isolation, text-edit)
28
+ * is a scoped context with no commit ceremony — every edit inside it is
29
+ * independently undoable and `exit` is non-destructive. A `strict` mode
30
+ * (free-transform, crop) is a transaction: the whole session collapses to one
31
+ * undoable step and leaving requires an explicit `commit` or `cancel`.
32
+ */
16
33
  interface ModeDefinition {
17
34
  id: string;
18
35
  kind: 'soft' | 'strict';
@@ -38,13 +55,28 @@ interface ModeDefinition {
38
55
  /** True iff a tool carrying `toolTags` is eligible for `mode`. */
39
56
  declare function eligibleForMode(mode: ModeDefinition, toolTags: readonly CapabilityTag[]): boolean;
40
57
 
58
+ /** The default mode: everything except anchor and text editing, no scoping,
59
+ * no workspace tint. */
41
60
  declare const NORMAL: ModeDefinition;
61
+ /** Editing the anchors of a path. Scoped to the target, entered by
62
+ * double-clicking it, left with Escape. */
42
63
  declare const PATH_EDIT: ModeDefinition;
64
+ /** Working inside one subtree with everything outside it dimmed and inert.
65
+ * Allows the same authoring tools as `NORMAL`, minus page edits. */
43
66
  declare const ISOLATION: ModeDefinition;
67
+ /** A transaction that transforms the selection: the whole session is one undo
68
+ * step, committed with Enter or discarded with Escape. */
44
69
  declare const FREE_TRANSFORM: ModeDefinition;
70
+ /** Editing the text of one node. Entered by double-clicking it, left with
71
+ * Escape. */
45
72
  declare const TEXT_EDIT: ModeDefinition;
73
+ /** A transaction that changes the page extents, committed with Enter or
74
+ * discarded with Escape. */
46
75
  declare const CROP: ModeDefinition;
76
+ /** The six modes this package ships, ready to hand to `createModeRegistry`. */
47
77
  declare const DEFAULT_MODES: readonly ModeDefinition[];
78
+ /** Look up one of the `DEFAULT_MODES` by id. Throws on an unknown id. For an
79
+ * app's own mode set, use the registry's `byId` instead. */
48
80
  declare function byId(id: string): ModeDefinition;
49
81
 
50
82
  export { ALL_TAGS as A, type CapabilityTag as C, DEFAULT_MODES as D, FREE_TRANSFORM as F, IMPLICIT_TAGS as I, type ModeDefinition as M, NORMAL as N, PATH_EDIT as P, TEXT_EDIT as T, type WorkspaceVisual as W, CROP as a, ISOLATION as b, byId as c, eligibleForMode as e, isCapabilityTag as i };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,16 @@
1
- import { M as ModeDefinition, C as CapabilityTag } from './default-CdTM_d8s.js';
2
- export { A as ALL_TAGS, a as CROP, D as DEFAULT_MODES, F as FREE_TRANSFORM, I as IMPLICIT_TAGS, b as ISOLATION, N as NORMAL, P as PATH_EDIT, T as TEXT_EDIT, W as WorkspaceVisual, c as byId, e as eligibleForMode, i as isCapabilityTag } from './default-CdTM_d8s.js';
1
+ import { M as ModeDefinition, C as CapabilityTag } from './default-CD2GkJ8k.js';
2
+ export { A as ALL_TAGS, a as CROP, D as DEFAULT_MODES, F as FREE_TRANSFORM, I as IMPLICIT_TAGS, b as ISOLATION, N as NORMAL, P as PATH_EDIT, T as TEXT_EDIT, W as WorkspaceVisual, c as byId, e as eligibleForMode, i as isCapabilityTag } from './default-CD2GkJ8k.js';
3
3
 
4
+ /** Options for `createModeRegistry`: the modes to register and which one is
5
+ * active at startup. */
4
6
  interface CreateModeRegistryOptions {
5
7
  modes: readonly ModeDefinition[];
6
8
  initial: string;
7
9
  }
10
+ /** Holds the set of available modes and which one is active, and notifies
11
+ * subscribers when that changes. `getVersion` is a monotonic counter for
12
+ * render-cache invalidation. Unknown mode ids throw rather than being
13
+ * ignored. */
8
14
  interface ModeRegistry {
9
15
  current(): ModeDefinition;
10
16
  setMode(id: string): void;
@@ -12,16 +18,23 @@ interface ModeRegistry {
12
18
  getVersion(): number;
13
19
  subscribe(listener: () => void): () => void;
14
20
  }
21
+ /** Build a mode registry. Throws if `initial` does not name one of `modes`. */
15
22
  declare function createModeRegistry(opts: CreateModeRegistryOptions): ModeRegistry;
16
23
 
17
24
  /** Loose draw-command type — the kit's `DrawCommand` union shape is over in
18
25
  * `src/renderer`. We keep this opaque to avoid a cross-package import in
19
26
  * the type surface; consumers cast to the real DrawCommand at use site. */
20
27
  type ModeDrawCommand = unknown;
28
+ /** Produces the draw commands for one mode's persistent decorations. Called
29
+ * once per paint while that mode is active. */
21
30
  type ModeDecorationPainter = () => ModeDrawCommand[];
31
+ /** Options for `createModeDecorations`. */
22
32
  interface CreateModeDecorationsOptions {
23
33
  registry: ModeRegistry;
24
34
  }
35
+ /** Per-mode decoration painters, keyed by mode id. Mode decorations are drawn
36
+ * for as long as the mode is active and belong to the mode rather than to any
37
+ * tool — path-edit's anchor dots and handle lines are the motivating case. */
25
38
  interface ModeDecorations {
26
39
  /** Register (or replace) the painter for a mode id. */
27
40
  register(modeId: string, painter: ModeDecorationPainter): void;
@@ -31,8 +44,13 @@ interface ModeDecorations {
31
44
  /** Monotonic version. Bumps on mode change or painter registration. */
32
45
  getVersion(): number;
33
46
  }
47
+ /** Build a decoration registry bound to a mode registry, so switching modes
48
+ * switches which painter runs. */
34
49
  declare function createModeDecorations(opts: CreateModeDecorationsOptions): ModeDecorations;
35
50
 
51
+ /** The part of a tool this package needs in order to judge eligibility: its
52
+ * id and the capabilities it claims. Structural, so a real `ToolDef`
53
+ * satisfies it without this package depending on the kit. */
36
54
  interface ToolLike {
37
55
  id: string;
38
56
  capabilities?: readonly CapabilityTag[];
@@ -47,6 +65,7 @@ declare function eligibleContribution(reg: ModeRegistry, entry: {
47
65
  };
48
66
  }): boolean;
49
67
 
68
+ /** Options for `createScopingDim`. */
50
69
  interface CreateScopingDimOptions {
51
70
  registry: ModeRegistry;
52
71
  /** Returns the set of node ids that are *in* scope for the current mode.
@@ -56,12 +75,18 @@ interface CreateScopingDimOptions {
56
75
  /** Alpha for out-of-scope nodes. Default 0.3 (spec). */
57
76
  dimAlpha?: number;
58
77
  }
78
+ /** Answers, per node, how a scoping mode should treat it: dimmed or not,
79
+ * hit-testable or not. Consult it from the renderer and the hit-tester —
80
+ * it computes nothing itself and holds no state. */
59
81
  interface ScopingDim {
60
82
  /** Multiplier in [0, 1] to apply to a node's render alpha. */
61
83
  alphaFor(id: string): number;
62
84
  /** Whether the node should respond to pointer hits. */
63
85
  isPointerInteractive(id: string): boolean;
64
86
  }
87
+ /** Build the scoping lookup for a mode registry. In a mode with
88
+ * `scoping: false` every node is full-alpha and interactive, so this is safe
89
+ * to consult unconditionally. */
65
90
  declare function createScopingDim(opts: CreateScopingDimOptions): ScopingDim;
66
91
 
67
92
  export { CapabilityTag, type CreateModeDecorationsOptions, type CreateModeRegistryOptions, type CreateScopingDimOptions, type ModeDecorationPainter, type ModeDecorations, ModeDefinition, type ModeDrawCommand, type ModeRegistry, type ScopingDim, type ToolLike, createModeDecorations, createModeRegistry, createScopingDim, eligibleContribution, eligibleTool };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { CROP, DEFAULT_MODES, FREE_TRANSFORM, ISOLATION, NORMAL, PATH_EDIT, TEXT_EDIT, byId } from './chunk-KILROWYB.js';
1
+ export { CROP, DEFAULT_MODES, FREE_TRANSFORM, ISOLATION, NORMAL, PATH_EDIT, TEXT_EDIT, byId } from './chunk-YAJYQMBG.js';
2
2
 
3
3
  // src/capabilities.ts
4
4
  var ALL_TAGS = [
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/capabilities.ts","../src/modeDefinition.ts","../src/registry.ts","../src/decorations.ts","../src/eligibility.ts","../src/scopingLayer.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** The full vocabulary of capability tags shipped in the default preset.\n * Apps and other consumers can add their own tags; this list is what\n * `weasel-modes` itself uses. */\nexport const ALL_TAGS = [\n 'navigation',\n 'creates-selection',\n 'creates-paths',\n 'creates-shapes',\n 'creates-text',\n 'edits-anchors',\n 'edits-text',\n 'transforms-selection',\n 'samples-color',\n 'applies-fill',\n 'edits-page',\n] as const;\n\nexport type CapabilityTag = (typeof ALL_TAGS)[number] | (string & {});\n\n/** Tags that are implicitly allowed in every mode — never listed per-mode.\n * A tool tagged with any of these is always eligible. */\nexport const IMPLICIT_TAGS: readonly CapabilityTag[] = ['navigation'];\n\nexport function isCapabilityTag(value: string): value is CapabilityTag {\n return (ALL_TAGS as readonly string[]).includes(value);\n}\n","import { IMPLICIT_TAGS, type CapabilityTag } from './capabilities';\n\nexport interface WorkspaceVisual {\n tint?: string;\n gradient?: 'top-down' | 'bottom-up';\n intensity?: number;\n}\n\nexport interface ModeDefinition {\n id: string;\n kind: 'soft' | 'strict';\n /** Capability tags this mode allows beyond IMPLICIT_TAGS. */\n allows: CapabilityTag[];\n /** When true, out-of-target objects dim at the renderer layer. */\n scoping: boolean;\n workspace?: WorkspaceVisual;\n entry?: { shortcut?: string; trigger?: 'double-click-target' };\n exit?: { shortcut?: string };\n commit?: { shortcut?: string };\n cancel?: { shortcut?: string };\n}\n\n/** True iff a tool carrying `toolTags` is eligible for `mode`. */\nexport function eligibleForMode(mode: ModeDefinition, toolTags: readonly CapabilityTag[]): boolean {\n if (toolTags.length === 0) return false;\n const allowed = new Set<CapabilityTag>([...mode.allows, ...IMPLICIT_TAGS]);\n for (const t of toolTags) {\n if (allowed.has(t)) return true;\n }\n return false;\n}\n","import type { ModeDefinition } from './modeDefinition';\n\nexport interface CreateModeRegistryOptions {\n modes: readonly ModeDefinition[];\n initial: string;\n}\n\nexport interface ModeRegistry {\n current(): ModeDefinition;\n setMode(id: string): void;\n byId(id: string): ModeDefinition;\n getVersion(): number;\n subscribe(listener: () => void): () => void;\n}\n\nexport function createModeRegistry(opts: CreateModeRegistryOptions): ModeRegistry {\n const byIdMap = new Map(opts.modes.map((m) => [m.id, m]));\n const initial = byIdMap.get(opts.initial);\n if (!initial) throw new Error(`Initial mode \"${opts.initial}\" not in modes list`);\n\n let active: ModeDefinition = initial;\n let version = 0;\n const listeners = new Set<() => void>();\n\n function bump(): void {\n version++;\n for (const l of listeners) l();\n }\n\n return {\n current: () => active,\n setMode(id: string): void {\n const next = byIdMap.get(id);\n if (!next) throw new Error(`Unknown mode id: ${id}`);\n if (next === active) return;\n active = next;\n bump();\n },\n byId(id: string): ModeDefinition {\n const m = byIdMap.get(id);\n if (!m) throw new Error(`Unknown mode id: ${id}`);\n return m;\n },\n getVersion: () => version,\n subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => { listeners.delete(listener); };\n },\n };\n}\n","import type { ModeRegistry } from './registry';\n\n/** Loose draw-command type — the kit's `DrawCommand` union shape is over in\n * `src/renderer`. We keep this opaque to avoid a cross-package import in\n * the type surface; consumers cast to the real DrawCommand at use site. */\nexport type ModeDrawCommand = unknown;\n\nexport type ModeDecorationPainter = () => ModeDrawCommand[];\n\nexport interface CreateModeDecorationsOptions {\n registry: ModeRegistry;\n}\n\nexport interface ModeDecorations {\n /** Register (or replace) the painter for a mode id. */\n register(modeId: string, painter: ModeDecorationPainter): void;\n /** Paint the active mode's decorations. Returns an empty array if no\n * painter is registered for the active mode. */\n paint(): ModeDrawCommand[];\n /** Monotonic version. Bumps on mode change or painter registration. */\n getVersion(): number;\n}\n\nexport function createModeDecorations(opts: CreateModeDecorationsOptions): ModeDecorations {\n const { registry } = opts;\n const painters = new Map<string, ModeDecorationPainter>();\n let ver = 0;\n\n registry.subscribe(() => {\n ver++;\n });\n\n return {\n register(modeId: string, painter: ModeDecorationPainter): void {\n painters.set(modeId, painter);\n ver++;\n },\n paint(): ModeDrawCommand[] {\n const painter = painters.get(registry.current().id);\n if (!painter) return [];\n return painter();\n },\n getVersion: () => ver,\n };\n}\n","import type { ModeRegistry } from './registry';\nimport type { CapabilityTag } from './capabilities';\nimport { eligibleForMode } from './modeDefinition';\n\nexport interface ToolLike {\n id: string;\n capabilities?: readonly CapabilityTag[];\n}\n\n/** True iff `tool` is usable in the registry's currently active mode. */\nexport function eligibleTool(reg: ModeRegistry, tool: ToolLike): boolean {\n return eligibleForMode(reg.current(), tool.capabilities ?? []);\n}\n\n/** True iff a contribution is usable in the registry's active mode. */\nexport function eligibleContribution(\n reg: ModeRegistry,\n entry: { id: string; eligibility?: { capabilities?: readonly CapabilityTag[] } },\n): boolean {\n return eligibleForMode(reg.current(), entry.eligibility?.capabilities ?? []);\n}\n","import type { ModeRegistry } from './registry';\n\nexport interface CreateScopingDimOptions {\n registry: ModeRegistry;\n /** Returns the set of node ids that are *in* scope for the current mode.\n * The app computes this — typically: selection-only for path-edit,\n * isolated subtree for isolation, empty for non-scoping modes. */\n getTargetIds: () => ReadonlySet<string>;\n /** Alpha for out-of-scope nodes. Default 0.3 (spec). */\n dimAlpha?: number;\n}\n\nexport interface ScopingDim {\n /** Multiplier in [0, 1] to apply to a node's render alpha. */\n alphaFor(id: string): number;\n /** Whether the node should respond to pointer hits. */\n isPointerInteractive(id: string): boolean;\n}\n\nexport function createScopingDim(opts: CreateScopingDimOptions): ScopingDim {\n const dim = opts.dimAlpha ?? 0.3;\n return {\n alphaFor(id) {\n const mode = opts.registry.current();\n if (!mode.scoping) return 1;\n return opts.getTargetIds().has(id) ? 1 : dim;\n },\n isPointerInteractive(id) {\n const mode = opts.registry.current();\n if (!mode.scoping) return true;\n return opts.getTargetIds().has(id);\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/capabilities.ts","../src/modeDefinition.ts","../src/registry.ts","../src/decorations.ts","../src/eligibility.ts","../src/scopingLayer.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** The full vocabulary of capability tags shipped in the default preset.\n * Apps and other consumers can add their own tags; this list is what\n * `weasel-modes` itself uses. */\nexport const ALL_TAGS = [\n 'navigation',\n 'creates-selection',\n 'creates-paths',\n 'creates-shapes',\n 'creates-text',\n 'edits-anchors',\n 'edits-text',\n 'transforms-selection',\n 'samples-color',\n 'applies-fill',\n 'edits-page',\n] as const;\n\n/** One capability a tool or contribution declares, and a mode allows. Any\n * string is accepted so apps can add tags of their own; `ALL_TAGS` is the\n * set this package ships. */\nexport type CapabilityTag = (typeof ALL_TAGS)[number] | (string & {});\n\n/** Tags that are implicitly allowed in every mode — never listed per-mode.\n * A tool tagged with any of these is always eligible. */\nexport const IMPLICIT_TAGS: readonly CapabilityTag[] = ['navigation'];\n\n/** Whether a string is one of the shipped `ALL_TAGS`. An app-defined tag is\n * still a valid `CapabilityTag` and will answer `false` here. */\nexport function isCapabilityTag(value: string): value is CapabilityTag {\n return (ALL_TAGS as readonly string[]).includes(value);\n}\n","import { IMPLICIT_TAGS, type CapabilityTag } from './capabilities';\n\n/** How a mode tints the workspace — the area around the page — so the user\n * can see at a glance which mode is active. */\nexport interface WorkspaceVisual {\n tint?: string;\n gradient?: 'top-down' | 'bottom-up';\n intensity?: number;\n}\n\n/**\n * A mode: an app-level editing context that narrows which tools are usable and\n * how the workspace looks. Tools live inside modes; a tool is never \"in\" one.\n *\n * `kind` picks the lifecycle. A `soft` mode (path-edit, isolation, text-edit)\n * is a scoped context with no commit ceremony — every edit inside it is\n * independently undoable and `exit` is non-destructive. A `strict` mode\n * (free-transform, crop) is a transaction: the whole session collapses to one\n * undoable step and leaving requires an explicit `commit` or `cancel`.\n */\nexport interface ModeDefinition {\n id: string;\n kind: 'soft' | 'strict';\n /** Capability tags this mode allows beyond IMPLICIT_TAGS. */\n allows: CapabilityTag[];\n /** When true, out-of-target objects dim at the renderer layer. */\n scoping: boolean;\n workspace?: WorkspaceVisual;\n entry?: { shortcut?: string; trigger?: 'double-click-target' };\n exit?: { shortcut?: string };\n commit?: { shortcut?: string };\n cancel?: { shortcut?: string };\n}\n\n/** True iff a tool carrying `toolTags` is eligible for `mode`. */\nexport function eligibleForMode(mode: ModeDefinition, toolTags: readonly CapabilityTag[]): boolean {\n if (toolTags.length === 0) return false;\n const allowed = new Set<CapabilityTag>([...mode.allows, ...IMPLICIT_TAGS]);\n for (const t of toolTags) {\n if (allowed.has(t)) return true;\n }\n return false;\n}\n","import type { ModeDefinition } from './modeDefinition';\n\n/** Options for `createModeRegistry`: the modes to register and which one is\n * active at startup. */\nexport interface CreateModeRegistryOptions {\n modes: readonly ModeDefinition[];\n initial: string;\n}\n\n/** Holds the set of available modes and which one is active, and notifies\n * subscribers when that changes. `getVersion` is a monotonic counter for\n * render-cache invalidation. Unknown mode ids throw rather than being\n * ignored. */\nexport interface ModeRegistry {\n current(): ModeDefinition;\n setMode(id: string): void;\n byId(id: string): ModeDefinition;\n getVersion(): number;\n subscribe(listener: () => void): () => void;\n}\n\n/** Build a mode registry. Throws if `initial` does not name one of `modes`. */\nexport function createModeRegistry(opts: CreateModeRegistryOptions): ModeRegistry {\n const byIdMap = new Map(opts.modes.map((m) => [m.id, m]));\n const initial = byIdMap.get(opts.initial);\n if (!initial) throw new Error(`Initial mode \"${opts.initial}\" not in modes list`);\n\n let active: ModeDefinition = initial;\n let version = 0;\n const listeners = new Set<() => void>();\n\n function bump(): void {\n version++;\n for (const l of listeners) l();\n }\n\n return {\n current: () => active,\n setMode(id: string): void {\n const next = byIdMap.get(id);\n if (!next) throw new Error(`Unknown mode id: ${id}`);\n if (next === active) return;\n active = next;\n bump();\n },\n byId(id: string): ModeDefinition {\n const m = byIdMap.get(id);\n if (!m) throw new Error(`Unknown mode id: ${id}`);\n return m;\n },\n getVersion: () => version,\n subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => { listeners.delete(listener); };\n },\n };\n}\n","import type { ModeRegistry } from './registry';\n\n/** Loose draw-command type — the kit's `DrawCommand` union shape is over in\n * `src/renderer`. We keep this opaque to avoid a cross-package import in\n * the type surface; consumers cast to the real DrawCommand at use site. */\nexport type ModeDrawCommand = unknown;\n\n/** Produces the draw commands for one mode's persistent decorations. Called\n * once per paint while that mode is active. */\nexport type ModeDecorationPainter = () => ModeDrawCommand[];\n\n/** Options for `createModeDecorations`. */\nexport interface CreateModeDecorationsOptions {\n registry: ModeRegistry;\n}\n\n/** Per-mode decoration painters, keyed by mode id. Mode decorations are drawn\n * for as long as the mode is active and belong to the mode rather than to any\n * tool — path-edit's anchor dots and handle lines are the motivating case. */\nexport interface ModeDecorations {\n /** Register (or replace) the painter for a mode id. */\n register(modeId: string, painter: ModeDecorationPainter): void;\n /** Paint the active mode's decorations. Returns an empty array if no\n * painter is registered for the active mode. */\n paint(): ModeDrawCommand[];\n /** Monotonic version. Bumps on mode change or painter registration. */\n getVersion(): number;\n}\n\n/** Build a decoration registry bound to a mode registry, so switching modes\n * switches which painter runs. */\nexport function createModeDecorations(opts: CreateModeDecorationsOptions): ModeDecorations {\n const { registry } = opts;\n const painters = new Map<string, ModeDecorationPainter>();\n let ver = 0;\n\n registry.subscribe(() => {\n ver++;\n });\n\n return {\n register(modeId: string, painter: ModeDecorationPainter): void {\n painters.set(modeId, painter);\n ver++;\n },\n paint(): ModeDrawCommand[] {\n const painter = painters.get(registry.current().id);\n if (!painter) return [];\n return painter();\n },\n getVersion: () => ver,\n };\n}\n","import type { ModeRegistry } from './registry';\nimport type { CapabilityTag } from './capabilities';\nimport { eligibleForMode } from './modeDefinition';\n\n/** The part of a tool this package needs in order to judge eligibility: its\n * id and the capabilities it claims. Structural, so a real `ToolDef`\n * satisfies it without this package depending on the kit. */\nexport interface ToolLike {\n id: string;\n capabilities?: readonly CapabilityTag[];\n}\n\n/** True iff `tool` is usable in the registry's currently active mode. */\nexport function eligibleTool(reg: ModeRegistry, tool: ToolLike): boolean {\n return eligibleForMode(reg.current(), tool.capabilities ?? []);\n}\n\n/** True iff a contribution is usable in the registry's active mode. */\nexport function eligibleContribution(\n reg: ModeRegistry,\n entry: { id: string; eligibility?: { capabilities?: readonly CapabilityTag[] } },\n): boolean {\n return eligibleForMode(reg.current(), entry.eligibility?.capabilities ?? []);\n}\n","import type { ModeRegistry } from './registry';\n\n/** Options for `createScopingDim`. */\nexport interface CreateScopingDimOptions {\n registry: ModeRegistry;\n /** Returns the set of node ids that are *in* scope for the current mode.\n * The app computes this — typically: selection-only for path-edit,\n * isolated subtree for isolation, empty for non-scoping modes. */\n getTargetIds: () => ReadonlySet<string>;\n /** Alpha for out-of-scope nodes. Default 0.3 (spec). */\n dimAlpha?: number;\n}\n\n/** Answers, per node, how a scoping mode should treat it: dimmed or not,\n * hit-testable or not. Consult it from the renderer and the hit-tester —\n * it computes nothing itself and holds no state. */\nexport interface ScopingDim {\n /** Multiplier in [0, 1] to apply to a node's render alpha. */\n alphaFor(id: string): number;\n /** Whether the node should respond to pointer hits. */\n isPointerInteractive(id: string): boolean;\n}\n\n/** Build the scoping lookup for a mode registry. In a mode with\n * `scoping: false` every node is full-alpha and interactive, so this is safe\n * to consult unconditionally. */\nexport function createScopingDim(opts: CreateScopingDimOptions): ScopingDim {\n const dim = opts.dimAlpha ?? 0.3;\n return {\n alphaFor(id) {\n const mode = opts.registry.current();\n if (!mode.scoping) return 1;\n return opts.getTargetIds().has(id) ? 1 : dim;\n },\n isPointerInteractive(id) {\n const mode = opts.registry.current();\n if (!mode.scoping) return true;\n return opts.getTargetIds().has(id);\n },\n };\n}\n"]}
@@ -1 +1 @@
1
- export { a as CROP, D as DEFAULT_MODES, F as FREE_TRANSFORM, b as ISOLATION, N as NORMAL, P as PATH_EDIT, T as TEXT_EDIT, c as byId } from '../default-CdTM_d8s.js';
1
+ export { a as CROP, D as DEFAULT_MODES, F as FREE_TRANSFORM, b as ISOLATION, N as NORMAL, P as PATH_EDIT, T as TEXT_EDIT, c as byId } from '../default-CD2GkJ8k.js';
@@ -1,3 +1,3 @@
1
- export { CROP, DEFAULT_MODES, FREE_TRANSFORM, ISOLATION, NORMAL, PATH_EDIT, TEXT_EDIT, byId } from '../chunk-KILROWYB.js';
1
+ export { CROP, DEFAULT_MODES, FREE_TRANSFORM, ISOLATION, NORMAL, PATH_EDIT, TEXT_EDIT, byId } from '../chunk-YAJYQMBG.js';
2
2
  //# sourceMappingURL=default.js.map
3
3
  //# sourceMappingURL=default.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weasel-js/modes",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "App-level modality primitives: capability tags, mode definitions, mode registry, and decoration layer.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/presets/default.ts"],"names":["m"],"mappings":"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","file":"chunk-KILROWYB.js","sourcesContent":["import type { ModeDefinition } from '../modeDefinition';\n\nexport const NORMAL: ModeDefinition = {\n id: 'normal',\n kind: 'soft',\n allows: [\n 'creates-selection',\n 'transforms-selection',\n 'creates-paths',\n 'creates-shapes',\n 'creates-text',\n 'samples-color',\n 'applies-fill',\n 'edits-page',\n ],\n scoping: false,\n};\n\nexport const PATH_EDIT: ModeDefinition = {\n id: 'path-edit',\n kind: 'soft',\n allows: ['edits-anchors'],\n scoping: true,\n workspace: { tint: '#3b82f6', gradient: 'bottom-up', intensity: 0.12 },\n entry: { trigger: 'double-click-target', shortcut: 'Enter' },\n exit: { shortcut: 'Escape' },\n};\n\nexport const ISOLATION: ModeDefinition = {\n id: 'isolation',\n kind: 'soft',\n allows: [\n 'creates-selection',\n 'transforms-selection',\n 'creates-paths',\n 'creates-shapes',\n 'creates-text',\n 'samples-color',\n 'applies-fill',\n ],\n scoping: true,\n workspace: { tint: '#8b5cf6', gradient: 'bottom-up', intensity: 0.12 },\n entry: { trigger: 'double-click-target' },\n exit: { shortcut: 'Escape' },\n};\n\nexport const FREE_TRANSFORM: ModeDefinition = {\n id: 'free-transform',\n kind: 'strict',\n allows: ['transforms-selection'],\n scoping: false,\n workspace: { tint: '#f59e0b', gradient: 'bottom-up', intensity: 0.12 },\n entry: { shortcut: 'Meta+T' },\n commit: { shortcut: 'Enter' },\n cancel: { shortcut: 'Escape' },\n};\n\nexport const TEXT_EDIT: ModeDefinition = {\n id: 'text-edit',\n kind: 'soft',\n allows: ['edits-text'],\n scoping: false,\n workspace: { tint: '#10b981', gradient: 'bottom-up', intensity: 0.12 },\n entry: { trigger: 'double-click-target' },\n exit: { shortcut: 'Escape' },\n};\n\nexport const CROP: ModeDefinition = {\n id: 'crop',\n kind: 'strict',\n allows: ['edits-page'],\n scoping: false,\n workspace: { tint: '#ef4444', gradient: 'bottom-up', intensity: 0.12 },\n entry: { shortcut: 'C' },\n commit: { shortcut: 'Enter' },\n cancel: { shortcut: 'Escape' },\n};\n\nexport const DEFAULT_MODES: readonly ModeDefinition[] = [\n NORMAL,\n PATH_EDIT,\n ISOLATION,\n FREE_TRANSFORM,\n TEXT_EDIT,\n CROP,\n];\n\nexport function byId(id: string): ModeDefinition {\n const m = DEFAULT_MODES.find((m) => m.id === id);\n if (!m) throw new Error(`Unknown mode id: ${id}`);\n return m;\n}\n"]}