@pixerate/editor 0.271.0 → 0.272.1

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 (96) hide show
  1. package/dist/agent/index.cjs +34 -32
  2. package/dist/agent/index.js +10 -8
  3. package/dist/animation-editor/index.cjs +60 -60
  4. package/dist/animation-editor/index.js +4 -4
  5. package/dist/brushes/index.cjs +69 -66
  6. package/dist/brushes/index.js +6 -3
  7. package/dist/brushes/patch.d.cts +1 -1
  8. package/dist/brushes/patch.d.ts +1 -1
  9. package/dist/brushes/schema.d.cts +1 -0
  10. package/dist/brushes/schema.d.ts +1 -0
  11. package/dist/brushes/types.d.cts +8 -0
  12. package/dist/brushes/types.d.ts +8 -0
  13. package/dist/{brushstrokeRender-WR2N5HHY.cjs → brushstrokeRender-5YBCLFDQ.cjs} +15 -15
  14. package/dist/{brushstrokeRender-A727TTNQ.js → brushstrokeRender-DZGFU3J4.js} +3 -3
  15. package/dist/canvas/index.cjs +13 -13
  16. package/dist/canvas/index.js +1 -1
  17. package/dist/chart/contract.d.cts +95 -0
  18. package/dist/chart/contract.d.ts +95 -0
  19. package/dist/chart/controller.d.cts +4 -0
  20. package/dist/chart/controller.d.ts +4 -0
  21. package/dist/chart/index.cjs +492 -26
  22. package/dist/chart/index.d.cts +1 -0
  23. package/dist/chart/index.d.ts +1 -0
  24. package/dist/chart/index.js +476 -10
  25. package/dist/chart/tools.d.cts +3 -0
  26. package/dist/chart/tools.d.ts +3 -0
  27. package/dist/chart-maps/index.cjs +4 -64
  28. package/dist/chart-maps/index.js +3 -63
  29. package/dist/{chunk-YNFSH72F.js → chunk-2F3YDXCN.js} +1 -1
  30. package/dist/{chunk-QYXUE37Q.js → chunk-2XDNF73E.js} +1 -1
  31. package/dist/{chunk-3KNQ2W6X.cjs → chunk-3JKCREEU.cjs} +59 -53
  32. package/dist/{chunk-NMZQMPFZ.js → chunk-4LZQO5Y7.js} +2 -0
  33. package/dist/{chunk-B5W677L7.js → chunk-5HP2VSAS.js} +1 -1
  34. package/dist/chunk-5UGRMVNE.js +328 -0
  35. package/dist/{chunk-X4FTNFIA.js → chunk-7MGPW2OV.js} +1 -1
  36. package/dist/{chunk-BPJSI4NT.cjs → chunk-7W2EQQBP.cjs} +2 -2
  37. package/dist/{chunk-DGITFN73.cjs → chunk-BWJBZAUE.cjs} +8 -8
  38. package/dist/chunk-CHZYVNXZ.js +336 -0
  39. package/dist/{chunk-LETF75CA.js → chunk-CLB2I2FU.js} +1 -1
  40. package/dist/chunk-DRMZ5R3S.cjs +67 -0
  41. package/dist/{chunk-BAZMCFVL.js → chunk-DZVRH63Y.js} +1 -1
  42. package/dist/chunk-EI5GFTKG.js +67 -0
  43. package/dist/{chunk-U2ASNGO6.cjs → chunk-FXVCB6YG.cjs} +35 -35
  44. package/dist/{chunk-EU2ZZA6L.cjs → chunk-GHGBIAWQ.cjs} +126 -39
  45. package/dist/chunk-JV7DKLMX.js +1073 -0
  46. package/dist/{chunk-2WOGXEDX.cjs → chunk-M3RZOF3G.cjs} +1 -1
  47. package/dist/chunk-MFBUQC67.cjs +328 -0
  48. package/dist/{chunk-GSTSZPSR.js → chunk-N3SKPQEH.js} +225 -18
  49. package/dist/{chunk-J5VQYK7T.cjs → chunk-N5IMDAHM.cjs} +251 -44
  50. package/dist/{chunk-ININNHLM.js → chunk-NAHJV3O6.js} +11 -5
  51. package/dist/{chunk-IBXEQOGI.js → chunk-PYHB2EHE.js} +2 -324
  52. package/dist/chunk-QEUI6OGE.cjs +336 -0
  53. package/dist/{chunk-6ZSFRRJI.js → chunk-RSF6BT34.js} +1 -1
  54. package/dist/{chunk-JJ2H25JG.cjs → chunk-SNAFFCAX.cjs} +200 -174
  55. package/dist/chunk-T2HUQBRC.cjs +1073 -0
  56. package/dist/{chunk-M2ZVPU7L.cjs → chunk-TNLKFW7D.cjs} +23 -23
  57. package/dist/{chunk-KZI3YWKW.js → chunk-UXYCY3HP.js} +93 -6
  58. package/dist/{chunk-GSLOH2IN.cjs → chunk-VF6ODSYX.cjs} +2 -0
  59. package/dist/{chunk-NQUKTRKN.cjs → chunk-WZLNNGG3.cjs} +45 -45
  60. package/dist/{chunk-NRBGAEFI.cjs → chunk-Y3RUYOOW.cjs} +8 -330
  61. package/dist/{chunk-DDL3ZBHK.cjs → chunk-YJANF2BL.cjs} +86 -86
  62. package/dist/{chunk-TNHDCP4R.js → chunk-ZEI7ZEA4.js} +31 -5
  63. package/dist/commands/index.cjs +7 -323
  64. package/dist/commands/index.js +5 -321
  65. package/dist/extensions/index.cjs +4 -4
  66. package/dist/extensions/index.js +3 -3
  67. package/dist/image-editor/index.cjs +789 -789
  68. package/dist/image-editor/index.js +5 -5
  69. package/dist/index.cjs +9 -9
  70. package/dist/index.js +3 -3
  71. package/dist/paint/index.cjs +2 -2
  72. package/dist/paint/index.js +1 -1
  73. package/dist/paint/wet/world.d.cts +37 -1
  74. package/dist/paint/wet/world.d.ts +37 -1
  75. package/dist/paint-editor/brush-controls.d.cts +2 -0
  76. package/dist/paint-editor/brush-controls.d.ts +2 -0
  77. package/dist/paint-editor/brush-model.d.cts +14 -1
  78. package/dist/paint-editor/brush-model.d.ts +14 -1
  79. package/dist/paint-editor/index.cjs +343 -306
  80. package/dist/paint-editor/index.js +48 -11
  81. package/dist/paint-editor/ui.d.cts +1 -1
  82. package/dist/paint-editor/ui.d.ts +1 -1
  83. package/dist/paint-editor/wet.d.cts +8 -1
  84. package/dist/paint-editor/wet.d.ts +8 -1
  85. package/dist/persistence/index.cjs +24 -1042
  86. package/dist/persistence/index.js +22 -1040
  87. package/dist/tools/index.cjs +5 -3
  88. package/dist/tools/index.js +8 -6
  89. package/dist/vector/index.cjs +999 -999
  90. package/dist/vector/index.js +5 -5
  91. package/package.json +1 -1
  92. package/skills/pixerate-brushes/SKILL.md +1 -0
  93. package/skills/pixerate-paint-editor/SKILL.md +4 -11
  94. package/skills/pixerate-paint-editor/references/brush-facets.md +1 -0
  95. package/skills/pixerate-paint-editor/references/ghost.md +47 -0
  96. package/skills/pixerate-paint-watercolour/SKILL.md +1 -0
