@pixerate/editor 0.27.0 → 0.29.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 (112) hide show
  1. package/README.md +44 -2
  2. package/dist/HistoryManager-DWSLd6Tf.d.ts +150 -0
  3. package/dist/HistoryManager-DeQQMBCj.d.cts +150 -0
  4. package/dist/agent/index.cjs +20 -427
  5. package/dist/agent/index.cjs.map +1 -1
  6. package/dist/agent/index.d.cts +4 -342
  7. package/dist/agent/index.d.ts +4 -342
  8. package/dist/agent/index.js +14 -421
  9. package/dist/agent/index.js.map +1 -1
  10. package/dist/animation/index.cjs +62 -0
  11. package/dist/animation/index.cjs.map +1 -0
  12. package/dist/animation/index.d.cts +226 -0
  13. package/dist/animation/index.d.ts +226 -0
  14. package/dist/animation/index.js +62 -0
  15. package/dist/animation/index.js.map +1 -0
  16. package/dist/{builtins-nMXh9sT3.d.cts → builtins-DxC3aUR2.d.cts} +2 -2
  17. package/dist/{builtins-haN82vjT.d.ts → builtins-NGxNxQfJ.d.ts} +2 -2
  18. package/dist/canvas/index.cjs +177 -743
  19. package/dist/canvas/index.cjs.map +1 -1
  20. package/dist/canvas/index.d.cts +6 -289
  21. package/dist/canvas/index.d.ts +6 -289
  22. package/dist/canvas/index.js +49 -615
  23. package/dist/canvas/index.js.map +1 -1
  24. package/dist/chunk-36BLROHV.js +438 -0
  25. package/dist/chunk-36BLROHV.js.map +1 -0
  26. package/dist/{chunk-JT7AXQTE.js → chunk-3RPOW4CS.js} +7 -7
  27. package/dist/{chunk-AGR7EVRW.cjs → chunk-6MDQKHRH.cjs} +6 -6
  28. package/dist/{chunk-AGR7EVRW.cjs.map → chunk-6MDQKHRH.cjs.map} +1 -1
  29. package/dist/chunk-A6PHCAGX.cjs +87 -0
  30. package/dist/chunk-A6PHCAGX.cjs.map +1 -0
  31. package/dist/{chunk-HRMEDT2L.js → chunk-D6AVOTSD.js} +6 -85
  32. package/dist/chunk-D6AVOTSD.js.map +1 -0
  33. package/dist/{chunk-ARRTP33I.js → chunk-D6S5M7HV.js} +2 -2
  34. package/dist/{chunk-ARRTP33I.js.map → chunk-D6S5M7HV.js.map} +1 -1
  35. package/dist/{chunk-6IO7WUSU.js → chunk-DDADOHJW.js} +17 -17
  36. package/dist/{chunk-CPOKFNBG.cjs → chunk-I6BZB5ZH.cjs} +2 -2
  37. package/dist/{chunk-CPOKFNBG.cjs.map → chunk-I6BZB5ZH.cjs.map} +1 -1
  38. package/dist/{chunk-4BVP64JJ.cjs → chunk-K2UYKJ6M.cjs} +24 -103
  39. package/dist/chunk-K2UYKJ6M.cjs.map +1 -0
  40. package/dist/chunk-PIIAZV3L.cjs +244 -0
  41. package/dist/chunk-PIIAZV3L.cjs.map +1 -0
  42. package/dist/chunk-PWJARCI4.js +637 -0
  43. package/dist/chunk-PWJARCI4.js.map +1 -0
  44. package/dist/{chunk-G2I4QJOT.cjs → chunk-QKLRAA4T.cjs} +3 -244
  45. package/dist/chunk-QKLRAA4T.cjs.map +1 -0
  46. package/dist/{chunk-2H7ZHPEJ.js → chunk-QVZZGT4X.js} +1 -242
  47. package/dist/chunk-QVZZGT4X.js.map +1 -0
  48. package/dist/chunk-TGD52FEZ.js +87 -0
  49. package/dist/chunk-TGD52FEZ.js.map +1 -0
  50. package/dist/chunk-TTYCN6ZH.cjs +637 -0
  51. package/dist/chunk-TTYCN6ZH.cjs.map +1 -0
  52. package/dist/{chunk-YK5TATOF.cjs → chunk-U7VGDSNY.cjs} +3 -3
  53. package/dist/{chunk-YK5TATOF.cjs.map → chunk-U7VGDSNY.cjs.map} +1 -1
  54. package/dist/chunk-V7CC7RZ6.cjs +438 -0
  55. package/dist/chunk-V7CC7RZ6.cjs.map +1 -0
  56. package/dist/chunk-YK57D7PW.js +244 -0
  57. package/dist/chunk-YK57D7PW.js.map +1 -0
  58. package/dist/commands/index.cjs +2 -2
  59. package/dist/commands/index.cjs.map +1 -1
  60. package/dist/commands/index.d.cts +2 -2
  61. package/dist/commands/index.d.ts +2 -2
  62. package/dist/commands/index.js +1 -1
  63. package/dist/commands/index.js.map +1 -1
  64. package/dist/easing-B6iAnJHk.d.cts +74 -0
  65. package/dist/easing-B6iAnJHk.d.ts +74 -0
  66. package/dist/extensions/index.cjs +5 -5
  67. package/dist/extensions/index.cjs.map +1 -1
  68. package/dist/extensions/index.js +4 -4
  69. package/dist/generation/index.cjs +4 -4
  70. package/dist/generation/index.cjs.map +1 -1
  71. package/dist/generation/index.js +3 -3
  72. package/dist/generation/index.js.map +1 -1
  73. package/dist/history/index.cjs +4 -2
  74. package/dist/history/index.cjs.map +1 -1
  75. package/dist/history/index.d.cts +3 -149
  76. package/dist/history/index.d.ts +3 -149
  77. package/dist/history/index.js +5 -3
  78. package/dist/image-editor/index.cjs.map +1 -1
  79. package/dist/image-editor/index.js.map +1 -1
  80. package/dist/index.cjs +24 -22
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.cts +3 -2
  83. package/dist/index.d.ts +3 -2
  84. package/dist/index.js +112 -110
  85. package/dist/index.js.map +1 -1
  86. package/dist/parameters/index.cjs +1283 -0
  87. package/dist/parameters/index.cjs.map +1 -0
  88. package/dist/parameters/index.d.cts +595 -0
  89. package/dist/parameters/index.d.ts +595 -0
  90. package/dist/parameters/index.js +1283 -0
  91. package/dist/parameters/index.js.map +1 -0
  92. package/dist/spreadsheet/index.cjs +4 -4
  93. package/dist/spreadsheet/index.cjs.map +1 -1
  94. package/dist/spreadsheet/index.js +8 -8
  95. package/dist/tokens/index.cjs +4 -2
  96. package/dist/tokens/index.cjs.map +1 -1
  97. package/dist/tokens/index.js +6 -4
  98. package/dist/tools/index.cjs +31 -0
  99. package/dist/tools/index.cjs.map +1 -0
  100. package/dist/tools/index.d.cts +343 -0
  101. package/dist/tools/index.d.ts +343 -0
  102. package/dist/tools/index.js +31 -0
  103. package/dist/tools/index.js.map +1 -0
  104. package/dist/{types-GT5a5YAX.d.cts → types-DsiXGmjo.d.cts} +1 -1
  105. package/dist/{types-GT5a5YAX.d.ts → types-DsiXGmjo.d.ts} +1 -1
  106. package/package.json +16 -1
  107. package/dist/chunk-2H7ZHPEJ.js.map +0 -1
  108. package/dist/chunk-4BVP64JJ.cjs.map +0 -1
  109. package/dist/chunk-G2I4QJOT.cjs.map +0 -1
  110. package/dist/chunk-HRMEDT2L.js.map +0 -1
  111. /package/dist/{chunk-JT7AXQTE.js.map → chunk-3RPOW4CS.js.map} +0 -0
  112. /package/dist/{chunk-6IO7WUSU.js.map → chunk-DDADOHJW.js.map} +0 -0
@@ -0,0 +1,244 @@
1
+ // src/dirty.ts
2
+ function defaultIsEqual(a, b) {
3
+ if (Object.is(a, b)) return true;
4
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
5
+ return false;
6
+ }
7
+ if (Array.isArray(a) && Array.isArray(b)) {
8
+ if (a.length !== b.length) return false;
9
+ for (let i = 0; i < a.length; i++) {
10
+ if (!defaultIsEqual(a[i], b[i])) return false;
11
+ }
12
+ return true;
13
+ }
14
+ if (Array.isArray(a) !== Array.isArray(b)) {
15
+ return false;
16
+ }
17
+ const keysA = Object.keys(a);
18
+ const keysB = Object.keys(b);
19
+ if (keysA.length !== keysB.length) return false;
20
+ for (const key of keysA) {
21
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
22
+ if (!defaultIsEqual(a[key], b[key])) return false;
23
+ }
24
+ return true;
25
+ }
26
+ var isThenable = (value) => !!value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
27
+ var DirtyTracker = class {
28
+ baseline;
29
+ isEqual;
30
+ normalize;
31
+ normalizedBaseline = null;
32
+ /** `isDirty` result as of a revision (and baseline version). */
33
+ cache = null;
34
+ baselineVersion = 0;
35
+ suppressDepth = 0;
36
+ hasValue = false;
37
+ lastValue;
38
+ dirtyState = false;
39
+ listeners = /* @__PURE__ */ new Set();
40
+ constructor(initialBaseline, options) {
41
+ this.baseline = initialBaseline;
42
+ if (typeof options === "function") {
43
+ this.isEqual = options;
44
+ } else {
45
+ this.isEqual = options?.isEqual ?? defaultIsEqual;
46
+ this.normalize = options?.normalize;
47
+ }
48
+ }
49
+ /**
50
+ * Returns current baseline value.
51
+ */
52
+ getBaseline() {
53
+ return this.baseline;
54
+ }
55
+ changeBaseline(value) {
56
+ this.baseline = value;
57
+ this.baselineVersion++;
58
+ this.normalizedBaseline = null;
59
+ this.cache = null;
60
+ }
61
+ /**
62
+ * Sets a new baseline value without triggering any dirty state.
63
+ * If a value is being tracked (`update`), `dirty` is recomputed against it.
64
+ */
65
+ setBaseline(value) {
66
+ this.changeBaseline(value);
67
+ if (this.hasValue) {
68
+ this.setDirtyState(this.compare(this.lastValue));
69
+ }
70
+ }
71
+ compare(current) {
72
+ if (!this.normalize) return !this.isEqual(this.baseline, current);
73
+ this.normalizedBaseline ??= { value: this.normalize(this.baseline) };
74
+ return !this.isEqual(
75
+ this.normalizedBaseline.value,
76
+ this.normalize(current)
77
+ );
78
+ }
79
+ /**
80
+ * Checks whether the given value differs from the baseline.
81
+ * With a `revision` (e.g. `HistoryManager.getRevision()`), the result is
82
+ * cached and reused for the same revision until the baseline changes.
83
+ */
84
+ isDirty(current, revision) {
85
+ if (revision === void 0) return this.compare(current);
86
+ const cache = this.cache;
87
+ if (cache && cache.revision === revision && cache.baseline === this.baselineVersion) {
88
+ return cache.dirty;
89
+ }
90
+ const dirty = this.compare(current);
91
+ this.cache = { revision, baseline: this.baselineVersion, dirty };
92
+ return dirty;
93
+ }
94
+ /**
95
+ * Resets the baseline to the provided current value (or clears dirty status).
96
+ */
97
+ reset(current) {
98
+ this.changeBaseline(current);
99
+ this.hasValue = true;
100
+ this.lastValue = current;
101
+ this.setDirtyState(false);
102
+ }
103
+ /**
104
+ * Records the current value and returns whether it is dirty. While
105
+ * suppressed (`suppress`), the value becomes the new baseline instead, so
106
+ * programmatic loads and restores never mark the document dirty.
107
+ */
108
+ update(current, revision) {
109
+ this.hasValue = true;
110
+ this.lastValue = current;
111
+ if (this.suppressDepth > 0) {
112
+ this.changeBaseline(current);
113
+ this.setDirtyState(false);
114
+ return false;
115
+ }
116
+ const dirty = this.isDirty(current, revision);
117
+ this.setDirtyState(dirty);
118
+ return dirty;
119
+ }
120
+ /** Whether the last value passed to `update` differs from the baseline. */
121
+ get dirty() {
122
+ return this.dirtyState;
123
+ }
124
+ /** Whether `suppress` is currently running. */
125
+ get suppressed() {
126
+ return this.suppressDepth > 0;
127
+ }
128
+ /**
129
+ * Marks the document saved: `current` (default: the last value passed to
130
+ * `update`) becomes the baseline.
131
+ */
132
+ markClean(...args) {
133
+ if (args.length > 0) {
134
+ this.reset(args[0]);
135
+ } else if (this.hasValue) {
136
+ this.reset(this.lastValue);
137
+ } else {
138
+ this.setDirtyState(false);
139
+ }
140
+ }
141
+ /**
142
+ * Runs `fn` without marking anything dirty: every `update` during it
143
+ * rebaselines. Re-entrant (depth counter). If `fn` returns a promise, the
144
+ * suppression lasts until it settles.
145
+ */
146
+ suppress(fn) {
147
+ this.suppressDepth++;
148
+ let result;
149
+ try {
150
+ result = fn();
151
+ } catch (error) {
152
+ this.suppressDepth--;
153
+ throw error;
154
+ }
155
+ if (isThenable(result)) {
156
+ const release = () => {
157
+ this.suppressDepth--;
158
+ };
159
+ return result.then(
160
+ (value) => {
161
+ release();
162
+ return value;
163
+ },
164
+ (error) => {
165
+ release();
166
+ throw error;
167
+ }
168
+ );
169
+ }
170
+ this.suppressDepth--;
171
+ return result;
172
+ }
173
+ /**
174
+ * Subscribes to changes of `dirty`. The listener is called immediately with
175
+ * the current value. Returns an unsubscribe function.
176
+ */
177
+ subscribe(listener) {
178
+ this.listeners.add(listener);
179
+ listener(this.dirtyState);
180
+ return () => {
181
+ this.listeners.delete(listener);
182
+ };
183
+ }
184
+ setDirtyState(dirty) {
185
+ if (dirty === this.dirtyState) return;
186
+ this.dirtyState = dirty;
187
+ this.listeners.forEach((listener) => {
188
+ try {
189
+ listener(dirty);
190
+ } catch (err) {
191
+ console.error("[DirtyTracker] Error in listener:", err);
192
+ }
193
+ });
194
+ }
195
+ };
196
+ function createDirtyTracker(initialBaseline, options) {
197
+ return new DirtyTracker(initialBaseline, options);
198
+ }
199
+ function withoutDirty(tracker, fn) {
200
+ return tracker.suppress(fn);
201
+ }
202
+ function bindDirtyToHistory(options) {
203
+ const { history, getValue } = options;
204
+ const tracker = options.tracker ?? new DirtyTracker(getValue(), {
205
+ isEqual: options.isEqual,
206
+ normalize: options.normalize
207
+ });
208
+ const unsubscribe = history.subscribe(() => {
209
+ tracker.update(getValue(), history.getRevision?.());
210
+ });
211
+ return {
212
+ tracker,
213
+ get dirty() {
214
+ return tracker.dirty;
215
+ },
216
+ markClean: () => tracker.markClean(getValue()),
217
+ suppress: (fn) => {
218
+ const rebaseline = () => {
219
+ if (!tracker.suppressed) tracker.reset(getValue());
220
+ };
221
+ const result = tracker.suppress(fn);
222
+ if (isThenable(result)) {
223
+ return result.then((value) => {
224
+ rebaseline();
225
+ return value;
226
+ });
227
+ }
228
+ rebaseline();
229
+ return result;
230
+ },
231
+ refresh: () => tracker.update(getValue()),
232
+ subscribe: (listener) => tracker.subscribe(listener),
233
+ destroy: unsubscribe
234
+ };
235
+ }
236
+
237
+ export {
238
+ defaultIsEqual,
239
+ DirtyTracker,
240
+ createDirtyTracker,
241
+ withoutDirty,
242
+ bindDirtyToHistory
243
+ };
244
+ //# sourceMappingURL=chunk-YK57D7PW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dirty.ts"],"sourcesContent":["// Portions ported from soundcraft-main/crates/engine/src/lib.rs (MIT OR Apache-2.0,\n// © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n\n/**\n * Compares two values for structural equality.\n * Handles primitives, objects, arrays, and null/undefined.\n */\nexport function defaultIsEqual<T>(a: T, b: T): boolean {\n if (Object.is(a, b)) return true;\n if (\n typeof a !== \"object\" ||\n a === null ||\n typeof b !== \"object\" ||\n b === null\n ) {\n return false;\n }\n\n // Fast path for arrays\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!defaultIsEqual(a[i], b[i])) return false;\n }\n return true;\n }\n if (Array.isArray(a) !== Array.isArray(b)) {\n return false;\n }\n\n const keysA = Object.keys(a) as (keyof T)[];\n const keysB = Object.keys(b) as (keyof T)[];\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(b, key)) return false;\n if (!defaultIsEqual(a[key], b[key])) return false;\n }\n\n return true;\n}\n\nexport interface DirtyTrackerOptions<T> {\n /**\n * Equality check. When `normalize` is set it receives the normalized values.\n */\n isEqual?: (a: T, b: T) => boolean;\n /**\n * Projects a value onto the part that counts as \"the document\" before\n * comparing, e.g. `({ selection, viewport, ...doc }) => doc` so that\n * selecting, scrolling or zooming never marks the document dirty.\n */\n normalize?: (value: T) => unknown;\n}\n\ntype Thenable = { then: (...args: any[]) => any };\n\nconst isThenable = (value: unknown): value is Thenable =>\n !!value &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as Thenable).then === \"function\";\n\n/**\n * Tracks a baseline (saved) value and determines whether the current value is\n * dirty (modified).\n *\n * Stateless use: `isDirty(current)`. Stateful use: feed values with\n * `update(current, revision?)`, read `dirty`, `subscribe` to changes, call\n * `markClean()` after saving and wrap programmatic loads in `suppress(fn)`.\n */\nexport class DirtyTracker<T = unknown> {\n private baseline: T;\n private readonly isEqual: (a: T, b: T) => boolean;\n private readonly normalize?: (value: T) => unknown;\n private normalizedBaseline: { value: unknown } | null = null;\n /** `isDirty` result as of a revision (and baseline version). */\n private cache: { revision: number; baseline: number; dirty: boolean } | null =\n null;\n private baselineVersion = 0;\n private suppressDepth = 0;\n private hasValue = false;\n private lastValue: T | undefined;\n private dirtyState = false;\n private listeners = new Set<(dirty: boolean) => void>();\n\n constructor(\n initialBaseline: T,\n options?: DirtyTrackerOptions<T> | ((a: T, b: T) => boolean),\n ) {\n this.baseline = initialBaseline;\n if (typeof options === \"function\") {\n this.isEqual = options;\n } else {\n this.isEqual = options?.isEqual ?? defaultIsEqual;\n this.normalize = options?.normalize;\n }\n }\n\n /**\n * Returns current baseline value.\n */\n public getBaseline(): T {\n return this.baseline;\n }\n\n private changeBaseline(value: T): void {\n this.baseline = value;\n this.baselineVersion++;\n this.normalizedBaseline = null;\n this.cache = null;\n }\n\n /**\n * Sets a new baseline value without triggering any dirty state.\n * If a value is being tracked (`update`), `dirty` is recomputed against it.\n */\n public setBaseline(value: T): void {\n this.changeBaseline(value);\n if (this.hasValue) {\n this.setDirtyState(this.compare(this.lastValue as T));\n }\n }\n\n private compare(current: T): boolean {\n if (!this.normalize) return !this.isEqual(this.baseline, current);\n this.normalizedBaseline ??= { value: this.normalize(this.baseline) };\n return !this.isEqual(\n this.normalizedBaseline.value as T,\n this.normalize(current) as T,\n );\n }\n\n /**\n * Checks whether the given value differs from the baseline.\n * With a `revision` (e.g. `HistoryManager.getRevision()`), the result is\n * cached and reused for the same revision until the baseline changes.\n */\n public isDirty(current: T, revision?: number): boolean {\n if (revision === undefined) return this.compare(current);\n const cache = this.cache;\n if (\n cache &&\n cache.revision === revision &&\n cache.baseline === this.baselineVersion\n ) {\n return cache.dirty;\n }\n const dirty = this.compare(current);\n this.cache = { revision, baseline: this.baselineVersion, dirty };\n return dirty;\n }\n\n /**\n * Resets the baseline to the provided current value (or clears dirty status).\n */\n public reset(current: T): void {\n this.changeBaseline(current);\n this.hasValue = true;\n this.lastValue = current;\n this.setDirtyState(false);\n }\n\n /**\n * Records the current value and returns whether it is dirty. While\n * suppressed (`suppress`), the value becomes the new baseline instead, so\n * programmatic loads and restores never mark the document dirty.\n */\n public update(current: T, revision?: number): boolean {\n this.hasValue = true;\n this.lastValue = current;\n if (this.suppressDepth > 0) {\n this.changeBaseline(current);\n this.setDirtyState(false);\n return false;\n }\n const dirty = this.isDirty(current, revision);\n this.setDirtyState(dirty);\n return dirty;\n }\n\n /** Whether the last value passed to `update` differs from the baseline. */\n public get dirty(): boolean {\n return this.dirtyState;\n }\n\n /** Whether `suppress` is currently running. */\n public get suppressed(): boolean {\n return this.suppressDepth > 0;\n }\n\n /**\n * Marks the document saved: `current` (default: the last value passed to\n * `update`) becomes the baseline.\n */\n public markClean(...args: [] | [current: T]): void {\n if (args.length > 0) {\n this.reset(args[0] as T);\n } else if (this.hasValue) {\n this.reset(this.lastValue as T);\n } else {\n this.setDirtyState(false);\n }\n }\n\n /**\n * Runs `fn` without marking anything dirty: every `update` during it\n * rebaselines. Re-entrant (depth counter). If `fn` returns a promise, the\n * suppression lasts until it settles.\n */\n public suppress<R>(fn: () => R): R {\n this.suppressDepth++;\n let result: R;\n try {\n result = fn();\n } catch (error) {\n this.suppressDepth--;\n throw error;\n }\n if (isThenable(result)) {\n const release = () => {\n this.suppressDepth--;\n };\n return (result as unknown as Promise<unknown>).then(\n (value) => {\n release();\n return value;\n },\n (error) => {\n release();\n throw error;\n },\n ) as unknown as R;\n }\n this.suppressDepth--;\n return result;\n }\n\n /**\n * Subscribes to changes of `dirty`. The listener is called immediately with\n * the current value. Returns an unsubscribe function.\n */\n public subscribe(listener: (dirty: boolean) => void): () => void {\n this.listeners.add(listener);\n listener(this.dirtyState);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private setDirtyState(dirty: boolean): void {\n if (dirty === this.dirtyState) return;\n this.dirtyState = dirty;\n this.listeners.forEach((listener) => {\n try {\n listener(dirty);\n } catch (err) {\n console.error(\"[DirtyTracker] Error in listener:\", err);\n }\n });\n }\n}\n\n/**\n * Creates a DirtyTracker instance.\n */\nexport function createDirtyTracker<T>(\n initialBaseline: T,\n options?: DirtyTrackerOptions<T> | ((a: T, b: T) => boolean),\n): DirtyTracker<T> {\n return new DirtyTracker(initialBaseline, options);\n}\n\n/**\n * Runs `fn` with dirty tracking suppressed (see `DirtyTracker.suppress`).\n */\nexport function withoutDirty<R>(tracker: DirtyTracker<any>, fn: () => R): R {\n return tracker.suppress(fn);\n}\n\n/** Minimal history surface used by `bindDirtyToHistory` (`HistoryManager` satisfies it). */\nexport interface DirtyHistorySource {\n subscribe(listener: (...args: any[]) => void): () => void;\n getRevision?(): number;\n}\n\nexport interface BindDirtyToHistoryOptions<T> extends DirtyTrackerOptions<T> {\n history: DirtyHistorySource;\n /** Reads the current document value. */\n getValue: () => T;\n /** Existing tracker to drive (default: a new one baselined on `getValue()`). */\n tracker?: DirtyTracker<T>;\n}\n\nexport interface HistoryDirtyBinding<T> {\n readonly tracker: DirtyTracker<T>;\n /** Whether the document differs from the saved baseline. */\n readonly dirty: boolean;\n /** Marks the current value as saved. */\n markClean(): void;\n /** Runs `fn` (e.g. a load or restore) without marking the document dirty. */\n suppress<R>(fn: () => R): R;\n /** Re-reads the value after a change made outside the history. */\n refresh(): boolean;\n subscribe(listener: (dirty: boolean) => void): () => void;\n /** Stops listening to the history. */\n destroy(): void;\n}\n\n/**\n * Keeps a `DirtyTracker` in step with a `HistoryManager`: every history change\n * re-reads the value (cached per history revision), so undoing back to the\n * saved state makes the document clean again.\n */\nexport function bindDirtyToHistory<T>(\n options: BindDirtyToHistoryOptions<T>,\n): HistoryDirtyBinding<T> {\n const { history, getValue } = options;\n const tracker =\n options.tracker ??\n new DirtyTracker<T>(getValue(), {\n isEqual: options.isEqual,\n normalize: options.normalize,\n });\n const unsubscribe = history.subscribe(() => {\n tracker.update(getValue(), history.getRevision?.());\n });\n return {\n tracker,\n get dirty() {\n return tracker.dirty;\n },\n markClean: () => tracker.markClean(getValue()),\n suppress: <R>(fn: () => R): R => {\n // The loaded value becomes the baseline even if the load never touched the history.\n const rebaseline = () => {\n if (!tracker.suppressed) tracker.reset(getValue());\n };\n const result = tracker.suppress(fn);\n if (isThenable(result)) {\n return (result as unknown as Promise<unknown>).then((value) => {\n rebaseline();\n return value;\n }) as unknown as R;\n }\n rebaseline();\n return result;\n },\n refresh: () => tracker.update(getValue()),\n subscribe: (listener) => tracker.subscribe(listener),\n destroy: unsubscribe,\n };\n}\n"],"mappings":"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","names":[]}
@@ -10,7 +10,7 @@
10
10
  var _chunkHXDMZQTDcjs = require('../chunk-HXDMZQTD.cjs');
