@a-company/atelier 0.28.2 → 0.36.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.
package/dist/mcp.cjs CHANGED
@@ -21,18 +21,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // ../mcp/src/index.ts
22
22
  var src_exports = {};
23
23
  __export(src_exports, {
24
+ BRIDGE_PROTOCOL_VERSION: () => BRIDGE_PROTOCOL_VERSION,
24
25
  DocumentStore: () => DocumentStore,
26
+ WebSocketServerTransport: () => WebSocketServerTransport,
25
27
  createServer: () => createServer,
28
+ isBridgeEnvelope: () => isBridgeEnvelope,
26
29
  registerAssetTools: () => register12,
27
30
  registerDeltaTools: () => register5,
28
31
  registerDocumentTools: () => register,
29
32
  registerExportTools: () => register11,
33
+ registerImportImageTools: () => register19,
30
34
  registerInteractionTools: () => register14,
31
35
  registerLayerEffectTools: () => register9,
32
36
  registerLayerTools: () => register2,
37
+ registerOverlayTools: () => register17,
33
38
  registerPerformanceTools: () => register16,
34
39
  registerPresetTools: () => register6,
35
40
  registerPreviewTools: () => register7,
41
+ registerRecipeTools: () => register18,
36
42
  registerRefTools: () => register15,
37
43
  registerShapeTools: () => register3,
38
44
  registerStateConfigTools: () => register10,
@@ -52,13 +58,15 @@ function generateId() {
52
58
  }
53
59
  var DocumentStore = class {
54
60
  docs = /* @__PURE__ */ new Map();
61
+ listeners = [];
55
62
  /** Create a new document entry. Returns the assigned ID. */
56
- create(doc, id) {
63
+ create(doc, id, source = "system") {
57
64
  const docId = id ?? generateId();
58
65
  if (this.docs.has(docId)) {
59
66
  throw new Error(`Document "${docId}" already exists`);
60
67
  }
61
68
  this.docs.set(docId, doc);
69
+ this.emit(docId, doc, source);
62
70
  return docId;
63
71
  }
64
72
  /** Get a document by ID. */
@@ -66,12 +74,15 @@ var DocumentStore = class {
66
74
  return this.docs.get(id);
67
75
  }
68
76
  /** Set (overwrite) a document by ID. */
69
- set(id, doc) {
77
+ set(id, doc, source = "system") {
70
78
  this.docs.set(id, doc);
79
+ this.emit(id, doc, source);
71
80
  }
72
81
  /** Delete a document by ID. Returns true if it existed. */
73
- delete(id) {
74
- return this.docs.delete(id);
82
+ delete(id, source = "system") {
83
+ const existed = this.docs.delete(id);
84
+ if (existed) this.emit(id, null, source);
85
+ return existed;
75
86
  }
76
87
  /** Check if a document exists. */
77
88
  has(id) {
@@ -85,14 +96,43 @@ var DocumentStore = class {
85
96
  }
86
97
  return result;
87
98
  }
88
- /** Clear all documents (useful for testing). */
99
+ /** Clear all documents (useful for testing). Does NOT fire onChange. */
89
100
  clear() {
90
101
  this.docs.clear();
91
102
  }
103
+ /**
104
+ * Subscribe to mutation events. The listener is invoked after every
105
+ * create()/set()/delete() with the changed document ID, the new value
106
+ * (or `null` when deleted), and the source tag.
107
+ *
108
+ * Returns an unsubscribe function. Multiple listeners are supported and
109
+ * delivered in registration order. A listener that throws is isolated —
110
+ * the error is swallowed and remaining listeners still fire.
111
+ *
112
+ * TODO(#10): the live WS bridge will subscribe here to forward LLM-side
113
+ * mutations into the in-browser Studio (which exposes the symmetric
114
+ * `AtelierStudio.applyMutation()` API).
115
+ */
116
+ onChange(listener) {
117
+ this.listeners.push(listener);
118
+ return () => {
119
+ const idx = this.listeners.indexOf(listener);
120
+ if (idx !== -1) this.listeners.splice(idx, 1);
121
+ };
122
+ }
123
+ emit(id, doc, source) {
124
+ const snapshot = this.listeners.slice();
125
+ for (const listener of snapshot) {
126
+ try {
127
+ listener(id, doc, source);
128
+ } catch {
129
+ }
130
+ }
131
+ }
92
132
  };
93
133
 
94
134
  // ../mcp/src/tools/document.ts
95
- var import_zod15 = require("zod");
135
+ var import_zod16 = require("zod");
96
136
 
97
137
  // ../schema/dist/index.js
98
138
  var import_zod = require("zod");
@@ -109,6 +149,7 @@ var import_zod11 = require("zod");
109
149
  var import_zod12 = require("zod");
110
150
  var import_zod13 = require("zod");
111
151
  var import_zod14 = require("zod");
152
+ var import_zod15 = require("zod");
112
153
  var import_yaml = require("yaml");
113
154
  var PixelSchema = import_zod.z.number();
114
155
  var PercentageSchema = import_zod.z.string().regex(/^-?\d+(\.\d+)?%$/, {
@@ -350,6 +391,18 @@ var ImageVisualSchema = import_zod7.z.object({
350
391
  spritesheet: SpritesheetConfigSchema.optional(),
351
392
  frameIndex: import_zod7.z.number().int().min(0).optional()
352
393
  });
394
+ var VideoVisualSchema = import_zod7.z.object({
395
+ type: import_zod7.z.literal("video"),
396
+ assetId: import_zod7.z.string().min(1, "assetId is required"),
397
+ src: import_zod7.z.string().optional(),
398
+ startFrame: import_zod7.z.number().int().min(0).optional(),
399
+ sourceOffset: import_zod7.z.number().min(0).optional(),
400
+ sourceEnd: import_zod7.z.number().positive().optional(),
401
+ playbackRate: import_zod7.z.number().positive().optional(),
402
+ volume: import_zod7.z.number().min(0).max(1).optional(),
403
+ muted: import_zod7.z.boolean().optional(),
404
+ objectFit: import_zod7.z.enum(["contain", "cover", "fill"]).optional()
405
+ });
353
406
  var GroupVisualSchema = import_zod7.z.object({
354
407
  type: import_zod7.z.literal("group")
355
408
  });
@@ -363,6 +416,7 @@ var VisualSchema = import_zod7.z.discriminatedUnion("type", [
363
416
  ShapeVisualSchema,
364
417
  TextVisualSchema,
365
418
  ImageVisualSchema,
419
+ VideoVisualSchema,
366
420
  GroupVisualSchema,
367
421
  RefVisualSchema
368
422
  ]);
@@ -482,7 +536,7 @@ var VariableSchema = import_zod12.z.object({
482
536
  default: import_zod12.z.unknown().optional(),
483
537
  description: import_zod12.z.string().optional()
484
538
  });
485
- var AssetTypeSchema = import_zod13.z.enum(["image", "svg", "font", "animation", "audio"]);
539
+ var AssetTypeSchema = import_zod13.z.enum(["image", "svg", "font", "animation", "audio", "video"]);
486
540
  var AssetSchema = import_zod13.z.object({
487
541
  type: AssetTypeSchema,
488
542
  src: import_zod13.z.string().min(1, "Asset src is required"),
@@ -493,6 +547,12 @@ var AssetSchema = import_zod13.z.object({
493
547
  frameCount: import_zod13.z.number().int().positive().optional(),
494
548
  frameWidth: import_zod13.z.number().positive(),
495
549
  frameHeight: import_zod13.z.number().positive()
550
+ }).optional(),
551
+ videoMeta: import_zod13.z.object({
552
+ duration: import_zod13.z.number().positive("videoMeta.duration must be positive"),
553
+ fps: import_zod13.z.number().positive("videoMeta.fps must be positive"),
554
+ width: import_zod13.z.number().int().positive(),
555
+ height: import_zod13.z.number().int().positive()
496
556
  }).optional()
497
557
  });
498
558
  var CanvasSchema = import_zod14.z.object({
@@ -513,6 +573,121 @@ var AtelierDocumentSchema = import_zod14.z.object({
513
573
  layers: import_zod14.z.array(LayerSchema),
514
574
  states: import_zod14.z.record(import_zod14.z.string(), StateSchema)
515
575
  });
576
+ var SilencePolicySchema = import_zod15.z.object({
577
+ noise: import_zod15.z.string().optional(),
578
+ min_silence: import_zod15.z.number().nonnegative().optional(),
579
+ default_padding_pre: import_zod15.z.number().nonnegative().optional(),
580
+ default_padding_post: import_zod15.z.number().nonnegative().optional(),
581
+ match_tolerance: import_zod15.z.number().nonnegative().optional()
582
+ }).strict();
583
+ var CaptionStyleSchema = import_zod15.z.object({
584
+ font_family: import_zod15.z.string().optional(),
585
+ font_size: import_zod15.z.number().positive().optional(),
586
+ font_weight: import_zod15.z.union([import_zod15.z.literal("normal"), import_zod15.z.literal("bold"), import_zod15.z.number()]).optional(),
587
+ text_align: import_zod15.z.enum(["left", "center", "right"]).optional(),
588
+ color: import_zod15.z.string().optional(),
589
+ y_ratio: import_zod15.z.number().min(0).max(1).optional(),
590
+ width_ratio: import_zod15.z.number().min(0).max(1).optional(),
591
+ fade_seconds: import_zod15.z.number().nonnegative().optional()
592
+ }).strict();
593
+ var CaptionGroupingSchema = import_zod15.z.object({
594
+ max_words: import_zod15.z.number().int().positive().optional(),
595
+ pause_gap: import_zod15.z.number().nonnegative().optional()
596
+ }).strict();
597
+ var OverlayAnchorSchema = import_zod15.z.enum([
598
+ "top-left",
599
+ "top-right",
600
+ "bottom-left",
601
+ "bottom-right"
602
+ ]);
603
+ var OverlayTextStyleSchema = import_zod15.z.object({
604
+ font_family: import_zod15.z.string().optional(),
605
+ font_size: import_zod15.z.number().positive().optional(),
606
+ font_weight: import_zod15.z.union([import_zod15.z.literal("normal"), import_zod15.z.literal("bold"), import_zod15.z.number()]).optional(),
607
+ color: import_zod15.z.string().optional()
608
+ }).strict();
609
+ function validatePageNumberFormat(format, ctx) {
610
+ if (format.length === 0) {
611
+ ctx.addIssue({
612
+ code: import_zod15.z.ZodIssueCode.custom,
613
+ message: "format must be a non-empty string"
614
+ });
615
+ return;
616
+ }
617
+ const open = (format.match(/\{/g) ?? []).length;
618
+ const close = (format.match(/\}/g) ?? []).length;
619
+ if (open !== close) {
620
+ ctx.addIssue({
621
+ code: import_zod15.z.ZodIssueCode.custom,
622
+ message: `format has unbalanced braces (${open} '{' vs ${close} '}')`
623
+ });
624
+ return;
625
+ }
626
+ const groupRe = /\{([^{}]*)\}/g;
627
+ const groupRule = /^(current|total)(:0\d+d)?$/;
628
+ let m;
629
+ let sawCurrent = false;
630
+ let sawTotal = false;
631
+ while ((m = groupRe.exec(format)) !== null) {
632
+ const inner = m[1];
633
+ if (!groupRule.test(inner)) {
634
+ ctx.addIssue({
635
+ code: import_zod15.z.ZodIssueCode.custom,
636
+ message: `format placeholder "{${inner}}" is not recognized \u2014 expected {current}, {total}, {current:0Nd}, or {total:0Nd}`
637
+ });
638
+ return;
639
+ }
640
+ if (inner.startsWith("current")) sawCurrent = true;
641
+ if (inner.startsWith("total")) sawTotal = true;
642
+ }
643
+ if (!sawCurrent && !sawTotal) {
644
+ ctx.addIssue({
645
+ code: import_zod15.z.ZodIssueCode.custom,
646
+ message: "format must contain at least one {current} or {total} placeholder"
647
+ });
648
+ }
649
+ }
650
+ var OverlayHandleRuleSchema = import_zod15.z.object({
651
+ text: import_zod15.z.string().min(1),
652
+ anchor: OverlayAnchorSchema,
653
+ margin: import_zod15.z.number().nonnegative().optional(),
654
+ style: OverlayTextStyleSchema.optional()
655
+ }).strict();
656
+ var OverlayPageNumberRuleSchema = import_zod15.z.object({
657
+ format: import_zod15.z.string().superRefine(validatePageNumberFormat),
658
+ anchor: OverlayAnchorSchema,
659
+ margin: import_zod15.z.number().nonnegative().optional(),
660
+ style: OverlayTextStyleSchema.optional()
661
+ }).strict();
662
+ var OverlayRulesSchema = import_zod15.z.object({
663
+ handle: OverlayHandleRuleSchema.optional(),
664
+ page_number: OverlayPageNumberRuleSchema.optional()
665
+ }).strict();
666
+ var StudioRecipeSchema = import_zod15.z.object({
667
+ version: import_zod15.z.string(),
668
+ name: import_zod15.z.string(),
669
+ description: import_zod15.z.string().optional(),
670
+ author: import_zod15.z.string().optional(),
671
+ tags: import_zod15.z.array(import_zod15.z.string()).optional(),
672
+ silence_policy: SilencePolicySchema.optional(),
673
+ caption_style: CaptionStyleSchema.optional(),
674
+ caption_grouping: CaptionGroupingSchema.optional(),
675
+ // Phase 1.5 — first-class overlay rules
676
+ overlay_rules: OverlayRulesSchema.optional(),
677
+ // Reserved — Phase 3 (parse-opaque)
678
+ caption_highlight: import_zod15.z.unknown().optional(),
679
+ transition_kit: import_zod15.z.unknown().optional(),
680
+ palette: import_zod15.z.unknown().optional(),
681
+ audio_policy: import_zod15.z.unknown().optional(),
682
+ aspect_targets: import_zod15.z.array(import_zod15.z.unknown()).optional()
683
+ }).strict();
684
+ var RESERVED_RECIPE_FIELDS = [
685
+ "caption_highlight",
686
+ "transition_kit",
687
+ "palette",
688
+ "audio_policy",
689
+ "aspect_targets"
690
+ ];
516
691
  function formatErrors(error) {
517
692
  return error.issues.map((issue) => ({
518
693
  path: issue.path.join(".") || "(root)",
@@ -526,14 +701,30 @@ function validateDocument(input) {
526
701
  }
527
702
  return { success: false, errors: formatErrors(result.error) };
528
703
  }
704
+ function validateRecipe(recipe) {
705
+ const parsed = StudioRecipeSchema.safeParse(recipe);
706
+ if (!parsed.success) {
707
+ return { success: false, errors: formatErrors(parsed.error) };
708
+ }
709
+ const warnings = [];
710
+ const data = parsed.data;
711
+ for (const field of RESERVED_RECIPE_FIELDS) {
712
+ if (data[field] !== void 0) {
713
+ warnings.push(
714
+ `${field} is reserved for Phase 3 and currently has no effect.`
715
+ );
716
+ }
717
+ }
718
+ return { success: true, data, ...warnings.length > 0 && { warnings } };
719
+ }
529
720
  function parseAtelier(yamlString) {
530
721
  let parsed;
531
722
  try {
532
723
  parsed = (0, import_yaml.parse)(yamlString);
533
- } catch (err17) {
724
+ } catch (err20) {
534
725
  return {
535
726
  success: false,
536
- errors: [{ path: "(yaml)", message: `YAML parse error: ${err17.message}` }]
727
+ errors: [{ path: "(yaml)", message: `YAML parse error: ${err20.message}` }]
537
728
  };
538
729
  }
539
730
  return validateDocument(parsed);
@@ -559,13 +750,13 @@ function register(server, store) {
559
750
  "atelier_create",
560
751
  "Create a new Atelier animation document with canvas settings",
561
752
  {
562
- name: import_zod15.z.string().describe("Animation name"),
563
- width: import_zod15.z.number().positive().describe("Canvas width in pixels"),
564
- height: import_zod15.z.number().positive().describe("Canvas height in pixels"),
565
- fps: import_zod15.z.number().positive().int().describe("Frames per second"),
566
- background: import_zod15.z.string().optional().describe("Background color (hex string)"),
567
- description: import_zod15.z.string().optional().describe("Animation description"),
568
- tags: import_zod15.z.array(import_zod15.z.string()).optional().describe("Tags for categorization")
753
+ name: import_zod16.z.string().describe("Animation name"),
754
+ width: import_zod16.z.number().positive().describe("Canvas width in pixels"),
755
+ height: import_zod16.z.number().positive().describe("Canvas height in pixels"),
756
+ fps: import_zod16.z.number().positive().int().describe("Frames per second"),
757
+ background: import_zod16.z.string().optional().describe("Background color (hex string)"),
758
+ description: import_zod16.z.string().optional().describe("Animation description"),
759
+ tags: import_zod16.z.array(import_zod16.z.string()).optional().describe("Tags for categorization")
569
760
  },
570
761
  { readOnlyHint: false, destructiveHint: false },
571
762
  async ({ name, width, height, fps, background, description, tags }) => {
@@ -590,7 +781,7 @@ function register(server, store) {
590
781
  "atelier_info",
591
782
  "Get summary information about an Atelier document",
592
783
  {
593
- id: import_zod15.z.string().describe("Document ID")
784
+ id: import_zod16.z.string().describe("Document ID")
594
785
  },
595
786
  { readOnlyHint: true, destructiveHint: false },
596
787
  async ({ id }) => {
@@ -618,8 +809,8 @@ function register(server, store) {
618
809
  "atelier_load",
619
810
  "Load an Atelier document from a YAML string",
620
811
  {
621
- id: import_zod15.z.string().optional().describe("Custom document ID (auto-generated if omitted)"),
622
- yaml: import_zod15.z.string().describe("YAML string representing an Atelier document")
812
+ id: import_zod16.z.string().optional().describe("Custom document ID (auto-generated if omitted)"),
813
+ yaml: import_zod16.z.string().describe("YAML string representing an Atelier document")
623
814
  },
624
815
  { readOnlyHint: false, destructiveHint: false },
625
816
  async ({ id, yaml }) => {
@@ -635,7 +826,7 @@ function register(server, store) {
635
826
  "atelier_export",
636
827
  "Export an Atelier document as a YAML string",
637
828
  {
638
- id: import_zod15.z.string().describe("Document ID")
829
+ id: import_zod16.z.string().describe("Document ID")
639
830
  },
640
831
  { readOnlyHint: true, destructiveHint: false },
641
832
  async ({ id }) => {
@@ -658,7 +849,7 @@ function register(server, store) {
658
849
  }
659
850
 
660
851
  // ../mcp/src/tools/layers.ts
661
- var import_zod16 = require("zod");
852
+ var import_zod17 = require("zod");
662
853
  function getDoc2(store, id) {
663
854
  const doc = store.get(id);
664
855
  if (!doc) return { error: `Document "${id}" not found` };
@@ -670,69 +861,69 @@ function ok2(data) {
670
861
  function err2(message) {
671
862
  return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
672
863
  }
673
- var VisualInputSchema = import_zod16.z.object({
674
- type: import_zod16.z.enum(["shape", "text", "image", "group", "ref"]).describe("Visual type"),
864
+ var VisualInputSchema = import_zod17.z.object({
865
+ type: import_zod17.z.enum(["shape", "text", "image", "group", "ref"]).describe("Visual type"),
675
866
  // shape visual fields
676
- shape: import_zod16.z.object({
677
- type: import_zod16.z.enum(["rect", "ellipse", "path"]),
678
- cornerRadius: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.tuple([import_zod16.z.number(), import_zod16.z.number(), import_zod16.z.number(), import_zod16.z.number()])]).optional(),
679
- points: import_zod16.z.array(import_zod16.z.object({
680
- x: import_zod16.z.number(),
681
- y: import_zod16.z.number(),
682
- in: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional(),
683
- out: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional()
867
+ shape: import_zod17.z.object({
868
+ type: import_zod17.z.enum(["rect", "ellipse", "path"]),
869
+ cornerRadius: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.tuple([import_zod17.z.number(), import_zod17.z.number(), import_zod17.z.number(), import_zod17.z.number()])]).optional(),
870
+ points: import_zod17.z.array(import_zod17.z.object({
871
+ x: import_zod17.z.number(),
872
+ y: import_zod17.z.number(),
873
+ in: import_zod17.z.object({ x: import_zod17.z.number(), y: import_zod17.z.number() }).optional(),
874
+ out: import_zod17.z.object({ x: import_zod17.z.number(), y: import_zod17.z.number() }).optional()
684
875
  })).optional(),
685
- closed: import_zod16.z.boolean().optional()
876
+ closed: import_zod17.z.boolean().optional()
686
877
  }).optional(),
687
- fill: import_zod16.z.record(import_zod16.z.unknown()).optional(),
688
- stroke: import_zod16.z.record(import_zod16.z.unknown()).optional(),
878
+ fill: import_zod17.z.record(import_zod17.z.unknown()).optional(),
879
+ stroke: import_zod17.z.record(import_zod17.z.unknown()).optional(),
689
880
  // text visual fields
690
- content: import_zod16.z.string().optional(),
691
- style: import_zod16.z.record(import_zod16.z.unknown()).optional(),
881
+ content: import_zod17.z.string().optional(),
882
+ style: import_zod17.z.record(import_zod17.z.unknown()).optional(),
692
883
  // image visual fields
693
- assetId: import_zod16.z.string().optional(),
694
- sourceRect: import_zod16.z.object({
695
- x: import_zod16.z.number(),
696
- y: import_zod16.z.number(),
697
- width: import_zod16.z.number().positive(),
698
- height: import_zod16.z.number().positive()
884
+ assetId: import_zod17.z.string().optional(),
885
+ sourceRect: import_zod17.z.object({
886
+ x: import_zod17.z.number(),
887
+ y: import_zod17.z.number(),
888
+ width: import_zod17.z.number().positive(),
889
+ height: import_zod17.z.number().positive()
699
890
  }).optional(),
700
- spritesheet: import_zod16.z.object({
701
- columns: import_zod16.z.number().int().positive(),
702
- rows: import_zod16.z.number().int().positive(),
703
- frameCount: import_zod16.z.number().int().positive().optional(),
704
- frameWidth: import_zod16.z.number().positive(),
705
- frameHeight: import_zod16.z.number().positive()
891
+ spritesheet: import_zod17.z.object({
892
+ columns: import_zod17.z.number().int().positive(),
893
+ rows: import_zod17.z.number().int().positive(),
894
+ frameCount: import_zod17.z.number().int().positive().optional(),
895
+ frameWidth: import_zod17.z.number().positive(),
896
+ frameHeight: import_zod17.z.number().positive()
706
897
  }).optional(),
707
- frameIndex: import_zod16.z.number().int().min(0).optional(),
898
+ frameIndex: import_zod17.z.number().int().min(0).optional(),
708
899
  // ref visual fields
709
- src: import_zod16.z.string().optional(),
710
- state: import_zod16.z.string().optional(),
711
- frame: import_zod16.z.number().int().min(0).optional()
900
+ src: import_zod17.z.string().optional(),
901
+ state: import_zod17.z.string().optional(),
902
+ frame: import_zod17.z.number().int().min(0).optional()
712
903
  }).describe("Visual content definition");
713
904
  function register2(server, store) {
714
905
  server.tool(
715
906
  "atelier_add_layer",
716
907
  "Add a new layer to an Atelier document",
717
908
  {
718
- id: import_zod16.z.string().describe("Document ID"),
719
- layerId: import_zod16.z.string().describe("Unique layer ID"),
909
+ id: import_zod17.z.string().describe("Document ID"),
910
+ layerId: import_zod17.z.string().describe("Unique layer ID"),
720
911
  visual: VisualInputSchema.describe("Visual content definition"),
721
- x: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).describe("X position (pixels or percentage)"),
722
- y: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).describe("Y position (pixels or percentage)"),
723
- width: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).describe("Width (pixels or percentage)"),
724
- height: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).describe("Height (pixels or percentage)"),
725
- description: import_zod16.z.string().optional().describe("Layer description"),
726
- tags: import_zod16.z.array(import_zod16.z.string()).optional().describe("Tags"),
727
- opacity: import_zod16.z.number().min(0).max(1).optional().describe("Opacity 0-1"),
728
- rotation: import_zod16.z.number().optional().describe("Rotation in degrees"),
729
- parentId: import_zod16.z.string().optional().describe("Parent layer ID for transform inheritance"),
730
- anchorPoint: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional().describe("Anchor point (0-1 normalized)"),
731
- scale: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional().describe("Scale factors"),
732
- visible: import_zod16.z.boolean().optional().describe("Whether layer is visible"),
733
- tint: import_zod16.z.object({
734
- color: import_zod16.z.string().describe("Tint color (hex string)"),
735
- amount: import_zod16.z.number().min(0).max(1).describe("Tint amount (0 = none, 1 = full)")
912
+ x: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]).describe("X position (pixels or percentage)"),
913
+ y: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]).describe("Y position (pixels or percentage)"),
914
+ width: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]).describe("Width (pixels or percentage)"),
915
+ height: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]).describe("Height (pixels or percentage)"),
916
+ description: import_zod17.z.string().optional().describe("Layer description"),
917
+ tags: import_zod17.z.array(import_zod17.z.string()).optional().describe("Tags"),
918
+ opacity: import_zod17.z.number().min(0).max(1).optional().describe("Opacity 0-1"),
919
+ rotation: import_zod17.z.number().optional().describe("Rotation in degrees"),
920
+ parentId: import_zod17.z.string().optional().describe("Parent layer ID for transform inheritance"),
921
+ anchorPoint: import_zod17.z.object({ x: import_zod17.z.number(), y: import_zod17.z.number() }).optional().describe("Anchor point (0-1 normalized)"),
922
+ scale: import_zod17.z.object({ x: import_zod17.z.number(), y: import_zod17.z.number() }).optional().describe("Scale factors"),
923
+ visible: import_zod17.z.boolean().optional().describe("Whether layer is visible"),
924
+ tint: import_zod17.z.object({
925
+ color: import_zod17.z.string().describe("Tint color (hex string)"),
926
+ amount: import_zod17.z.number().min(0).max(1).describe("Tint amount (0 = none, 1 = full)")
736
927
  }).optional().describe("Color tint overlay")
737
928
  },
738
929
  { readOnlyHint: false, destructiveHint: false },
@@ -769,20 +960,20 @@ function register2(server, store) {
769
960
  "atelier_edit_layer",
770
961
  "Edit properties of an existing layer",
771
962
  {
772
- id: import_zod16.z.string().describe("Document ID"),
773
- layerId: import_zod16.z.string().describe("Layer ID to edit"),
774
- x: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).optional().describe("New X position"),
775
- y: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).optional().describe("New Y position"),
776
- width: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).optional().describe("New width"),
777
- height: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).optional().describe("New height"),
778
- description: import_zod16.z.string().optional().describe("New description"),
779
- tags: import_zod16.z.array(import_zod16.z.string()).optional().describe("New tags"),
780
- opacity: import_zod16.z.number().min(0).max(1).optional().describe("New opacity"),
781
- rotation: import_zod16.z.number().optional().describe("New rotation"),
782
- parentId: import_zod16.z.string().nullable().optional().describe("New parent layer ID (null to clear)"),
783
- anchorPoint: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional().describe("New anchor point"),
784
- scale: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional().describe("New scale"),
785
- visible: import_zod16.z.boolean().optional().describe("New visibility")
963
+ id: import_zod17.z.string().describe("Document ID"),
964
+ layerId: import_zod17.z.string().describe("Layer ID to edit"),
965
+ x: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]).optional().describe("New X position"),
966
+ y: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]).optional().describe("New Y position"),
967
+ width: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]).optional().describe("New width"),
968
+ height: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]).optional().describe("New height"),
969
+ description: import_zod17.z.string().optional().describe("New description"),
970
+ tags: import_zod17.z.array(import_zod17.z.string()).optional().describe("New tags"),
971
+ opacity: import_zod17.z.number().min(0).max(1).optional().describe("New opacity"),
972
+ rotation: import_zod17.z.number().optional().describe("New rotation"),
973
+ parentId: import_zod17.z.string().nullable().optional().describe("New parent layer ID (null to clear)"),
974
+ anchorPoint: import_zod17.z.object({ x: import_zod17.z.number(), y: import_zod17.z.number() }).optional().describe("New anchor point"),
975
+ scale: import_zod17.z.object({ x: import_zod17.z.number(), y: import_zod17.z.number() }).optional().describe("New scale"),
976
+ visible: import_zod17.z.boolean().optional().describe("New visibility")
786
977
  },
787
978
  { readOnlyHint: false, destructiveHint: false },
788
979
  async ({ id, layerId, x, y, width, height, description, tags, opacity, rotation, parentId, anchorPoint, scale, visible }) => {
@@ -817,8 +1008,8 @@ function register2(server, store) {
817
1008
  "atelier_remove_layer",
818
1009
  "Remove a layer from an Atelier document",
819
1010
  {
820
- id: import_zod16.z.string().describe("Document ID"),
821
- layerId: import_zod16.z.string().describe("Layer ID to remove")
1011
+ id: import_zod17.z.string().describe("Document ID"),
1012
+ layerId: import_zod17.z.string().describe("Layer ID to remove")
822
1013
  },
823
1014
  { readOnlyHint: false, destructiveHint: true },
824
1015
  async ({ id, layerId }) => {
@@ -846,7 +1037,7 @@ function register2(server, store) {
846
1037
  "atelier_list_layers",
847
1038
  "List all layers in an Atelier document",
848
1039
  {
849
- id: import_zod16.z.string().describe("Document ID")
1040
+ id: import_zod17.z.string().describe("Document ID")
850
1041
  },
851
1042
  { readOnlyHint: true, destructiveHint: false },
852
1043
  async ({ id }) => {
@@ -873,9 +1064,9 @@ function register2(server, store) {
873
1064
  "atelier_reorder",
874
1065
  "Move a layer to a new position in the layer stack",
875
1066
  {
876
- id: import_zod16.z.string().describe("Document ID"),
877
- layerId: import_zod16.z.string().describe("Layer ID to move"),
878
- position: import_zod16.z.number().int().min(0).describe("Target position index (0-based)")
1067
+ id: import_zod17.z.string().describe("Document ID"),
1068
+ layerId: import_zod17.z.string().describe("Layer ID to move"),
1069
+ position: import_zod17.z.number().int().min(0).describe("Target position index (0-based)")
879
1070
  },
880
1071
  { readOnlyHint: false, destructiveHint: false },
881
1072
  async ({ id, layerId, position }) => {
@@ -893,7 +1084,7 @@ function register2(server, store) {
893
1084
  }
894
1085
 
895
1086
  // ../mcp/src/tools/shapes.ts
896
- var import_zod17 = require("zod");
1087
+ var import_zod18 = require("zod");
897
1088
  function getDoc3(store, id) {
898
1089
  const doc = store.get(id);
899
1090
  if (!doc) return { error: `Document "${id}" not found` };
@@ -910,21 +1101,21 @@ function register3(server, store) {
910
1101
  "atelier_set_shape",
911
1102
  "Set the shape on a shape-type layer",
912
1103
  {
913
- id: import_zod17.z.string().describe("Document ID"),
914
- layerId: import_zod17.z.string().describe("Layer ID (must be a shape visual)"),
915
- shape: import_zod17.z.object({
916
- type: import_zod17.z.enum(["rect", "ellipse", "path"]).describe("Shape type"),
917
- cornerRadius: import_zod17.z.union([
918
- import_zod17.z.number(),
919
- import_zod17.z.tuple([import_zod17.z.number(), import_zod17.z.number(), import_zod17.z.number(), import_zod17.z.number()])
1104
+ id: import_zod18.z.string().describe("Document ID"),
1105
+ layerId: import_zod18.z.string().describe("Layer ID (must be a shape visual)"),
1106
+ shape: import_zod18.z.object({
1107
+ type: import_zod18.z.enum(["rect", "ellipse", "path"]).describe("Shape type"),
1108
+ cornerRadius: import_zod18.z.union([
1109
+ import_zod18.z.number(),
1110
+ import_zod18.z.tuple([import_zod18.z.number(), import_zod18.z.number(), import_zod18.z.number(), import_zod18.z.number()])
920
1111
  ]).optional().describe("Corner radius (rect only)"),
921
- points: import_zod17.z.array(import_zod17.z.object({
922
- x: import_zod17.z.number(),
923
- y: import_zod17.z.number(),
924
- in: import_zod17.z.object({ x: import_zod17.z.number(), y: import_zod17.z.number() }).optional(),
925
- out: import_zod17.z.object({ x: import_zod17.z.number(), y: import_zod17.z.number() }).optional()
1112
+ points: import_zod18.z.array(import_zod18.z.object({
1113
+ x: import_zod18.z.number(),
1114
+ y: import_zod18.z.number(),
1115
+ in: import_zod18.z.object({ x: import_zod18.z.number(), y: import_zod18.z.number() }).optional(),
1116
+ out: import_zod18.z.object({ x: import_zod18.z.number(), y: import_zod18.z.number() }).optional()
926
1117
  })).optional().describe("Path points (path only)"),
927
- closed: import_zod17.z.boolean().optional().describe("Whether path is closed (path only)")
1118
+ closed: import_zod18.z.boolean().optional().describe("Whether path is closed (path only)")
928
1119
  }).describe("Shape definition")
929
1120
  },
930
1121
  { readOnlyHint: false, destructiveHint: false },
@@ -943,20 +1134,20 @@ function register3(server, store) {
943
1134
  "atelier_set_fill",
944
1135
  "Set the fill on a shape-type layer",
945
1136
  {
946
- id: import_zod17.z.string().describe("Document ID"),
947
- layerId: import_zod17.z.string().describe("Layer ID (must be a shape visual)"),
948
- fill: import_zod17.z.object({
949
- type: import_zod17.z.enum(["solid", "linear-gradient", "radial-gradient"]).describe("Fill type"),
950
- color: import_zod17.z.unknown().optional().describe("Color for solid fill (hex string or RGBA/HSLA object)"),
951
- angle: import_zod17.z.number().optional().describe("Angle in degrees (linear-gradient only)"),
952
- center: import_zod17.z.object({
953
- x: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]),
954
- y: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()])
1137
+ id: import_zod18.z.string().describe("Document ID"),
1138
+ layerId: import_zod18.z.string().describe("Layer ID (must be a shape visual)"),
1139
+ fill: import_zod18.z.object({
1140
+ type: import_zod18.z.enum(["solid", "linear-gradient", "radial-gradient"]).describe("Fill type"),
1141
+ color: import_zod18.z.unknown().optional().describe("Color for solid fill (hex string or RGBA/HSLA object)"),
1142
+ angle: import_zod18.z.number().optional().describe("Angle in degrees (linear-gradient only)"),
1143
+ center: import_zod18.z.object({
1144
+ x: import_zod18.z.union([import_zod18.z.number(), import_zod18.z.string()]),
1145
+ y: import_zod18.z.union([import_zod18.z.number(), import_zod18.z.string()])
955
1146
  }).optional().describe("Center point (radial-gradient only)"),
956
- radius: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]).optional().describe("Radius (radial-gradient only)"),
957
- stops: import_zod17.z.array(import_zod17.z.object({
958
- offset: import_zod17.z.number().min(0).max(1),
959
- color: import_zod17.z.unknown()
1147
+ radius: import_zod18.z.union([import_zod18.z.number(), import_zod18.z.string()]).optional().describe("Radius (radial-gradient only)"),
1148
+ stops: import_zod18.z.array(import_zod18.z.object({
1149
+ offset: import_zod18.z.number().min(0).max(1),
1150
+ color: import_zod18.z.unknown()
960
1151
  })).optional().describe("Gradient stops")
961
1152
  }).describe("Fill definition")
962
1153
  },
@@ -976,14 +1167,14 @@ function register3(server, store) {
976
1167
  "atelier_set_stroke",
977
1168
  "Set the stroke on a shape-type layer",
978
1169
  {
979
- id: import_zod17.z.string().describe("Document ID"),
980
- layerId: import_zod17.z.string().describe("Layer ID (must be a shape visual)"),
981
- stroke: import_zod17.z.object({
982
- color: import_zod17.z.unknown().describe("Stroke color (hex string or RGBA/HSLA object)"),
983
- width: import_zod17.z.number().positive().describe("Stroke width in pixels"),
984
- dash: import_zod17.z.array(import_zod17.z.number()).optional().describe("Dash pattern"),
985
- lineCap: import_zod17.z.enum(["butt", "round", "square"]).optional().describe("Line cap style"),
986
- lineJoin: import_zod17.z.enum(["miter", "round", "bevel"]).optional().describe("Line join style")
1170
+ id: import_zod18.z.string().describe("Document ID"),
1171
+ layerId: import_zod18.z.string().describe("Layer ID (must be a shape visual)"),
1172
+ stroke: import_zod18.z.object({
1173
+ color: import_zod18.z.unknown().describe("Stroke color (hex string or RGBA/HSLA object)"),
1174
+ width: import_zod18.z.number().positive().describe("Stroke width in pixels"),
1175
+ dash: import_zod18.z.array(import_zod18.z.number()).optional().describe("Dash pattern"),
1176
+ lineCap: import_zod18.z.enum(["butt", "round", "square"]).optional().describe("Line cap style"),
1177
+ lineJoin: import_zod18.z.enum(["miter", "round", "bevel"]).optional().describe("Line join style")
987
1178
  }).describe("Stroke definition")
988
1179
  },
989
1180
  { readOnlyHint: false, destructiveHint: false },
@@ -1001,7 +1192,7 @@ function register3(server, store) {
1001
1192
  }
1002
1193
 
1003
1194
  // ../mcp/src/tools/states.ts
1004
- var import_zod18 = require("zod");
1195
+ var import_zod19 = require("zod");
1005
1196
  function getDoc4(store, id) {
1006
1197
  const doc = store.get(id);
1007
1198
  if (!doc) return { error: `Document "${id}" not found` };
@@ -1018,11 +1209,11 @@ function register4(server, store) {
1018
1209
  "atelier_add_state",
1019
1210
  "Add a named animation state to a document",
1020
1211
  {
1021
- id: import_zod18.z.string().describe("Document ID"),
1022
- stateName: import_zod18.z.string().describe("State name (e.g. 'intro', 'hover', 'exit')"),
1023
- duration: import_zod18.z.number().positive().int().describe("Duration in frames"),
1024
- description: import_zod18.z.string().optional().describe("State description"),
1025
- tags: import_zod18.z.array(import_zod18.z.string()).optional().describe("Tags")
1212
+ id: import_zod19.z.string().describe("Document ID"),
1213
+ stateName: import_zod19.z.string().describe("State name (e.g. 'intro', 'hover', 'exit')"),
1214
+ duration: import_zod19.z.number().positive().int().describe("Duration in frames"),
1215
+ description: import_zod19.z.string().optional().describe("State description"),
1216
+ tags: import_zod19.z.array(import_zod19.z.string()).optional().describe("Tags")
1026
1217
  },
1027
1218
  { readOnlyHint: false, destructiveHint: false },
1028
1219
  async ({ id, stateName, duration, description, tags }) => {
@@ -1045,11 +1236,11 @@ function register4(server, store) {
1045
1236
  "atelier_edit_state",
1046
1237
  "Edit metadata of an existing animation state",
1047
1238
  {
1048
- id: import_zod18.z.string().describe("Document ID"),
1049
- stateName: import_zod18.z.string().describe("State name to edit"),
1050
- duration: import_zod18.z.number().positive().int().optional().describe("New duration in frames"),
1051
- description: import_zod18.z.string().optional().describe("New description"),
1052
- tags: import_zod18.z.array(import_zod18.z.string()).optional().describe("New tags")
1239
+ id: import_zod19.z.string().describe("Document ID"),
1240
+ stateName: import_zod19.z.string().describe("State name to edit"),
1241
+ duration: import_zod19.z.number().positive().int().optional().describe("New duration in frames"),
1242
+ description: import_zod19.z.string().optional().describe("New description"),
1243
+ tags: import_zod19.z.array(import_zod19.z.string()).optional().describe("New tags")
1053
1244
  },
1054
1245
  { readOnlyHint: false, destructiveHint: false },
1055
1246
  async ({ id, stateName, duration, description, tags }) => {
@@ -1068,8 +1259,8 @@ function register4(server, store) {
1068
1259
  "atelier_remove_state",
1069
1260
  "Remove an animation state and all its deltas",
1070
1261
  {
1071
- id: import_zod18.z.string().describe("Document ID"),
1072
- stateName: import_zod18.z.string().describe("State name to remove")
1262
+ id: import_zod19.z.string().describe("Document ID"),
1263
+ stateName: import_zod19.z.string().describe("State name to remove")
1073
1264
  },
1074
1265
  { readOnlyHint: false, destructiveHint: true },
1075
1266
  async ({ id, stateName }) => {
@@ -1086,7 +1277,7 @@ function register4(server, store) {
1086
1277
  "atelier_list_states",
1087
1278
  "List all animation states in a document",
1088
1279
  {
1089
- id: import_zod18.z.string().describe("Document ID")
1280
+ id: import_zod19.z.string().describe("Document ID")
1090
1281
  },
1091
1282
  { readOnlyHint: true, destructiveHint: false },
1092
1283
  async ({ id }) => {
@@ -1106,7 +1297,7 @@ function register4(server, store) {
1106
1297
  }
1107
1298
 
1108
1299
  // ../mcp/src/tools/deltas.ts
1109
- var import_zod19 = require("zod");
1300
+ var import_zod20 = require("zod");
1110
1301
 
1111
1302
  // ../math/dist/index.js
1112
1303
  function linear(t) {
@@ -1662,7 +1853,18 @@ function resolveFrame(doc, stateName, frame, overrideDeltas) {
1662
1853
  }
1663
1854
  }
1664
1855
  }
1665
- return { id: layer.id, layer, computedProperties };
1856
+ const resolvedLayer = { id: layer.id, layer, computedProperties };
1857
+ if (layer.visual.type === "video") {
1858
+ const video = layer.visual;
1859
+ const fps = doc.canvas.fps;
1860
+ const startFrame = video.startFrame ?? 0;
1861
+ const sourceOffset = video.sourceOffset ?? 0;
1862
+ const playbackRate = video.playbackRate ?? 1;
1863
+ const relativeFrame = Math.max(0, frame - startFrame);
1864
+ const sourceTime = relativeFrame / fps * playbackRate + sourceOffset;
1865
+ resolvedLayer.videoSourceTime = video.sourceEnd !== void 0 ? Math.min(sourceTime, video.sourceEnd) : sourceTime;
1866
+ }
1867
+ return resolvedLayer;
1666
1868
  });
1667
1869
  return { frame, stateName, layers: resolvedLayers };
1668
1870
  }
@@ -1834,17 +2036,17 @@ function register5(server, store) {
1834
2036
  "atelier_add_delta",
1835
2037
  "Add an animation delta (keyframe transition) to a state",
1836
2038
  {
1837
- id: import_zod19.z.string().describe("Document ID"),
1838
- stateName: import_zod19.z.string().describe("State name"),
1839
- layer: import_zod19.z.string().describe("Target layer ID"),
2039
+ id: import_zod20.z.string().describe("Document ID"),
2040
+ stateName: import_zod20.z.string().describe("State name"),
2041
+ layer: import_zod20.z.string().describe("Target layer ID"),
1840
2042
  property: AnimatablePropertyEnum.describe("Property to animate"),
1841
- range: import_zod19.z.tuple([import_zod19.z.number().int().min(0), import_zod19.z.number().int().min(0)]).describe("Frame range [start, end] inclusive"),
1842
- from: import_zod19.z.unknown().describe("Starting value"),
1843
- to: import_zod19.z.unknown().describe("Ending value"),
2043
+ range: import_zod20.z.tuple([import_zod20.z.number().int().min(0), import_zod20.z.number().int().min(0)]).describe("Frame range [start, end] inclusive"),
2044
+ from: import_zod20.z.unknown().describe("Starting value"),
2045
+ to: import_zod20.z.unknown().describe("Ending value"),
1844
2046
  easing: EasingInputSchema.optional().describe("Easing function"),
1845
- description: import_zod19.z.string().optional().describe("Delta description"),
1846
- tags: import_zod19.z.array(import_zod19.z.string()).optional().describe("Tags"),
1847
- deltaId: import_zod19.z.string().optional().describe("Custom delta ID")
2047
+ description: import_zod20.z.string().optional().describe("Delta description"),
2048
+ tags: import_zod20.z.array(import_zod20.z.string()).optional().describe("Tags"),
2049
+ deltaId: import_zod20.z.string().optional().describe("Custom delta ID")
1848
2050
  },
1849
2051
  { readOnlyHint: false, destructiveHint: false },
1850
2052
  async ({ id, stateName, layer, property, range, from, to, easing, description, tags, deltaId }) => {
@@ -1882,17 +2084,17 @@ function register5(server, store) {
1882
2084
  "atelier_edit_delta",
1883
2085
  "Edit an existing delta by index within a state",
1884
2086
  {
1885
- id: import_zod19.z.string().describe("Document ID"),
1886
- stateName: import_zod19.z.string().describe("State name"),
1887
- deltaIndex: import_zod19.z.number().int().min(0).describe("Delta index within the state"),
1888
- layer: import_zod19.z.string().optional().describe("New target layer ID"),
2087
+ id: import_zod20.z.string().describe("Document ID"),
2088
+ stateName: import_zod20.z.string().describe("State name"),
2089
+ deltaIndex: import_zod20.z.number().int().min(0).describe("Delta index within the state"),
2090
+ layer: import_zod20.z.string().optional().describe("New target layer ID"),
1889
2091
  property: AnimatablePropertyEnum.optional().describe("New property to animate"),
1890
- range: import_zod19.z.tuple([import_zod19.z.number().int().min(0), import_zod19.z.number().int().min(0)]).optional().describe("New frame range [start, end]"),
1891
- from: import_zod19.z.unknown().optional().describe("New starting value"),
1892
- to: import_zod19.z.unknown().optional().describe("New ending value"),
2092
+ range: import_zod20.z.tuple([import_zod20.z.number().int().min(0), import_zod20.z.number().int().min(0)]).optional().describe("New frame range [start, end]"),
2093
+ from: import_zod20.z.unknown().optional().describe("New starting value"),
2094
+ to: import_zod20.z.unknown().optional().describe("New ending value"),
1893
2095
  easing: EasingInputSchema.optional().describe("New easing function"),
1894
- description: import_zod19.z.string().optional().describe("New description"),
1895
- tags: import_zod19.z.array(import_zod19.z.string()).optional().describe("New tags")
2096
+ description: import_zod20.z.string().optional().describe("New description"),
2097
+ tags: import_zod20.z.array(import_zod20.z.string()).optional().describe("New tags")
1896
2098
  },
1897
2099
  { readOnlyHint: false, destructiveHint: false },
1898
2100
  async ({ id, stateName, deltaIndex, layer, property, range, from, to, easing, description, tags }) => {
@@ -1931,9 +2133,9 @@ function register5(server, store) {
1931
2133
  "atelier_remove_delta",
1932
2134
  "Remove a delta by index from a state",
1933
2135
  {
1934
- id: import_zod19.z.string().describe("Document ID"),
1935
- stateName: import_zod19.z.string().describe("State name"),
1936
- deltaIndex: import_zod19.z.number().int().min(0).describe("Delta index to remove")
2136
+ id: import_zod20.z.string().describe("Document ID"),
2137
+ stateName: import_zod20.z.string().describe("State name"),
2138
+ deltaIndex: import_zod20.z.number().int().min(0).describe("Delta index to remove")
1937
2139
  },
1938
2140
  { readOnlyHint: false, destructiveHint: true },
1939
2141
  async ({ id, stateName, deltaIndex }) => {
@@ -1961,12 +2163,12 @@ function register5(server, store) {
1961
2163
  "atelier_apply_preset",
1962
2164
  "Apply a preset to a layer, expanding it into concrete deltas",
1963
2165
  {
1964
- id: import_zod19.z.string().describe("Document ID"),
1965
- stateName: import_zod19.z.string().describe("State name to add deltas to"),
1966
- presetName: import_zod19.z.string().describe("Preset name defined in the document"),
1967
- layerId: import_zod19.z.string().describe("Target layer ID"),
1968
- startFrame: import_zod19.z.number().int().min(0).optional().describe("Start frame (default: 0)"),
1969
- duration: import_zod19.z.number().positive().int().optional().describe("Duration for preset (default: state duration)")
2166
+ id: import_zod20.z.string().describe("Document ID"),
2167
+ stateName: import_zod20.z.string().describe("State name to add deltas to"),
2168
+ presetName: import_zod20.z.string().describe("Preset name defined in the document"),
2169
+ layerId: import_zod20.z.string().describe("Target layer ID"),
2170
+ startFrame: import_zod20.z.number().int().min(0).optional().describe("Start frame (default: 0)"),
2171
+ duration: import_zod20.z.number().positive().int().optional().describe("Duration for preset (default: state duration)")
1970
2172
  },
1971
2173
  { readOnlyHint: false, destructiveHint: false },
1972
2174
  async ({ id, stateName, presetName, layerId, startFrame, duration }) => {
@@ -2009,7 +2211,7 @@ ${errors.join("\n")}`);
2009
2211
  }
2010
2212
 
2011
2213
  // ../mcp/src/tools/presets.ts
2012
- var import_zod20 = require("zod");
2214
+ var import_zod21 = require("zod");
2013
2215
  function getDoc6(store, id) {
2014
2216
  const doc = store.get(id);
2015
2217
  if (!doc) return { error: `Document "${id}" not found` };
@@ -2023,11 +2225,11 @@ function err6(message) {
2023
2225
  }
2024
2226
  var AnimatablePropertyEnum2 = AnimatablePropertySchema;
2025
2227
  var EasingInputSchema2 = EasingSchema;
2026
- var PresetDeltaSchema2 = import_zod20.z.object({
2228
+ var PresetDeltaSchema2 = import_zod21.z.object({
2027
2229
  property: AnimatablePropertyEnum2.describe("Animatable property"),
2028
- offset: import_zod20.z.tuple([import_zod20.z.number().int().min(0), import_zod20.z.number().int().min(0)]).optional().describe("Relative frame offset [start, end]"),
2029
- from: import_zod20.z.unknown().describe("Starting value"),
2030
- to: import_zod20.z.unknown().describe("Ending value"),
2230
+ offset: import_zod21.z.tuple([import_zod21.z.number().int().min(0), import_zod21.z.number().int().min(0)]).optional().describe("Relative frame offset [start, end]"),
2231
+ from: import_zod21.z.unknown().describe("Starting value"),
2232
+ to: import_zod21.z.unknown().describe("Ending value"),
2031
2233
  easing: EasingInputSchema2.optional().describe("Easing function")
2032
2234
  });
2033
2235
  function register6(server, store) {
@@ -2035,11 +2237,11 @@ function register6(server, store) {
2035
2237
  "atelier_define_preset",
2036
2238
  "Define a reusable animation preset on a document",
2037
2239
  {
2038
- id: import_zod20.z.string().describe("Document ID"),
2039
- presetName: import_zod20.z.string().describe("Preset name"),
2040
- description: import_zod20.z.string().optional().describe("Preset description"),
2041
- tags: import_zod20.z.array(import_zod20.z.string()).optional().describe("Tags"),
2042
- deltas: import_zod20.z.array(PresetDeltaSchema2).min(1).describe("Array of preset delta definitions")
2240
+ id: import_zod21.z.string().describe("Document ID"),
2241
+ presetName: import_zod21.z.string().describe("Preset name"),
2242
+ description: import_zod21.z.string().optional().describe("Preset description"),
2243
+ tags: import_zod21.z.array(import_zod21.z.string()).optional().describe("Tags"),
2244
+ deltas: import_zod21.z.array(PresetDeltaSchema2).min(1).describe("Array of preset delta definitions")
2043
2245
  },
2044
2246
  { readOnlyHint: false, destructiveHint: false },
2045
2247
  async ({ id, presetName, description, tags, deltas }) => {
@@ -2060,7 +2262,7 @@ function register6(server, store) {
2060
2262
  "atelier_list_presets",
2061
2263
  "List all presets defined on a document",
2062
2264
  {
2063
- id: import_zod20.z.string().describe("Document ID")
2265
+ id: import_zod21.z.string().describe("Document ID")
2064
2266
  },
2065
2267
  { readOnlyHint: true, destructiveHint: false },
2066
2268
  async ({ id }) => {
@@ -2083,7 +2285,7 @@ function register6(server, store) {
2083
2285
  }
2084
2286
 
2085
2287
  // ../mcp/src/tools/preview.ts
2086
- var import_zod21 = require("zod");
2288
+ var import_zod22 = require("zod");
2087
2289
  function getDoc7(store, id) {
2088
2290
  const doc = store.get(id);
2089
2291
  if (!doc) return { error: `Document "${id}" not found` };
@@ -2100,7 +2302,7 @@ function register7(server, store) {
2100
2302
  "atelier_validate",
2101
2303
  "Validate an Atelier document for schema correctness and delta overlaps",
2102
2304
  {
2103
- id: import_zod21.z.string().describe("Document ID")
2305
+ id: import_zod22.z.string().describe("Document ID")
2104
2306
  },
2105
2307
  { readOnlyHint: true, destructiveHint: false },
2106
2308
  async ({ id }) => {
@@ -2129,9 +2331,9 @@ function register7(server, store) {
2129
2331
  "atelier_preview",
2130
2332
  "Preview the resolved state of all layers at a specific frame",
2131
2333
  {
2132
- id: import_zod21.z.string().describe("Document ID"),
2133
- stateName: import_zod21.z.string().describe("State name to preview"),
2134
- frame: import_zod21.z.number().int().min(0).describe("Frame number to resolve")
2334
+ id: import_zod22.z.string().describe("Document ID"),
2335
+ stateName: import_zod22.z.string().describe("State name to preview"),
2336
+ frame: import_zod22.z.number().int().min(0).describe("Frame number to resolve")
2135
2337
  },
2136
2338
  { readOnlyHint: true, destructiveHint: false },
2137
2339
  async ({ id, stateName, frame }) => {
@@ -2166,7 +2368,7 @@ function register7(server, store) {
2166
2368
  }
2167
2369
 
2168
2370
  // ../mcp/src/tools/templates.ts
2169
- var import_zod22 = require("zod");
2371
+ var import_zod23 = require("zod");
2170
2372
  function getDoc8(store, id) {
2171
2373
  const doc = store.get(id);
2172
2374
  if (!doc) return { error: `Document "${id}" not found` };
@@ -2183,8 +2385,8 @@ function register8(server, store) {
2183
2385
  "atelier_instantiate_template",
2184
2386
  "Instantiate a template document with variable bindings. Creates a new document in the store.",
2185
2387
  {
2186
- id: import_zod22.z.string().describe("Template document ID"),
2187
- bindings: import_zod22.z.record(import_zod22.z.unknown()).describe("Variable bindings: { variableName: value }")
2388
+ id: import_zod23.z.string().describe("Template document ID"),
2389
+ bindings: import_zod23.z.record(import_zod23.z.unknown()).describe("Variable bindings: { variableName: value }")
2188
2390
  },
2189
2391
  { readOnlyHint: false, destructiveHint: false },
2190
2392
  async ({ id, bindings }) => {
@@ -2207,7 +2409,7 @@ function register8(server, store) {
2207
2409
  "atelier_find_variables",
2208
2410
  "Scan a document for {{variableName}} patterns. Returns the list of variable references found.",
2209
2411
  {
2210
- id: import_zod22.z.string().describe("Document ID")
2412
+ id: import_zod23.z.string().describe("Document ID")
2211
2413
  },
2212
2414
  { readOnlyHint: true, destructiveHint: false },
2213
2415
  async ({ id }) => {
@@ -2227,7 +2429,7 @@ function register8(server, store) {
2227
2429
  }
2228
2430
 
2229
2431
  // ../mcp/src/tools/layer-effects.ts
2230
- var import_zod23 = require("zod");
2432
+ var import_zod24 = require("zod");
2231
2433
  function getDoc9(store, id) {
2232
2434
  const doc = store.get(id);
2233
2435
  if (!doc) return { error: `Document "${id}" not found` };
@@ -2244,9 +2446,9 @@ function register9(server, store) {
2244
2446
  "atelier_set_blend_mode",
2245
2447
  "Set or clear the blend mode on a layer",
2246
2448
  {
2247
- id: import_zod23.z.string().describe("Document ID"),
2248
- layerId: import_zod23.z.string().describe("Layer ID"),
2249
- blendMode: import_zod23.z.enum([
2449
+ id: import_zod24.z.string().describe("Document ID"),
2450
+ layerId: import_zod24.z.string().describe("Layer ID"),
2451
+ blendMode: import_zod24.z.enum([
2250
2452
  "normal",
2251
2453
  "multiply",
2252
2454
  "screen",
@@ -2284,13 +2486,13 @@ function register9(server, store) {
2284
2486
  "atelier_set_shadow",
2285
2487
  "Set or remove a drop shadow on a layer",
2286
2488
  {
2287
- id: import_zod23.z.string().describe("Document ID"),
2288
- layerId: import_zod23.z.string().describe("Layer ID"),
2289
- shadow: import_zod23.z.object({
2290
- color: import_zod23.z.unknown().describe("Shadow color (hex string or RGBA/HSLA object)"),
2291
- blur: import_zod23.z.number().min(0).describe("Blur radius in pixels"),
2292
- offsetX: import_zod23.z.number().optional().describe("Horizontal offset in pixels"),
2293
- offsetY: import_zod23.z.number().optional().describe("Vertical offset in pixels")
2489
+ id: import_zod24.z.string().describe("Document ID"),
2490
+ layerId: import_zod24.z.string().describe("Layer ID"),
2491
+ shadow: import_zod24.z.object({
2492
+ color: import_zod24.z.unknown().describe("Shadow color (hex string or RGBA/HSLA object)"),
2493
+ blur: import_zod24.z.number().min(0).describe("Blur radius in pixels"),
2494
+ offsetX: import_zod24.z.number().optional().describe("Horizontal offset in pixels"),
2495
+ offsetY: import_zod24.z.number().optional().describe("Vertical offset in pixels")
2294
2496
  }).nullable().describe("Shadow definition, or null to remove")
2295
2497
  },
2296
2498
  { readOnlyHint: false, destructiveHint: false },
@@ -2312,11 +2514,11 @@ function register9(server, store) {
2312
2514
  "atelier_set_tint",
2313
2515
  "Set or remove a color tint on a layer (works on any visual type)",
2314
2516
  {
2315
- id: import_zod23.z.string().describe("Document ID"),
2316
- layerId: import_zod23.z.string().describe("Layer ID"),
2317
- tint: import_zod23.z.object({
2318
- color: import_zod23.z.string().describe("Tint color (hex string)"),
2319
- amount: import_zod23.z.number().min(0).max(1).describe("Tint amount (0 = none, 1 = full)")
2517
+ id: import_zod24.z.string().describe("Document ID"),
2518
+ layerId: import_zod24.z.string().describe("Layer ID"),
2519
+ tint: import_zod24.z.object({
2520
+ color: import_zod24.z.string().describe("Tint color (hex string)"),
2521
+ amount: import_zod24.z.number().min(0).max(1).describe("Tint amount (0 = none, 1 = full)")
2320
2522
  }).nullable().describe("Tint definition, or null to remove")
2321
2523
  },
2322
2524
  { readOnlyHint: false, destructiveHint: false },
@@ -2338,18 +2540,18 @@ function register9(server, store) {
2338
2540
  "atelier_set_motion_path",
2339
2541
  "Set or remove a motion path on a layer for path-based position animation",
2340
2542
  {
2341
- id: import_zod23.z.string().describe("Document ID"),
2342
- layerId: import_zod23.z.string().describe("Layer ID"),
2343
- motionPath: import_zod23.z.object({
2344
- points: import_zod23.z.array(import_zod23.z.object({
2345
- x: import_zod23.z.number(),
2346
- y: import_zod23.z.number(),
2347
- in: import_zod23.z.object({ x: import_zod23.z.number(), y: import_zod23.z.number() }).optional(),
2348
- out: import_zod23.z.object({ x: import_zod23.z.number(), y: import_zod23.z.number() }).optional()
2543
+ id: import_zod24.z.string().describe("Document ID"),
2544
+ layerId: import_zod24.z.string().describe("Layer ID"),
2545
+ motionPath: import_zod24.z.object({
2546
+ points: import_zod24.z.array(import_zod24.z.object({
2547
+ x: import_zod24.z.number(),
2548
+ y: import_zod24.z.number(),
2549
+ in: import_zod24.z.object({ x: import_zod24.z.number(), y: import_zod24.z.number() }).optional(),
2550
+ out: import_zod24.z.object({ x: import_zod24.z.number(), y: import_zod24.z.number() }).optional()
2349
2551
  })).min(2).describe("Path points (minimum 2)"),
2350
- closed: import_zod23.z.boolean().optional().describe("Whether the path is closed"),
2351
- autoRotate: import_zod23.z.boolean().optional().describe("Auto-rotate layer to follow path tangent"),
2352
- autoRotateOffset: import_zod23.z.number().optional().describe("Rotation offset in degrees when autoRotate is enabled")
2552
+ closed: import_zod24.z.boolean().optional().describe("Whether the path is closed"),
2553
+ autoRotate: import_zod24.z.boolean().optional().describe("Auto-rotate layer to follow path tangent"),
2554
+ autoRotateOffset: import_zod24.z.number().optional().describe("Rotation offset in degrees when autoRotate is enabled")
2353
2555
  }).nullable().describe("Motion path definition, or null to remove")
2354
2556
  },
2355
2557
  { readOnlyHint: false, destructiveHint: false },
@@ -2371,21 +2573,21 @@ function register9(server, store) {
2371
2573
  "atelier_set_clip_path",
2372
2574
  "Set or remove a clip path on a layer to restrict rendering to within a shape",
2373
2575
  {
2374
- id: import_zod23.z.string().describe("Document ID"),
2375
- layerId: import_zod23.z.string().describe("Layer ID"),
2376
- clipPath: import_zod23.z.object({
2377
- type: import_zod23.z.enum(["rect", "ellipse", "path"]).describe("Clip shape type"),
2378
- cornerRadius: import_zod23.z.union([
2379
- import_zod23.z.number(),
2380
- import_zod23.z.tuple([import_zod23.z.number(), import_zod23.z.number(), import_zod23.z.number(), import_zod23.z.number()])
2576
+ id: import_zod24.z.string().describe("Document ID"),
2577
+ layerId: import_zod24.z.string().describe("Layer ID"),
2578
+ clipPath: import_zod24.z.object({
2579
+ type: import_zod24.z.enum(["rect", "ellipse", "path"]).describe("Clip shape type"),
2580
+ cornerRadius: import_zod24.z.union([
2581
+ import_zod24.z.number(),
2582
+ import_zod24.z.tuple([import_zod24.z.number(), import_zod24.z.number(), import_zod24.z.number(), import_zod24.z.number()])
2381
2583
  ]).optional().describe("Corner radius (rect only)"),
2382
- points: import_zod23.z.array(import_zod23.z.object({
2383
- x: import_zod23.z.number(),
2384
- y: import_zod23.z.number(),
2385
- in: import_zod23.z.object({ x: import_zod23.z.number(), y: import_zod23.z.number() }).optional(),
2386
- out: import_zod23.z.object({ x: import_zod23.z.number(), y: import_zod23.z.number() }).optional()
2584
+ points: import_zod24.z.array(import_zod24.z.object({
2585
+ x: import_zod24.z.number(),
2586
+ y: import_zod24.z.number(),
2587
+ in: import_zod24.z.object({ x: import_zod24.z.number(), y: import_zod24.z.number() }).optional(),
2588
+ out: import_zod24.z.object({ x: import_zod24.z.number(), y: import_zod24.z.number() }).optional()
2387
2589
  })).optional().describe("Path points (path only)"),
2388
- closed: import_zod23.z.boolean().optional().describe("Whether path is closed (path only)")
2590
+ closed: import_zod24.z.boolean().optional().describe("Whether path is closed (path only)")
2389
2591
  }).nullable().describe("Clip path shape, or null to remove")
2390
2592
  },
2391
2593
  { readOnlyHint: false, destructiveHint: false },
@@ -2407,45 +2609,45 @@ function register9(server, store) {
2407
2609
  "atelier_edit_visual",
2408
2610
  "Edit visual content of an existing layer (text content/style, image src/assetId, shape/fill/stroke)",
2409
2611
  {
2410
- id: import_zod23.z.string().describe("Document ID"),
2411
- layerId: import_zod23.z.string().describe("Layer ID"),
2612
+ id: import_zod24.z.string().describe("Document ID"),
2613
+ layerId: import_zod24.z.string().describe("Layer ID"),
2412
2614
  // Text fields
2413
- content: import_zod23.z.string().optional().describe("New text content (text layers only)"),
2414
- style: import_zod23.z.record(import_zod23.z.unknown()).optional().describe("Partial style update \u2014 only provided keys are overwritten (text layers only)"),
2615
+ content: import_zod24.z.string().optional().describe("New text content (text layers only)"),
2616
+ style: import_zod24.z.record(import_zod24.z.unknown()).optional().describe("Partial style update \u2014 only provided keys are overwritten (text layers only)"),
2415
2617
  // Image fields
2416
- src: import_zod23.z.string().optional().describe("Image source URL (image layers only)"),
2417
- assetId: import_zod23.z.string().optional().describe("Asset ID reference (image layers only)"),
2418
- sourceRect: import_zod23.z.object({
2419
- x: import_zod23.z.number(),
2420
- y: import_zod23.z.number(),
2421
- width: import_zod23.z.number().positive(),
2422
- height: import_zod23.z.number().positive()
2618
+ src: import_zod24.z.string().optional().describe("Image source URL (image layers only)"),
2619
+ assetId: import_zod24.z.string().optional().describe("Asset ID reference (image layers only)"),
2620
+ sourceRect: import_zod24.z.object({
2621
+ x: import_zod24.z.number(),
2622
+ y: import_zod24.z.number(),
2623
+ width: import_zod24.z.number().positive(),
2624
+ height: import_zod24.z.number().positive()
2423
2625
  }).nullable().optional().describe("Source rectangle crop (image layers only). Null to remove."),
2424
- spritesheet: import_zod23.z.object({
2425
- columns: import_zod23.z.number().int().positive(),
2426
- rows: import_zod23.z.number().int().positive(),
2427
- frameCount: import_zod23.z.number().int().positive().optional(),
2428
- frameWidth: import_zod23.z.number().positive(),
2429
- frameHeight: import_zod23.z.number().positive()
2626
+ spritesheet: import_zod24.z.object({
2627
+ columns: import_zod24.z.number().int().positive(),
2628
+ rows: import_zod24.z.number().int().positive(),
2629
+ frameCount: import_zod24.z.number().int().positive().optional(),
2630
+ frameWidth: import_zod24.z.number().positive(),
2631
+ frameHeight: import_zod24.z.number().positive()
2430
2632
  }).nullable().optional().describe("Spritesheet config (image layers only). Null to remove."),
2431
- frameIndex: import_zod23.z.number().int().min(0).optional().describe("Spritesheet frame index (image layers only)"),
2633
+ frameIndex: import_zod24.z.number().int().min(0).optional().describe("Spritesheet frame index (image layers only)"),
2432
2634
  // Shape fields
2433
- shape: import_zod23.z.object({
2434
- type: import_zod23.z.enum(["rect", "ellipse", "path"]),
2435
- cornerRadius: import_zod23.z.union([
2436
- import_zod23.z.number(),
2437
- import_zod23.z.tuple([import_zod23.z.number(), import_zod23.z.number(), import_zod23.z.number(), import_zod23.z.number()])
2635
+ shape: import_zod24.z.object({
2636
+ type: import_zod24.z.enum(["rect", "ellipse", "path"]),
2637
+ cornerRadius: import_zod24.z.union([
2638
+ import_zod24.z.number(),
2639
+ import_zod24.z.tuple([import_zod24.z.number(), import_zod24.z.number(), import_zod24.z.number(), import_zod24.z.number()])
2438
2640
  ]).optional(),
2439
- points: import_zod23.z.array(import_zod23.z.object({
2440
- x: import_zod23.z.number(),
2441
- y: import_zod23.z.number(),
2442
- in: import_zod23.z.object({ x: import_zod23.z.number(), y: import_zod23.z.number() }).optional(),
2443
- out: import_zod23.z.object({ x: import_zod23.z.number(), y: import_zod23.z.number() }).optional()
2641
+ points: import_zod24.z.array(import_zod24.z.object({
2642
+ x: import_zod24.z.number(),
2643
+ y: import_zod24.z.number(),
2644
+ in: import_zod24.z.object({ x: import_zod24.z.number(), y: import_zod24.z.number() }).optional(),
2645
+ out: import_zod24.z.object({ x: import_zod24.z.number(), y: import_zod24.z.number() }).optional()
2444
2646
  })).optional(),
2445
- closed: import_zod23.z.boolean().optional()
2647
+ closed: import_zod24.z.boolean().optional()
2446
2648
  }).optional().describe("New shape (shape layers only)"),
2447
- fill: import_zod23.z.record(import_zod23.z.unknown()).optional().describe("New fill definition (shape layers only)"),
2448
- stroke: import_zod23.z.record(import_zod23.z.unknown()).optional().describe("New stroke definition (shape layers only)")
2649
+ fill: import_zod24.z.record(import_zod24.z.unknown()).optional().describe("New fill definition (shape layers only)"),
2650
+ stroke: import_zod24.z.record(import_zod24.z.unknown()).optional().describe("New stroke definition (shape layers only)")
2449
2651
  },
2450
2652
  { readOnlyHint: false, destructiveHint: false },
2451
2653
  async ({ id, layerId, content, style, src, assetId, sourceRect, spritesheet, frameIndex, shape, fill, stroke }) => {
@@ -2526,7 +2728,7 @@ function register9(server, store) {
2526
2728
  }
2527
2729
 
2528
2730
  // ../mcp/src/tools/state-config.ts
2529
- var import_zod24 = require("zod");
2731
+ var import_zod25 = require("zod");
2530
2732
  function getDoc10(store, id) {
2531
2733
  const doc = store.get(id);
2532
2734
  if (!doc) return { error: `Document "${id}" not found` };
@@ -2538,27 +2740,27 @@ function ok10(data) {
2538
2740
  function err10(message) {
2539
2741
  return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
2540
2742
  }
2541
- var EasingInputSchema3 = import_zod24.z.union([
2542
- import_zod24.z.enum(["ease-in", "ease-out", "ease-in-out"]),
2543
- import_zod24.z.object({ type: import_zod24.z.literal("linear") }),
2544
- import_zod24.z.object({
2545
- type: import_zod24.z.literal("cubic-bezier"),
2546
- x1: import_zod24.z.number(),
2547
- y1: import_zod24.z.number(),
2548
- x2: import_zod24.z.number(),
2549
- y2: import_zod24.z.number()
2743
+ var EasingInputSchema3 = import_zod25.z.union([
2744
+ import_zod25.z.enum(["ease-in", "ease-out", "ease-in-out"]),
2745
+ import_zod25.z.object({ type: import_zod25.z.literal("linear") }),
2746
+ import_zod25.z.object({
2747
+ type: import_zod25.z.literal("cubic-bezier"),
2748
+ x1: import_zod25.z.number(),
2749
+ y1: import_zod25.z.number(),
2750
+ x2: import_zod25.z.number(),
2751
+ y2: import_zod25.z.number()
2550
2752
  }),
2551
- import_zod24.z.object({
2552
- type: import_zod24.z.literal("spring"),
2553
- mass: import_zod24.z.number().optional(),
2554
- stiffness: import_zod24.z.number().optional(),
2555
- damping: import_zod24.z.number().optional(),
2556
- velocity: import_zod24.z.number().optional()
2753
+ import_zod25.z.object({
2754
+ type: import_zod25.z.literal("spring"),
2755
+ mass: import_zod25.z.number().optional(),
2756
+ stiffness: import_zod25.z.number().optional(),
2757
+ damping: import_zod25.z.number().optional(),
2758
+ velocity: import_zod25.z.number().optional()
2557
2759
  }),
2558
- import_zod24.z.object({
2559
- type: import_zod24.z.literal("step"),
2560
- steps: import_zod24.z.number().int().positive(),
2561
- position: import_zod24.z.enum(["start", "end"]).optional()
2760
+ import_zod25.z.object({
2761
+ type: import_zod25.z.literal("step"),
2762
+ steps: import_zod25.z.number().int().positive(),
2763
+ position: import_zod25.z.enum(["start", "end"]).optional()
2562
2764
  })
2563
2765
  ]).describe("Easing function");
2564
2766
  function register10(server, store) {
@@ -2566,14 +2768,14 @@ function register10(server, store) {
2566
2768
  "atelier_set_audio",
2567
2769
  "Set or remove an audio track on a state",
2568
2770
  {
2569
- id: import_zod24.z.string().describe("Document ID"),
2570
- stateName: import_zod24.z.string().describe("State name"),
2571
- audio: import_zod24.z.object({
2572
- src: import_zod24.z.string().describe("Audio source \u2014 asset reference, file path, or URL"),
2573
- offset: import_zod24.z.number().min(0).optional().describe("Skip into audio in seconds (default: 0)"),
2574
- volume: import_zod24.z.number().min(0).max(1).optional().describe("Playback volume 0\u20131 (default: 1)"),
2575
- loop: import_zod24.z.boolean().optional().describe("Loop for state duration (default: false)"),
2576
- startFrame: import_zod24.z.number().int().min(0).optional().describe("Frame to start playing (default: 0)")
2771
+ id: import_zod25.z.string().describe("Document ID"),
2772
+ stateName: import_zod25.z.string().describe("State name"),
2773
+ audio: import_zod25.z.object({
2774
+ src: import_zod25.z.string().describe("Audio source \u2014 asset reference, file path, or URL"),
2775
+ offset: import_zod25.z.number().min(0).optional().describe("Skip into audio in seconds (default: 0)"),
2776
+ volume: import_zod25.z.number().min(0).max(1).optional().describe("Playback volume 0\u20131 (default: 1)"),
2777
+ loop: import_zod25.z.boolean().optional().describe("Loop for state duration (default: false)"),
2778
+ startFrame: import_zod25.z.number().int().min(0).optional().describe("Frame to start playing (default: 0)")
2577
2779
  }).nullable().describe("Audio configuration, or null to remove")
2578
2780
  },
2579
2781
  { readOnlyHint: false, destructiveHint: false },
@@ -2595,11 +2797,11 @@ function register10(server, store) {
2595
2797
  "atelier_configure_transition",
2596
2798
  "Configure or remove a transition from one state to a target state",
2597
2799
  {
2598
- id: import_zod24.z.string().describe("Document ID"),
2599
- stateName: import_zod24.z.string().describe("Source state name"),
2600
- targetState: import_zod24.z.string().describe("Target state name"),
2601
- transition: import_zod24.z.object({
2602
- duration: import_zod24.z.number().positive().int().describe("Transition duration in frames"),
2800
+ id: import_zod25.z.string().describe("Document ID"),
2801
+ stateName: import_zod25.z.string().describe("Source state name"),
2802
+ targetState: import_zod25.z.string().describe("Target state name"),
2803
+ transition: import_zod25.z.object({
2804
+ duration: import_zod25.z.number().positive().int().describe("Transition duration in frames"),
2603
2805
  easing: EasingInputSchema3.optional()
2604
2806
  }).nullable().describe("Transition config, or null to remove")
2605
2807
  },
@@ -2636,9 +2838,9 @@ function register10(server, store) {
2636
2838
  "atelier_set_state_parent",
2637
2839
  "Set or clear the parent state for hierarchical delta inheritance",
2638
2840
  {
2639
- id: import_zod24.z.string().describe("Document ID"),
2640
- stateName: import_zod24.z.string().describe("State name"),
2641
- parent: import_zod24.z.string().nullable().describe("Parent state name, or null to clear")
2841
+ id: import_zod25.z.string().describe("Document ID"),
2842
+ stateName: import_zod25.z.string().describe("State name"),
2843
+ parent: import_zod25.z.string().nullable().describe("Parent state name, or null to clear")
2642
2844
  },
2643
2845
  { readOnlyHint: false, destructiveHint: false },
2644
2846
  async ({ id, stateName, parent }) => {
@@ -2673,7 +2875,7 @@ function register10(server, store) {
2673
2875
  }
2674
2876
 
2675
2877
  // ../mcp/src/tools/export.ts
2676
- var import_zod25 = require("zod");
2878
+ var import_zod26 = require("zod");
2677
2879
 
2678
2880
  // ../canvas/dist/index.js
2679
2881
  function colorToCSS(color) {
@@ -3858,12 +4060,12 @@ function register11(server, store) {
3858
4060
  "atelier_export_svg",
3859
4061
  "Export a frame as an SVG string",
3860
4062
  {
3861
- id: import_zod25.z.string().describe("Document ID"),
3862
- stateName: import_zod25.z.string().optional().describe("State name (defaults to first state)"),
3863
- frame: import_zod25.z.number().int().min(0).optional().describe("Frame number (defaults to 0)"),
3864
- xmlDeclaration: import_zod25.z.boolean().optional().describe("Include XML declaration (default: false)"),
3865
- viewBox: import_zod25.z.boolean().optional().describe("Include viewBox attribute (default: true)"),
3866
- indent: import_zod25.z.number().int().min(0).optional().describe("Indent size in spaces (default: 2)")
4063
+ id: import_zod26.z.string().describe("Document ID"),
4064
+ stateName: import_zod26.z.string().optional().describe("State name (defaults to first state)"),
4065
+ frame: import_zod26.z.number().int().min(0).optional().describe("Frame number (defaults to 0)"),
4066
+ xmlDeclaration: import_zod26.z.boolean().optional().describe("Include XML declaration (default: false)"),
4067
+ viewBox: import_zod26.z.boolean().optional().describe("Include viewBox attribute (default: true)"),
4068
+ indent: import_zod26.z.number().int().min(0).optional().describe("Indent size in spaces (default: 2)")
3867
4069
  },
3868
4070
  { readOnlyHint: true, destructiveHint: false },
3869
4071
  async ({ id, stateName, frame, xmlDeclaration, viewBox, indent }) => {
@@ -3894,8 +4096,8 @@ function register11(server, store) {
3894
4096
  "atelier_export_lottie",
3895
4097
  "Export a document to Lottie JSON format (lossy, best-effort)",
3896
4098
  {
3897
- id: import_zod25.z.string().describe("Document ID"),
3898
- stateName: import_zod25.z.string().optional().describe("State to export (defaults to first state)")
4099
+ id: import_zod26.z.string().describe("Document ID"),
4100
+ stateName: import_zod26.z.string().optional().describe("State to export (defaults to first state)")
3899
4101
  },
3900
4102
  { readOnlyHint: true, destructiveHint: false },
3901
4103
  async ({ id, stateName }) => {
@@ -3915,7 +4117,7 @@ function register11(server, store) {
3915
4117
  }
3916
4118
 
3917
4119
  // ../mcp/src/tools/assets.ts
3918
- var import_zod26 = require("zod");
4120
+ var import_zod27 = require("zod");
3919
4121
  function getDoc12(store, id) {
3920
4122
  const doc = store.get(id);
3921
4123
  if (!doc) return { error: `Document "${id}" not found` };
@@ -3932,17 +4134,17 @@ function register12(server, store) {
3932
4134
  "atelier_add_asset",
3933
4135
  "Register an external asset (image, SVG, font, animation, audio) on a document",
3934
4136
  {
3935
- id: import_zod26.z.string().describe("Document ID"),
3936
- assetId: import_zod26.z.string().describe("Unique asset identifier"),
3937
- type: import_zod26.z.enum(["image", "svg", "font", "animation", "audio"]).describe("Asset type"),
3938
- src: import_zod26.z.string().describe("File path or URL"),
3939
- description: import_zod26.z.string().optional().describe("Human-readable description"),
3940
- spritesheet: import_zod26.z.object({
3941
- columns: import_zod26.z.number().int().positive().describe("Number of columns in the spritesheet grid"),
3942
- rows: import_zod26.z.number().int().positive().describe("Number of rows in the spritesheet grid"),
3943
- frameCount: import_zod26.z.number().int().positive().optional().describe("Total frame count (defaults to columns \xD7 rows)"),
3944
- frameWidth: import_zod26.z.number().positive().describe("Width of each frame in pixels"),
3945
- frameHeight: import_zod26.z.number().positive().describe("Height of each frame in pixels")
4137
+ id: import_zod27.z.string().describe("Document ID"),
4138
+ assetId: import_zod27.z.string().describe("Unique asset identifier"),
4139
+ type: import_zod27.z.enum(["image", "svg", "font", "animation", "audio"]).describe("Asset type"),
4140
+ src: import_zod27.z.string().describe("File path or URL"),
4141
+ description: import_zod27.z.string().optional().describe("Human-readable description"),
4142
+ spritesheet: import_zod27.z.object({
4143
+ columns: import_zod27.z.number().int().positive().describe("Number of columns in the spritesheet grid"),
4144
+ rows: import_zod27.z.number().int().positive().describe("Number of rows in the spritesheet grid"),
4145
+ frameCount: import_zod27.z.number().int().positive().optional().describe("Total frame count (defaults to columns \xD7 rows)"),
4146
+ frameWidth: import_zod27.z.number().positive().describe("Width of each frame in pixels"),
4147
+ frameHeight: import_zod27.z.number().positive().describe("Height of each frame in pixels")
3946
4148
  }).optional().describe("Spritesheet metadata (image assets only)")
3947
4149
  },
3948
4150
  { readOnlyHint: false, destructiveHint: false },
@@ -3965,7 +4167,7 @@ function register12(server, store) {
3965
4167
  "atelier_list_assets",
3966
4168
  "List all registered assets with usage info (which layers/states reference them)",
3967
4169
  {
3968
- id: import_zod26.z.string().describe("Document ID")
4170
+ id: import_zod27.z.string().describe("Document ID")
3969
4171
  },
3970
4172
  { readOnlyHint: true, destructiveHint: false },
3971
4173
  async ({ id }) => {
@@ -3992,8 +4194,8 @@ function register12(server, store) {
3992
4194
  "atelier_remove_asset",
3993
4195
  "Remove a registered asset. Warns if layers or state audio still reference it.",
3994
4196
  {
3995
- id: import_zod26.z.string().describe("Document ID"),
3996
- assetId: import_zod26.z.string().describe("Asset ID to remove")
4197
+ id: import_zod27.z.string().describe("Document ID"),
4198
+ assetId: import_zod27.z.string().describe("Asset ID to remove")
3997
4199
  },
3998
4200
  { readOnlyHint: false, destructiveHint: true },
3999
4201
  async ({ id, assetId }) => {
@@ -4017,7 +4219,7 @@ function register12(server, store) {
4017
4219
  }
4018
4220
 
4019
4221
  // ../mcp/src/tools/variables.ts
4020
- var import_zod27 = require("zod");
4222
+ var import_zod28 = require("zod");
4021
4223
  function getDoc13(store, id) {
4022
4224
  const doc = store.get(id);
4023
4225
  if (!doc) return { error: `Document "${id}" not found` };
@@ -4034,11 +4236,11 @@ function register13(server, store) {
4034
4236
  "atelier_add_variable",
4035
4237
  "Define a template variable on a document",
4036
4238
  {
4037
- id: import_zod27.z.string().describe("Document ID"),
4038
- variableName: import_zod27.z.string().describe("Variable name (used in {{variableName}} patterns)"),
4039
- type: import_zod27.z.enum(["string", "number", "color", "asset", "boolean"]).describe("Variable type"),
4040
- description: import_zod27.z.string().optional().describe("Human-readable description"),
4041
- default: import_zod27.z.unknown().optional().describe("Default value")
4239
+ id: import_zod28.z.string().describe("Document ID"),
4240
+ variableName: import_zod28.z.string().describe("Variable name (used in {{variableName}} patterns)"),
4241
+ type: import_zod28.z.enum(["string", "number", "color", "asset", "boolean"]).describe("Variable type"),
4242
+ description: import_zod28.z.string().optional().describe("Human-readable description"),
4243
+ default: import_zod28.z.unknown().optional().describe("Default value")
4042
4244
  },
4043
4245
  { readOnlyHint: false, destructiveHint: false },
4044
4246
  async (args) => {
@@ -4059,7 +4261,7 @@ function register13(server, store) {
4059
4261
  "atelier_list_variables",
4060
4262
  "List all declared variables with usage info (which {{references}} exist in the document)",
4061
4263
  {
4062
- id: import_zod27.z.string().describe("Document ID")
4264
+ id: import_zod28.z.string().describe("Document ID")
4063
4265
  },
4064
4266
  { readOnlyHint: true, destructiveHint: false },
4065
4267
  async ({ id }) => {
@@ -4083,8 +4285,8 @@ function register13(server, store) {
4083
4285
  "atelier_remove_variable",
4084
4286
  "Remove a declared variable. Warns if {{references}} still exist in the document.",
4085
4287
  {
4086
- id: import_zod27.z.string().describe("Document ID"),
4087
- variableName: import_zod27.z.string().describe("Variable name to remove")
4288
+ id: import_zod28.z.string().describe("Document ID"),
4289
+ variableName: import_zod28.z.string().describe("Variable name to remove")
4088
4290
  },
4089
4291
  { readOnlyHint: false, destructiveHint: true },
4090
4292
  async ({ id, variableName }) => {
@@ -4107,7 +4309,7 @@ function register13(server, store) {
4107
4309
  }
4108
4310
 
4109
4311
  // ../mcp/src/tools/interactions.ts
4110
- var import_zod28 = require("zod");
4312
+ var import_zod29 = require("zod");
4111
4313
  function getDoc14(store, id) {
4112
4314
  const doc = store.get(id);
4113
4315
  if (!doc) return { error: `Document "${id}" not found` };
@@ -4124,23 +4326,23 @@ function register14(server, store) {
4124
4326
  "atelier_add_interaction",
4125
4327
  "Add a trigger-action interaction to a layer (click, hover, timer, signal \u2192 go-to-state, emit-signal, set-variable, toggle-visibility)",
4126
4328
  {
4127
- id: import_zod28.z.string().describe("Document ID"),
4128
- layerId: import_zod28.z.string().describe("Layer to attach the interaction to"),
4129
- interactionId: import_zod28.z.string().describe("Unique interaction ID"),
4130
- trigger: import_zod28.z.object({
4131
- type: import_zod28.z.enum(["click", "hover", "pointerdown", "pointerup", "timer", "signal"]).describe("Trigger type"),
4132
- delay: import_zod28.z.number().optional().describe("Delay in ms (timer trigger only)"),
4133
- signal: import_zod28.z.string().optional().describe("Signal name (signal trigger only)")
4329
+ id: import_zod29.z.string().describe("Document ID"),
4330
+ layerId: import_zod29.z.string().describe("Layer to attach the interaction to"),
4331
+ interactionId: import_zod29.z.string().describe("Unique interaction ID"),
4332
+ trigger: import_zod29.z.object({
4333
+ type: import_zod29.z.enum(["click", "hover", "pointerdown", "pointerup", "timer", "signal"]).describe("Trigger type"),
4334
+ delay: import_zod29.z.number().optional().describe("Delay in ms (timer trigger only)"),
4335
+ signal: import_zod29.z.string().optional().describe("Signal name (signal trigger only)")
4134
4336
  }).describe("What triggers the interaction"),
4135
- action: import_zod28.z.object({
4136
- type: import_zod28.z.enum(["go-to-state", "emit-signal", "set-variable", "toggle-visibility"]).describe("Action type"),
4137
- state: import_zod28.z.string().optional().describe("Target state (go-to-state only)"),
4138
- signal: import_zod28.z.string().optional().describe("Signal to emit (emit-signal only)"),
4139
- variable: import_zod28.z.string().optional().describe("Variable name (set-variable only)"),
4140
- value: import_zod28.z.unknown().optional().describe("Value to set (set-variable only)"),
4141
- targetLayer: import_zod28.z.string().optional().describe("Target layer ID (toggle-visibility, defaults to self)")
4337
+ action: import_zod29.z.object({
4338
+ type: import_zod29.z.enum(["go-to-state", "emit-signal", "set-variable", "toggle-visibility"]).describe("Action type"),
4339
+ state: import_zod29.z.string().optional().describe("Target state (go-to-state only)"),
4340
+ signal: import_zod29.z.string().optional().describe("Signal to emit (emit-signal only)"),
4341
+ variable: import_zod29.z.string().optional().describe("Variable name (set-variable only)"),
4342
+ value: import_zod29.z.unknown().optional().describe("Value to set (set-variable only)"),
4343
+ targetLayer: import_zod29.z.string().optional().describe("Target layer ID (toggle-visibility, defaults to self)")
4142
4344
  }).describe("What happens when triggered"),
4143
- description: import_zod28.z.string().optional().describe("Human-readable description")
4345
+ description: import_zod29.z.string().optional().describe("Human-readable description")
4144
4346
  },
4145
4347
  { readOnlyHint: false, destructiveHint: false },
4146
4348
  async ({ id, layerId, interactionId, trigger, action, description }) => {
@@ -4189,8 +4391,8 @@ function register14(server, store) {
4189
4391
  "atelier_list_interactions",
4190
4392
  "List all interactions across all layers or for a specific layer",
4191
4393
  {
4192
- id: import_zod28.z.string().describe("Document ID"),
4193
- layerId: import_zod28.z.string().optional().describe("Optional: filter to a specific layer")
4394
+ id: import_zod29.z.string().describe("Document ID"),
4395
+ layerId: import_zod29.z.string().optional().describe("Optional: filter to a specific layer")
4194
4396
  },
4195
4397
  { readOnlyHint: true, destructiveHint: false },
4196
4398
  async ({ id, layerId }) => {
@@ -4217,9 +4419,9 @@ function register14(server, store) {
4217
4419
  "atelier_remove_interaction",
4218
4420
  "Remove an interaction from a layer",
4219
4421
  {
4220
- id: import_zod28.z.string().describe("Document ID"),
4221
- layerId: import_zod28.z.string().describe("Layer ID"),
4222
- interactionId: import_zod28.z.string().describe("Interaction ID to remove")
4422
+ id: import_zod29.z.string().describe("Document ID"),
4423
+ layerId: import_zod29.z.string().describe("Layer ID"),
4424
+ interactionId: import_zod29.z.string().describe("Interaction ID to remove")
4223
4425
  },
4224
4426
  { readOnlyHint: false, destructiveHint: true },
4225
4427
  async ({ id, layerId, interactionId }) => {
@@ -4241,7 +4443,7 @@ function register14(server, store) {
4241
4443
  }
4242
4444
 
4243
4445
  // ../mcp/src/tools/refs.ts
4244
- var import_zod29 = require("zod");
4446
+ var import_zod30 = require("zod");
4245
4447
  function getDoc15(store, id) {
4246
4448
  const doc = store.get(id);
4247
4449
  if (!doc) return { error: `Document "${id}" not found` };
@@ -4258,9 +4460,9 @@ function register15(server, store) {
4258
4460
  "atelier_set_ref",
4259
4461
  "Set the source reference on a ref-type layer. Can point to a file path or another in-memory document ID.",
4260
4462
  {
4261
- id: import_zod29.z.string().describe("Document ID"),
4262
- layerId: import_zod29.z.string().describe("Layer ID (must be a ref visual)"),
4263
- src: import_zod29.z.string().describe("File path, URL, or in-memory document ID")
4463
+ id: import_zod30.z.string().describe("Document ID"),
4464
+ layerId: import_zod30.z.string().describe("Layer ID (must be a ref visual)"),
4465
+ src: import_zod30.z.string().describe("File path, URL, or in-memory document ID")
4264
4466
  },
4265
4467
  { readOnlyHint: false, destructiveHint: false },
4266
4468
  async ({ id, layerId, src }) => {
@@ -4280,7 +4482,7 @@ function register15(server, store) {
4280
4482
  "atelier_resolve_refs",
4281
4483
  "Check which ref layers can be resolved (point to valid in-memory documents)",
4282
4484
  {
4283
- id: import_zod29.z.string().describe("Document ID")
4485
+ id: import_zod30.z.string().describe("Document ID")
4284
4486
  },
4285
4487
  { readOnlyHint: true, destructiveHint: false },
4286
4488
  async ({ id }) => {
@@ -4307,7 +4509,7 @@ function register15(server, store) {
4307
4509
  }
4308
4510
 
4309
4511
  // ../mcp/src/tools/performance.ts
4310
- var import_zod30 = require("zod");
4512
+ var import_zod31 = require("zod");
4311
4513
  function getDoc16(store, id) {
4312
4514
  const doc = store.get(id);
4313
4515
  if (!doc) return { error: `Document "${id}" not found` };
@@ -4324,9 +4526,9 @@ function register16(server, store) {
4324
4526
  "atelier_profile",
4325
4527
  "Profile frame resolution performance \u2014 returns timing, layer count, property count, and delta count for a frame",
4326
4528
  {
4327
- id: import_zod30.z.string().describe("Document ID"),
4328
- stateName: import_zod30.z.string().describe("State name to profile"),
4329
- frame: import_zod30.z.number().int().min(0).describe("Frame number to resolve")
4529
+ id: import_zod31.z.string().describe("Document ID"),
4530
+ stateName: import_zod31.z.string().describe("State name to profile"),
4531
+ frame: import_zod31.z.number().int().min(0).describe("Frame number to resolve")
4330
4532
  },
4331
4533
  { readOnlyHint: true, destructiveHint: false },
4332
4534
  async ({ id, stateName, frame }) => {
@@ -4372,10 +4574,10 @@ function register16(server, store) {
4372
4574
  "atelier_diff_frames",
4373
4575
  "Compare two frames and return only the properties that changed between them",
4374
4576
  {
4375
- id: import_zod30.z.string().describe("Document ID"),
4376
- stateName: import_zod30.z.string().describe("State name"),
4377
- frameA: import_zod30.z.number().int().min(0).describe("First frame number"),
4378
- frameB: import_zod30.z.number().int().min(0).describe("Second frame number")
4577
+ id: import_zod31.z.string().describe("Document ID"),
4578
+ stateName: import_zod31.z.string().describe("State name"),
4579
+ frameA: import_zod31.z.number().int().min(0).describe("First frame number"),
4580
+ frameB: import_zod31.z.number().int().min(0).describe("Second frame number")
4379
4581
  },
4380
4582
  { readOnlyHint: true, destructiveHint: false },
4381
4583
  async ({ id, stateName, frameA, frameB }) => {
@@ -4414,9 +4616,9 @@ function register16(server, store) {
4414
4616
  "atelier_batch_preview",
4415
4617
  "Preview multiple frames at once \u2014 reduces round-trips for scrubbing or timeline inspection",
4416
4618
  {
4417
- id: import_zod30.z.string().describe("Document ID"),
4418
- stateName: import_zod30.z.string().describe("State name to preview"),
4419
- frames: import_zod30.z.array(import_zod30.z.number().int().min(0)).min(1).max(60).describe("Array of frame numbers to resolve (max 60)")
4619
+ id: import_zod31.z.string().describe("Document ID"),
4620
+ stateName: import_zod31.z.string().describe("State name to preview"),
4621
+ frames: import_zod31.z.array(import_zod31.z.number().int().min(0)).min(1).max(60).describe("Array of frame numbers to resolve (max 60)")
4420
4622
  },
4421
4623
  { readOnlyHint: true, destructiveHint: false },
4422
4624
  async ({ id, stateName, frames }) => {
@@ -4452,7 +4654,7 @@ function register16(server, store) {
4452
4654
  "atelier_complexity",
4453
4655
  "Analyze document complexity \u2014 layer count, delta count, expression usage, state hierarchy depth",
4454
4656
  {
4455
- id: import_zod30.z.string().describe("Document ID")
4657
+ id: import_zod31.z.string().describe("Document ID")
4456
4658
  },
4457
4659
  { readOnlyHint: true, destructiveHint: false },
4458
4660
  async ({ id }) => {
@@ -4524,9 +4726,678 @@ function register16(server, store) {
4524
4726
  );
4525
4727
  }
4526
4728
 
4729
+ // ../mcp/src/tools/overlays.ts
4730
+ var import_zod32 = require("zod");
4731
+ function getDoc17(store, id) {
4732
+ const doc = store.get(id);
4733
+ if (!doc) return { error: `Document "${id}" not found` };
4734
+ return { doc };
4735
+ }
4736
+ function ok17(data) {
4737
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, ...data }) }] };
4738
+ }
4739
+ function err17(message) {
4740
+ return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
4741
+ }
4742
+ var AnchorEnum = import_zod32.z.enum(["top-left", "top-right", "bottom-left", "bottom-right"]);
4743
+ var OverlayStyleSchema = import_zod32.z.object({
4744
+ font_family: import_zod32.z.string().optional(),
4745
+ font_size: import_zod32.z.number().positive().optional(),
4746
+ font_weight: import_zod32.z.union([import_zod32.z.number(), import_zod32.z.enum(["normal", "bold"])]).optional(),
4747
+ color: import_zod32.z.string().optional()
4748
+ }).optional();
4749
+ var DEFAULT_OVERLAY_MARGIN = 24;
4750
+ var DEFAULT_TEXT_STYLE = {
4751
+ fontFamily: "Inter",
4752
+ fontSize: 24,
4753
+ fontWeight: 600,
4754
+ color: "#F5F5F7"
4755
+ };
4756
+ function anchorToFrame(anchor, canvas, margin) {
4757
+ switch (anchor) {
4758
+ case "top-left":
4759
+ return { frame: { x: margin, y: margin }, anchorPoint: { x: 0, y: 0 } };
4760
+ case "top-right":
4761
+ return { frame: { x: canvas.width - margin, y: margin }, anchorPoint: { x: 1, y: 0 } };
4762
+ case "bottom-left":
4763
+ return { frame: { x: margin, y: canvas.height - margin }, anchorPoint: { x: 0, y: 1 } };
4764
+ case "bottom-right":
4765
+ return {
4766
+ frame: { x: canvas.width - margin, y: canvas.height - margin },
4767
+ anchorPoint: { x: 1, y: 1 }
4768
+ };
4769
+ }
4770
+ }
4771
+ function mergeStyle(style) {
4772
+ return {
4773
+ fontFamily: style?.font_family ?? DEFAULT_TEXT_STYLE.fontFamily,
4774
+ fontSize: style?.font_size ?? DEFAULT_TEXT_STYLE.fontSize,
4775
+ fontWeight: style?.font_weight ?? DEFAULT_TEXT_STYLE.fontWeight,
4776
+ color: style?.color ?? DEFAULT_TEXT_STYLE.color
4777
+ };
4778
+ }
4779
+ function renderPageNumberFormat(format, currentIndex, totalCount) {
4780
+ return format.replace(
4781
+ /\{(current|total)(?::0(\d+)d)?\}/g,
4782
+ (_, name, padWidth) => {
4783
+ const value = name === "current" ? currentIndex : totalCount;
4784
+ const str = String(value);
4785
+ if (padWidth) {
4786
+ const width = parseInt(padWidth, 10);
4787
+ return str.padStart(width, "0");
4788
+ }
4789
+ return str;
4790
+ }
4791
+ );
4792
+ }
4793
+ function register17(server, store) {
4794
+ server.tool(
4795
+ "atelier_add_handle",
4796
+ "Add an anchored creator-handle TextVisual overlay layer (tag: overlay)",
4797
+ {
4798
+ id: import_zod32.z.string().describe("Document ID"),
4799
+ text: import_zod32.z.string().min(1).describe('Handle text, e.g. "@username"'),
4800
+ anchor: AnchorEnum.describe("Anchored corner"),
4801
+ margin: import_zod32.z.number().nonnegative().optional().describe("Px from anchored edges (default 24)"),
4802
+ style: OverlayStyleSchema.describe("Text style overrides (snake_case to match recipe)"),
4803
+ layerId: import_zod32.z.string().optional().describe("Layer ID (default: overlay-handle)")
4804
+ },
4805
+ { readOnlyHint: false, destructiveHint: false },
4806
+ async ({ id, text, anchor, margin, style, layerId }) => {
4807
+ const result = getDoc17(store, id);
4808
+ if ("error" in result) return err17(result.error);
4809
+ const { doc } = result;
4810
+ const targetId = layerId ?? "overlay-handle";
4811
+ if (doc.layers.some((l) => l.id === targetId)) {
4812
+ return err17(`Layer "${targetId}" already exists in document "${id}"`);
4813
+ }
4814
+ const m = margin ?? DEFAULT_OVERLAY_MARGIN;
4815
+ const { frame, anchorPoint } = anchorToFrame(anchor, doc.canvas, m);
4816
+ const layer = {
4817
+ id: targetId,
4818
+ tags: ["overlay"],
4819
+ visual: { type: "text", content: text, style: mergeStyle(style) },
4820
+ frame,
4821
+ bounds: { width: 600, height: 80 },
4822
+ anchorPoint
4823
+ };
4824
+ doc.layers.push(layer);
4825
+ store.set(id, doc);
4826
+ return ok17({ layerId: targetId, layer });
4827
+ }
4828
+ );
4829
+ server.tool(
4830
+ "atelier_add_page_number",
4831
+ "Add an anchored page-number TextVisual overlay layer (tag: overlay). Substitutes {current} / {total} (and zero-pad forms {current:02d} / {total:02d}) into format.",
4832
+ {
4833
+ id: import_zod32.z.string().describe("Document ID"),
4834
+ format: import_zod32.z.string().min(1).describe('Format string, e.g. "{current}/{total}" or "{current:02d}/{total:02d}"'),
4835
+ anchor: AnchorEnum.describe("Anchored corner"),
4836
+ currentIndex: import_zod32.z.number().int().nonnegative().describe("1-based current index"),
4837
+ totalCount: import_zod32.z.number().int().positive().describe("Total count"),
4838
+ margin: import_zod32.z.number().nonnegative().optional().describe("Px from anchored edges (default 24)"),
4839
+ style: OverlayStyleSchema.describe("Text style overrides (snake_case to match recipe)"),
4840
+ layerId: import_zod32.z.string().optional().describe("Layer ID (default: overlay-page-number)")
4841
+ },
4842
+ { readOnlyHint: false, destructiveHint: false },
4843
+ async ({ id, format, anchor, currentIndex, totalCount, margin, style, layerId }) => {
4844
+ const result = getDoc17(store, id);
4845
+ if ("error" in result) return err17(result.error);
4846
+ const { doc } = result;
4847
+ const targetId = layerId ?? "overlay-page-number";
4848
+ if (doc.layers.some((l) => l.id === targetId)) {
4849
+ return err17(`Layer "${targetId}" already exists in document "${id}"`);
4850
+ }
4851
+ const content = renderPageNumberFormat(format, currentIndex, totalCount);
4852
+ const m = margin ?? DEFAULT_OVERLAY_MARGIN;
4853
+ const { frame, anchorPoint } = anchorToFrame(anchor, doc.canvas, m);
4854
+ const layer = {
4855
+ id: targetId,
4856
+ tags: ["overlay"],
4857
+ visual: { type: "text", content, style: mergeStyle(style) },
4858
+ frame,
4859
+ bounds: { width: 200, height: 80 },
4860
+ anchorPoint
4861
+ };
4862
+ doc.layers.push(layer);
4863
+ store.set(id, doc);
4864
+ return ok17({ layerId: targetId, layer, rendered: content });
4865
+ }
4866
+ );
4867
+ }
4868
+
4869
+ // ../mcp/src/tools/recipes.ts
4870
+ var import_node_fs = require("fs");
4871
+ var import_node_path = require("path");
4872
+ var import_node_os = require("os");
4873
+ var import_zod33 = require("zod");
4874
+ var import_yaml2 = require("yaml");
4875
+ function getDoc18(store, id) {
4876
+ const doc = store.get(id);
4877
+ if (!doc) return { error: `Document "${id}" not found` };
4878
+ return { doc };
4879
+ }
4880
+ function ok18(data) {
4881
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, ...data }) }] };
4882
+ }
4883
+ function err18(message, extra) {
4884
+ return {
4885
+ content: [{ type: "text", text: JSON.stringify({ error: message, ...extra }) }],
4886
+ isError: true
4887
+ };
4888
+ }
4889
+ function resolveRecipePath(pathOrName, projectDir) {
4890
+ if ((0, import_node_path.isAbsolute)(pathOrName) || pathOrName.includes("/") || pathOrName.includes("\\")) {
4891
+ return (0, import_node_path.resolve)(pathOrName);
4892
+ }
4893
+ const candidates = [];
4894
+ const exts = [".recipe.yaml", ".recipe.json", ".yaml", ".yml", ".json"];
4895
+ const projectRecipesDir = (0, import_node_path.join)((0, import_node_path.resolve)(projectDir), ".atelier", "recipes");
4896
+ for (const ext of exts) candidates.push((0, import_node_path.join)(projectRecipesDir, `${pathOrName}${ext}`));
4897
+ const userRecipesDir = (0, import_node_path.join)((0, import_node_os.homedir)(), ".atelier", "recipes");
4898
+ for (const ext of exts) candidates.push((0, import_node_path.join)(userRecipesDir, `${pathOrName}${ext}`));
4899
+ for (const candidate of candidates) {
4900
+ if ((0, import_node_fs.existsSync)(candidate)) return candidate;
4901
+ }
4902
+ throw new Error(
4903
+ `Recipe "${pathOrName}" not found. Looked in:
4904
+ ${candidates.map((c) => ` ${c}`).join("\n")}`
4905
+ );
4906
+ }
4907
+ function parseRecipeText(raw, path) {
4908
+ if (path.endsWith(".json")) return JSON.parse(raw);
4909
+ return (0, import_yaml2.parse)(raw);
4910
+ }
4911
+ function loadRecipe(pathOrName, projectDir) {
4912
+ const path = resolveRecipePath(pathOrName, projectDir);
4913
+ const raw = (0, import_node_fs.readFileSync)(path, "utf-8");
4914
+ const parsed = parseRecipeText(raw, path);
4915
+ const result = validateRecipe(parsed);
4916
+ if (!result.success) {
4917
+ const msg = result.errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
4918
+ throw new Error(`Invalid recipe at ${path}:
4919
+ ${msg}`);
4920
+ }
4921
+ return { recipe: result.data, path, warnings: result.warnings ?? [] };
4922
+ }
4923
+ function recipeToYaml(recipe) {
4924
+ return (0, import_yaml2.stringify)(recipe);
4925
+ }
4926
+ var DEFAULT_OVERLAY_MARGIN2 = 24;
4927
+ var DEFAULT_OVERLAY_TEXT_STYLE = {
4928
+ fontFamily: "Inter",
4929
+ fontSize: 24,
4930
+ fontWeight: 600,
4931
+ color: "#F5F5F7"
4932
+ };
4933
+ var HANDLE_LAYER_ID = "overlay-handle";
4934
+ var PAGE_NUMBER_LAYER_ID = "overlay-page-number";
4935
+ function applyRecipeToOverlay(doc, recipe, ctx) {
4936
+ const preserved = doc.layers.filter((l) => !(l.tags ?? []).includes("overlay"));
4937
+ const overlayLayers = [];
4938
+ const warnings = [];
4939
+ const rules = recipe.overlay_rules;
4940
+ if (!rules) {
4941
+ return { doc: { ...doc, layers: preserved }, warnings };
4942
+ }
4943
+ if (rules.handle) {
4944
+ overlayLayers.push(buildHandleLayer(rules.handle, doc.canvas));
4945
+ }
4946
+ if (rules.page_number) {
4947
+ if (ctx?.currentIndex != null && ctx?.totalCount != null) {
4948
+ overlayLayers.push(
4949
+ buildPageNumberLayer(rules.page_number, doc.canvas, ctx.currentIndex, ctx.totalCount)
4950
+ );
4951
+ } else {
4952
+ warnings.push(
4953
+ "recipe.overlay_rules.page_number is present but currentIndex/totalCount were not provided \u2014 the page_number layer was skipped."
4954
+ );
4955
+ }
4956
+ }
4957
+ return { doc: { ...doc, layers: [...preserved, ...overlayLayers] }, warnings };
4958
+ }
4959
+ function buildHandleLayer(rule, canvas) {
4960
+ const margin = rule.margin ?? DEFAULT_OVERLAY_MARGIN2;
4961
+ const { frame, anchorPoint } = anchorToFrame2(rule.anchor, canvas, margin);
4962
+ return {
4963
+ id: HANDLE_LAYER_ID,
4964
+ tags: ["overlay"],
4965
+ visual: { type: "text", content: rule.text, style: mergeOverlayStyle(rule.style) },
4966
+ frame,
4967
+ bounds: { width: 600, height: 80 },
4968
+ anchorPoint
4969
+ };
4970
+ }
4971
+ function buildPageNumberLayer(rule, canvas, currentIndex, totalCount) {
4972
+ const margin = rule.margin ?? DEFAULT_OVERLAY_MARGIN2;
4973
+ const { frame, anchorPoint } = anchorToFrame2(rule.anchor, canvas, margin);
4974
+ return {
4975
+ id: PAGE_NUMBER_LAYER_ID,
4976
+ tags: ["overlay"],
4977
+ visual: {
4978
+ type: "text",
4979
+ content: renderPageNumberFormat2(rule.format, currentIndex, totalCount),
4980
+ style: mergeOverlayStyle(rule.style)
4981
+ },
4982
+ frame,
4983
+ bounds: { width: 200, height: 80 },
4984
+ anchorPoint
4985
+ };
4986
+ }
4987
+ function anchorToFrame2(anchor, canvas, margin) {
4988
+ switch (anchor) {
4989
+ case "top-left":
4990
+ return { frame: { x: margin, y: margin }, anchorPoint: { x: 0, y: 0 } };
4991
+ case "top-right":
4992
+ return { frame: { x: canvas.width - margin, y: margin }, anchorPoint: { x: 1, y: 0 } };
4993
+ case "bottom-left":
4994
+ return { frame: { x: margin, y: canvas.height - margin }, anchorPoint: { x: 0, y: 1 } };
4995
+ case "bottom-right":
4996
+ return {
4997
+ frame: { x: canvas.width - margin, y: canvas.height - margin },
4998
+ anchorPoint: { x: 1, y: 1 }
4999
+ };
5000
+ }
5001
+ }
5002
+ function mergeOverlayStyle(style) {
5003
+ return {
5004
+ fontFamily: style?.font_family ?? DEFAULT_OVERLAY_TEXT_STYLE.fontFamily,
5005
+ fontSize: style?.font_size ?? DEFAULT_OVERLAY_TEXT_STYLE.fontSize,
5006
+ fontWeight: style?.font_weight ?? DEFAULT_OVERLAY_TEXT_STYLE.fontWeight,
5007
+ color: style?.color ?? DEFAULT_OVERLAY_TEXT_STYLE.color
5008
+ };
5009
+ }
5010
+ function renderPageNumberFormat2(format, currentIndex, totalCount) {
5011
+ return format.replace(
5012
+ /\{(current|total)(?::0(\d+)d)?\}/g,
5013
+ (_, name, padWidth) => {
5014
+ const value = name === "current" ? currentIndex : totalCount;
5015
+ const str = String(value);
5016
+ if (padWidth) return str.padStart(parseInt(padWidth, 10), "0");
5017
+ return str;
5018
+ }
5019
+ );
5020
+ }
5021
+ var RECIPE_EXTS = [".recipe.yaml", ".recipe.yml", ".recipe.json"];
5022
+ function isRecipeFile(name) {
5023
+ return RECIPE_EXTS.some((ext) => name.endsWith(ext));
5024
+ }
5025
+ function listRecipesIn(dir) {
5026
+ if (!(0, import_node_fs.existsSync)(dir)) return [];
5027
+ let entries;
5028
+ try {
5029
+ entries = (0, import_node_fs.readdirSync)(dir);
5030
+ } catch {
5031
+ return [];
5032
+ }
5033
+ return entries.filter(isRecipeFile).map((name) => ({ name, path: (0, import_node_path.join)(dir, name) })).sort((a, b) => a.name.localeCompare(b.name));
5034
+ }
5035
+ function register18(server, store) {
5036
+ server.tool(
5037
+ "atelier_recipe_list",
5038
+ "List discoverable Studio Recipes \u2014 project-local <projectDir>/.atelier/recipes/*.recipe.* plus ~/.atelier/recipes/*.recipe.*. Returns { name, path } entries with absolute paths.",
5039
+ {
5040
+ projectDir: import_zod33.z.string().optional().describe("Project root to search (default: process.cwd())")
5041
+ },
5042
+ { readOnlyHint: true, destructiveHint: false },
5043
+ async ({ projectDir }) => {
5044
+ const root = (0, import_node_path.resolve)(projectDir ?? process.cwd());
5045
+ const projectRecipes = listRecipesIn((0, import_node_path.join)(root, ".atelier", "recipes"));
5046
+ const userRecipes = listRecipesIn((0, import_node_path.join)((0, import_node_os.homedir)(), ".atelier", "recipes"));
5047
+ const seen = new Set(projectRecipes.map((r) => r.name));
5048
+ const recipes = [...projectRecipes, ...userRecipes.filter((r) => !seen.has(r.name))];
5049
+ return ok18({ count: recipes.length, recipes });
5050
+ }
5051
+ );
5052
+ server.tool(
5053
+ "atelier_recipe_get",
5054
+ "Load a recipe and return BOTH the parsed object and its serialized YAML text (so the agent can show/explain it), plus any validation warnings.",
5055
+ {
5056
+ path: import_zod33.z.string().describe("Recipe path or bare name (resolved like the CLI)"),
5057
+ projectDir: import_zod33.z.string().optional().describe("Project root for name resolution (default: process.cwd())")
5058
+ },
5059
+ { readOnlyHint: true, destructiveHint: false },
5060
+ async ({ path, projectDir }) => {
5061
+ const root = (0, import_node_path.resolve)(projectDir ?? process.cwd());
5062
+ try {
5063
+ const loaded = loadRecipe(path, root);
5064
+ return ok18({
5065
+ path: loaded.path,
5066
+ recipe: loaded.recipe,
5067
+ yaml: recipeToYaml(loaded.recipe),
5068
+ warnings: loaded.warnings
5069
+ });
5070
+ } catch (e) {
5071
+ return err18(e.message);
5072
+ }
5073
+ }
5074
+ );
5075
+ server.tool(
5076
+ "atelier_recipe_validate",
5077
+ "Validate a recipe from a path OR inline YAML text. Returns { success, warnings, errors }.",
5078
+ {
5079
+ path: import_zod33.z.string().optional().describe("Recipe path or bare name to validate"),
5080
+ yaml: import_zod33.z.string().optional().describe("Inline YAML text to validate (alternative to path)"),
5081
+ projectDir: import_zod33.z.string().optional().describe("Project root for name resolution (default: process.cwd())")
5082
+ },
5083
+ { readOnlyHint: true, destructiveHint: false },
5084
+ async ({ path, yaml, projectDir }) => {
5085
+ if (!path && yaml === void 0) {
5086
+ return err18("Provide either `path` or `yaml` to validate");
5087
+ }
5088
+ const root = (0, import_node_path.resolve)(projectDir ?? process.cwd());
5089
+ let parsed;
5090
+ try {
5091
+ if (yaml !== void 0) {
5092
+ parsed = (0, import_yaml2.parse)(yaml);
5093
+ } else {
5094
+ const resolved = resolveRecipePath(path, root);
5095
+ parsed = parseRecipeText((0, import_node_fs.readFileSync)(resolved, "utf-8"), resolved);
5096
+ }
5097
+ } catch (e) {
5098
+ return ok18({ valid: false, warnings: [], errors: [{ path: "(yaml)", message: e.message }] });
5099
+ }
5100
+ const result = validateRecipe(parsed);
5101
+ if (!result.success) {
5102
+ return ok18({ valid: false, warnings: [], errors: result.errors });
5103
+ }
5104
+ return ok18({ valid: true, warnings: result.warnings ?? [], errors: [] });
5105
+ }
5106
+ );
5107
+ server.tool(
5108
+ "atelier_recipe_save",
5109
+ "Write a recipe to disk. Accepts a recipe object (serialized via YAML) OR raw YAML text. Validates BEFORE writing and REFUSES to write an invalid recipe (returns an error with the validation issues). Creates parent directories as needed.",
5110
+ {
5111
+ path: import_zod33.z.string().describe("Absolute or relative file path to write the recipe to"),
5112
+ recipe: import_zod33.z.record(import_zod33.z.unknown()).optional().describe("Recipe object (will be serialized to YAML)"),
5113
+ yaml: import_zod33.z.string().optional().describe("Raw YAML text (alternative to `recipe`)")
5114
+ },
5115
+ { readOnlyHint: false, destructiveHint: true },
5116
+ async ({ path, recipe, yaml }) => {
5117
+ if (recipe === void 0 && yaml === void 0) {
5118
+ return err18("Provide either `recipe` (object) or `yaml` (text) to save");
5119
+ }
5120
+ let parsed;
5121
+ let yamlText;
5122
+ if (yaml !== void 0) {
5123
+ yamlText = yaml;
5124
+ try {
5125
+ parsed = (0, import_yaml2.parse)(yaml);
5126
+ } catch (e) {
5127
+ return err18(`YAML parse error: ${e.message}`);
5128
+ }
5129
+ } else {
5130
+ parsed = recipe;
5131
+ yamlText = (0, import_yaml2.stringify)(recipe);
5132
+ }
5133
+ const result = validateRecipe(parsed);
5134
+ if (!result.success) {
5135
+ return err18("Refusing to write an invalid recipe", { errors: result.errors });
5136
+ }
5137
+ const absPath = (0, import_node_path.resolve)(path);
5138
+ try {
5139
+ (0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(absPath), { recursive: true });
5140
+ (0, import_node_fs.writeFileSync)(absPath, yamlText, "utf-8");
5141
+ } catch (e) {
5142
+ return err18(`Failed to write recipe: ${e.message}`);
5143
+ }
5144
+ return ok18({ path: absPath, warnings: result.warnings ?? [] });
5145
+ }
5146
+ );
5147
+ server.tool(
5148
+ "atelier_recipe_apply",
5149
+ `Apply a recipe's overlay_rules to an in-store document (handle + page-number overlays). Accepts a recipe object OR a path/name. The result is stored back with source="llm". NOTE: only overlay_rules are applied here \u2014 silence-trim / transcribe / caption application remain CLI-only. page_number overlays require currentIndex + totalCount; if a recipe requests page_number without them, that layer is skipped and a warning is returned.`,
5150
+ {
5151
+ documentId: import_zod33.z.string().describe("In-store document ID to apply the recipe to"),
5152
+ path: import_zod33.z.string().optional().describe("Recipe path or bare name (alternative to `recipe`)"),
5153
+ recipe: import_zod33.z.record(import_zod33.z.unknown()).optional().describe("Recipe object (alternative to `path`)"),
5154
+ projectDir: import_zod33.z.string().optional().describe("Project root for name resolution (default: process.cwd())"),
5155
+ currentIndex: import_zod33.z.number().int().nonnegative().optional().describe("1-based carousel index for page_number"),
5156
+ totalCount: import_zod33.z.number().int().positive().optional().describe("Total carousel size for page_number")
5157
+ },
5158
+ { readOnlyHint: false, destructiveHint: false },
5159
+ async ({ documentId, path, recipe, projectDir, currentIndex, totalCount }) => {
5160
+ const result = getDoc18(store, documentId);
5161
+ if ("error" in result) return err18(result.error);
5162
+ const { doc } = result;
5163
+ if (path === void 0 && recipe === void 0) {
5164
+ return err18("Provide either `path` or `recipe` to apply");
5165
+ }
5166
+ let resolvedRecipe;
5167
+ if (recipe !== void 0) {
5168
+ const validated = validateRecipe(recipe);
5169
+ if (!validated.success) {
5170
+ return err18("Invalid recipe", { errors: validated.errors });
5171
+ }
5172
+ resolvedRecipe = validated.data;
5173
+ } else {
5174
+ const root = (0, import_node_path.resolve)(projectDir ?? process.cwd());
5175
+ try {
5176
+ resolvedRecipe = loadRecipe(path, root).recipe;
5177
+ } catch (e) {
5178
+ return err18(e.message);
5179
+ }
5180
+ }
5181
+ const { doc: updated, warnings } = applyRecipeToOverlay(doc, resolvedRecipe, {
5182
+ currentIndex,
5183
+ totalCount
5184
+ });
5185
+ store.set(documentId, updated, "llm");
5186
+ return ok18({
5187
+ documentId,
5188
+ warnings,
5189
+ layerCount: updated.layers.length,
5190
+ document: updated
5191
+ });
5192
+ }
5193
+ );
5194
+ }
5195
+
5196
+ // ../mcp/src/tools/import-images.ts
5197
+ var import_node_fs2 = require("fs");
5198
+ var import_node_path2 = require("path");
5199
+ var import_zod34 = require("zod");
5200
+ function getDoc19(store, id) {
5201
+ const doc = store.get(id);
5202
+ if (!doc) return { error: `Document "${id}" not found` };
5203
+ return { doc };
5204
+ }
5205
+ function ok19(data) {
5206
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, ...data }) }] };
5207
+ }
5208
+ function err19(message) {
5209
+ return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
5210
+ }
5211
+ var IMAGE_EXTS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".webp", ".gif", ".svg"]);
5212
+ function isImageFile(name) {
5213
+ return IMAGE_EXTS.has((0, import_node_path2.extname)(name).toLowerCase());
5214
+ }
5215
+ function matchesGlob(name, glob) {
5216
+ if (!glob) return true;
5217
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
5218
+ return new RegExp(`^${escaped}$`, "i").test(name);
5219
+ }
5220
+ function deriveAssetId(filename, used) {
5221
+ const stem = (0, import_node_path2.basename)(filename, (0, import_node_path2.extname)(filename));
5222
+ const base = stem.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "image";
5223
+ let candidate = base;
5224
+ let n = 2;
5225
+ while (used.has(candidate)) {
5226
+ candidate = `${base}-${n}`;
5227
+ n++;
5228
+ }
5229
+ used.add(candidate);
5230
+ return candidate;
5231
+ }
5232
+ function nextImageLayerId(existing, seq) {
5233
+ let candidate = `image-${seq}`;
5234
+ let n = seq;
5235
+ while (existing.has(candidate)) {
5236
+ n++;
5237
+ candidate = `image-${n}`;
5238
+ }
5239
+ existing.add(candidate);
5240
+ return candidate;
5241
+ }
5242
+ function register19(server, store) {
5243
+ server.tool(
5244
+ "atelier_import_images",
5245
+ `Import every image in a folder into a document. Registers each as an asset and, unless asLayers is false, creates one fit-to-canvas ImageVisual layer per image (centered). Extensions: .png .jpg .jpeg .webp .gif .svg. Optional glob filters by filename (e.g. "*.png"); results are sorted by name. If the folder has exactly one image AND the doc's layer[0] is the welcome "background" placeholder, that image replaces the placeholder in place. Asset src is the absolute file path. Layer bounds default to a centered min(canvas.w, canvas.h) square because natural image dimensions are not measured (no image-dimension dependency).`,
5246
+ {
5247
+ documentId: import_zod34.z.string().describe("In-store document ID to import into"),
5248
+ dir: import_zod34.z.string().describe("Directory containing the images"),
5249
+ glob: import_zod34.z.string().optional().describe('Optional filename glob, e.g. "*.png" (default: all images)'),
5250
+ asLayers: import_zod34.z.boolean().optional().describe("Create layers per image (default: true). false = register assets only.")
5251
+ },
5252
+ { readOnlyHint: false, destructiveHint: false },
5253
+ async ({ documentId, dir, glob, asLayers }) => {
5254
+ const result = getDoc19(store, documentId);
5255
+ if ("error" in result) return err19(result.error);
5256
+ const { doc } = result;
5257
+ const absDir = (0, import_node_path2.resolve)(dir);
5258
+ if (!(0, import_node_fs2.existsSync)(absDir)) return err19(`Directory "${absDir}" does not exist`);
5259
+ if (!(0, import_node_fs2.statSync)(absDir).isDirectory()) return err19(`Path "${absDir}" is not a directory`);
5260
+ const files = (0, import_node_fs2.readdirSync)(absDir).filter((name) => isImageFile(name) && matchesGlob(name, glob)).sort((a, b) => a.localeCompare(b));
5261
+ if (files.length === 0) {
5262
+ return err19(`No image files matched in "${absDir}"${glob ? ` (glob: ${glob})` : ""}`);
5263
+ }
5264
+ const makeLayers = asLayers !== false;
5265
+ const canvas = doc.canvas;
5266
+ const square = Math.min(canvas.width, canvas.height);
5267
+ const allowBackgroundSwap = files.length === 1 && doc.layers[0]?.id === "background";
5268
+ const newAssets = { ...doc.assets ?? {} };
5269
+ const newLayers = [...doc.layers];
5270
+ const usedAssetIds = new Set(Object.keys(newAssets));
5271
+ const existingLayerIds = new Set(newLayers.map((l) => l.id));
5272
+ const assetIds = [];
5273
+ const layerIds = [];
5274
+ let layerSeq = newLayers.length + 1;
5275
+ for (const file of files) {
5276
+ const absPath = (0, import_node_path2.join)(absDir, file);
5277
+ const assetId = deriveAssetId(file, usedAssetIds);
5278
+ newAssets[assetId] = { type: "image", src: absPath, description: file };
5279
+ assetIds.push(assetId);
5280
+ if (!makeLayers) continue;
5281
+ const layerId = nextImageLayerId(existingLayerIds, layerSeq);
5282
+ layerSeq++;
5283
+ const layer = {
5284
+ id: layerId,
5285
+ visual: { type: "image", assetId, src: absPath },
5286
+ frame: { x: canvas.width / 2, y: canvas.height / 2 },
5287
+ bounds: { width: square, height: square },
5288
+ anchorPoint: { x: 0.5, y: 0.5 },
5289
+ opacity: 1
5290
+ };
5291
+ if (allowBackgroundSwap) {
5292
+ newLayers[0] = layer;
5293
+ } else {
5294
+ newLayers.unshift(layer);
5295
+ }
5296
+ layerIds.push(layerId);
5297
+ }
5298
+ const newDoc = { ...doc, assets: newAssets, layers: newLayers };
5299
+ store.set(documentId, newDoc, "llm");
5300
+ return ok19({
5301
+ imported: files.length,
5302
+ assetIds,
5303
+ layerIds,
5304
+ asLayers: makeLayers,
5305
+ note: "Layer bounds default to a centered square (min canvas dimension) \u2014 natural image dimensions are not measured."
5306
+ });
5307
+ }
5308
+ );
5309
+ }
5310
+
5311
+ // ../mcp/src/bridge-protocol.ts
5312
+ var BRIDGE_PROTOCOL_VERSION = 1;
5313
+ function isBridgeEnvelope(value) {
5314
+ if (!value || typeof value !== "object") return false;
5315
+ const t = value.type;
5316
+ return t === "hello" || t === "doc:load" || t === "doc:loaded" || t === "doc:patch" || t === "llm:mutation" || t === "error";
5317
+ }
5318
+
5319
+ // ../mcp/src/transports/ws-transport.ts
5320
+ var WebSocketServerTransport = class {
5321
+ constructor(ws) {
5322
+ this.ws = ws;
5323
+ }
5324
+ onclose;
5325
+ onerror;
5326
+ onmessage;
5327
+ sessionId;
5328
+ started = false;
5329
+ closed = false;
5330
+ async start() {
5331
+ if (this.started) {
5332
+ throw new Error(
5333
+ "WebSocketServerTransport already started \u2014 the MCP SDK calls start() automatically; do not call it again"
5334
+ );
5335
+ }
5336
+ this.started = true;
5337
+ this.ws.on("message", (data) => {
5338
+ let text;
5339
+ try {
5340
+ if (typeof data === "string") {
5341
+ text = data;
5342
+ } else if (data instanceof Buffer) {
5343
+ text = data.toString("utf-8");
5344
+ } else if (Array.isArray(data)) {
5345
+ text = Buffer.concat(data).toString("utf-8");
5346
+ } else if (data instanceof ArrayBuffer) {
5347
+ text = Buffer.from(data).toString("utf-8");
5348
+ } else {
5349
+ text = String(data);
5350
+ }
5351
+ } catch (err20) {
5352
+ this.onerror?.(err20 instanceof Error ? err20 : new Error(String(err20)));
5353
+ return;
5354
+ }
5355
+ let parsed;
5356
+ try {
5357
+ parsed = JSON.parse(text);
5358
+ } catch (err20) {
5359
+ this.onerror?.(
5360
+ new Error(
5361
+ `WebSocketServerTransport: failed to parse JSON-RPC message: ${err20 instanceof Error ? err20.message : String(err20)}`
5362
+ )
5363
+ );
5364
+ return;
5365
+ }
5366
+ this.onmessage?.(parsed);
5367
+ });
5368
+ this.ws.on("close", () => {
5369
+ if (this.closed) return;
5370
+ this.closed = true;
5371
+ this.onclose?.();
5372
+ });
5373
+ this.ws.on("error", (err20) => {
5374
+ this.onerror?.(err20);
5375
+ });
5376
+ }
5377
+ async send(message) {
5378
+ if (this.closed) {
5379
+ throw new Error("WebSocketServerTransport: cannot send after close");
5380
+ }
5381
+ if (this.ws.readyState !== 1) {
5382
+ throw new Error(
5383
+ `WebSocketServerTransport: WebSocket is not OPEN (readyState=${this.ws.readyState})`
5384
+ );
5385
+ }
5386
+ this.ws.send(JSON.stringify(message));
5387
+ }
5388
+ async close() {
5389
+ if (this.closed) return;
5390
+ this.closed = true;
5391
+ try {
5392
+ this.ws.close();
5393
+ } catch {
5394
+ }
5395
+ this.onclose?.();
5396
+ }
5397
+ };
5398
+
4527
5399
  // ../mcp/src/index.ts
4528
- function createServer() {
4529
- const store = new DocumentStore();
5400
+ function createServer(store = new DocumentStore()) {
4530
5401
  const server = new import_mcp.McpServer(
4531
5402
  { name: "atelier", version: "0.1.0" },
4532
5403
  { capabilities: { tools: {} } }
@@ -4547,6 +5418,9 @@ function createServer() {
4547
5418
  register14(server, store);
4548
5419
  register15(server, store);
4549
5420
  register16(server, store);
5421
+ register17(server, store);
5422
+ register18(server, store);
5423
+ register19(server, store);
4550
5424
  return { server, store };
4551
5425
  }
4552
5426
  var isMain = typeof process !== "undefined" && process.argv[1] && (process.argv[1].endsWith("/index.js") || process.argv[1].endsWith("/index.cjs") || process.argv[1].includes("atelier-mcp"));
@@ -4560,18 +5434,24 @@ if (isMain) {
4560
5434
  }
4561
5435
  // Annotate the CommonJS export names for ESM import in node:
4562
5436
  0 && (module.exports = {
5437
+ BRIDGE_PROTOCOL_VERSION,
4563
5438
  DocumentStore,
5439
+ WebSocketServerTransport,
4564
5440
  createServer,
5441
+ isBridgeEnvelope,
4565
5442
  registerAssetTools,
4566
5443
  registerDeltaTools,
4567
5444
  registerDocumentTools,
4568
5445
  registerExportTools,
5446
+ registerImportImageTools,
4569
5447
  registerInteractionTools,
4570
5448
  registerLayerEffectTools,
4571
5449
  registerLayerTools,
5450
+ registerOverlayTools,
4572
5451
  registerPerformanceTools,
4573
5452
  registerPresetTools,
4574
5453
  registerPreviewTools,
5454
+ registerRecipeTools,
4575
5455
  registerRefTools,
4576
5456
  registerShapeTools,
4577
5457
  registerStateConfigTools,