@@ -1,16 +1,9 @@
1
1
  import {
2
- parseSkillFrontmatter
3
- } from "./chunk-K4BGRCSH.js";
2
+ renderStyle
3
+ } from "./chunk-5UGRMVNE.js";
4
4
  import {
5
5
  readingTime
6
6
  } from "./chunk-EHTX34RE.js";
7
- import {
8
- validateJsonSchema
9
- } from "./chunk-V2ZCJRDM.js";
10
- import {
11
- isUnsafeJsonKey,
12
- stripPrototypeKeys
13
- } from "./chunk-HF5QZS7Z.js";
14
7
 
15
8
  // src/tools/skill-evals.ts
16
9
  function listSkillEvals(files) {
@@ -182,316 +175,6 @@ async function runToolScript(source, script, options = {}) {
182
175
  return finish(true);
183
176
  }
184
177
 
185
- // src/tools/style.ts
186
- var STYLE_SKILL_NAME = /^[a-z0-9][a-z0-9-]{0,63}$/;
187
- var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
188
- function parseStyleDomains(markdown) {
189
- const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(markdown);
190
- const line = match?.[1].split(/\r?\n/).find((l) => /^domains:/.test(l));
191
- if (!line) return void 0;
192
- const raw = line.slice("domains:".length).trim().replace(/^\[|\]$/g, "");
193
- return raw.split(",").map((d) => d.trim().replace(/^(['"])(.*)\1$/, "$2")).filter(Boolean);
194
- }
195
- function memoryStyleStore(initial = {}, state = {}) {
196
- const files = /* @__PURE__ */ new Map();
197
- const states = new Map(Object.entries(state));
198
- const listeners = /* @__PURE__ */ new Set();
199
- const notify = () => listeners.forEach((l) => l());
200
- for (const [path, text] of Object.entries(initial)) {
201
- const [dir, ...rest] = path.replace(/\\/g, "/").replace(/^\.?\//, "").split("/");
202
- if (!dir || !rest.length) continue;
203
- if (!files.has(dir)) files.set(dir, /* @__PURE__ */ new Map());
204
- files.get(dir).set(rest.join("/"), text);
205
- }
206
- return {
207
- list: () => [.../* @__PURE__ */ new Set([...files.keys(), ...states.keys()])].sort().map((name) => ({ name, ...states.get(name) })),
208
- read: (name, file) => files.get(name)?.get(file),
209
- files: (name) => [...files.get(name)?.keys() ?? []].sort(),
210
- subscribe(listener) {
211
- listeners.add(listener);
212
- return () => listeners.delete(listener);
213
- },
214
- set(name, skillFiles) {
215
- files.set(name, new Map(Object.entries(skillFiles)));
216
- notify();
217
- },
218
- remove(name) {
219
- files.delete(name);
220
- states.delete(name);
221
- notify();
222
- },
223
- setState(name, next) {
224
- if (next) states.set(name, next);
225
- else states.delete(name);
226
- notify();
227
- }
228
- };
229
- }
230
- function resolveTokens(value, tokens, path, errors) {
231
- if (Array.isArray(value)) return value.map((v, i) => resolveTokens(v, tokens, `${path}[${i}]`, errors));
232
- if (!isPlainObject(value)) return value;
233
- if ("$token" in value) {
234
- const token = value.$token;
235
- const mode = value.mode;
236
- if (typeof token !== "string" || !token || mode !== void 0 && typeof mode !== "string" || Object.keys(value).some((k) => k !== "$token" && k !== "mode")) {
237
- errors.push(`${path || "profile"}: a token reference is { "$token": "<variable name or id>", "mode"?: "<mode>" }.`);
238
- return void 0;
239
- }
240
- if (!tokens) {
241
- errors.push(`${path}: "${token}" is a token reference, but this host has no design tokens to resolve it.`);
242
- return void 0;
243
- }
244
- const resolved = tokens.resolve({ token, ...mode ? { mode } : {} });
245
- if (!resolved.ok) errors.push(`${path}: ${resolved.message}`);
246
- return resolved.ok ? resolved.value : void 0;
247
- }
248
- const out = {};
249
- for (const [k, v] of Object.entries(value)) out[k] = resolveTokens(v, tokens, path ? `${path}.${k}` : k, errors);
250
- return out;
251
- }
252
- function checkListErrors(list, domain) {
253
- if (list === void 0) return [];
254
- if (!Array.isArray(list)) return ['checks: expected a list of check ids or { "id": ..., options } objects.'];
255
- const errors = [];
256
- list.forEach((entry, i) => {
257
- const id = typeof entry === "string" ? entry : isPlainObject(entry) ? entry.id : void 0;
258
- if (typeof id !== "string" || !id) {
259
- errors.push(`checks[${i}]: expected a check id or { "id": ..., options }.`);
260
- return;
261
- }
262
- const check = domain.checks.find((c) => c.id === id);
263
- if (!check || typeof entry === "string") return;
264
- const { id: _id, ...options } = entry;
265
- const schema = check.optionsSchema ?? { type: "object", properties: {}, additionalProperties: false };
266
- for (const e of validateJsonSchema(options, schema)) errors.push(`checks[${i}] (${id})${e.path}: ${e.message}`);
267
- });
268
- return errors;
269
- }
270
- function mergeProfile(target, value, layer, skill, sources, locked, issues, prefix = "") {
271
- for (const [key, next] of Object.entries(value)) {
272
- if (isUnsafeJsonKey(key) || next === void 0) continue;
273
- const path = prefix ? `${prefix}.${key}` : key;
274
- const lockedBy = [...locked.entries()].find(([p]) => p === path || path.startsWith(`${p}.`) || p.startsWith(`${path}.`));
275
- if (isPlainObject(next)) {
276
- if (!isPlainObject(target[key])) {
277
- if (lockedBy && lockedBy[1] !== layer.id && target[key] !== void 0) {
278
- issues.push({ skill, layer: layer.id, message: `"${path}" is locked by the ${lockedBy[1]} layer, so this skill's value is ignored.` });
279
- continue;
280
- }
281
- target[key] = {};
282
- delete sources[path];
283
- }
284
- mergeProfile(target[key], next, layer, skill, sources, locked, issues, path);
285
- continue;
286
- }
287
- if (lockedBy && lockedBy[1] !== layer.id) {
288
- issues.push({ skill, layer: layer.id, message: `"${path}" is locked by the ${lockedBy[1]} layer, so this skill's value is ignored.` });
289
- continue;
290
- }
291
- target[key] = Array.isArray(next) ? structuredClone(next) : next;
292
- for (const p of Object.keys(sources)) if (p.startsWith(`${path}.`)) delete sources[p];
293
- sources[path] = layer.id;
294
- if (layer.locked) locked.set(path, layer.id);
295
- }
296
- }
297
- function createStyleResolver(options) {
298
- const layers = [...options.layers];
299
- const domains = [...options.domains];
300
- const ids = /* @__PURE__ */ new Set();
301
- for (const layer of layers) {
302
- if (ids.has(layer.id)) throw new Error(`Two style layers are called "${layer.id}"; give each layer its own id.`);
303
- ids.add(layer.id);
304
- }
305
- const domainIds = domains.map((d) => d.id);
306
- const requireDomain = (id) => {
307
- const domain = domains.find((d) => d.id === id);
308
- if (!domain) throw new Error(`There is no house-style domain "${id}"; expected one of ${domainIds.map((d) => `"${d}"`).join(", ") || "(none registered)"}.`);
309
- return domain;
310
- };
311
- async function load() {
312
- const issues = [];
313
- const entries = await Promise.all(layers.map(async (layer) => [...await layer.store.list()]));
314
- const names = [...new Set(entries.flat().map((e) => e.name))];
315
- const skills = [];
316
- for (const name of names) {
317
- if (!STYLE_SKILL_NAME.test(name)) {
318
- issues.push({ skill: name, message: `"${name}" is not a valid skill name (lowercase letters, digits and hyphens, at most 64).` });
319
- continue;
320
- }
321
- const copies = [];
322
- for (let i = 0; i < layers.length; i++) {
323
- const text = await layers[i].store.read(name, "SKILL.md");
324
- if (text !== void 0) copies.push({ index: i, text });
325
- }
326
- if (!copies.length) continue;
327
- const lockedCopy = copies.find((c) => layers[c.index].locked);
328
- const winner = lockedCopy ?? copies[copies.length - 1];
329
- const layer = layers[winner.index];
330
- const skillIssues = [];
331
- for (const c of copies) {
332
- if (c.index > winner.index) skillIssues.push(`The ${layers[c.index].id} layer's copy is ignored: the ${layer.id} layer is locked.`);
333
- }
334
- let enabled = true;
335
- let order = 0;
336
- for (let i = 0; i < layers.length; i++) {
337
- const entry = entries[i].find((e) => e.name === name);
338
- if (!entry) continue;
339
- if (i === winner.index && entry.order !== void 0) order = entry.order;
340
- if (entry.enabled === void 0) continue;
341
- if (layer.required && i !== winner.index) {
342
- if (entry.enabled === false) skillIssues.push(`The ${layers[i].id} layer cannot switch it off: the ${layer.id} layer requires it.`);
343
- continue;
344
- }
345
- enabled = entry.enabled;
346
- }
347
- const { description = "", body } = parseSkillFrontmatter(winner.text);
348
- const named = parseStyleDomains(winner.text);
349
- const applies = /* @__PURE__ */ new Set();
350
- for (const d of named ?? domainIds) {
351
- if (domainIds.includes(d)) applies.add(d);
352
- else skillIssues.push(`It names an unknown domain "${d}"; the domains here are ${domainIds.join(", ")}.`);
353
- }
354
- const profiles = /* @__PURE__ */ new Map();
355
- for (const domain of domains) {
356
- const raw = await layer.store.read(name, `${domain.id}.json`);
357
- if (raw === void 0) continue;
358
- const file = `${domain.id}.json`;
359
- if (!applies.has(domain.id)) {
360
- skillIssues.push(`${file} is left out: the skill's domains do not include ${domain.id}.`);
361
- continue;
362
- }
363
- let parsed;
364
- try {
365
- parsed = stripPrototypeKeys(JSON.parse(raw));
366
- } catch (error) {
367
- skillIssues.push(`${file} is not valid JSON: ${error.message}`);
368
- continue;
369
- }
370
- if (!isPlainObject(parsed)) {
371
- skillIssues.push(`${file} must hold a JSON object.`);
372
- continue;
373
- }
374
- const errors = [];
375
- const resolved = resolveTokens(parsed, options.tokens, "", errors);
376
- const { checks, ...rest } = resolved;
377
- errors.push(...validateJsonSchema(rest, domain.profileSchema).map((e) => `${e.path || "(profile)"}: ${e.message}`));
378
- errors.push(...checkListErrors(checks, domain));
379
- if (errors.length) {
380
- skillIssues.push(`${file} is left out: ${errors.join(" ")}`);
381
- continue;
382
- }
383
- profiles.set(domain.id, resolved);
384
- }
385
- skills.push({
386
- info: {
387
- name,
388
- description,
389
- layer: layer.id,
390
- domains: [...applies],
391
- profiles: [...profiles.keys()],
392
- enabled,
393
- order,
394
- issues: skillIssues
395
- },
396
- layerIndex: winner.index,
397
- body: body.trim(),
398
- profiles,
399
- applies
400
- });
401
- }
402
- skills.sort((a, b) => a.layerIndex - b.layerIndex || a.info.order - b.info.order || a.info.name.localeCompare(b.info.name));
403
- return { skills, issues };
404
- }
405
- async function resolve2(domainId) {
406
- requireDomain(domainId);
407
- const { skills, issues: loadIssues } = await load();
408
- const issues = [...loadIssues];
409
- const profile = {};
410
- const sources = {};
411
- const locked = /* @__PURE__ */ new Map();
412
- const applied = [];
413
- for (const skill of skills) {
414
- if (!skill.applies.has(domainId)) continue;
415
- for (const message of skill.info.issues) issues.push({ skill: skill.info.name, layer: skill.info.layer, message });
416
- if (!skill.info.enabled) continue;
417
- applied.push({ name: skill.info.name, description: skill.info.description, layer: skill.info.layer, body: skill.body });
418
- const own = skill.profiles.get(domainId);
419
- if (own) mergeProfile(profile, own, layers[skill.layerIndex], skill.info.name, sources, locked, issues);
420
- }
421
- return { domain: domainId, skills: applied, profile, sources, issues };
422
- }
423
- async function winnerLayer(name) {
424
- const copies = [];
425
- for (let i = 0; i < layers.length; i++) if (await layers[i].store.read(name, "SKILL.md") !== void 0) copies.push(i);
426
- if (!copies.length) return void 0;
427
- return layers[copies.find((i) => layers[i].locked) ?? copies[copies.length - 1]];
428
- }
429
- return {
430
- layers,
431
- domains,
432
- async list(opts = {}) {
433
- if (opts.domain !== void 0) requireDomain(opts.domain);
434
- const { skills } = await load();
435
- return skills.filter((s) => opts.domain === void 0 || s.applies.has(opts.domain)).map((s) => s.info);
436
- },
437
- resolve: resolve2,
438
- async readFile(name, file) {
439
- const layer = await winnerLayer(name);
440
- return layer ? layer.store.read(name, file) : void 0;
441
- },
442
- async files(name) {
443
- const layer = await winnerLayer(name);
444
- return layer ? [...await layer.store.files(name)] : void 0;
445
- },
446
- async check(domainId, state) {
447
- const domain = requireDomain(domainId);
448
- const style = await resolve2(domainId);
449
- return { ...runStyleChecks(domain, state, style), style };
450
- },
451
- subscribe(listener) {
452
- const offs = [...layers.map((l) => l.store.subscribe?.(listener)), options.tokens?.subscribe?.(listener)];
453
- return () => offs.forEach((off) => off?.());
454
- }
455
- };
456
- }
457
- function runStyleChecks(domain, state, style) {
458
- const report = { findings: [], ran: [], skipped: [] };
459
- const list = Array.isArray(style.profile.checks) ? style.profile.checks : [];
460
- const layer = style.sources.checks;
461
- for (const entry of list) {
462
- const id = typeof entry === "string" ? entry : isPlainObject(entry) && typeof entry.id === "string" ? entry.id : void 0;
463
- if (!id) continue;
464
- const check = domain.checks.find((c) => c.id === id);
465
- if (!check) {
466
- report.skipped.push({ check: id, reason: `There is no "${id}" check for ${domain.id}; it has ${domain.checks.map((c) => `"${c.id}"`).join(", ")}.` });
467
- continue;
468
- }
469
- const { id: _id, ...options } = isPlainObject(entry) ? entry : { id };
470
- try {
471
- for (const f of check.run(state, style.profile, options)) report.findings.push({ ...f, check: id, ...layer ? { layer } : {} });
472
- report.ran.push(id);
473
- } catch (error) {
474
- report.skipped.push({ check: id, reason: `The check could not run: ${error.message}` });
475
- }
476
- }
477
- return report;
478
- }
479
- var escapeAttribute = (value) => value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
480
- function renderStyle(style) {
481
- if (!style.skills.length) return `No house style applies to ${style.domain}.`;
482
- const blocks = style.skills.map((s) => {
483
- const body = s.body.replace(/<(\/?)style_skill/gi, "&lt;$1style_skill");
484
- return `<style_skill name="${escapeAttribute(s.name)}" layer="${escapeAttribute(s.layer)}">
485
- ${body}
486
- </style_skill>`;
487
- });
488
- const profile = Object.keys(style.profile).length ? `
489
-
490
- Merged ${style.domain} profile:
491
- ${JSON.stringify(style.profile, null, 2)}` : "";
492
- return `${blocks.join("\n\n")}${profile}`;
493
- }
494
-
495
178
  // src/tools/style-tools.ts
496
179
  var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
497
180
  function createStyleToolProvider(styles, options = {}) {
@@ -773,11 +456,6 @@ export {
773
456
  toolResultValue,
774
457
  toolResultError,
775
458
  runToolScript,
776
- parseStyleDomains,
777
- memoryStyleStore,
778
- createStyleResolver,
779
- runStyleChecks,
780
- renderStyle,
781
459
  createStyleToolProvider,
782
460
  createOriginMiddleware,
783
461
  ConformanceIds,
@@ -0,0 +1,336 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+
5
+ var _chunkKIJIONZFcjs = require('./chunk-KIJIONZF.cjs');
6
+
7
+
8
+
9
+ var _chunkQJVOJUTDcjs = require('./chunk-QJVOJUTD.cjs');
10
+
11
+ // src/commands/paletteController.ts
12
+ function createCommandPaletteController(registry, options = {}) {
13
+ const listeners = /* @__PURE__ */ new Set();
14
+ let open = false;
15
+ let query = "";
16
+ let activeIndex = 0;
17
+ let recents = [..._nullishCoalesce(options.recents, () => ( []))];
18
+ let lastResult = null;
19
+ let state;
20
+ const context = () => {
21
+ const c = options.context;
22
+ if (c === void 0) return registry.context;
23
+ return typeof c === "function" ? c() : c;
24
+ };
25
+ const scope = () => typeof options.scope === "function" ? options.scope() : options.scope;
26
+ function computeItems() {
27
+ if (!open) return [];
28
+ const entries = registry.list({ scope: scope(), context: context() });
29
+ const platform = _nullishCoalesce(options.platform, () => ( registry.shortcuts.platform));
30
+ return _chunkKIJIONZFcjs.rankCommands.call(void 0, query, entries, {
31
+ recents,
32
+ recentLimit: options.recentLimit,
33
+ limit: _nullishCoalesce(options.limit, () => ( 50))
34
+ }).map((ranked) => ({
35
+ ...ranked,
36
+ id: ranked.entry.id,
37
+ label: ranked.entry.label,
38
+ ...ranked.entry.group !== void 0 ? { group: ranked.entry.group } : {},
39
+ ...ranked.entry.shortcuts[0] ? { shortcut: _chunkQJVOJUTDcjs.formatShortcut.call(void 0, ranked.entry.shortcuts[0], platform) } : {},
40
+ enabled: ranked.entry.enabled,
41
+ ...ranked.entry.disabledReason ? { disabledReason: ranked.entry.disabledReason } : {}
42
+ }));
43
+ }
44
+ function update() {
45
+ const items = computeItems();
46
+ activeIndex = items.length === 0 ? 0 : Math.min(Math.max(activeIndex, 0), items.length - 1);
47
+ state = { open, query, activeIndex, items, recents, lastResult };
48
+ for (const listener of Array.from(listeners)) listener(state);
49
+ }
50
+ state = { open, query, activeIndex, items: [], recents, lastResult };
51
+ let unsubscribeRegistry = null;
52
+ const onRegistryChange = () => {
53
+ if (open) update();
54
+ };
55
+ const controller = {
56
+ getState: () => state,
57
+ subscribe(listener) {
58
+ listeners.add(listener);
59
+ unsubscribeRegistry ??= registry.subscribe(onRegistryChange);
60
+ if (open) update();
61
+ return () => {
62
+ listeners.delete(listener);
63
+ if (listeners.size === 0) {
64
+ _optionalChain([unsubscribeRegistry, 'optionalCall', _ => _()]);
65
+ unsubscribeRegistry = null;
66
+ }
67
+ };
68
+ },
69
+ setOpen(next) {
70
+ if (next === open) return;
71
+ open = next;
72
+ query = "";
73
+ activeIndex = 0;
74
+ update();
75
+ },
76
+ toggle() {
77
+ controller.setOpen(!open);
78
+ },
79
+ setQuery(next) {
80
+ if (next === query) return;
81
+ query = next;
82
+ activeIndex = 0;
83
+ update();
84
+ },
85
+ setActiveIndex(index) {
86
+ activeIndex = index;
87
+ update();
88
+ },
89
+ /** Moves the active row by `delta`, wrapping around. */
90
+ move(delta) {
91
+ const n = state.items.length;
92
+ if (n === 0) return;
93
+ activeIndex = ((activeIndex + delta) % n + n) % n;
94
+ update();
95
+ },
96
+ /** Runs a command by id, closes the palette and records it as recent. Disabled commands do nothing. */
97
+ run(id) {
98
+ if (!registry.isEnabled(id, void 0, context()).enabled) return null;
99
+ open = false;
100
+ query = "";
101
+ activeIndex = 0;
102
+ recents = _chunkKIJIONZFcjs.pushRecent.call(void 0, recents, id, _nullishCoalesce(options.recentLimit, () => ( _chunkKIJIONZFcjs.DEFAULT_RECENT_LIMIT)));
103
+ _optionalChain([options, 'access', _2 => _2.onRecentsChange, 'optionalCall', _3 => _3(recents)]);
104
+ const result = registry.execute(id, void 0, context());
105
+ lastResult = result;
106
+ update();
107
+ _optionalChain([options, 'access', _4 => _4.onRun, 'optionalCall', _5 => _5(result)]);
108
+ return result;
109
+ },
110
+ runActive() {
111
+ const item = state.items[state.activeIndex];
112
+ return item ? controller.run(item.id) : null;
113
+ },
114
+ /**
115
+ * Keyboard handling for the palette input: ArrowUp/ArrowDown (wrap), Home/End,
116
+ * Enter (run the active row) and Escape (close). Returns whether it handled the key.
117
+ */
118
+ handleKeyDown(event) {
119
+ if (!open) return false;
120
+ switch (event.key) {
121
+ case "ArrowDown":
122
+ controller.move(1);
123
+ break;
124
+ case "ArrowUp":
125
+ controller.move(-1);
126
+ break;
127
+ case "Home":
128
+ controller.setActiveIndex(0);
129
+ break;
130
+ case "End":
131
+ controller.setActiveIndex(state.items.length - 1);
132
+ break;
133
+ case "Enter":
134
+ controller.runActive();
135
+ break;
136
+ case "Escape":
137
+ controller.setOpen(false);
138
+ break;
139
+ default:
140
+ return false;
141
+ }
142
+ _optionalChain([event, 'access', _6 => _6.preventDefault, 'optionalCall', _7 => _7()]);
143
+ return true;
144
+ },
145
+ /** Re-reads commands and enablement. */
146
+ refresh() {
147
+ update();
148
+ },
149
+ destroy() {
150
+ _optionalChain([unsubscribeRegistry, 'optionalCall', _8 => _8()]);
151
+ unsubscribeRegistry = null;
152
+ listeners.clear();
153
+ }
154
+ };
155
+ return controller;
156
+ }
157
+ function matchPaletteHotkey(event, hotkey, platform) {
158
+ const map = new (0, _chunkQJVOJUTDcjs.ShortcutMap)([{ command: "palette", chord: hotkey, allowInEditable: true }], platform ? { platform } : {});
159
+ return map.resolve(event) !== null;
160
+ }
161
+
162
+ // src/commands/animation.ts
163
+ var EASING_DESCRIPTION = "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).";
164
+ var VALUES = {
165
+ type: "object",
166
+ description: 'Property \u2192 value, e.g. {"x": 200, "opacity": 0.5, "fill": "#ff0000"}. Properties: x, y, width, height, rotation (degrees), scale, opacity (0\u20131), fill, stroke, strokeWidth, cornerRadius, motionPath (0\u20131).',
167
+ additionalProperties: { type: ["number", "string"] }
168
+ };
169
+ function createCanvasAnimationCommands(controller, options = {}) {
170
+ const prefix = _nullishCoalesce(options.prefix, () => ( "canvas.animation."));
171
+ const group = _nullishCoalesce(options.group, () => ( "Animation"));
172
+ const scope = options.scope === null ? void 0 : _nullishCoalesce(options.scope, () => ( "canvas"));
173
+ const base = { group, ...scope ? { scope } : {} };
174
+ const hasSelection = () => controller.getState().selection.length > 0 ? true : "Select keyframes first.";
175
+ return [
176
+ {
177
+ ...base,
178
+ id: `${prefix}togglePlay`,
179
+ label: "Play / Pause Animation",
180
+ keywords: ["play", "pause", "preview", "timeline"],
181
+ description: "Plays or pauses the canvas animation from the playhead.",
182
+ run: () => {
183
+ controller.toggle();
184
+ return { playing: controller.getState().playing, time: controller.getState().time };
185
+ }
186
+ },
187
+ {
188
+ ...base,
189
+ id: `${prefix}play`,
190
+ label: "Play Animation",
191
+ keywords: ["preview", "timeline"],
192
+ enabled: () => controller.getState().playing ? "Already playing." : true,
193
+ run: () => controller.play()
194
+ },
195
+ {
196
+ ...base,
197
+ id: `${prefix}pause`,
198
+ label: "Pause Animation",
199
+ enabled: () => controller.getState().playing ? true : "Not playing.",
200
+ run: () => controller.pause()
201
+ },
202
+ {
203
+ ...base,
204
+ id: `${prefix}stop`,
205
+ label: "Stop Animation",
206
+ keywords: ["rewind", "reset"],
207
+ description: "Stops playback and returns to the start.",
208
+ run: () => controller.stop()
209
+ },
210
+ {
211
+ ...base,
212
+ id: `${prefix}seek`,
213
+ label: "Go to Time\u2026",
214
+ keywords: ["playhead", "scrub", "time"],
215
+ description: "Moves the playhead.",
216
+ params: {
217
+ type: "object",
218
+ properties: { time: { type: "number", minimum: 0, description: "Time in milliseconds." } },
219
+ required: ["time"]
220
+ },
221
+ run: (_ctx, params) => {
222
+ controller.seek(params.time);
223
+ return { time: controller.getState().time };
224
+ }
225
+ },
226
+ {
227
+ ...base,
228
+ id: `${prefix}addKeyframe`,
229
+ label: "Add Keyframe at Playhead",
230
+ keywords: ["key", "animate", "record"],
231
+ description: "Keys the selected nodes (or `ids`) at the playhead (or `time`): their animated properties, `properties`, or position.",
232
+ params: {
233
+ type: "object",
234
+ properties: {
235
+ ids: { type: "array", items: { type: "string" }, description: "Node ids (default: selected nodes)." },
236
+ properties: { type: "array", items: { type: "string" }, description: "Properties to key (default: animated ones, or x and y)." },
237
+ time: { type: "number", minimum: 0, description: "Time in ms (default: the playhead)." }
238
+ }
239
+ },
240
+ run: (_ctx, params) => controller.addKeyframe(_optionalChain([params, 'optionalAccess', _9 => _9.ids]), _optionalChain([params, 'optionalAccess', _10 => _10.properties]), _optionalChain([params, 'optionalAccess', _11 => _11.time]))
241
+ },
242
+ {
243
+ ...base,
244
+ id: `${prefix}deleteKeyframes`,
245
+ label: "Delete Keyframes",
246
+ keywords: ["remove", "clear"],
247
+ enabled: hasSelection,
248
+ run: () => controller.deleteKeyframes()
249
+ },
250
+ {
251
+ ...base,
252
+ id: `${prefix}setEasing`,
253
+ label: "Set Keyframe Easing\u2026",
254
+ keywords: ["interpolation", "ease", "bezier", "spring", "hold", "curve"],
255
+ description: "Sets the easing of the segments leaving the selected keyframes.",
256
+ params: {
257
+ type: "object",
258
+ properties: { easing: { type: "string", description: EASING_DESCRIPTION } },
259
+ required: ["easing"]
260
+ },
261
+ enabled: hasSelection,
262
+ run: (_ctx, params) => controller.setEasing(params.easing)
263
+ },
264
+ {
265
+ ...base,
266
+ id: `${prefix}toggleRecord`,
267
+ label: "Toggle Record Mode",
268
+ keywords: ["auto key", "record", "keyframe"],
269
+ description: "In record mode, changing a property at the playhead keys it.",
270
+ run: () => {
271
+ controller.toggleRecording();
272
+ return { recording: controller.getState().recording };
273
+ }
274
+ },
275
+ {
276
+ ...base,
277
+ id: `${prefix}nextKeyframe`,
278
+ label: "Next Keyframe",
279
+ keywords: ["jump", "navigate"],
280
+ run: () => controller.nextKeyframe()
281
+ },
282
+ {
283
+ ...base,
284
+ id: `${prefix}previousKeyframe`,
285
+ label: "Previous Keyframe",
286
+ keywords: ["jump", "navigate"],
287
+ run: () => controller.previousKeyframe()
288
+ },
289
+ {
290
+ ...base,
291
+ id: `${prefix}animateNode`,
292
+ label: "Animate Node\u2026",
293
+ keywords: ["tween", "transition", "move", "fade"],
294
+ description: "Animates a node from `from` (default: its current values) to `to` over `duration` ms, starting at `delay` ms.",
295
+ params: {
296
+ type: "object",
297
+ properties: {
298
+ nodeId: { type: "string", description: "Node to animate." },
299
+ to: VALUES,
300
+ from: VALUES,
301
+ duration: { type: "number", exclusiveMinimum: 0, description: "Duration in ms (default 1000)." },
302
+ delay: { type: "number", minimum: 0, description: "Start time in ms (default 0)." },
303
+ easing: { type: "string", description: EASING_DESCRIPTION }
304
+ },
305
+ required: ["nodeId", "to"]
306
+ },
307
+ run: (_ctx, params) => controller.animateNode(params.nodeId, {
308
+ to: params.to,
309
+ ...params.from ? { from: params.from } : {},
310
+ ...params.duration !== void 0 ? { duration: params.duration } : {},
311
+ ...params.delay !== void 0 ? { delay: params.delay } : {},
312
+ ...params.easing !== void 0 ? { easing: params.easing } : {}
313
+ })
314
+ },
315
+ {
316
+ ...base,
317
+ id: `${prefix}exportCss`,
318
+ label: "Export Animation as CSS",
319
+ keywords: ["keyframes", "css", "web animations"],
320
+ description: "Returns CSS @keyframes for a node's animation.",
321
+ readOnly: true,
322
+ params: {
323
+ type: "object",
324
+ properties: { nodeId: { type: "string" } },
325
+ required: ["nodeId"]
326
+ },
327
+ run: (_ctx, params) => controller.exportCss(params.nodeId)
328
+ }
329
+ ];
330
+ }
331
+
332
+
333
+
334
+
335
+
336
+ exports.createCommandPaletteController = createCommandPaletteController; exports.matchPaletteHotkey = matchPaletteHotkey; exports.createCanvasAnimationCommands = createCanvasAnimationCommands;
@@ -35,7 +35,7 @@ import {
35
35
  searchDocument,
36
36
  sliceContainsTable,
37
37
  syncInitialContent
38
- } from "./chunk-X4FTNFIA.js";
38
+ } from "./chunk-7MGPW2OV.js";
39
39
  import {
40
40
  chartDataToTable
41
41
  } from "./chunk-47V3WXDG.js";