11
11
 
12
12
 
13
- var _chunkCPOKFNBGcjs = require('../chunk-CPOKFNBG.cjs');
13
+ var _chunkI6BZB5ZHcjs = require('../chunk-I6BZB5ZH.cjs');
14
14
 
15
15
 
16
16
 
@@ -205,7 +205,7 @@ var CommandRegistry = (_class = class {
205
205
  const unknown = unknownParams(spec, input);
206
206
  if (unknown) return { ok: false, id, code: "invalid_params", message: unknown };
207
207
  if (spec.params) {
208
- const details = _chunkCPOKFNBGcjs.validateJsonSchema.call(void 0, input, spec.params);
208
+ const details = _chunkI6BZB5ZHcjs.validateJsonSchema.call(void 0, input, spec.params);
209
209
  if (details.length > 0) {
210
210
  return {
211
211
  ok: false,
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/commands/index.cjs","../../src/commands/registry.ts","../../src/commands/palette.ts","../../src/commands/paletteController.ts","../../src/commands/agentTools.ts","../../src/commands/animation.ts"],"names":[],"mappings":"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A;AAC8B,MAAA;AACxB,MAAA;AACL,MAAA;AACD,QAAA;AACM,QAAA;AACmD,UAAA;AACO,UAAA;AACpB,UAAA;AAClD,QAAA;AACD,MAAA;AAE6C,MAAA;AAC9C,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACqB,MAAA;AACnB,MAAA;AAC6B,MAAA;AACvC,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACgE,MAAA;AAC1D,MAAA;AACL,MAAA;AACD,QAAA;AACoE,QAAA;AACvD,QAAA;AACpB,MAAA;AACS,MAAA;AAC6E,MAAA;AACvF,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACoC,MAAA;AAC9B,MAAA;AACF,MAAA;AACiB,QAAA;AACyB,QAAA;AACrD,MAAA;AACD,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACsB,MAAA;AACM,MAAA;AACpC,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACsB,MAAA;AACU,MAAA;AACxC,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACyC,MAAA;AACnC,MAAA;AACL,MAAA;AACD,QAAA;AACM,QAAA;AAC+C,UAAA;AACtD,UAAA;AACE,UAAA;AACwD,UAAA;AACZ,UAAA;AACQ,UAAA;AAC3D,QAAA;AACyB,QAAA;AAC1B,MAAA;AAEuC,MAAA;AAC1B,QAAA;AACgC,QAAA;AAC0B,QAAA;AACT,QAAA;AACG,QAAA;AAC/D,MAAA;AACH,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACwC,MAAA;AAClC,MAAA;AACH,MAAA;AACF,MAAA;AACD,QAAA;AACmC,QAAA;AACtB,QAAA;AACpB,MAAA;AACsF,MAAA;AACvF,IAAA;AACD,EAAA;AACD;AL4oBkF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/editor/editor/packages/core/dist/commands/index.cjs","sourcesContent":[null,"// Portions ported from filmcraft-main/crates/engine/src/commands.rs and crates/engine/src/lib.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\nimport { validateJsonSchema, type JsonSchema, type SchemaValidationError } from '../agent/schema.js';\nimport type { KeyboardEventLike, Platform } from './chords.js';\nimport {\n\tShortcutMap,\n\ttoShortcutBindings,\n\ttype ShortcutInput,\n\ttype ShortcutMapOptions,\n\ttype ShortcutResolveOptions\n} from './shortcuts.js';\n\n/** Object JSON Schema describing a command's parameters. */\nexport type CommandParamsSchema = JsonSchema & { type: 'object' };\n\n/**\n * Metadata and implementation of one command. Menus, shortcuts, the palette and agent\n * tools all run commands by id, so every entry point shares enablement and validation.\n */\nexport interface CommandSpec<TContext = any, TParams = any, TResult = unknown> {\n\t/** Unique id, e.g. `history.undo` or `canvas.alignLeft`. */\n\tid: string;\n\tlabel: string;\n\t/** Group / menu name shown next to the label, e.g. `Edit` or `Arrange`. */\n\tgroup?: string;\n\t/** Extra search terms for the palette. */\n\tkeywords?: readonly string[];\n\tdescription?: string;\n\t/** Object JSON Schema for `params`. Unknown keys are rejected unless `additionalProperties` allows them. */\n\tparams?: CommandParamsSchema;\n\t/** Default shortcut(s): platform-neutral chord text such as `Mod+Shift+Z`. */\n\tshortcut?: ShortcutInput;\n\t/** Scope the command (and its shortcuts) belong to, e.g. `canvas`. Omit for global commands. */\n\tscope?: string;\n\t/**\n\t * Whether the command can run: `true` (or `undefined`), `false`, or a reason string\n\t * (\"Nothing to undo\"). Receives the params a caller named, so a command acting on the\n\t * selection can still run on targets passed explicitly (`params` is `undefined` for menus).\n\t */\n\tenabled?: (context: TContext, params: TParams | undefined) => boolean | string | undefined;\n\trun: (context: TContext, params: TParams) => TResult;\n\t/**\n\t * Runs inside the registry's `history.batch(label, …)`, so all history entries the\n\t * command records become one undo step and a throw rolls back its partial changes.\n\t */\n\tundoable?: boolean;\n\t/** The command only reads state (agent tools mark it read-only). */\n\treadOnly?: boolean;\n\t/** Hide from the palette and `list()` unless `includeHidden` (still runnable by id). */\n\thidden?: boolean;\n}\n\nexport type CommandErrorCode = 'unknown_command' | 'disabled' | 'invalid_params' | 'failed';\n\nexport type CommandResult<T = unknown> =\n\t| { ok: true; id: string; result: T }\n\t| { ok: false; id: string; code: CommandErrorCode; message: string; details?: SchemaValidationError[] };\n\n/** A command as listed for menus, palettes and agents. */\nexport interface CommandEntry {\n\tid: string;\n\tlabel: string;\n\tgroup?: string;\n\tkeywords?: readonly string[];\n\tdescription?: string;\n\tparams?: CommandParamsSchema;\n\tscope?: string;\n\t/** Canonical chord text bound on the registry's platform (first = primary). */\n\tshortcuts: string[];\n\tenabled: boolean;\n\t/** Why the command is disabled (only when `enabled` is false). */\n\tdisabledReason?: string;\n\tundoable: boolean;\n\treadOnly: boolean;\n}\n\nexport interface CommandListOptions<TContext = any> {\n\t/** Only global commands plus commands of these scopes. */\n\tscope?: string | readonly string[];\n\tenabledOnly?: boolean;\n\tincludeHidden?: boolean;\n\t/** Context for the enablement check (default: the registry's context). */\n\tcontext?: TContext;\n}\n\n/** Minimal history surface (`HistoryManager` satisfies it). */\nexport interface CommandHistoryTarget {\n\tbatch(name: string, fn: () => void): void;\n}\n\nexport interface CommandRegistryOptions<TContext = any> {\n\t/** Default context passed to `enabled` / `run` (a value or a getter). */\n\tcontext?: TContext | (() => TContext);\n\t/** Wraps `undoable` commands in one history step (see `CommandSpec.undoable`). */\n\thistory?: CommandHistoryTarget;\n\t/** Options for the registry's `ShortcutMap`. */\n\tshortcuts?: ShortcutMapOptions;\n}\n\nexport class CommandRegistryError extends Error {\n\treadonly code: 'duplicate_command' | 'invalid_command';\n\n\tconstructor(code: CommandRegistryError['code'], message: string) {\n\t\tsuper(message);\n\t\tthis.name = 'CommandRegistryError';\n\t\tthis.code = code;\n\t}\n}\n\nconst COMMAND_ID_REGEX = /^[A-Za-z0-9_.:-]{1,128}$/;\n\ninterface Registered {\n\tspec: CommandSpec;\n\tunbind: () => void;\n}\n\nfunction enablement(result: boolean | string | undefined): { enabled: boolean; reason?: string } {\n\tif (result === undefined || result === true) return { enabled: true };\n\tif (result === false) return { enabled: false, reason: 'This command is not available right now.' };\n\treturn { enabled: false, reason: String(result) };\n}\n\n/** Errors for params the command does not accept, naming the accepted keys. */\nfunction unknownParams(spec: CommandSpec, params: Record<string, unknown>): string | null {\n\tconst keys = Object.keys(params).filter((k) => params[k] !== undefined);\n\tif (keys.length === 0) return null;\n\tconst schema = spec.params;\n\tif (schema && schema.additionalProperties !== undefined && schema.additionalProperties !== false) return null;\n\tconst accepted = Object.keys(schema?.properties ?? {});\n\tconst bad = keys.find((k) => !accepted.includes(k));\n\tif (!bad) return null;\n\treturn `Unknown parameter \"${bad}\" for command \"${spec.id}\"; expected ${\n\t\taccepted.length ? accepted.map((k) => `\"${k}\"`).join(', ') : 'no parameters'\n\t}.`;\n}\n\nconst isThenable = (value: unknown): value is PromiseLike<unknown> =>\n\t!!value && (typeof value === 'object' || typeof value === 'function') && typeof (value as any).then === 'function';\n\n/**\n * Registry of commands: one id per action, shared by shortcuts (`shortcuts`), the\n * command palette (`list` + `rankCommands`) and agent tools (`createCommandToolProvider`).\n */\nexport class CommandRegistry<TContext = any> {\n\t/** Shortcut bindings of the registered commands (plus any user overrides). */\n\treadonly shortcuts: ShortcutMap;\n\n\tprivate readonly commands = new Map<string, Registered>();\n\tprivate readonly listeners = new Set<() => void>();\n\tprivate readonly options: CommandRegistryOptions<TContext>;\n\tprivate version = 0;\n\t// Set while register/unregister change the shortcut map, so they notify once.\n\tprivate muted = false;\n\n\tconstructor(options: CommandRegistryOptions<TContext> = {}) {\n\t\tthis.options = options;\n\t\tthis.shortcuts = new ShortcutMap([], options.shortcuts);\n\t\tthis.shortcuts.subscribe(() => {\n\t\t\tif (!this.muted) this.notify();\n\t\t});\n\t}\n\n\t/** Monotonic counter bumped on every change (useful as a snapshot key). */\n\tgetVersion(): number {\n\t\treturn this.version;\n\t}\n\n\t/** The default context. */\n\tget context(): TContext {\n\t\tconst ctx = this.options.context;\n\t\treturn (typeof ctx === 'function' ? (ctx as () => TContext)() : ctx) as TContext;\n\t}\n\n\t/**\n\t * Registers a command (or several) and returns a function that unregisters it.\n\t * Throws a `CommandRegistryError` for a duplicate or invalid id, or invalid chord text.\n\t */\n\tregister(spec: CommandSpec<TContext> | readonly CommandSpec<TContext>[]): () => void {\n\t\tconst specs = (Array.isArray(spec) ? spec : [spec]) as CommandSpec<TContext>[];\n\t\tconst seen = new Set<string>();\n\t\tfor (const s of specs) {\n\t\t\tif (!s || typeof s.id !== 'string' || !COMMAND_ID_REGEX.test(s.id)) {\n\t\t\t\tthrow new CommandRegistryError('invalid_command', `Invalid command id \"${s?.id}\".`);\n\t\t\t}\n\t\t\tif (typeof s.run !== 'function') {\n\t\t\t\tthrow new CommandRegistryError('invalid_command', `Command \"${s.id}\" has no run function.`);\n\t\t\t}\n\t\t\tif (this.commands.has(s.id) || seen.has(s.id)) {\n\t\t\t\tthrow new CommandRegistryError('duplicate_command', `Command \"${s.id}\" is already registered.`);\n\t\t\t}\n\t\t\tseen.add(s.id);\n\t\t}\n\t\t// Parse every chord before registering anything, so a bad shortcut registers nothing.\n\t\tconst bindings = specs.map((s) => toShortcutBindings(s.id, s.shortcut, s.scope));\n\t\tconst unbinds: Array<() => void> = [];\n\t\tthis.muted = true;\n\t\ttry {\n\t\t\tfor (const b of bindings) unbinds.push(this.shortcuts.bind(b));\n\t\t} catch (error) {\n\t\t\tunbinds.forEach((u) => u());\n\t\t\tthrow new CommandRegistryError('invalid_command', error instanceof Error ? error.message : String(error));\n\t\t} finally {\n\t\t\tthis.muted = false;\n\t\t}\n\t\tspecs.forEach((s, i) => this.commands.set(s.id, { spec: s, unbind: unbinds[i] }));\n\t\tthis.notify();\n\t\tlet registered = true;\n\t\treturn () => {\n\t\t\tif (!registered) return;\n\t\t\tregistered = false;\n\t\t\tlet changed = false;\n\t\t\tfor (const s of specs) {\n\t\t\t\tconst entry = this.commands.get(s.id);\n\t\t\t\tif (entry && entry.spec === s) {\n\t\t\t\t\tthis.commands.delete(s.id);\n\t\t\t\t\tthis.unbindQuietly(entry);\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (changed) this.notify();\n\t\t};\n\t}\n\n\t/** Removes a command by id. Returns whether it was registered. */\n\tunregister(id: string): boolean {\n\t\tconst entry = this.commands.get(id);\n\t\tif (!entry) return false;\n\t\tthis.commands.delete(id);\n\t\tthis.unbindQuietly(entry);\n\t\tthis.notify();\n\t\treturn true;\n\t}\n\n\tprivate unbindQuietly(entry: Registered) {\n\t\tthis.muted = true;\n\t\ttry {\n\t\t\tentry.unbind();\n\t\t} finally {\n\t\t\tthis.muted = false;\n\t\t}\n\t}\n\n\thas(id: string): boolean {\n\t\treturn this.commands.has(id);\n\t}\n\n\tget(id: string): CommandSpec<TContext> | undefined {\n\t\treturn this.commands.get(id)?.spec;\n\t}\n\n\t/** Registered ids in registration order. */\n\tids(): string[] {\n\t\treturn [...this.commands.keys()];\n\t}\n\n\t/** Whether `id` can run now (`{ enabled, reason }`); `params` are the targets a caller named. */\n\tisEnabled(id: string, params?: unknown, context: TContext = this.context): { enabled: boolean; reason?: string } {\n\t\tconst spec = this.get(id);\n\t\tif (!spec) return { enabled: false, reason: `Unknown command \"${id}\".` };\n\t\tif (!spec.enabled) return { enabled: true };\n\t\ttry {\n\t\t\treturn enablement(spec.enabled(context, params as any));\n\t\t} catch (error) {\n\t\t\treturn { enabled: false, reason: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** Commands for menus, palettes and agents, with their current enablement. */\n\tlist(options: CommandListOptions<TContext> = {}): CommandEntry[] {\n\t\tconst scopes = options.scope === undefined ? null : typeof options.scope === 'string' ? [options.scope] : options.scope;\n\t\tconst context = 'context' in options ? (options.context as TContext) : this.context;\n\t\tconst platform = this.shortcuts.platform;\n\t\tconst out: CommandEntry[] = [];\n\t\tfor (const { spec } of this.commands.values()) {\n\t\t\tif (spec.hidden && !options.includeHidden) continue;\n\t\t\tif (scopes && spec.scope !== undefined && !scopes.includes(spec.scope)) continue;\n\t\t\tconst { enabled, reason } = this.isEnabled(spec.id, undefined, context);\n\t\t\tif (options.enabledOnly && !enabled) continue;\n\t\t\tout.push({\n\t\t\t\tid: spec.id,\n\t\t\t\tlabel: spec.label,\n\t\t\t\t...(spec.group !== undefined ? { group: spec.group } : {}),\n\t\t\t\t...(spec.keywords ? { keywords: spec.keywords } : {}),\n\t\t\t\t...(spec.description !== undefined ? { description: spec.description } : {}),\n\t\t\t\t...(spec.params ? { params: spec.params } : {}),\n\t\t\t\t...(spec.scope !== undefined ? { scope: spec.scope } : {}),\n\t\t\t\tshortcuts: this.shortcuts.forCommand(spec.id, platform),\n\t\t\t\tenabled,\n\t\t\t\t...(enabled ? {} : { disabledReason: reason }),\n\t\t\t\tundoable: !!spec.undoable,\n\t\t\t\treadOnly: !!spec.readOnly\n\t\t\t});\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate check(id: string, params: unknown, context: TContext): CommandResult<never> | { spec: CommandSpec<TContext>; params: any } {\n\t\tconst spec = this.get(id);\n\t\tif (!spec) return { ok: false, id, code: 'unknown_command', message: `Unknown command \"${id}\".` };\n\t\tconst input = params ?? {};\n\t\tif (typeof input !== 'object' || Array.isArray(input)) {\n\t\t\treturn { ok: false, id, code: 'invalid_params', message: `Parameters for command \"${id}\" must be an object.` };\n\t\t}\n\t\tconst unknown = unknownParams(spec, input as Record<string, unknown>);\n\t\tif (unknown) return { ok: false, id, code: 'invalid_params', message: unknown };\n\t\tif (spec.params) {\n\t\t\tconst details = validateJsonSchema(input, spec.params);\n\t\t\tif (details.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tid,\n\t\t\t\t\tcode: 'invalid_params',\n\t\t\t\t\tmessage: `Invalid parameters for command \"${id}\": ${details.map((d) => `${d.path} ${d.message}`).join('; ')}`,\n\t\t\t\t\tdetails\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tconst { enabled, reason } = this.isEnabled(id, params === undefined ? undefined : input, context);\n\t\tif (!enabled) return { ok: false, id, code: 'disabled', message: reason ?? 'Disabled.' };\n\t\treturn { spec, params: input };\n\t}\n\n\tprivate invoke(spec: CommandSpec<TContext>, params: any, context: TContext): unknown {\n\t\tconst history = this.options.history;\n\t\tif (!spec.undoable || !history) return spec.run(context, params);\n\t\tlet value: unknown;\n\t\t// `batch` rolls back already-recorded steps and rethrows when `run` throws.\n\t\thistory.batch(spec.label, () => {\n\t\t\tvalue = spec.run(context, params);\n\t\t});\n\t\treturn value;\n\t}\n\n\t/**\n\t * Runs a command synchronously and returns a structured result; never throws.\n\t * A command whose `run` returns a promise yields `{ ok: true, result: promise }`\n\t * (use `executeAsync` to await it and catch its rejection).\n\t */\n\texecute<T = unknown>(id: string, params?: unknown, context: TContext = this.context): CommandResult<T> {\n\t\tconst checked = this.check(id, params, context);\n\t\tif ('ok' in checked) return checked;\n\t\ttry {\n\t\t\tconst result = this.invoke(checked.spec, checked.params, context);\n\t\t\tthis.notify();\n\t\t\treturn { ok: true, id, result: result as T };\n\t\t} catch (error) {\n\t\t\tthis.notify();\n\t\t\treturn { ok: false, id, code: 'failed', message: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** Like `execute`, but awaits async commands (rejections become `failed` results). */\n\tasync executeAsync<T = unknown>(id: string, params?: unknown, context: TContext = this.context): Promise<CommandResult<T>> {\n\t\tconst outcome = this.execute<T>(id, params, context);\n\t\tif (!outcome.ok || !isThenable(outcome.result)) return outcome;\n\t\ttry {\n\t\t\tconst result = await outcome.result;\n\t\t\tthis.notify();\n\t\t\treturn { ok: true, id, result: result as T };\n\t\t} catch (error) {\n\t\t\tthis.notify();\n\t\t\treturn { ok: false, id, code: 'failed', message: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/**\n\t * Resolves a keydown event to a command (see `ShortcutMap.resolve`) and runs it when\n\t * enabled, calling `preventDefault()`. Disabled commands leave the event alone, so the\n\t * browser or a focused editor can still handle it. Returns the result, or `null`.\n\t */\n\thandleKeyDown(\n\t\tevent: KeyboardEventLike,\n\t\toptions: ShortcutResolveOptions & { context?: TContext } = {}\n\t): CommandResult | null {\n\t\tif (event.defaultPrevented) return null;\n\t\tconst match = this.shortcuts.resolve(event, options);\n\t\tif (!match) return null;\n\t\tconst context = 'context' in options ? (options.context as TContext) : this.context;\n\t\tif (!this.isEnabled(match.command, undefined, context).enabled) return null;\n\t\tevent.preventDefault?.();\n\t\treturn this.execute(match.command, undefined, context);\n\t}\n\n\t/** Display-ready primary shortcut of a command (`⇧⌘Z` / `Ctrl+Shift+Z`), or `undefined`. */\n\tshortcutFor(id: string, platform: Platform = this.shortcuts.platform): string | undefined {\n\t\treturn this.shortcuts.forCommand(id, platform)[0];\n\t}\n\n\t/**\n\t * Subscribes to changes: commands registered or removed, shortcut overrides, executed\n\t * commands and `invalidate()` calls (e.g. when enablement inputs change).\n\t */\n\tsubscribe(listener: () => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\t/** Tells subscribers that enablement may have changed (e.g. the selection changed). */\n\tinvalidate(): void {\n\t\tthis.notify();\n\t}\n\n\tprivate notify(): void {\n\t\tthis.version++;\n\t\tfor (const listener of Array.from(this.listeners)) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[CommandRegistry] Error in listener:', error);\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport function createCommandRegistry<TContext = any>(options?: CommandRegistryOptions<TContext>): CommandRegistry<TContext> {\n\treturn new CommandRegistry<TContext>(options);\n}\n\n/** Identity helper that types a command spec. */\nexport function defineCommand<TContext = any, TParams = any, TResult = unknown>(\n\tspec: CommandSpec<TContext, TParams, TResult>\n): CommandSpec<TContext, TParams, TResult> {\n\treturn spec;\n}\n","// Portions ported from photocraft-main/crates/ui-egui/src/palette.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n\n/** The fields `rankCommands` searches (a `CommandEntry` satisfies it). */\nexport interface RankableCommand {\n\tid: string;\n\tlabel: string;\n\tgroup?: string;\n\tkeywords?: readonly string[];\n\tenabled?: boolean;\n}\n\nexport interface FuzzyMatch {\n\tscore: number;\n\t/** Indices of the matched characters in the text (for highlighting). */\n\tindices: number[];\n}\n\nconst WORD_SEPARATORS = new Set([' ', '-', '_', '.', '/', ':', '›', '(']);\n\n/**\n * Subsequence fuzzy match: every query character must appear in order. Consecutive\n * matches score 8, others 1; a match at a word start adds 5; longer texts lose\n * `length / 8`. Case-insensitive. Returns `null` when the query does not match.\n */\nexport function fuzzyMatch(query: string, text: string): FuzzyMatch | null {\n\tconst q = query.trim().toLowerCase();\n\tif (!q) return { score: 0, indices: [] };\n\tconst t = text.toLowerCase();\n\tlet score = 0;\n\tlet ti = 0;\n\tlet last = -2;\n\tconst indices: number[] = [];\n\tfor (const qc of q) {\n\t\tif (qc === ' ') continue;\n\t\tlet found = false;\n\t\twhile (ti < t.length) {\n\t\t\tif (t[ti] === qc) {\n\t\t\t\tscore += last === ti - 1 ? 8 : 1;\n\t\t\t\tif (ti === 0 || WORD_SEPARATORS.has(t[ti - 1])) score += 5;\n\t\t\t\tlast = ti;\n\t\t\t\tindices.push(ti);\n\t\t\t\tti++;\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tti++;\n\t\t}\n\t\tif (!found) return null;\n\t}\n\treturn { score: score - Math.floor(t.length / 8), indices };\n}\n\n/** `fuzzyMatch(query, text)?.score ?? null`. */\nexport function fuzzyScore(query: string, text: string): number | null {\n\treturn fuzzyMatch(query, text)?.score ?? null;\n}\n\n/**\n * Bonus that puts a command whose name is exactly the query ahead of every fuzzy match\n * (\"undo\" finds Undo before \"Undo Selection Change\"). Trailing ellipses are ignored.\n */\nexport function exactBonus(query: string, names: readonly string[]): number {\n\tconst q = query.trim().toLowerCase();\n\tif (!q) return 0;\n\treturn names.some((n) => n.replace(/(…|\\.\\.\\.)$/, '').trim().toLowerCase() === q) ? 1000 : 0;\n}\n\n/** Default number of recently run commands a palette remembers. */\nexport const DEFAULT_RECENT_LIMIT = 8;\n\n/** Moves `id` to the front of `list` (deduplicated, newest first, at most `cap`). Returns a new array. */\nexport function pushRecent(list: readonly string[], id: string, cap: number = DEFAULT_RECENT_LIMIT): string[] {\n\treturn [id, ...list.filter((x) => x !== id)].slice(0, Math.max(0, cap));\n}\n\nexport interface RankedCommand<T extends RankableCommand = RankableCommand> {\n\tentry: T;\n\tscore: number;\n\t/** Matched character indices in `entry.label` (empty when another field matched). */\n\tlabelIndices: number[];\n\t/** The entry was shown because it was run recently (empty query). */\n\trecent: boolean;\n}\n\nexport interface RankCommandsOptions {\n\t/** Recently run ids, newest first. Shown first for an empty query and boosted slightly otherwise. */\n\trecents?: readonly string[];\n\t/** Maximum number of recents shown for an empty query (default 8). */\n\trecentLimit?: number;\n\t/** Maximum number of results. */\n\tlimit?: number;\n\t/** Sort disabled commands after enabled ones (default `true`). */\n\tdisabledLast?: boolean;\n\t/** With an empty query: `all` (default) lists recents then every other command; `recents` lists only recents. */\n\temptyQuery?: 'all' | 'recents';\n}\n\n/**\n * Ranks commands for a palette query. Searches the label, `group label`, keywords and id;\n * an exact label or keyword match always wins. An empty query lists recent commands\n * first (deduplicated and capped), then the rest in their original order.\n */\nexport function rankCommands<T extends RankableCommand>(\n\tquery: string,\n\tentries: readonly T[],\n\toptions: RankCommandsOptions = {}\n): RankedCommand<T>[] {\n\tconst recents = options.recents ?? [];\n\tconst disabledLast = options.disabledLast ?? true;\n\tconst q = query.trim();\n\tlet ranked: Array<RankedCommand<T> & { order: number }>;\n\n\tif (!q) {\n\t\tconst byId = new Map(entries.map((e) => [e.id, e]));\n\t\tconst recentIds = [...new Set(recents)].filter((id) => byId.has(id)).slice(0, options.recentLimit ?? DEFAULT_RECENT_LIMIT);\n\t\tconst recentSet = new Set(recentIds);\n\t\tranked = recentIds.map((id, i) => ({ entry: byId.get(id)!, score: 1000 - i, labelIndices: [], recent: true, order: i }));\n\t\tif ((options.emptyQuery ?? 'all') === 'all') {\n\t\t\tentries.forEach((entry, i) => {\n\t\t\t\tif (!recentSet.has(entry.id)) ranked.push({ entry, score: 0, labelIndices: [], recent: false, order: recentIds.length + i });\n\t\t\t});\n\t\t}\n\t} else {\n\t\tranked = [];\n\t\tentries.forEach((entry, order) => {\n\t\t\tconst label = fuzzyMatch(q, entry.label);\n\t\t\tconst candidates: number[] = [];\n\t\t\tif (label) candidates.push(label.score);\n\t\t\tif (entry.group) {\n\t\t\t\tconst grouped = fuzzyScore(q, `${entry.group} ${entry.label}`);\n\t\t\t\tif (grouped !== null) candidates.push(grouped - 1);\n\t\t\t}\n\t\t\tfor (const keyword of entry.keywords ?? []) {\n\t\t\t\tconst s = fuzzyScore(q, keyword);\n\t\t\t\tif (s !== null) candidates.push(s - 2);\n\t\t\t}\n\t\t\tconst byId = fuzzyScore(q, entry.id);\n\t\t\tif (byId !== null) candidates.push(byId - 3);\n\t\t\tif (candidates.length === 0) return;\n\t\t\tconst recentIndex = recents.indexOf(entry.id);\n\t\t\tconst score =\n\t\t\t\tMath.max(...candidates) +\n\t\t\t\texactBonus(q, [entry.label, ...(entry.keywords ?? [])]) +\n\t\t\t\t(recentIndex >= 0 ? Math.max(1, 4 - recentIndex) : 0);\n\t\t\tranked.push({ entry, score, labelIndices: label?.indices ?? [], recent: false, order });\n\t\t});\n\t\tranked.sort((a, b) => b.score - a.score || a.entry.label.length - b.entry.label.length || a.order - b.order);\n\t}\n\n\tif (disabledLast) {\n\t\t// Stable partition: enabled first, keeping the ranking within each half.\n\t\tranked = [...ranked.filter((r) => r.entry.enabled !== false), ...ranked.filter((r) => r.entry.enabled === false)];\n\t}\n\tconst out = ranked.map(({ order: _order, ...rest }) => rest);\n\treturn options.limit !== undefined ? out.slice(0, options.limit) : out;\n}\n","import { formatShortcut, type KeyboardEventLike, type Platform } from './chords.js';\nimport { DEFAULT_RECENT_LIMIT, pushRecent, rankCommands, type RankedCommand } from './palette.js';\nimport type { CommandEntry, CommandRegistry, CommandResult } from './registry.js';\nimport { ShortcutMap } from './shortcuts.js';\n\n/** One palette row. */\nexport interface CommandPaletteItem extends RankedCommand<CommandEntry> {\n\tid: string;\n\tlabel: string;\n\tgroup?: string;\n\t/** Display-ready primary shortcut (`⇧⌘Z` / `Ctrl+Shift+Z`). */\n\tshortcut?: string;\n\tenabled: boolean;\n\tdisabledReason?: string;\n}\n\nexport interface CommandPaletteState {\n\topen: boolean;\n\tquery: string;\n\tactiveIndex: number;\n\titems: CommandPaletteItem[];\n\trecents: string[];\n\t/** Result of the last command the palette ran. */\n\tlastResult: CommandResult | null;\n}\n\nexport interface CommandPaletteOptions<TContext = any> {\n\t/** Context for enablement and runs (default: the registry's). */\n\tcontext?: TContext | (() => TContext);\n\t/** Only global commands plus these scopes. */\n\tscope?: string | readonly string[] | (() => string | readonly string[] | undefined);\n\t/** Initial recently run ids (newest first), e.g. restored from storage. */\n\trecents?: readonly string[];\n\t/** Called whenever the recents change, e.g. to persist them. */\n\tonRecentsChange?: (recents: string[]) => void;\n\trecentLimit?: number;\n\t/** Maximum rows (default 50). */\n\tlimit?: number;\n\t/** Platform for shortcut display (default: detected). */\n\tplatform?: Platform;\n\t/** Called after a command ran from the palette. */\n\tonRun?: (result: CommandResult) => void;\n}\n\n/**\n * Framework-agnostic command palette state (open, query, ranked rows, active row and\n * recents) over a `CommandRegistry`. The React hook and Svelte rune wrap it.\n */\nexport function createCommandPaletteController<TContext = any>(\n\tregistry: CommandRegistry<TContext>,\n\toptions: CommandPaletteOptions<TContext> = {}\n) {\n\tconst listeners = new Set<(state: CommandPaletteState) => void>();\n\tlet open = false;\n\tlet query = '';\n\tlet activeIndex = 0;\n\tlet recents = [...(options.recents ?? [])];\n\tlet lastResult: CommandResult | null = null;\n\tlet state: CommandPaletteState;\n\n\tconst context = (): TContext => {\n\t\tconst c = options.context;\n\t\tif (c === undefined) return registry.context;\n\t\treturn (typeof c === 'function' ? (c as () => TContext)() : c) as TContext;\n\t};\n\tconst scope = () => (typeof options.scope === 'function' ? options.scope() : options.scope);\n\n\tfunction computeItems(): CommandPaletteItem[] {\n\t\tif (!open) return [];\n\t\tconst entries = registry.list({ scope: scope(), context: context() });\n\t\tconst platform = options.platform ?? registry.shortcuts.platform;\n\t\treturn rankCommands(query, entries, {\n\t\t\trecents,\n\t\t\trecentLimit: options.recentLimit,\n\t\t\tlimit: options.limit ?? 50\n\t\t}).map((ranked) => ({\n\t\t\t...ranked,\n\t\t\tid: ranked.entry.id,\n\t\t\tlabel: ranked.entry.label,\n\t\t\t...(ranked.entry.group !== undefined ? { group: ranked.entry.group } : {}),\n\t\t\t...(ranked.entry.shortcuts[0] ? { shortcut: formatShortcut(ranked.entry.shortcuts[0], platform) } : {}),\n\t\t\tenabled: ranked.entry.enabled,\n\t\t\t...(ranked.entry.disabledReason ? { disabledReason: ranked.entry.disabledReason } : {})\n\t\t}));\n\t}\n\n\tfunction update() {\n\t\tconst items = computeItems();\n\t\tactiveIndex = items.length === 0 ? 0 : Math.min(Math.max(activeIndex, 0), items.length - 1);\n\t\tstate = { open, query, activeIndex, items, recents, lastResult };\n\t\tfor (const listener of Array.from(listeners)) listener(state);\n\t}\n\n\tstate = { open, query, activeIndex, items: [], recents, lastResult };\n\t// Follow registry changes only while someone listens (safe with React StrictMode remounts).\n\tlet unsubscribeRegistry: (() => void) | null = null;\n\tconst onRegistryChange = () => {\n\t\tif (open) update();\n\t};\n\n\tconst controller = {\n\t\tgetState: () => state,\n\t\tsubscribe(listener: (state: CommandPaletteState) => void): () => void {\n\t\t\tlisteners.add(listener);\n\t\t\tunsubscribeRegistry ??= registry.subscribe(onRegistryChange);\n\t\t\tif (open) update(); // catch up with changes made while nobody listened\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t\tif (listeners.size === 0) {\n\t\t\t\t\tunsubscribeRegistry?.();\n\t\t\t\t\tunsubscribeRegistry = null;\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\tsetOpen(next: boolean) {\n\t\t\tif (next === open) return;\n\t\t\topen = next;\n\t\t\tquery = '';\n\t\t\tactiveIndex = 0;\n\t\t\tupdate();\n\t\t},\n\t\ttoggle() {\n\t\t\tcontroller.setOpen(!open);\n\t\t},\n\t\tsetQuery(next: string) {\n\t\t\tif (next === query) return;\n\t\t\tquery = next;\n\t\t\tactiveIndex = 0;\n\t\t\tupdate();\n\t\t},\n\t\tsetActiveIndex(index: number) {\n\t\t\tactiveIndex = index;\n\t\t\tupdate();\n\t\t},\n\t\t/** Moves the active row by `delta`, wrapping around. */\n\t\tmove(delta: number) {\n\t\t\tconst n = state.items.length;\n\t\t\tif (n === 0) return;\n\t\t\tactiveIndex = (((activeIndex + delta) % n) + n) % n;\n\t\t\tupdate();\n\t\t},\n\t\t/** Runs a command by id, closes the palette and records it as recent. Disabled commands do nothing. */\n\t\trun(id: string): CommandResult | null {\n\t\t\tif (!registry.isEnabled(id, undefined, context()).enabled) return null;\n\t\t\topen = false;\n\t\t\tquery = '';\n\t\t\tactiveIndex = 0;\n\t\t\trecents = pushRecent(recents, id, options.recentLimit ?? DEFAULT_RECENT_LIMIT);\n\t\t\toptions.onRecentsChange?.(recents);\n\t\t\tconst result = registry.execute(id, undefined, context());\n\t\t\tlastResult = result;\n\t\t\tupdate();\n\t\t\toptions.onRun?.(result);\n\t\t\treturn result;\n\t\t},\n\t\trunActive(): CommandResult | null {\n\t\t\tconst item = state.items[state.activeIndex];\n\t\t\treturn item ? controller.run(item.id) : null;\n\t\t},\n\t\t/**\n\t\t * Keyboard handling for the palette input: ArrowUp/ArrowDown (wrap), Home/End,\n\t\t * Enter (run the active row) and Escape (close). Returns whether it handled the key.\n\t\t */\n\t\thandleKeyDown(event: KeyboardEventLike): boolean {\n\t\t\tif (!open) return false;\n\t\t\tswitch (event.key) {\n\t\t\t\tcase 'ArrowDown':\n\t\t\t\t\tcontroller.move(1);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'ArrowUp':\n\t\t\t\t\tcontroller.move(-1);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'Home':\n\t\t\t\t\tcontroller.setActiveIndex(0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'End':\n\t\t\t\t\tcontroller.setActiveIndex(state.items.length - 1);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'Enter':\n\t\t\t\t\tcontroller.runActive();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'Escape':\n\t\t\t\t\tcontroller.setOpen(false);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\tevent.preventDefault?.();\n\t\t\treturn true;\n\t\t},\n\t\t/** Re-reads commands and enablement. */\n\t\trefresh() {\n\t\t\tupdate();\n\t\t},\n\t\tdestroy() {\n\t\t\tunsubscribeRegistry?.();\n\t\t\tunsubscribeRegistry = null;\n\t\t\tlisteners.clear();\n\t\t}\n\t};\n\treturn controller;\n}\n\nexport type CommandPaletteController = ReturnType<typeof createCommandPaletteController>;\n\n/**\n * Matches a global \"open palette\" hotkey (e.g. `Mod+K`), also while typing in inputs.\n * Returns `true` when `event` is the hotkey.\n */\nexport function matchPaletteHotkey(event: KeyboardEventLike, hotkey: string, platform?: Platform): boolean {\n\tconst map = new ShortcutMap([{ command: 'palette', chord: hotkey, allowInEditable: true }], platform ? { platform } : {});\n\treturn map.resolve(event) !== null;\n}\n","// Portions ported from filmcraft-main/crates/automation/src/server.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\nimport type { AgentTool, AgentToolProvider, McpToolResult } from '../agent/registry.js';\nimport { rankCommands } from './palette.js';\nimport type { CommandRegistry, CommandResult } from './registry.js';\n\nexport interface CommandToolProviderOptions<TContext = any> {\n\t/** Provider id (default `commands`). */\n\tid?: string;\n\t/** Always prefix the tool names with `<namespace>__`. */\n\tnamespace?: string;\n\t/** Context for enablement and runs (default: the registry's). */\n\tcontext?: TContext | (() => TContext);\n\t/** Only expose global commands plus these scopes. */\n\tscope?: string | readonly string[];\n\t/** Commands agents may not see or run (e.g. ones that open dialogs). */\n\texclude?: (id: string) => boolean;\n}\n\nconst toJson = (value: unknown): unknown => {\n\tif (value === undefined) return null;\n\ttry {\n\t\treturn JSON.parse(JSON.stringify(value));\n\t} catch {\n\t\treturn String(value);\n\t}\n};\n\nfunction errorResult(result: Extract<CommandResult, { ok: false }>): McpToolResult {\n\treturn {\n\t\tisError: true,\n\t\tcontent: [{ type: 'text', text: `${result.code}: ${result.message}` }],\n\t\tstructuredContent: {\n\t\t\terror: { code: result.code, message: result.message, command: result.id, ...(result.details ? { details: result.details } : {}) }\n\t\t}\n\t};\n}\n\n/**\n * Exposes a `CommandRegistry` to agents through a `ToolRegistry`:\n *\n * - `list_commands {query?, enabledOnly?}`: ids, labels, params schemas, shortcuts and\n * enablement (with the reason a command is disabled).\n * - `run_command {id, params?}`: runs one command; disabled commands, unknown params and\n * failures come back as `isError` results with a `{code, message}` error.\n * - `run_commands {steps: [{id, params?}], stopOnError?}`: runs steps **in order**, each as\n * its own command (and undo step). It is not atomic: steps that succeeded before a failure\n * stay applied. For all-or-nothing behaviour, register a command that runs the steps\n * inside `HistoryManager.batch` (or mark it `undoable` on a registry with `history`).\n *\n * The id enum in `run_command`'s schema follows the registered commands, so the\n * `ToolRegistry` announces `listChanged` when commands are added or removed.\n */\nexport function createCommandToolProvider<TContext = any>(\n\tregistry: CommandRegistry<TContext>,\n\toptions: CommandToolProviderOptions<TContext> = {}\n): AgentToolProvider {\n\tconst context = (): TContext => {\n\t\tconst c = options.context;\n\t\tif (c === undefined) return registry.context;\n\t\treturn (typeof c === 'function' ? (c as () => TContext)() : c) as TContext;\n\t};\n\tconst visible = () =>\n\t\tregistry.list({ scope: options.scope, context: context() }).filter((e) => !options.exclude?.(e.id));\n\tconst allowed = (id: string) => registry.has(id) && !options.exclude?.(id) && visible().some((e) => e.id === id);\n\n\tasync function runOne(id: string, params: unknown): Promise<CommandResult> {\n\t\tif (!allowed(id)) return { ok: false, id, code: 'unknown_command', message: `Unknown command \"${id}\".` };\n\t\treturn registry.executeAsync(id, params ?? {}, context());\n\t}\n\n\tconst listCommands: AgentTool = {\n\t\tname: 'list_commands',\n\t\ttitle: 'List commands',\n\t\tdescription:\n\t\t\t'Lists the commands you can run with run_command: id, label, group, params (JSON Schema), shortcuts, and whether each is enabled now (with the reason when it is not).',\n\t\tinputSchema: {\n\t\t\ttype: 'object',\n\t\t\tproperties: {\n\t\t\t\tquery: { type: 'string', description: 'Fuzzy filter on label, keywords, group and id.' },\n\t\t\t\tenabledOnly: { type: 'boolean', description: 'Only commands that can run now (default false).' }\n\t\t\t},\n\t\t\tadditionalProperties: false\n\t\t},\n\t\tannotations: { title: 'List commands', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },\n\t\thandler: ({ query = '', enabledOnly = false }) => {\n\t\t\tlet entries = visible();\n\t\t\tif (enabledOnly) entries = entries.filter((e) => e.enabled);\n\t\t\tif (String(query).trim()) entries = rankCommands(String(query), entries, { disabledLast: false }).map((r) => r.entry);\n\t\t\treturn {\n\t\t\t\tcommands: entries.map((e) => ({\n\t\t\t\t\tid: e.id,\n\t\t\t\t\tlabel: e.label,\n\t\t\t\t\t...(e.group ? { group: e.group } : {}),\n\t\t\t\t\t...(e.description ? { description: e.description } : {}),\n\t\t\t\t\t...(e.params ? { params: e.params } : {}),\n\t\t\t\t\t...(e.shortcuts.length ? { shortcuts: e.shortcuts } : {}),\n\t\t\t\t\tenabled: e.enabled,\n\t\t\t\t\t...(e.disabledReason ? { disabledReason: e.disabledReason } : {}),\n\t\t\t\t\treadOnly: e.readOnly\n\t\t\t\t}))\n\t\t\t};\n\t\t}\n\t};\n\n\tconst runCommandTool = (): AgentTool => {\n\t\tconst ids = visible().map((e) => e.id);\n\t\treturn {\n\t\t\tname: 'run_command',\n\t\t\ttitle: 'Run a command',\n\t\t\tdescription:\n\t\t\t\t'Runs one command by id with optional params (see list_commands for each command\\'s params schema). Unknown params are rejected.',\n\t\t\tinputSchema: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {\n\t\t\t\t\tid: { type: 'string', description: 'Command id from list_commands.', ...(ids.length ? { enum: ids } : {}) },\n\t\t\t\t\tparams: { type: 'object', description: 'Command parameters.' }\n\t\t\t\t},\n\t\t\t\trequired: ['id'],\n\t\t\t\tadditionalProperties: false\n\t\t\t},\n\t\t\tannotations: { title: 'Run a command', readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },\n\t\t\thandler: async ({ id, params }) => {\n\t\t\t\tconst result = await runOne(String(id), params);\n\t\t\t\treturn result.ok ? { id: result.id, ok: true, result: toJson(result.result) } : errorResult(result);\n\t\t\t}\n\t\t};\n\t};\n\n\tconst runCommands: AgentTool = {\n\t\tname: 'run_commands',\n\t\ttitle: 'Run several commands',\n\t\tdescription:\n\t\t\t'Runs several commands in order: {steps: [{id, params?}], stopOnError?: true}. Each step is its own command and undo step; the batch is NOT atomic (steps before a failure stay applied). Returns {completed, failed, results: [{id, ok, result | code, message}]}.',\n\t\tinputSchema: {\n\t\t\ttype: 'object',\n\t\t\tproperties: {\n\t\t\t\tsteps: {\n\t\t\t\t\ttype: 'array',\n\t\t\t\t\tminItems: 1,\n\t\t\t\t\titems: {\n\t\t\t\t\t\ttype: 'object',\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\tid: { type: 'string', description: 'Command id.' },\n\t\t\t\t\t\t\tparams: { type: 'object', description: 'Command parameters.' }\n\t\t\t\t\t\t},\n\t\t\t\t\t\trequired: ['id'],\n\t\t\t\t\t\tadditionalProperties: false\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tstopOnError: { type: 'boolean', description: 'Stop at the first failing step (default true).' }\n\t\t\t},\n\t\t\trequired: ['steps'],\n\t\t\tadditionalProperties: false\n\t\t},\n\t\tannotations: {\n\t\t\ttitle: 'Run several commands',\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: false\n\t\t},\n\t\thandler: async ({ steps, stopOnError = true }) => {\n\t\t\tlet completed = 0;\n\t\t\tlet failed = 0;\n\t\t\tconst results: Array<Record<string, unknown>> = [];\n\t\t\tfor (const step of steps as Array<{ id: string; params?: unknown }>) {\n\t\t\t\tconst result = await runOne(step.id, step.params);\n\t\t\t\tif (result.ok) {\n\t\t\t\t\tcompleted++;\n\t\t\t\t\tresults.push({ id: result.id, ok: true, result: toJson(result.result) });\n\t\t\t\t} else {\n\t\t\t\t\tfailed++;\n\t\t\t\t\tresults.push({ id: result.id, ok: false, code: result.code, message: result.message });\n\t\t\t\t\tif (stopOnError) break;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst summary = { completed, failed, skipped: steps.length - completed - failed, results };\n\t\t\treturn failed > 0\n\t\t\t\t? { isError: true, content: [{ type: 'text', text: JSON.stringify(summary) }], structuredContent: summary }\n\t\t\t\t: summary;\n\t\t}\n\t};\n\n\treturn {\n\t\tid: options.id ?? 'commands',\n\t\tnamespace: options.namespace,\n\t\ttools: () => [listCommands, runCommandTool(), runCommands],\n\t\tsubscribe: (notify) => registry.subscribe(notify)\n\t};\n}\n","import type { CommandSpec } from './registry.js';\n\n/**\n * Animation controller surface used by `createCanvasAnimationCommands`\n * (`createCanvasAnimationController`, `useCanvasAnimation` and `createCanvasAnimation` satisfy it).\n */\nexport interface AnimationCommandTarget {\n\tgetState(): { playing: boolean; recording: boolean; time: number; duration: number; selection: readonly unknown[] };\n\tplay(): void;\n\tpause(): void;\n\ttoggle(): void;\n\tstop(): void;\n\tseek(time: number): void;\n\ttoggleRecording(): void;\n\taddKeyframe(ids?: Iterable<string>, keys?: readonly string[], time?: number): unknown;\n\tdeleteKeyframes(): unknown;\n\tsetEasing(easing: any, refs?: readonly any[]): unknown;\n\tnextKeyframe(): unknown;\n\tpreviousKeyframe(): unknown;\n\tanimateNode(nodeId: string, options: any): unknown;\n\texportCss(nodeId: string, options?: any): { css: string; exact: boolean };\n}\n\nexport interface AnimationCommandOptions {\n\t/** Id prefix (default `canvas.animation.`). */\n\tprefix?: string;\n\tgroup?: string;\n\t/** Scope (default `canvas`). Pass `null` for global. */\n\tscope?: string | null;\n}\n\nconst EASING_DESCRIPTION =\n\t'Easing preset (linear, ease, ease-in, ease-out, ease-in-out, ease-in-back, ease-out-back, spring, hold) or a CSS cubic-bezier(x1, y1, x2, y2).';\n\nconst VALUES = {\n\ttype: 'object',\n\tdescription: 'Property → value, e.g. {\"x\": 200, \"opacity\": 0.5, \"fill\": \"#ff0000\"}. Properties: x, y, width, height, rotation (degrees), scale, opacity (0–1), fill, stroke, strokeWidth, cornerRadius, motionPath (0–1).',\n\tadditionalProperties: { type: ['number', 'string'] }\n} as const;\n\n/**\n * Palette / shortcut / agent commands for canvas animation: play / pause, seek, add keyframe at\n * the playhead, delete keyframes, set easing, record toggle, keyframe navigation, \"animate\n * node from A to B over T\" and CSS export. Edits are undoable when the controller has a history.\n */\nexport function createCanvasAnimationCommands(controller: AnimationCommandTarget, options: AnimationCommandOptions = {}): CommandSpec[] {\n\tconst prefix = options.prefix ?? 'canvas.animation.';\n\tconst group = options.group ?? 'Animation';\n\tconst scope = options.scope === null ? undefined : options.scope ?? 'canvas';\n\tconst base = { group, ...(scope ? { scope } : {}) };\n\tconst hasSelection = () => (controller.getState().selection.length > 0 ? true : 'Select keyframes first.');\n\treturn [\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}togglePlay`,\n\t\t\tlabel: 'Play / Pause Animation',\n\t\t\tkeywords: ['play', 'pause', 'preview', 'timeline'],\n\t\t\tdescription: 'Plays or pauses the canvas animation from the playhead.',\n\t\t\trun: () => {\n\t\t\t\tcontroller.toggle();\n\t\t\t\treturn { playing: controller.getState().playing, time: controller.getState().time };\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}play`,\n\t\t\tlabel: 'Play Animation',\n\t\t\tkeywords: ['preview', 'timeline'],\n\t\t\tenabled: () => (controller.getState().playing ? 'Already playing.' : true),\n\t\t\trun: () => controller.play()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}pause`,\n\t\t\tlabel: 'Pause Animation',\n\t\t\tenabled: () => (controller.getState().playing ? true : 'Not playing.'),\n\t\t\trun: () => controller.pause()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}stop`,\n\t\t\tlabel: 'Stop Animation',\n\t\t\tkeywords: ['rewind', 'reset'],\n\t\t\tdescription: 'Stops playback and returns to the start.',\n\t\t\trun: () => controller.stop()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}seek`,\n\t\t\tlabel: 'Go to Time…',\n\t\t\tkeywords: ['playhead', 'scrub', 'time'],\n\t\t\tdescription: 'Moves the playhead.',\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: { time: { type: 'number', minimum: 0, description: 'Time in milliseconds.' } },\n\t\t\t\trequired: ['time']\n\t\t\t},\n\t\t\trun: (_ctx: unknown, params: { time: number }) => {\n\t\t\t\tcontroller.seek(params.time);\n\t\t\t\treturn { time: controller.getState().time };\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}addKeyframe`,\n\t\t\tlabel: 'Add Keyframe at Playhead',\n\t\t\tkeywords: ['key', 'animate', 'record'],\n\t\t\tdescription: 'Keys the selected nodes (or `ids`) at the playhead (or `time`): their animated properties, `properties`, or position.',\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {\n\t\t\t\t\tids: { type: 'array', items: { type: 'string' }, description: 'Node ids (default: selected nodes).' },\n\t\t\t\t\tproperties: { type: 'array', items: { type: 'string' }, description: 'Properties to key (default: animated ones, or x and y).' },\n\t\t\t\t\ttime: { type: 'number', minimum: 0, description: 'Time in ms (default: the playhead).' }\n\t\t\t\t}\n\t\t\t},\n\t\t\trun: (_ctx: unknown, params: { ids?: string[]; properties?: string[]; time?: number }) =>\n\t\t\t\tcontroller.addKeyframe(params?.ids, params?.properties, params?.time)\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}deleteKeyframes`,\n\t\t\tlabel: 'Delete Keyframes',\n\t\t\tkeywords: ['remove', 'clear'],\n\t\t\tenabled: hasSelection,\n\t\t\trun: () => controller.deleteKeyframes()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}setEasing`,\n\t\t\tlabel: 'Set Keyframe Easing…',\n\t\t\tkeywords: ['interpolation', 'ease', 'bezier', 'spring', 'hold', 'curve'],\n\t\t\tdescription: 'Sets the easing of the segments leaving the selected keyframes.',\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: { easing: { type: 'string', description: EASING_DESCRIPTION } },\n\t\t\t\trequired: ['easing']\n\t\t\t},\n\t\t\tenabled: hasSelection,\n\t\t\trun: (_ctx: unknown, params: { easing: string }) => controller.setEasing(params.easing)\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}toggleRecord`,\n\t\t\tlabel: 'Toggle Record Mode',\n\t\t\tkeywords: ['auto key', 'record', 'keyframe'],\n\t\t\tdescription: 'In record mode, changing a property at the playhead keys it.',\n\t\t\trun: () => {\n\t\t\t\tcontroller.toggleRecording();\n\t\t\t\treturn { recording: controller.getState().recording };\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}nextKeyframe`,\n\t\t\tlabel: 'Next Keyframe',\n\t\t\tkeywords: ['jump', 'navigate'],\n\t\t\trun: () => controller.nextKeyframe()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}previousKeyframe`,\n\t\t\tlabel: 'Previous Keyframe',\n\t\t\tkeywords: ['jump', 'navigate'],\n\t\t\trun: () => controller.previousKeyframe()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}animateNode`,\n\t\t\tlabel: 'Animate Node…',\n\t\t\tkeywords: ['tween', 'transition', 'move', 'fade'],\n\t\t\tdescription: 'Animates a node from `from` (default: its current values) to `to` over `duration` ms, starting at `delay` ms.',\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {\n\t\t\t\t\tnodeId: { type: 'string', description: 'Node to animate.' },\n\t\t\t\t\tto: VALUES,\n\t\t\t\t\tfrom: VALUES,\n\t\t\t\t\tduration: { type: 'number', exclusiveMinimum: 0, description: 'Duration in ms (default 1000).' },\n\t\t\t\t\tdelay: { type: 'number', minimum: 0, description: 'Start time in ms (default 0).' },\n\t\t\t\t\teasing: { type: 'string', description: EASING_DESCRIPTION }\n\t\t\t\t},\n\t\t\t\trequired: ['nodeId', 'to']\n\t\t\t},\n\t\t\trun: (_ctx: unknown, params: { nodeId: string; to: Record<string, number | string>; from?: Record<string, number | string>; duration?: number; delay?: number; easing?: string }) =>\n\t\t\t\tcontroller.animateNode(params.nodeId, {\n\t\t\t\t\tto: params.to,\n\t\t\t\t\t...(params.from ? { from: params.from } : {}),\n\t\t\t\t\t...(params.duration !== undefined ? { duration: params.duration } : {}),\n\t\t\t\t\t...(params.delay !== undefined ? { delay: params.delay } : {}),\n\t\t\t\t\t...(params.easing !== undefined ? { easing: params.easing } : {})\n\t\t\t\t})\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}exportCss`,\n\t\t\tlabel: 'Export Animation as CSS',\n\t\t\tkeywords: ['keyframes', 'css', 'web animations'],\n\t\t\tdescription: 'Returns CSS @keyframes for a node\\'s animation.',\n\t\t\treadOnly: true,\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: { nodeId: { type: 'string' } },\n\t\t\t\trequired: ['nodeId']\n\t\t\t},\n\t\t\trun: (_ctx: unknown, params: { nodeId: string }) => controller.exportCss(params.nodeId)\n\t\t}\n\t];\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/commands/index.cjs","../../src/commands/registry.ts","../../src/commands/palette.ts","../../src/commands/paletteController.ts","../../src/commands/agentTools.ts","../../src/commands/animation.ts"],"names":[],"mappings":"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A;AAC8B,MAAA;AACxB,MAAA;AACL,MAAA;AACD,QAAA;AACM,QAAA;AACmD,UAAA;AACO,UAAA;AACpB,UAAA;AAClD,QAAA;AACD,MAAA;AAE6C,MAAA;AAC9C,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACqB,MAAA;AACnB,MAAA;AAC6B,MAAA;AACvC,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACgE,MAAA;AAC1D,MAAA;AACL,MAAA;AACD,QAAA;AACoE,QAAA;AACvD,QAAA;AACpB,MAAA;AACS,MAAA;AAC6E,MAAA;AACvF,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACoC,MAAA;AAC9B,MAAA;AACF,MAAA;AACiB,QAAA;AACyB,QAAA;AACrD,MAAA;AACD,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACsB,MAAA;AACM,MAAA;AACpC,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACsB,MAAA;AACU,MAAA;AACxC,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACyC,MAAA;AACnC,MAAA;AACL,MAAA;AACD,QAAA;AACM,QAAA;AAC+C,UAAA;AACtD,UAAA;AACE,UAAA;AACwD,UAAA;AACZ,UAAA;AACQ,UAAA;AAC3D,QAAA;AACyB,QAAA;AAC1B,MAAA;AAEuC,MAAA;AAC1B,QAAA;AACgC,QAAA;AAC0B,QAAA;AACT,QAAA;AACG,QAAA;AAC/D,MAAA;AACH,IAAA;AACA,IAAA;AACI,MAAA;AACU,MAAA;AACN,MAAA;AACwC,MAAA;AAClC,MAAA;AACH,MAAA;AACF,MAAA;AACD,QAAA;AACmC,QAAA;AACtB,QAAA;AACpB,MAAA;AACsF,MAAA;AACvF,IAAA;AACD,EAAA;AACD;AL4oBkF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/editor/editor/packages/core/dist/commands/index.cjs","sourcesContent":[null,"// Portions ported from filmcraft-main/crates/engine/src/commands.rs and crates/engine/src/lib.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\nimport { validateJsonSchema, type JsonSchema, type SchemaValidationError } from '../tools/schema.js';\nimport type { KeyboardEventLike, Platform } from './chords.js';\nimport {\n\tShortcutMap,\n\ttoShortcutBindings,\n\ttype ShortcutInput,\n\ttype ShortcutMapOptions,\n\ttype ShortcutResolveOptions\n} from './shortcuts.js';\n\n/** Object JSON Schema describing a command's parameters. */\nexport type CommandParamsSchema = JsonSchema & { type: 'object' };\n\n/**\n * Metadata and implementation of one command. Menus, shortcuts, the palette and agent\n * tools all run commands by id, so every entry point shares enablement and validation.\n */\nexport interface CommandSpec<TContext = any, TParams = any, TResult = unknown> {\n\t/** Unique id, e.g. `history.undo` or `canvas.alignLeft`. */\n\tid: string;\n\tlabel: string;\n\t/** Group / menu name shown next to the label, e.g. `Edit` or `Arrange`. */\n\tgroup?: string;\n\t/** Extra search terms for the palette. */\n\tkeywords?: readonly string[];\n\tdescription?: string;\n\t/** Object JSON Schema for `params`. Unknown keys are rejected unless `additionalProperties` allows them. */\n\tparams?: CommandParamsSchema;\n\t/** Default shortcut(s): platform-neutral chord text such as `Mod+Shift+Z`. */\n\tshortcut?: ShortcutInput;\n\t/** Scope the command (and its shortcuts) belong to, e.g. `canvas`. Omit for global commands. */\n\tscope?: string;\n\t/**\n\t * Whether the command can run: `true` (or `undefined`), `false`, or a reason string\n\t * (\"Nothing to undo\"). Receives the params a caller named, so a command acting on the\n\t * selection can still run on targets passed explicitly (`params` is `undefined` for menus).\n\t */\n\tenabled?: (context: TContext, params: TParams | undefined) => boolean | string | undefined;\n\trun: (context: TContext, params: TParams) => TResult;\n\t/**\n\t * Runs inside the registry's `history.batch(label, …)`, so all history entries the\n\t * command records become one undo step and a throw rolls back its partial changes.\n\t */\n\tundoable?: boolean;\n\t/** The command only reads state (agent tools mark it read-only). */\n\treadOnly?: boolean;\n\t/** Hide from the palette and `list()` unless `includeHidden` (still runnable by id). */\n\thidden?: boolean;\n}\n\nexport type CommandErrorCode = 'unknown_command' | 'disabled' | 'invalid_params' | 'failed';\n\nexport type CommandResult<T = unknown> =\n\t| { ok: true; id: string; result: T }\n\t| { ok: false; id: string; code: CommandErrorCode; message: string; details?: SchemaValidationError[] };\n\n/** A command as listed for menus, palettes and agents. */\nexport interface CommandEntry {\n\tid: string;\n\tlabel: string;\n\tgroup?: string;\n\tkeywords?: readonly string[];\n\tdescription?: string;\n\tparams?: CommandParamsSchema;\n\tscope?: string;\n\t/** Canonical chord text bound on the registry's platform (first = primary). */\n\tshortcuts: string[];\n\tenabled: boolean;\n\t/** Why the command is disabled (only when `enabled` is false). */\n\tdisabledReason?: string;\n\tundoable: boolean;\n\treadOnly: boolean;\n}\n\nexport interface CommandListOptions<TContext = any> {\n\t/** Only global commands plus commands of these scopes. */\n\tscope?: string | readonly string[];\n\tenabledOnly?: boolean;\n\tincludeHidden?: boolean;\n\t/** Context for the enablement check (default: the registry's context). */\n\tcontext?: TContext;\n}\n\n/** Minimal history surface (`HistoryManager` satisfies it). */\nexport interface CommandHistoryTarget {\n\tbatch(name: string, fn: () => void): void;\n}\n\nexport interface CommandRegistryOptions<TContext = any> {\n\t/** Default context passed to `enabled` / `run` (a value or a getter). */\n\tcontext?: TContext | (() => TContext);\n\t/** Wraps `undoable` commands in one history step (see `CommandSpec.undoable`). */\n\thistory?: CommandHistoryTarget;\n\t/** Options for the registry's `ShortcutMap`. */\n\tshortcuts?: ShortcutMapOptions;\n}\n\nexport class CommandRegistryError extends Error {\n\treadonly code: 'duplicate_command' | 'invalid_command';\n\n\tconstructor(code: CommandRegistryError['code'], message: string) {\n\t\tsuper(message);\n\t\tthis.name = 'CommandRegistryError';\n\t\tthis.code = code;\n\t}\n}\n\nconst COMMAND_ID_REGEX = /^[A-Za-z0-9_.:-]{1,128}$/;\n\ninterface Registered {\n\tspec: CommandSpec;\n\tunbind: () => void;\n}\n\nfunction enablement(result: boolean | string | undefined): { enabled: boolean; reason?: string } {\n\tif (result === undefined || result === true) return { enabled: true };\n\tif (result === false) return { enabled: false, reason: 'This command is not available right now.' };\n\treturn { enabled: false, reason: String(result) };\n}\n\n/** Errors for params the command does not accept, naming the accepted keys. */\nfunction unknownParams(spec: CommandSpec, params: Record<string, unknown>): string | null {\n\tconst keys = Object.keys(params).filter((k) => params[k] !== undefined);\n\tif (keys.length === 0) return null;\n\tconst schema = spec.params;\n\tif (schema && schema.additionalProperties !== undefined && schema.additionalProperties !== false) return null;\n\tconst accepted = Object.keys(schema?.properties ?? {});\n\tconst bad = keys.find((k) => !accepted.includes(k));\n\tif (!bad) return null;\n\treturn `Unknown parameter \"${bad}\" for command \"${spec.id}\"; expected ${\n\t\taccepted.length ? accepted.map((k) => `\"${k}\"`).join(', ') : 'no parameters'\n\t}.`;\n}\n\nconst isThenable = (value: unknown): value is PromiseLike<unknown> =>\n\t!!value && (typeof value === 'object' || typeof value === 'function') && typeof (value as any).then === 'function';\n\n/**\n * Registry of commands: one id per action, shared by shortcuts (`shortcuts`), the\n * command palette (`list` + `rankCommands`) and agent tools (`createCommandToolProvider`).\n */\nexport class CommandRegistry<TContext = any> {\n\t/** Shortcut bindings of the registered commands (plus any user overrides). */\n\treadonly shortcuts: ShortcutMap;\n\n\tprivate readonly commands = new Map<string, Registered>();\n\tprivate readonly listeners = new Set<() => void>();\n\tprivate readonly options: CommandRegistryOptions<TContext>;\n\tprivate version = 0;\n\t// Set while register/unregister change the shortcut map, so they notify once.\n\tprivate muted = false;\n\n\tconstructor(options: CommandRegistryOptions<TContext> = {}) {\n\t\tthis.options = options;\n\t\tthis.shortcuts = new ShortcutMap([], options.shortcuts);\n\t\tthis.shortcuts.subscribe(() => {\n\t\t\tif (!this.muted) this.notify();\n\t\t});\n\t}\n\n\t/** Monotonic counter bumped on every change (useful as a snapshot key). */\n\tgetVersion(): number {\n\t\treturn this.version;\n\t}\n\n\t/** The default context. */\n\tget context(): TContext {\n\t\tconst ctx = this.options.context;\n\t\treturn (typeof ctx === 'function' ? (ctx as () => TContext)() : ctx) as TContext;\n\t}\n\n\t/**\n\t * Registers a command (or several) and returns a function that unregisters it.\n\t * Throws a `CommandRegistryError` for a duplicate or invalid id, or invalid chord text.\n\t */\n\tregister(spec: CommandSpec<TContext> | readonly CommandSpec<TContext>[]): () => void {\n\t\tconst specs = (Array.isArray(spec) ? spec : [spec]) as CommandSpec<TContext>[];\n\t\tconst seen = new Set<string>();\n\t\tfor (const s of specs) {\n\t\t\tif (!s || typeof s.id !== 'string' || !COMMAND_ID_REGEX.test(s.id)) {\n\t\t\t\tthrow new CommandRegistryError('invalid_command', `Invalid command id \"${s?.id}\".`);\n\t\t\t}\n\t\t\tif (typeof s.run !== 'function') {\n\t\t\t\tthrow new CommandRegistryError('invalid_command', `Command \"${s.id}\" has no run function.`);\n\t\t\t}\n\t\t\tif (this.commands.has(s.id) || seen.has(s.id)) {\n\t\t\t\tthrow new CommandRegistryError('duplicate_command', `Command \"${s.id}\" is already registered.`);\n\t\t\t}\n\t\t\tseen.add(s.id);\n\t\t}\n\t\t// Parse every chord before registering anything, so a bad shortcut registers nothing.\n\t\tconst bindings = specs.map((s) => toShortcutBindings(s.id, s.shortcut, s.scope));\n\t\tconst unbinds: Array<() => void> = [];\n\t\tthis.muted = true;\n\t\ttry {\n\t\t\tfor (const b of bindings) unbinds.push(this.shortcuts.bind(b));\n\t\t} catch (error) {\n\t\t\tunbinds.forEach((u) => u());\n\t\t\tthrow new CommandRegistryError('invalid_command', error instanceof Error ? error.message : String(error));\n\t\t} finally {\n\t\t\tthis.muted = false;\n\t\t}\n\t\tspecs.forEach((s, i) => this.commands.set(s.id, { spec: s, unbind: unbinds[i] }));\n\t\tthis.notify();\n\t\tlet registered = true;\n\t\treturn () => {\n\t\t\tif (!registered) return;\n\t\t\tregistered = false;\n\t\t\tlet changed = false;\n\t\t\tfor (const s of specs) {\n\t\t\t\tconst entry = this.commands.get(s.id);\n\t\t\t\tif (entry && entry.spec === s) {\n\t\t\t\t\tthis.commands.delete(s.id);\n\t\t\t\t\tthis.unbindQuietly(entry);\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (changed) this.notify();\n\t\t};\n\t}\n\n\t/** Removes a command by id. Returns whether it was registered. */\n\tunregister(id: string): boolean {\n\t\tconst entry = this.commands.get(id);\n\t\tif (!entry) return false;\n\t\tthis.commands.delete(id);\n\t\tthis.unbindQuietly(entry);\n\t\tthis.notify();\n\t\treturn true;\n\t}\n\n\tprivate unbindQuietly(entry: Registered) {\n\t\tthis.muted = true;\n\t\ttry {\n\t\t\tentry.unbind();\n\t\t} finally {\n\t\t\tthis.muted = false;\n\t\t}\n\t}\n\n\thas(id: string): boolean {\n\t\treturn this.commands.has(id);\n\t}\n\n\tget(id: string): CommandSpec<TContext> | undefined {\n\t\treturn this.commands.get(id)?.spec;\n\t}\n\n\t/** Registered ids in registration order. */\n\tids(): string[] {\n\t\treturn [...this.commands.keys()];\n\t}\n\n\t/** Whether `id` can run now (`{ enabled, reason }`); `params` are the targets a caller named. */\n\tisEnabled(id: string, params?: unknown, context: TContext = this.context): { enabled: boolean; reason?: string } {\n\t\tconst spec = this.get(id);\n\t\tif (!spec) return { enabled: false, reason: `Unknown command \"${id}\".` };\n\t\tif (!spec.enabled) return { enabled: true };\n\t\ttry {\n\t\t\treturn enablement(spec.enabled(context, params as any));\n\t\t} catch (error) {\n\t\t\treturn { enabled: false, reason: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** Commands for menus, palettes and agents, with their current enablement. */\n\tlist(options: CommandListOptions<TContext> = {}): CommandEntry[] {\n\t\tconst scopes = options.scope === undefined ? null : typeof options.scope === 'string' ? [options.scope] : options.scope;\n\t\tconst context = 'context' in options ? (options.context as TContext) : this.context;\n\t\tconst platform = this.shortcuts.platform;\n\t\tconst out: CommandEntry[] = [];\n\t\tfor (const { spec } of this.commands.values()) {\n\t\t\tif (spec.hidden && !options.includeHidden) continue;\n\t\t\tif (scopes && spec.scope !== undefined && !scopes.includes(spec.scope)) continue;\n\t\t\tconst { enabled, reason } = this.isEnabled(spec.id, undefined, context);\n\t\t\tif (options.enabledOnly && !enabled) continue;\n\t\t\tout.push({\n\t\t\t\tid: spec.id,\n\t\t\t\tlabel: spec.label,\n\t\t\t\t...(spec.group !== undefined ? { group: spec.group } : {}),\n\t\t\t\t...(spec.keywords ? { keywords: spec.keywords } : {}),\n\t\t\t\t...(spec.description !== undefined ? { description: spec.description } : {}),\n\t\t\t\t...(spec.params ? { params: spec.params } : {}),\n\t\t\t\t...(spec.scope !== undefined ? { scope: spec.scope } : {}),\n\t\t\t\tshortcuts: this.shortcuts.forCommand(spec.id, platform),\n\t\t\t\tenabled,\n\t\t\t\t...(enabled ? {} : { disabledReason: reason }),\n\t\t\t\tundoable: !!spec.undoable,\n\t\t\t\treadOnly: !!spec.readOnly\n\t\t\t});\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate check(id: string, params: unknown, context: TContext): CommandResult<never> | { spec: CommandSpec<TContext>; params: any } {\n\t\tconst spec = this.get(id);\n\t\tif (!spec) return { ok: false, id, code: 'unknown_command', message: `Unknown command \"${id}\".` };\n\t\tconst input = params ?? {};\n\t\tif (typeof input !== 'object' || Array.isArray(input)) {\n\t\t\treturn { ok: false, id, code: 'invalid_params', message: `Parameters for command \"${id}\" must be an object.` };\n\t\t}\n\t\tconst unknown = unknownParams(spec, input as Record<string, unknown>);\n\t\tif (unknown) return { ok: false, id, code: 'invalid_params', message: unknown };\n\t\tif (spec.params) {\n\t\t\tconst details = validateJsonSchema(input, spec.params);\n\t\t\tif (details.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tid,\n\t\t\t\t\tcode: 'invalid_params',\n\t\t\t\t\tmessage: `Invalid parameters for command \"${id}\": ${details.map((d) => `${d.path} ${d.message}`).join('; ')}`,\n\t\t\t\t\tdetails\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tconst { enabled, reason } = this.isEnabled(id, params === undefined ? undefined : input, context);\n\t\tif (!enabled) return { ok: false, id, code: 'disabled', message: reason ?? 'Disabled.' };\n\t\treturn { spec, params: input };\n\t}\n\n\tprivate invoke(spec: CommandSpec<TContext>, params: any, context: TContext): unknown {\n\t\tconst history = this.options.history;\n\t\tif (!spec.undoable || !history) return spec.run(context, params);\n\t\tlet value: unknown;\n\t\t// `batch` rolls back already-recorded steps and rethrows when `run` throws.\n\t\thistory.batch(spec.label, () => {\n\t\t\tvalue = spec.run(context, params);\n\t\t});\n\t\treturn value;\n\t}\n\n\t/**\n\t * Runs a command synchronously and returns a structured result; never throws.\n\t * A command whose `run` returns a promise yields `{ ok: true, result: promise }`\n\t * (use `executeAsync` to await it and catch its rejection).\n\t */\n\texecute<T = unknown>(id: string, params?: unknown, context: TContext = this.context): CommandResult<T> {\n\t\tconst checked = this.check(id, params, context);\n\t\tif ('ok' in checked) return checked;\n\t\ttry {\n\t\t\tconst result = this.invoke(checked.spec, checked.params, context);\n\t\t\tthis.notify();\n\t\t\treturn { ok: true, id, result: result as T };\n\t\t} catch (error) {\n\t\t\tthis.notify();\n\t\t\treturn { ok: false, id, code: 'failed', message: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/** Like `execute`, but awaits async commands (rejections become `failed` results). */\n\tasync executeAsync<T = unknown>(id: string, params?: unknown, context: TContext = this.context): Promise<CommandResult<T>> {\n\t\tconst outcome = this.execute<T>(id, params, context);\n\t\tif (!outcome.ok || !isThenable(outcome.result)) return outcome;\n\t\ttry {\n\t\t\tconst result = await outcome.result;\n\t\t\tthis.notify();\n\t\t\treturn { ok: true, id, result: result as T };\n\t\t} catch (error) {\n\t\t\tthis.notify();\n\t\t\treturn { ok: false, id, code: 'failed', message: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t}\n\n\t/**\n\t * Resolves a keydown event to a command (see `ShortcutMap.resolve`) and runs it when\n\t * enabled, calling `preventDefault()`. Disabled commands leave the event alone, so the\n\t * browser or a focused editor can still handle it. Returns the result, or `null`.\n\t */\n\thandleKeyDown(\n\t\tevent: KeyboardEventLike,\n\t\toptions: ShortcutResolveOptions & { context?: TContext } = {}\n\t): CommandResult | null {\n\t\tif (event.defaultPrevented) return null;\n\t\tconst match = this.shortcuts.resolve(event, options);\n\t\tif (!match) return null;\n\t\tconst context = 'context' in options ? (options.context as TContext) : this.context;\n\t\tif (!this.isEnabled(match.command, undefined, context).enabled) return null;\n\t\tevent.preventDefault?.();\n\t\treturn this.execute(match.command, undefined, context);\n\t}\n\n\t/** Display-ready primary shortcut of a command (`⇧⌘Z` / `Ctrl+Shift+Z`), or `undefined`. */\n\tshortcutFor(id: string, platform: Platform = this.shortcuts.platform): string | undefined {\n\t\treturn this.shortcuts.forCommand(id, platform)[0];\n\t}\n\n\t/**\n\t * Subscribes to changes: commands registered or removed, shortcut overrides, executed\n\t * commands and `invalidate()` calls (e.g. when enablement inputs change).\n\t */\n\tsubscribe(listener: () => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.listeners.delete(listener);\n\t\t};\n\t}\n\n\t/** Tells subscribers that enablement may have changed (e.g. the selection changed). */\n\tinvalidate(): void {\n\t\tthis.notify();\n\t}\n\n\tprivate notify(): void {\n\t\tthis.version++;\n\t\tfor (const listener of Array.from(this.listeners)) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('[CommandRegistry] Error in listener:', error);\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport function createCommandRegistry<TContext = any>(options?: CommandRegistryOptions<TContext>): CommandRegistry<TContext> {\n\treturn new CommandRegistry<TContext>(options);\n}\n\n/** Identity helper that types a command spec. */\nexport function defineCommand<TContext = any, TParams = any, TResult = unknown>(\n\tspec: CommandSpec<TContext, TParams, TResult>\n): CommandSpec<TContext, TParams, TResult> {\n\treturn spec;\n}\n","// Portions ported from photocraft-main/crates/ui-egui/src/palette.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n\n/** The fields `rankCommands` searches (a `CommandEntry` satisfies it). */\nexport interface RankableCommand {\n\tid: string;\n\tlabel: string;\n\tgroup?: string;\n\tkeywords?: readonly string[];\n\tenabled?: boolean;\n}\n\nexport interface FuzzyMatch {\n\tscore: number;\n\t/** Indices of the matched characters in the text (for highlighting). */\n\tindices: number[];\n}\n\nconst WORD_SEPARATORS = new Set([' ', '-', '_', '.', '/', ':', '›', '(']);\n\n/**\n * Subsequence fuzzy match: every query character must appear in order. Consecutive\n * matches score 8, others 1; a match at a word start adds 5; longer texts lose\n * `length / 8`. Case-insensitive. Returns `null` when the query does not match.\n */\nexport function fuzzyMatch(query: string, text: string): FuzzyMatch | null {\n\tconst q = query.trim().toLowerCase();\n\tif (!q) return { score: 0, indices: [] };\n\tconst t = text.toLowerCase();\n\tlet score = 0;\n\tlet ti = 0;\n\tlet last = -2;\n\tconst indices: number[] = [];\n\tfor (const qc of q) {\n\t\tif (qc === ' ') continue;\n\t\tlet found = false;\n\t\twhile (ti < t.length) {\n\t\t\tif (t[ti] === qc) {\n\t\t\t\tscore += last === ti - 1 ? 8 : 1;\n\t\t\t\tif (ti === 0 || WORD_SEPARATORS.has(t[ti - 1])) score += 5;\n\t\t\t\tlast = ti;\n\t\t\t\tindices.push(ti);\n\t\t\t\tti++;\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tti++;\n\t\t}\n\t\tif (!found) return null;\n\t}\n\treturn { score: score - Math.floor(t.length / 8), indices };\n}\n\n/** `fuzzyMatch(query, text)?.score ?? null`. */\nexport function fuzzyScore(query: string, text: string): number | null {\n\treturn fuzzyMatch(query, text)?.score ?? null;\n}\n\n/**\n * Bonus that puts a command whose name is exactly the query ahead of every fuzzy match\n * (\"undo\" finds Undo before \"Undo Selection Change\"). Trailing ellipses are ignored.\n */\nexport function exactBonus(query: string, names: readonly string[]): number {\n\tconst q = query.trim().toLowerCase();\n\tif (!q) return 0;\n\treturn names.some((n) => n.replace(/(…|\\.\\.\\.)$/, '').trim().toLowerCase() === q) ? 1000 : 0;\n}\n\n/** Default number of recently run commands a palette remembers. */\nexport const DEFAULT_RECENT_LIMIT = 8;\n\n/** Moves `id` to the front of `list` (deduplicated, newest first, at most `cap`). Returns a new array. */\nexport function pushRecent(list: readonly string[], id: string, cap: number = DEFAULT_RECENT_LIMIT): string[] {\n\treturn [id, ...list.filter((x) => x !== id)].slice(0, Math.max(0, cap));\n}\n\nexport interface RankedCommand<T extends RankableCommand = RankableCommand> {\n\tentry: T;\n\tscore: number;\n\t/** Matched character indices in `entry.label` (empty when another field matched). */\n\tlabelIndices: number[];\n\t/** The entry was shown because it was run recently (empty query). */\n\trecent: boolean;\n}\n\nexport interface RankCommandsOptions {\n\t/** Recently run ids, newest first. Shown first for an empty query and boosted slightly otherwise. */\n\trecents?: readonly string[];\n\t/** Maximum number of recents shown for an empty query (default 8). */\n\trecentLimit?: number;\n\t/** Maximum number of results. */\n\tlimit?: number;\n\t/** Sort disabled commands after enabled ones (default `true`). */\n\tdisabledLast?: boolean;\n\t/** With an empty query: `all` (default) lists recents then every other command; `recents` lists only recents. */\n\temptyQuery?: 'all' | 'recents';\n}\n\n/**\n * Ranks commands for a palette query. Searches the label, `group label`, keywords and id;\n * an exact label or keyword match always wins. An empty query lists recent commands\n * first (deduplicated and capped), then the rest in their original order.\n */\nexport function rankCommands<T extends RankableCommand>(\n\tquery: string,\n\tentries: readonly T[],\n\toptions: RankCommandsOptions = {}\n): RankedCommand<T>[] {\n\tconst recents = options.recents ?? [];\n\tconst disabledLast = options.disabledLast ?? true;\n\tconst q = query.trim();\n\tlet ranked: Array<RankedCommand<T> & { order: number }>;\n\n\tif (!q) {\n\t\tconst byId = new Map(entries.map((e) => [e.id, e]));\n\t\tconst recentIds = [...new Set(recents)].filter((id) => byId.has(id)).slice(0, options.recentLimit ?? DEFAULT_RECENT_LIMIT);\n\t\tconst recentSet = new Set(recentIds);\n\t\tranked = recentIds.map((id, i) => ({ entry: byId.get(id)!, score: 1000 - i, labelIndices: [], recent: true, order: i }));\n\t\tif ((options.emptyQuery ?? 'all') === 'all') {\n\t\t\tentries.forEach((entry, i) => {\n\t\t\t\tif (!recentSet.has(entry.id)) ranked.push({ entry, score: 0, labelIndices: [], recent: false, order: recentIds.length + i });\n\t\t\t});\n\t\t}\n\t} else {\n\t\tranked = [];\n\t\tentries.forEach((entry, order) => {\n\t\t\tconst label = fuzzyMatch(q, entry.label);\n\t\t\tconst candidates: number[] = [];\n\t\t\tif (label) candidates.push(label.score);\n\t\t\tif (entry.group) {\n\t\t\t\tconst grouped = fuzzyScore(q, `${entry.group} ${entry.label}`);\n\t\t\t\tif (grouped !== null) candidates.push(grouped - 1);\n\t\t\t}\n\t\t\tfor (const keyword of entry.keywords ?? []) {\n\t\t\t\tconst s = fuzzyScore(q, keyword);\n\t\t\t\tif (s !== null) candidates.push(s - 2);\n\t\t\t}\n\t\t\tconst byId = fuzzyScore(q, entry.id);\n\t\t\tif (byId !== null) candidates.push(byId - 3);\n\t\t\tif (candidates.length === 0) return;\n\t\t\tconst recentIndex = recents.indexOf(entry.id);\n\t\t\tconst score =\n\t\t\t\tMath.max(...candidates) +\n\t\t\t\texactBonus(q, [entry.label, ...(entry.keywords ?? [])]) +\n\t\t\t\t(recentIndex >= 0 ? Math.max(1, 4 - recentIndex) : 0);\n\t\t\tranked.push({ entry, score, labelIndices: label?.indices ?? [], recent: false, order });\n\t\t});\n\t\tranked.sort((a, b) => b.score - a.score || a.entry.label.length - b.entry.label.length || a.order - b.order);\n\t}\n\n\tif (disabledLast) {\n\t\t// Stable partition: enabled first, keeping the ranking within each half.\n\t\tranked = [...ranked.filter((r) => r.entry.enabled !== false), ...ranked.filter((r) => r.entry.enabled === false)];\n\t}\n\tconst out = ranked.map(({ order: _order, ...rest }) => rest);\n\treturn options.limit !== undefined ? out.slice(0, options.limit) : out;\n}\n","import { formatShortcut, type KeyboardEventLike, type Platform } from './chords.js';\nimport { DEFAULT_RECENT_LIMIT, pushRecent, rankCommands, type RankedCommand } from './palette.js';\nimport type { CommandEntry, CommandRegistry, CommandResult } from './registry.js';\nimport { ShortcutMap } from './shortcuts.js';\n\n/** One palette row. */\nexport interface CommandPaletteItem extends RankedCommand<CommandEntry> {\n\tid: string;\n\tlabel: string;\n\tgroup?: string;\n\t/** Display-ready primary shortcut (`⇧⌘Z` / `Ctrl+Shift+Z`). */\n\tshortcut?: string;\n\tenabled: boolean;\n\tdisabledReason?: string;\n}\n\nexport interface CommandPaletteState {\n\topen: boolean;\n\tquery: string;\n\tactiveIndex: number;\n\titems: CommandPaletteItem[];\n\trecents: string[];\n\t/** Result of the last command the palette ran. */\n\tlastResult: CommandResult | null;\n}\n\nexport interface CommandPaletteOptions<TContext = any> {\n\t/** Context for enablement and runs (default: the registry's). */\n\tcontext?: TContext | (() => TContext);\n\t/** Only global commands plus these scopes. */\n\tscope?: string | readonly string[] | (() => string | readonly string[] | undefined);\n\t/** Initial recently run ids (newest first), e.g. restored from storage. */\n\trecents?: readonly string[];\n\t/** Called whenever the recents change, e.g. to persist them. */\n\tonRecentsChange?: (recents: string[]) => void;\n\trecentLimit?: number;\n\t/** Maximum rows (default 50). */\n\tlimit?: number;\n\t/** Platform for shortcut display (default: detected). */\n\tplatform?: Platform;\n\t/** Called after a command ran from the palette. */\n\tonRun?: (result: CommandResult) => void;\n}\n\n/**\n * Framework-agnostic command palette state (open, query, ranked rows, active row and\n * recents) over a `CommandRegistry`. The React hook and Svelte rune wrap it.\n */\nexport function createCommandPaletteController<TContext = any>(\n\tregistry: CommandRegistry<TContext>,\n\toptions: CommandPaletteOptions<TContext> = {}\n) {\n\tconst listeners = new Set<(state: CommandPaletteState) => void>();\n\tlet open = false;\n\tlet query = '';\n\tlet activeIndex = 0;\n\tlet recents = [...(options.recents ?? [])];\n\tlet lastResult: CommandResult | null = null;\n\tlet state: CommandPaletteState;\n\n\tconst context = (): TContext => {\n\t\tconst c = options.context;\n\t\tif (c === undefined) return registry.context;\n\t\treturn (typeof c === 'function' ? (c as () => TContext)() : c) as TContext;\n\t};\n\tconst scope = () => (typeof options.scope === 'function' ? options.scope() : options.scope);\n\n\tfunction computeItems(): CommandPaletteItem[] {\n\t\tif (!open) return [];\n\t\tconst entries = registry.list({ scope: scope(), context: context() });\n\t\tconst platform = options.platform ?? registry.shortcuts.platform;\n\t\treturn rankCommands(query, entries, {\n\t\t\trecents,\n\t\t\trecentLimit: options.recentLimit,\n\t\t\tlimit: options.limit ?? 50\n\t\t}).map((ranked) => ({\n\t\t\t...ranked,\n\t\t\tid: ranked.entry.id,\n\t\t\tlabel: ranked.entry.label,\n\t\t\t...(ranked.entry.group !== undefined ? { group: ranked.entry.group } : {}),\n\t\t\t...(ranked.entry.shortcuts[0] ? { shortcut: formatShortcut(ranked.entry.shortcuts[0], platform) } : {}),\n\t\t\tenabled: ranked.entry.enabled,\n\t\t\t...(ranked.entry.disabledReason ? { disabledReason: ranked.entry.disabledReason } : {})\n\t\t}));\n\t}\n\n\tfunction update() {\n\t\tconst items = computeItems();\n\t\tactiveIndex = items.length === 0 ? 0 : Math.min(Math.max(activeIndex, 0), items.length - 1);\n\t\tstate = { open, query, activeIndex, items, recents, lastResult };\n\t\tfor (const listener of Array.from(listeners)) listener(state);\n\t}\n\n\tstate = { open, query, activeIndex, items: [], recents, lastResult };\n\t// Follow registry changes only while someone listens (safe with React StrictMode remounts).\n\tlet unsubscribeRegistry: (() => void) | null = null;\n\tconst onRegistryChange = () => {\n\t\tif (open) update();\n\t};\n\n\tconst controller = {\n\t\tgetState: () => state,\n\t\tsubscribe(listener: (state: CommandPaletteState) => void): () => void {\n\t\t\tlisteners.add(listener);\n\t\t\tunsubscribeRegistry ??= registry.subscribe(onRegistryChange);\n\t\t\tif (open) update(); // catch up with changes made while nobody listened\n\t\t\treturn () => {\n\t\t\t\tlisteners.delete(listener);\n\t\t\t\tif (listeners.size === 0) {\n\t\t\t\t\tunsubscribeRegistry?.();\n\t\t\t\t\tunsubscribeRegistry = null;\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\tsetOpen(next: boolean) {\n\t\t\tif (next === open) return;\n\t\t\topen = next;\n\t\t\tquery = '';\n\t\t\tactiveIndex = 0;\n\t\t\tupdate();\n\t\t},\n\t\ttoggle() {\n\t\t\tcontroller.setOpen(!open);\n\t\t},\n\t\tsetQuery(next: string) {\n\t\t\tif (next === query) return;\n\t\t\tquery = next;\n\t\t\tactiveIndex = 0;\n\t\t\tupdate();\n\t\t},\n\t\tsetActiveIndex(index: number) {\n\t\t\tactiveIndex = index;\n\t\t\tupdate();\n\t\t},\n\t\t/** Moves the active row by `delta`, wrapping around. */\n\t\tmove(delta: number) {\n\t\t\tconst n = state.items.length;\n\t\t\tif (n === 0) return;\n\t\t\tactiveIndex = (((activeIndex + delta) % n) + n) % n;\n\t\t\tupdate();\n\t\t},\n\t\t/** Runs a command by id, closes the palette and records it as recent. Disabled commands do nothing. */\n\t\trun(id: string): CommandResult | null {\n\t\t\tif (!registry.isEnabled(id, undefined, context()).enabled) return null;\n\t\t\topen = false;\n\t\t\tquery = '';\n\t\t\tactiveIndex = 0;\n\t\t\trecents = pushRecent(recents, id, options.recentLimit ?? DEFAULT_RECENT_LIMIT);\n\t\t\toptions.onRecentsChange?.(recents);\n\t\t\tconst result = registry.execute(id, undefined, context());\n\t\t\tlastResult = result;\n\t\t\tupdate();\n\t\t\toptions.onRun?.(result);\n\t\t\treturn result;\n\t\t},\n\t\trunActive(): CommandResult | null {\n\t\t\tconst item = state.items[state.activeIndex];\n\t\t\treturn item ? controller.run(item.id) : null;\n\t\t},\n\t\t/**\n\t\t * Keyboard handling for the palette input: ArrowUp/ArrowDown (wrap), Home/End,\n\t\t * Enter (run the active row) and Escape (close). Returns whether it handled the key.\n\t\t */\n\t\thandleKeyDown(event: KeyboardEventLike): boolean {\n\t\t\tif (!open) return false;\n\t\t\tswitch (event.key) {\n\t\t\t\tcase 'ArrowDown':\n\t\t\t\t\tcontroller.move(1);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'ArrowUp':\n\t\t\t\t\tcontroller.move(-1);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'Home':\n\t\t\t\t\tcontroller.setActiveIndex(0);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'End':\n\t\t\t\t\tcontroller.setActiveIndex(state.items.length - 1);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'Enter':\n\t\t\t\t\tcontroller.runActive();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'Escape':\n\t\t\t\t\tcontroller.setOpen(false);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t\tevent.preventDefault?.();\n\t\t\treturn true;\n\t\t},\n\t\t/** Re-reads commands and enablement. */\n\t\trefresh() {\n\t\t\tupdate();\n\t\t},\n\t\tdestroy() {\n\t\t\tunsubscribeRegistry?.();\n\t\t\tunsubscribeRegistry = null;\n\t\t\tlisteners.clear();\n\t\t}\n\t};\n\treturn controller;\n}\n\nexport type CommandPaletteController = ReturnType<typeof createCommandPaletteController>;\n\n/**\n * Matches a global \"open palette\" hotkey (e.g. `Mod+K`), also while typing in inputs.\n * Returns `true` when `event` is the hotkey.\n */\nexport function matchPaletteHotkey(event: KeyboardEventLike, hotkey: string, platform?: Platform): boolean {\n\tconst map = new ShortcutMap([{ command: 'palette', chord: hotkey, allowInEditable: true }], platform ? { platform } : {});\n\treturn map.resolve(event) !== null;\n}\n","// Portions ported from filmcraft-main/crates/automation/src/server.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\nimport type { AgentTool, AgentToolProvider, McpToolResult } from '../tools/registry.js';\nimport { rankCommands } from './palette.js';\nimport type { CommandRegistry, CommandResult } from './registry.js';\n\nexport interface CommandToolProviderOptions<TContext = any> {\n\t/** Provider id (default `commands`). */\n\tid?: string;\n\t/** Always prefix the tool names with `<namespace>__`. */\n\tnamespace?: string;\n\t/** Context for enablement and runs (default: the registry's). */\n\tcontext?: TContext | (() => TContext);\n\t/** Only expose global commands plus these scopes. */\n\tscope?: string | readonly string[];\n\t/** Commands agents may not see or run (e.g. ones that open dialogs). */\n\texclude?: (id: string) => boolean;\n}\n\nconst toJson = (value: unknown): unknown => {\n\tif (value === undefined) return null;\n\ttry {\n\t\treturn JSON.parse(JSON.stringify(value));\n\t} catch {\n\t\treturn String(value);\n\t}\n};\n\nfunction errorResult(result: Extract<CommandResult, { ok: false }>): McpToolResult {\n\treturn {\n\t\tisError: true,\n\t\tcontent: [{ type: 'text', text: `${result.code}: ${result.message}` }],\n\t\tstructuredContent: {\n\t\t\terror: { code: result.code, message: result.message, command: result.id, ...(result.details ? { details: result.details } : {}) }\n\t\t}\n\t};\n}\n\n/**\n * Exposes a `CommandRegistry` to agents through a `ToolRegistry`:\n *\n * - `list_commands {query?, enabledOnly?}`: ids, labels, params schemas, shortcuts and\n * enablement (with the reason a command is disabled).\n * - `run_command {id, params?}`: runs one command; disabled commands, unknown params and\n * failures come back as `isError` results with a `{code, message}` error.\n * - `run_commands {steps: [{id, params?}], stopOnError?}`: runs steps **in order**, each as\n * its own command (and undo step). It is not atomic: steps that succeeded before a failure\n * stay applied. For all-or-nothing behaviour, register a command that runs the steps\n * inside `HistoryManager.batch` (or mark it `undoable` on a registry with `history`).\n *\n * The id enum in `run_command`'s schema follows the registered commands, so the\n * `ToolRegistry` announces `listChanged` when commands are added or removed.\n */\nexport function createCommandToolProvider<TContext = any>(\n\tregistry: CommandRegistry<TContext>,\n\toptions: CommandToolProviderOptions<TContext> = {}\n): AgentToolProvider {\n\tconst context = (): TContext => {\n\t\tconst c = options.context;\n\t\tif (c === undefined) return registry.context;\n\t\treturn (typeof c === 'function' ? (c as () => TContext)() : c) as TContext;\n\t};\n\tconst visible = () =>\n\t\tregistry.list({ scope: options.scope, context: context() }).filter((e) => !options.exclude?.(e.id));\n\tconst allowed = (id: string) => registry.has(id) && !options.exclude?.(id) && visible().some((e) => e.id === id);\n\n\tasync function runOne(id: string, params: unknown): Promise<CommandResult> {\n\t\tif (!allowed(id)) return { ok: false, id, code: 'unknown_command', message: `Unknown command \"${id}\".` };\n\t\treturn registry.executeAsync(id, params ?? {}, context());\n\t}\n\n\tconst listCommands: AgentTool = {\n\t\tname: 'list_commands',\n\t\ttitle: 'List commands',\n\t\tdescription:\n\t\t\t'Lists the commands you can run with run_command: id, label, group, params (JSON Schema), shortcuts, and whether each is enabled now (with the reason when it is not).',\n\t\tinputSchema: {\n\t\t\ttype: 'object',\n\t\t\tproperties: {\n\t\t\t\tquery: { type: 'string', description: 'Fuzzy filter on label, keywords, group and id.' },\n\t\t\t\tenabledOnly: { type: 'boolean', description: 'Only commands that can run now (default false).' }\n\t\t\t},\n\t\t\tadditionalProperties: false\n\t\t},\n\t\tannotations: { title: 'List commands', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },\n\t\thandler: ({ query = '', enabledOnly = false }) => {\n\t\t\tlet entries = visible();\n\t\t\tif (enabledOnly) entries = entries.filter((e) => e.enabled);\n\t\t\tif (String(query).trim()) entries = rankCommands(String(query), entries, { disabledLast: false }).map((r) => r.entry);\n\t\t\treturn {\n\t\t\t\tcommands: entries.map((e) => ({\n\t\t\t\t\tid: e.id,\n\t\t\t\t\tlabel: e.label,\n\t\t\t\t\t...(e.group ? { group: e.group } : {}),\n\t\t\t\t\t...(e.description ? { description: e.description } : {}),\n\t\t\t\t\t...(e.params ? { params: e.params } : {}),\n\t\t\t\t\t...(e.shortcuts.length ? { shortcuts: e.shortcuts } : {}),\n\t\t\t\t\tenabled: e.enabled,\n\t\t\t\t\t...(e.disabledReason ? { disabledReason: e.disabledReason } : {}),\n\t\t\t\t\treadOnly: e.readOnly\n\t\t\t\t}))\n\t\t\t};\n\t\t}\n\t};\n\n\tconst runCommandTool = (): AgentTool => {\n\t\tconst ids = visible().map((e) => e.id);\n\t\treturn {\n\t\t\tname: 'run_command',\n\t\t\ttitle: 'Run a command',\n\t\t\tdescription:\n\t\t\t\t'Runs one command by id with optional params (see list_commands for each command\\'s params schema). Unknown params are rejected.',\n\t\t\tinputSchema: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {\n\t\t\t\t\tid: { type: 'string', description: 'Command id from list_commands.', ...(ids.length ? { enum: ids } : {}) },\n\t\t\t\t\tparams: { type: 'object', description: 'Command parameters.' }\n\t\t\t\t},\n\t\t\t\trequired: ['id'],\n\t\t\t\tadditionalProperties: false\n\t\t\t},\n\t\t\tannotations: { title: 'Run a command', readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },\n\t\t\thandler: async ({ id, params }) => {\n\t\t\t\tconst result = await runOne(String(id), params);\n\t\t\t\treturn result.ok ? { id: result.id, ok: true, result: toJson(result.result) } : errorResult(result);\n\t\t\t}\n\t\t};\n\t};\n\n\tconst runCommands: AgentTool = {\n\t\tname: 'run_commands',\n\t\ttitle: 'Run several commands',\n\t\tdescription:\n\t\t\t'Runs several commands in order: {steps: [{id, params?}], stopOnError?: true}. Each step is its own command and undo step; the batch is NOT atomic (steps before a failure stay applied). Returns {completed, failed, results: [{id, ok, result | code, message}]}.',\n\t\tinputSchema: {\n\t\t\ttype: 'object',\n\t\t\tproperties: {\n\t\t\t\tsteps: {\n\t\t\t\t\ttype: 'array',\n\t\t\t\t\tminItems: 1,\n\t\t\t\t\titems: {\n\t\t\t\t\t\ttype: 'object',\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\tid: { type: 'string', description: 'Command id.' },\n\t\t\t\t\t\t\tparams: { type: 'object', description: 'Command parameters.' }\n\t\t\t\t\t\t},\n\t\t\t\t\t\trequired: ['id'],\n\t\t\t\t\t\tadditionalProperties: false\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tstopOnError: { type: 'boolean', description: 'Stop at the first failing step (default true).' }\n\t\t\t},\n\t\t\trequired: ['steps'],\n\t\t\tadditionalProperties: false\n\t\t},\n\t\tannotations: {\n\t\t\ttitle: 'Run several commands',\n\t\t\treadOnlyHint: false,\n\t\t\tdestructiveHint: true,\n\t\t\tidempotentHint: false,\n\t\t\topenWorldHint: false\n\t\t},\n\t\thandler: async ({ steps, stopOnError = true }) => {\n\t\t\tlet completed = 0;\n\t\t\tlet failed = 0;\n\t\t\tconst results: Array<Record<string, unknown>> = [];\n\t\t\tfor (const step of steps as Array<{ id: string; params?: unknown }>) {\n\t\t\t\tconst result = await runOne(step.id, step.params);\n\t\t\t\tif (result.ok) {\n\t\t\t\t\tcompleted++;\n\t\t\t\t\tresults.push({ id: result.id, ok: true, result: toJson(result.result) });\n\t\t\t\t} else {\n\t\t\t\t\tfailed++;\n\t\t\t\t\tresults.push({ id: result.id, ok: false, code: result.code, message: result.message });\n\t\t\t\t\tif (stopOnError) break;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst summary = { completed, failed, skipped: steps.length - completed - failed, results };\n\t\t\treturn failed > 0\n\t\t\t\t? { isError: true, content: [{ type: 'text', text: JSON.stringify(summary) }], structuredContent: summary }\n\t\t\t\t: summary;\n\t\t}\n\t};\n\n\treturn {\n\t\tid: options.id ?? 'commands',\n\t\tnamespace: options.namespace,\n\t\ttools: () => [listCommands, runCommandTool(), runCommands],\n\t\tsubscribe: (notify) => registry.subscribe(notify)\n\t};\n}\n","import type { CommandSpec } from './registry.js';\n\n/**\n * Animation controller surface used by `createCanvasAnimationCommands`\n * (`createCanvasAnimationController`, `useCanvasAnimation` and `createCanvasAnimation` satisfy it).\n */\nexport interface AnimationCommandTarget {\n\tgetState(): { playing: boolean; recording: boolean; time: number; duration: number; selection: readonly unknown[] };\n\tplay(): void;\n\tpause(): void;\n\ttoggle(): void;\n\tstop(): void;\n\tseek(time: number): void;\n\ttoggleRecording(): void;\n\taddKeyframe(ids?: Iterable<string>, keys?: readonly string[], time?: number): unknown;\n\tdeleteKeyframes(): unknown;\n\tsetEasing(easing: any, refs?: readonly any[]): unknown;\n\tnextKeyframe(): unknown;\n\tpreviousKeyframe(): unknown;\n\tanimateNode(nodeId: string, options: any): unknown;\n\texportCss(nodeId: string, options?: any): { css: string; exact: boolean };\n}\n\nexport interface AnimationCommandOptions {\n\t/** Id prefix (default `canvas.animation.`). */\n\tprefix?: string;\n\tgroup?: string;\n\t/** Scope (default `canvas`). Pass `null` for global. */\n\tscope?: string | null;\n}\n\nconst EASING_DESCRIPTION =\n\t'Easing preset (linear, ease, ease-in, ease-out, ease-in-out, ease-in-back, ease-out-back, spring, hold) or a CSS cubic-bezier(x1, y1, x2, y2).';\n\nconst VALUES = {\n\ttype: 'object',\n\tdescription: 'Property → value, e.g. {\"x\": 200, \"opacity\": 0.5, \"fill\": \"#ff0000\"}. Properties: x, y, width, height, rotation (degrees), scale, opacity (0–1), fill, stroke, strokeWidth, cornerRadius, motionPath (0–1).',\n\tadditionalProperties: { type: ['number', 'string'] }\n} as const;\n\n/**\n * Palette / shortcut / agent commands for canvas animation: play / pause, seek, add keyframe at\n * the playhead, delete keyframes, set easing, record toggle, keyframe navigation, \"animate\n * node from A to B over T\" and CSS export. Edits are undoable when the controller has a history.\n */\nexport function createCanvasAnimationCommands(controller: AnimationCommandTarget, options: AnimationCommandOptions = {}): CommandSpec[] {\n\tconst prefix = options.prefix ?? 'canvas.animation.';\n\tconst group = options.group ?? 'Animation';\n\tconst scope = options.scope === null ? undefined : options.scope ?? 'canvas';\n\tconst base = { group, ...(scope ? { scope } : {}) };\n\tconst hasSelection = () => (controller.getState().selection.length > 0 ? true : 'Select keyframes first.');\n\treturn [\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}togglePlay`,\n\t\t\tlabel: 'Play / Pause Animation',\n\t\t\tkeywords: ['play', 'pause', 'preview', 'timeline'],\n\t\t\tdescription: 'Plays or pauses the canvas animation from the playhead.',\n\t\t\trun: () => {\n\t\t\t\tcontroller.toggle();\n\t\t\t\treturn { playing: controller.getState().playing, time: controller.getState().time };\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}play`,\n\t\t\tlabel: 'Play Animation',\n\t\t\tkeywords: ['preview', 'timeline'],\n\t\t\tenabled: () => (controller.getState().playing ? 'Already playing.' : true),\n\t\t\trun: () => controller.play()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}pause`,\n\t\t\tlabel: 'Pause Animation',\n\t\t\tenabled: () => (controller.getState().playing ? true : 'Not playing.'),\n\t\t\trun: () => controller.pause()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}stop`,\n\t\t\tlabel: 'Stop Animation',\n\t\t\tkeywords: ['rewind', 'reset'],\n\t\t\tdescription: 'Stops playback and returns to the start.',\n\t\t\trun: () => controller.stop()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}seek`,\n\t\t\tlabel: 'Go to Time…',\n\t\t\tkeywords: ['playhead', 'scrub', 'time'],\n\t\t\tdescription: 'Moves the playhead.',\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: { time: { type: 'number', minimum: 0, description: 'Time in milliseconds.' } },\n\t\t\t\trequired: ['time']\n\t\t\t},\n\t\t\trun: (_ctx: unknown, params: { time: number }) => {\n\t\t\t\tcontroller.seek(params.time);\n\t\t\t\treturn { time: controller.getState().time };\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}addKeyframe`,\n\t\t\tlabel: 'Add Keyframe at Playhead',\n\t\t\tkeywords: ['key', 'animate', 'record'],\n\t\t\tdescription: 'Keys the selected nodes (or `ids`) at the playhead (or `time`): their animated properties, `properties`, or position.',\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {\n\t\t\t\t\tids: { type: 'array', items: { type: 'string' }, description: 'Node ids (default: selected nodes).' },\n\t\t\t\t\tproperties: { type: 'array', items: { type: 'string' }, description: 'Properties to key (default: animated ones, or x and y).' },\n\t\t\t\t\ttime: { type: 'number', minimum: 0, description: 'Time in ms (default: the playhead).' }\n\t\t\t\t}\n\t\t\t},\n\t\t\trun: (_ctx: unknown, params: { ids?: string[]; properties?: string[]; time?: number }) =>\n\t\t\t\tcontroller.addKeyframe(params?.ids, params?.properties, params?.time)\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}deleteKeyframes`,\n\t\t\tlabel: 'Delete Keyframes',\n\t\t\tkeywords: ['remove', 'clear'],\n\t\t\tenabled: hasSelection,\n\t\t\trun: () => controller.deleteKeyframes()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}setEasing`,\n\t\t\tlabel: 'Set Keyframe Easing…',\n\t\t\tkeywords: ['interpolation', 'ease', 'bezier', 'spring', 'hold', 'curve'],\n\t\t\tdescription: 'Sets the easing of the segments leaving the selected keyframes.',\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: { easing: { type: 'string', description: EASING_DESCRIPTION } },\n\t\t\t\trequired: ['easing']\n\t\t\t},\n\t\t\tenabled: hasSelection,\n\t\t\trun: (_ctx: unknown, params: { easing: string }) => controller.setEasing(params.easing)\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}toggleRecord`,\n\t\t\tlabel: 'Toggle Record Mode',\n\t\t\tkeywords: ['auto key', 'record', 'keyframe'],\n\t\t\tdescription: 'In record mode, changing a property at the playhead keys it.',\n\t\t\trun: () => {\n\t\t\t\tcontroller.toggleRecording();\n\t\t\t\treturn { recording: controller.getState().recording };\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}nextKeyframe`,\n\t\t\tlabel: 'Next Keyframe',\n\t\t\tkeywords: ['jump', 'navigate'],\n\t\t\trun: () => controller.nextKeyframe()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}previousKeyframe`,\n\t\t\tlabel: 'Previous Keyframe',\n\t\t\tkeywords: ['jump', 'navigate'],\n\t\t\trun: () => controller.previousKeyframe()\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}animateNode`,\n\t\t\tlabel: 'Animate Node…',\n\t\t\tkeywords: ['tween', 'transition', 'move', 'fade'],\n\t\t\tdescription: 'Animates a node from `from` (default: its current values) to `to` over `duration` ms, starting at `delay` ms.',\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {\n\t\t\t\t\tnodeId: { type: 'string', description: 'Node to animate.' },\n\t\t\t\t\tto: VALUES,\n\t\t\t\t\tfrom: VALUES,\n\t\t\t\t\tduration: { type: 'number', exclusiveMinimum: 0, description: 'Duration in ms (default 1000).' },\n\t\t\t\t\tdelay: { type: 'number', minimum: 0, description: 'Start time in ms (default 0).' },\n\t\t\t\t\teasing: { type: 'string', description: EASING_DESCRIPTION }\n\t\t\t\t},\n\t\t\t\trequired: ['nodeId', 'to']\n\t\t\t},\n\t\t\trun: (_ctx: unknown, params: { nodeId: string; to: Record<string, number | string>; from?: Record<string, number | string>; duration?: number; delay?: number; easing?: string }) =>\n\t\t\t\tcontroller.animateNode(params.nodeId, {\n\t\t\t\t\tto: params.to,\n\t\t\t\t\t...(params.from ? { from: params.from } : {}),\n\t\t\t\t\t...(params.duration !== undefined ? { duration: params.duration } : {}),\n\t\t\t\t\t...(params.delay !== undefined ? { delay: params.delay } : {}),\n\t\t\t\t\t...(params.easing !== undefined ? { easing: params.easing } : {})\n\t\t\t\t})\n\t\t},\n\t\t{\n\t\t\t...base,\n\t\t\tid: `${prefix}exportCss`,\n\t\t\tlabel: 'Export Animation as CSS',\n\t\t\tkeywords: ['keyframes', 'css', 'web animations'],\n\t\t\tdescription: 'Returns CSS @keyframes for a node\\'s animation.',\n\t\t\treadOnly: true,\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: { nodeId: { type: 'string' } },\n\t\t\t\trequired: ['nodeId']\n\t\t\t},\n\t\t\trun: (_ctx: unknown, params: { nodeId: string }) => controller.exportCss(params.nodeId)\n\t\t}\n\t];\n}\n"]}
@@ -1,9 +1,9 @@
1
1
  import { a as CommandRegistry, P as Platform, b as CommandResult, c as CommandEntry, K as KeyboardEventLike, C as CommandSpec } from '../registry-D0GADKc7.cjs';
2
2
  export { d as Chord, e as CommandErrorCode, f as CommandHistoryTarget, g as CommandListOptions, h as CommandParamsSchema, i as CommandRegistryError, j as CommandRegistryOptions, E as EffectiveChord, k as PLATFORMS, S as ShortcutBinding, l as ShortcutConflict, m as ShortcutInput, n as ShortcutMap, o as ShortcutMapOptions, p as ShortcutMatch, q as ShortcutResolveOptions, r as chordToString, s as codeFromKey, t as codeToKeyText, u as createCommandRegistry, v as defineCommand, w as detectPlatform, x as effectiveChord, y as effectiveChordKey, z as eventCode, A as formatShortcut, B as isEditableEventTarget, D as keyToCode, F as normalizeChord, G as parseChord, H as shortcutCode, I as toShortcutBindings } from '../registry-D0GADKc7.cjs';
3
3
  import { A as AgentToolProvider } from '../registry-DnzpgY27.cjs';
4
- export { A as ArrangeCommandOptions, a as ArrangeCommandTarget, C as ComponentCommandOptions, b as ComponentCommandTarget, D as DEFAULT_HISTORY_SHORTCUTS, H as HistoryCommandOptions, c as HistoryCommandTarget, S as ShapeCommandOptions, d as ShapeCommandTarget, e as SpreadsheetCommandOptions, f as SpreadsheetCommandTarget, g as createArrangeCommands, h as createComponentCommands, i as createHistoryCommands, j as createShapeCommands, k as createSpreadsheetCommands, l as hasVariantCommands, m as matchHistoryShortcut } from '../builtins-nMXh9sT3.cjs';
4
+ export { A as ArrangeCommandOptions, a as ArrangeCommandTarget, b as ComponentCommandOptions, C as ComponentCommandTarget, D as DEFAULT_HISTORY_SHORTCUTS, H as HistoryCommandOptions, c as HistoryCommandTarget, S as ShapeCommandOptions, d as ShapeCommandTarget, e as SpreadsheetCommandOptions, f as SpreadsheetCommandTarget, g as createArrangeCommands, h as createComponentCommands, i as createHistoryCommands, j as createShapeCommands, k as createSpreadsheetCommands, l as hasVariantCommands, m as matchHistoryShortcut } from '../builtins-DxC3aUR2.cjs';
5
5
  import '../schema-DYDGdYL9.cjs';
6
- import '../types-GT5a5YAX.cjs';
6
+ import '../types-DsiXGmjo.cjs';
7
7
  import '../arrange-BICutYPk.cjs';
8
8
 
9
9
  /** The fields `rankCommands` searches (a `CommandEntry` satisfies it). */