@a-company/atelier 0.29.0 → 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+)?%$/, {
@@ -532,6 +573,121 @@ var AtelierDocumentSchema = import_zod14.z.object({
532
573
  layers: import_zod14.z.array(LayerSchema),
533
574
  states: import_zod14.z.record(import_zod14.z.string(), StateSchema)
534
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
+ ];
535
691
  function formatErrors(error) {
536
692
  return error.issues.map((issue) => ({
537
693
  path: issue.path.join(".") || "(root)",
@@ -545,14 +701,30 @@ function validateDocument(input) {
545
701
  }
546
702
  return { success: false, errors: formatErrors(result.error) };
547
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
+ }
548
720
  function parseAtelier(yamlString) {
549
721
  let parsed;
550
722
  try {
551
723
  parsed = (0, import_yaml.parse)(yamlString);
552
- } catch (err17) {
724
+ } catch (err20) {
553
725
  return {
554
726
  success: false,
555
- errors: [{ path: "(yaml)", message: `YAML parse error: ${err17.message}` }]
727
+ errors: [{ path: "(yaml)", message: `YAML parse error: ${err20.message}` }]
556
728
  };
557
729
  }
558
730
  return validateDocument(parsed);
@@ -578,13 +750,13 @@ function register(server, store) {
578
750
  "atelier_create",
579
751
  "Create a new Atelier animation document with canvas settings",
580
752
  {
581
- name: import_zod15.z.string().describe("Animation name"),
582
- width: import_zod15.z.number().positive().describe("Canvas width in pixels"),
583
- height: import_zod15.z.number().positive().describe("Canvas height in pixels"),
584
- fps: import_zod15.z.number().positive().int().describe("Frames per second"),
585
- background: import_zod15.z.string().optional().describe("Background color (hex string)"),
586
- description: import_zod15.z.string().optional().describe("Animation description"),
587
- 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")
588
760
  },
589
761
  { readOnlyHint: false, destructiveHint: false },
590
762
  async ({ name, width, height, fps, background, description, tags }) => {
@@ -609,7 +781,7 @@ function register(server, store) {
609
781
  "atelier_info",
610
782
  "Get summary information about an Atelier document",
611
783
  {
612
- id: import_zod15.z.string().describe("Document ID")
784
+ id: import_zod16.z.string().describe("Document ID")
613
785
  },
614
786
  { readOnlyHint: true, destructiveHint: false },
615
787
  async ({ id }) => {
@@ -637,8 +809,8 @@ function register(server, store) {
637
809
  "atelier_load",
638
810
  "Load an Atelier document from a YAML string",
639
811
  {
640
- id: import_zod15.z.string().optional().describe("Custom document ID (auto-generated if omitted)"),
641
- 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")
642
814
  },
643
815
  { readOnlyHint: false, destructiveHint: false },
644
816
  async ({ id, yaml }) => {
@@ -654,7 +826,7 @@ function register(server, store) {
654
826
  "atelier_export",
655
827
  "Export an Atelier document as a YAML string",
656
828
  {
657
- id: import_zod15.z.string().describe("Document ID")
829
+ id: import_zod16.z.string().describe("Document ID")
658
830
  },
659
831
  { readOnlyHint: true, destructiveHint: false },
660
832
  async ({ id }) => {
@@ -677,7 +849,7 @@ function register(server, store) {
677
849
  }
678
850
 
679
851
  // ../mcp/src/tools/layers.ts
680
- var import_zod16 = require("zod");
852
+ var import_zod17 = require("zod");
681
853
  function getDoc2(store, id) {
682
854
  const doc = store.get(id);
683
855
  if (!doc) return { error: `Document "${id}" not found` };
@@ -689,69 +861,69 @@ function ok2(data) {
689
861
  function err2(message) {
690
862
  return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
691
863
  }
692
- var VisualInputSchema = import_zod16.z.object({
693
- 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"),
694
866
  // shape visual fields
695
- shape: import_zod16.z.object({
696
- type: import_zod16.z.enum(["rect", "ellipse", "path"]),
697
- 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(),
698
- points: import_zod16.z.array(import_zod16.z.object({
699
- x: import_zod16.z.number(),
700
- y: import_zod16.z.number(),
701
- in: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional(),
702
- 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()
703
875
  })).optional(),
704
- closed: import_zod16.z.boolean().optional()
876
+ closed: import_zod17.z.boolean().optional()
705
877
  }).optional(),
706
- fill: import_zod16.z.record(import_zod16.z.unknown()).optional(),
707
- 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(),
708
880
  // text visual fields
709
- content: import_zod16.z.string().optional(),
710
- 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(),
711
883
  // image visual fields
712
- assetId: import_zod16.z.string().optional(),
713
- sourceRect: import_zod16.z.object({
714
- x: import_zod16.z.number(),
715
- y: import_zod16.z.number(),
716
- width: import_zod16.z.number().positive(),
717
- 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()
718
890
  }).optional(),
719
- spritesheet: import_zod16.z.object({
720
- columns: import_zod16.z.number().int().positive(),
721
- rows: import_zod16.z.number().int().positive(),
722
- frameCount: import_zod16.z.number().int().positive().optional(),
723
- frameWidth: import_zod16.z.number().positive(),
724
- 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()
725
897
  }).optional(),
726
- frameIndex: import_zod16.z.number().int().min(0).optional(),
898
+ frameIndex: import_zod17.z.number().int().min(0).optional(),
727
899
  // ref visual fields
728
- src: import_zod16.z.string().optional(),
729
- state: import_zod16.z.string().optional(),
730
- 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()
731
903
  }).describe("Visual content definition");
732
904
  function register2(server, store) {
733
905
  server.tool(
734
906
  "atelier_add_layer",
735
907
  "Add a new layer to an Atelier document",
736
908
  {
737
- id: import_zod16.z.string().describe("Document ID"),
738
- 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"),
739
911
  visual: VisualInputSchema.describe("Visual content definition"),
740
- x: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).describe("X position (pixels or percentage)"),
741
- y: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).describe("Y position (pixels or percentage)"),
742
- width: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).describe("Width (pixels or percentage)"),
743
- height: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).describe("Height (pixels or percentage)"),
744
- description: import_zod16.z.string().optional().describe("Layer description"),
745
- tags: import_zod16.z.array(import_zod16.z.string()).optional().describe("Tags"),
746
- opacity: import_zod16.z.number().min(0).max(1).optional().describe("Opacity 0-1"),
747
- rotation: import_zod16.z.number().optional().describe("Rotation in degrees"),
748
- parentId: import_zod16.z.string().optional().describe("Parent layer ID for transform inheritance"),
749
- anchorPoint: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional().describe("Anchor point (0-1 normalized)"),
750
- scale: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional().describe("Scale factors"),
751
- visible: import_zod16.z.boolean().optional().describe("Whether layer is visible"),
752
- tint: import_zod16.z.object({
753
- color: import_zod16.z.string().describe("Tint color (hex string)"),
754
- 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)")
755
927
  }).optional().describe("Color tint overlay")
756
928
  },
757
929
  { readOnlyHint: false, destructiveHint: false },
@@ -788,20 +960,20 @@ function register2(server, store) {
788
960
  "atelier_edit_layer",
789
961
  "Edit properties of an existing layer",
790
962
  {
791
- id: import_zod16.z.string().describe("Document ID"),
792
- layerId: import_zod16.z.string().describe("Layer ID to edit"),
793
- x: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).optional().describe("New X position"),
794
- y: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).optional().describe("New Y position"),
795
- width: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).optional().describe("New width"),
796
- height: import_zod16.z.union([import_zod16.z.number(), import_zod16.z.string()]).optional().describe("New height"),
797
- description: import_zod16.z.string().optional().describe("New description"),
798
- tags: import_zod16.z.array(import_zod16.z.string()).optional().describe("New tags"),
799
- opacity: import_zod16.z.number().min(0).max(1).optional().describe("New opacity"),
800
- rotation: import_zod16.z.number().optional().describe("New rotation"),
801
- parentId: import_zod16.z.string().nullable().optional().describe("New parent layer ID (null to clear)"),
802
- anchorPoint: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional().describe("New anchor point"),
803
- scale: import_zod16.z.object({ x: import_zod16.z.number(), y: import_zod16.z.number() }).optional().describe("New scale"),
804
- 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")
805
977
  },
806
978
  { readOnlyHint: false, destructiveHint: false },
807
979
  async ({ id, layerId, x, y, width, height, description, tags, opacity, rotation, parentId, anchorPoint, scale, visible }) => {
@@ -836,8 +1008,8 @@ function register2(server, store) {
836
1008
  "atelier_remove_layer",
837
1009
  "Remove a layer from an Atelier document",
838
1010
  {
839
- id: import_zod16.z.string().describe("Document ID"),
840
- 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")
841
1013
  },
842
1014
  { readOnlyHint: false, destructiveHint: true },
843
1015
  async ({ id, layerId }) => {
@@ -865,7 +1037,7 @@ function register2(server, store) {
865
1037
  "atelier_list_layers",
866
1038
  "List all layers in an Atelier document",
867
1039
  {
868
- id: import_zod16.z.string().describe("Document ID")
1040
+ id: import_zod17.z.string().describe("Document ID")
869
1041
  },
870
1042
  { readOnlyHint: true, destructiveHint: false },
871
1043
  async ({ id }) => {
@@ -892,9 +1064,9 @@ function register2(server, store) {
892
1064
  "atelier_reorder",
893
1065
  "Move a layer to a new position in the layer stack",
894
1066
  {
895
- id: import_zod16.z.string().describe("Document ID"),
896
- layerId: import_zod16.z.string().describe("Layer ID to move"),
897
- 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)")
898
1070
  },
899
1071
  { readOnlyHint: false, destructiveHint: false },
900
1072
  async ({ id, layerId, position }) => {
@@ -912,7 +1084,7 @@ function register2(server, store) {
912
1084
  }
913
1085
 
914
1086
  // ../mcp/src/tools/shapes.ts
915
- var import_zod17 = require("zod");
1087
+ var import_zod18 = require("zod");
916
1088
  function getDoc3(store, id) {
917
1089
  const doc = store.get(id);
918
1090
  if (!doc) return { error: `Document "${id}" not found` };
@@ -929,21 +1101,21 @@ function register3(server, store) {
929
1101
  "atelier_set_shape",
930
1102
  "Set the shape on a shape-type layer",
931
1103
  {
932
- id: import_zod17.z.string().describe("Document ID"),
933
- layerId: import_zod17.z.string().describe("Layer ID (must be a shape visual)"),
934
- shape: import_zod17.z.object({
935
- type: import_zod17.z.enum(["rect", "ellipse", "path"]).describe("Shape type"),
936
- cornerRadius: import_zod17.z.union([
937
- import_zod17.z.number(),
938
- 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()])
939
1111
  ]).optional().describe("Corner radius (rect only)"),
940
- points: import_zod17.z.array(import_zod17.z.object({
941
- x: import_zod17.z.number(),
942
- y: import_zod17.z.number(),
943
- in: import_zod17.z.object({ x: import_zod17.z.number(), y: import_zod17.z.number() }).optional(),
944
- 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()
945
1117
  })).optional().describe("Path points (path only)"),
946
- 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)")
947
1119
  }).describe("Shape definition")
948
1120
  },
949
1121
  { readOnlyHint: false, destructiveHint: false },
@@ -962,20 +1134,20 @@ function register3(server, store) {
962
1134
  "atelier_set_fill",
963
1135
  "Set the fill on a shape-type layer",
964
1136
  {
965
- id: import_zod17.z.string().describe("Document ID"),
966
- layerId: import_zod17.z.string().describe("Layer ID (must be a shape visual)"),
967
- fill: import_zod17.z.object({
968
- type: import_zod17.z.enum(["solid", "linear-gradient", "radial-gradient"]).describe("Fill type"),
969
- color: import_zod17.z.unknown().optional().describe("Color for solid fill (hex string or RGBA/HSLA object)"),
970
- angle: import_zod17.z.number().optional().describe("Angle in degrees (linear-gradient only)"),
971
- center: import_zod17.z.object({
972
- x: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]),
973
- 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()])
974
1146
  }).optional().describe("Center point (radial-gradient only)"),
975
- radius: import_zod17.z.union([import_zod17.z.number(), import_zod17.z.string()]).optional().describe("Radius (radial-gradient only)"),
976
- stops: import_zod17.z.array(import_zod17.z.object({
977
- offset: import_zod17.z.number().min(0).max(1),
978
- 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()
979
1151
  })).optional().describe("Gradient stops")
980
1152
  }).describe("Fill definition")
981
1153
  },
@@ -995,14 +1167,14 @@ function register3(server, store) {
995
1167
  "atelier_set_stroke",
996
1168
  "Set the stroke on a shape-type layer",
997
1169
  {
998
- id: import_zod17.z.string().describe("Document ID"),
999
- layerId: import_zod17.z.string().describe("Layer ID (must be a shape visual)"),
1000
- stroke: import_zod17.z.object({
1001
- color: import_zod17.z.unknown().describe("Stroke color (hex string or RGBA/HSLA object)"),
1002
- width: import_zod17.z.number().positive().describe("Stroke width in pixels"),
1003
- dash: import_zod17.z.array(import_zod17.z.number()).optional().describe("Dash pattern"),
1004
- lineCap: import_zod17.z.enum(["butt", "round", "square"]).optional().describe("Line cap style"),
1005
- 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")
1006
1178
  }).describe("Stroke definition")
1007
1179
  },
1008
1180
  { readOnlyHint: false, destructiveHint: false },
@@ -1020,7 +1192,7 @@ function register3(server, store) {
1020
1192
  }
1021
1193
 
1022
1194
  // ../mcp/src/tools/states.ts
1023
- var import_zod18 = require("zod");
1195
+ var import_zod19 = require("zod");
1024
1196
  function getDoc4(store, id) {
1025
1197
  const doc = store.get(id);
1026
1198
  if (!doc) return { error: `Document "${id}" not found` };
@@ -1037,11 +1209,11 @@ function register4(server, store) {
1037
1209
  "atelier_add_state",
1038
1210
  "Add a named animation state to a document",
1039
1211
  {
1040
- id: import_zod18.z.string().describe("Document ID"),
1041
- stateName: import_zod18.z.string().describe("State name (e.g. 'intro', 'hover', 'exit')"),
1042
- duration: import_zod18.z.number().positive().int().describe("Duration in frames"),
1043
- description: import_zod18.z.string().optional().describe("State description"),
1044
- 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")
1045
1217
  },
1046
1218
  { readOnlyHint: false, destructiveHint: false },
1047
1219
  async ({ id, stateName, duration, description, tags }) => {
@@ -1064,11 +1236,11 @@ function register4(server, store) {
1064
1236
  "atelier_edit_state",
1065
1237
  "Edit metadata of an existing animation state",
1066
1238
  {
1067
- id: import_zod18.z.string().describe("Document ID"),
1068
- stateName: import_zod18.z.string().describe("State name to edit"),
1069
- duration: import_zod18.z.number().positive().int().optional().describe("New duration in frames"),
1070
- description: import_zod18.z.string().optional().describe("New description"),
1071
- 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")
1072
1244
  },
1073
1245
  { readOnlyHint: false, destructiveHint: false },
1074
1246
  async ({ id, stateName, duration, description, tags }) => {
@@ -1087,8 +1259,8 @@ function register4(server, store) {
1087
1259
  "atelier_remove_state",
1088
1260
  "Remove an animation state and all its deltas",
1089
1261
  {
1090
- id: import_zod18.z.string().describe("Document ID"),
1091
- 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")
1092
1264
  },
1093
1265
  { readOnlyHint: false, destructiveHint: true },
1094
1266
  async ({ id, stateName }) => {
@@ -1105,7 +1277,7 @@ function register4(server, store) {
1105
1277
  "atelier_list_states",
1106
1278
  "List all animation states in a document",
1107
1279
  {
1108
- id: import_zod18.z.string().describe("Document ID")
1280
+ id: import_zod19.z.string().describe("Document ID")
1109
1281
  },
1110
1282
  { readOnlyHint: true, destructiveHint: false },
1111
1283
  async ({ id }) => {
@@ -1125,7 +1297,7 @@ function register4(server, store) {
1125
1297
  }
1126
1298
 
1127
1299
  // ../mcp/src/tools/deltas.ts
1128
- var import_zod19 = require("zod");
1300
+ var import_zod20 = require("zod");
1129
1301
 
1130
1302
  // ../math/dist/index.js
1131
1303
  function linear(t) {
@@ -1864,17 +2036,17 @@ function register5(server, store) {
1864
2036
  "atelier_add_delta",
1865
2037
  "Add an animation delta (keyframe transition) to a state",
1866
2038
  {
1867
- id: import_zod19.z.string().describe("Document ID"),
1868
- stateName: import_zod19.z.string().describe("State name"),
1869
- 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"),
1870
2042
  property: AnimatablePropertyEnum.describe("Property to animate"),
1871
- 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"),
1872
- from: import_zod19.z.unknown().describe("Starting value"),
1873
- 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"),
1874
2046
  easing: EasingInputSchema.optional().describe("Easing function"),
1875
- description: import_zod19.z.string().optional().describe("Delta description"),
1876
- tags: import_zod19.z.array(import_zod19.z.string()).optional().describe("Tags"),
1877
- 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")
1878
2050
  },
1879
2051
  { readOnlyHint: false, destructiveHint: false },
1880
2052
  async ({ id, stateName, layer, property, range, from, to, easing, description, tags, deltaId }) => {
@@ -1912,17 +2084,17 @@ function register5(server, store) {
1912
2084
  "atelier_edit_delta",
1913
2085
  "Edit an existing delta by index within a state",
1914
2086
  {
1915
- id: import_zod19.z.string().describe("Document ID"),
1916
- stateName: import_zod19.z.string().describe("State name"),
1917
- deltaIndex: import_zod19.z.number().int().min(0).describe("Delta index within the state"),
1918
- 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"),
1919
2091
  property: AnimatablePropertyEnum.optional().describe("New property to animate"),
1920
- 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]"),
1921
- from: import_zod19.z.unknown().optional().describe("New starting value"),
1922
- 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"),
1923
2095
  easing: EasingInputSchema.optional().describe("New easing function"),
1924
- description: import_zod19.z.string().optional().describe("New description"),
1925
- 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")
1926
2098
  },
1927
2099
  { readOnlyHint: false, destructiveHint: false },
1928
2100
  async ({ id, stateName, deltaIndex, layer, property, range, from, to, easing, description, tags }) => {
@@ -1961,9 +2133,9 @@ function register5(server, store) {
1961
2133
  "atelier_remove_delta",
1962
2134
  "Remove a delta by index from a state",
1963
2135
  {
1964
- id: import_zod19.z.string().describe("Document ID"),
1965
- stateName: import_zod19.z.string().describe("State name"),
1966
- 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")
1967
2139
  },
1968
2140
  { readOnlyHint: false, destructiveHint: true },
1969
2141
  async ({ id, stateName, deltaIndex }) => {
@@ -1991,12 +2163,12 @@ function register5(server, store) {
1991
2163
  "atelier_apply_preset",
1992
2164
  "Apply a preset to a layer, expanding it into concrete deltas",
1993
2165
  {
1994
- id: import_zod19.z.string().describe("Document ID"),
1995
- stateName: import_zod19.z.string().describe("State name to add deltas to"),
1996
- presetName: import_zod19.z.string().describe("Preset name defined in the document"),
1997
- layerId: import_zod19.z.string().describe("Target layer ID"),
1998
- startFrame: import_zod19.z.number().int().min(0).optional().describe("Start frame (default: 0)"),
1999
- 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)")
2000
2172
  },
2001
2173
  { readOnlyHint: false, destructiveHint: false },
2002
2174
  async ({ id, stateName, presetName, layerId, startFrame, duration }) => {
@@ -2039,7 +2211,7 @@ ${errors.join("\n")}`);
2039
2211
  }
2040
2212
 
2041
2213
  // ../mcp/src/tools/presets.ts
2042
- var import_zod20 = require("zod");
2214
+ var import_zod21 = require("zod");
2043
2215
  function getDoc6(store, id) {
2044
2216
  const doc = store.get(id);
2045
2217
  if (!doc) return { error: `Document "${id}" not found` };
@@ -2053,11 +2225,11 @@ function err6(message) {
2053
2225
  }
2054
2226
  var AnimatablePropertyEnum2 = AnimatablePropertySchema;
2055
2227
  var EasingInputSchema2 = EasingSchema;
2056
- var PresetDeltaSchema2 = import_zod20.z.object({
2228
+ var PresetDeltaSchema2 = import_zod21.z.object({
2057
2229
  property: AnimatablePropertyEnum2.describe("Animatable property"),
2058
- 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]"),
2059
- from: import_zod20.z.unknown().describe("Starting value"),
2060
- 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"),
2061
2233
  easing: EasingInputSchema2.optional().describe("Easing function")
2062
2234
  });
2063
2235
  function register6(server, store) {
@@ -2065,11 +2237,11 @@ function register6(server, store) {
2065
2237
  "atelier_define_preset",
2066
2238
  "Define a reusable animation preset on a document",
2067
2239
  {
2068
- id: import_zod20.z.string().describe("Document ID"),
2069
- presetName: import_zod20.z.string().describe("Preset name"),
2070
- description: import_zod20.z.string().optional().describe("Preset description"),
2071
- tags: import_zod20.z.array(import_zod20.z.string()).optional().describe("Tags"),
2072
- 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")
2073
2245
  },
2074
2246
  { readOnlyHint: false, destructiveHint: false },
2075
2247
  async ({ id, presetName, description, tags, deltas }) => {
@@ -2090,7 +2262,7 @@ function register6(server, store) {
2090
2262
  "atelier_list_presets",
2091
2263
  "List all presets defined on a document",
2092
2264
  {
2093
- id: import_zod20.z.string().describe("Document ID")
2265
+ id: import_zod21.z.string().describe("Document ID")
2094
2266
  },
2095
2267
  { readOnlyHint: true, destructiveHint: false },
2096
2268
  async ({ id }) => {
@@ -2113,7 +2285,7 @@ function register6(server, store) {
2113
2285
  }
2114
2286
 
2115
2287
  // ../mcp/src/tools/preview.ts
2116
- var import_zod21 = require("zod");
2288
+ var import_zod22 = require("zod");
2117
2289
  function getDoc7(store, id) {
2118
2290
  const doc = store.get(id);
2119
2291
  if (!doc) return { error: `Document "${id}" not found` };
@@ -2130,7 +2302,7 @@ function register7(server, store) {
2130
2302
  "atelier_validate",
2131
2303
  "Validate an Atelier document for schema correctness and delta overlaps",
2132
2304
  {
2133
- id: import_zod21.z.string().describe("Document ID")
2305
+ id: import_zod22.z.string().describe("Document ID")
2134
2306
  },
2135
2307
  { readOnlyHint: true, destructiveHint: false },
2136
2308
  async ({ id }) => {
@@ -2159,9 +2331,9 @@ function register7(server, store) {
2159
2331
  "atelier_preview",
2160
2332
  "Preview the resolved state of all layers at a specific frame",
2161
2333
  {
2162
- id: import_zod21.z.string().describe("Document ID"),
2163
- stateName: import_zod21.z.string().describe("State name to preview"),
2164
- 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")
2165
2337
  },
2166
2338
  { readOnlyHint: true, destructiveHint: false },
2167
2339
  async ({ id, stateName, frame }) => {
@@ -2196,7 +2368,7 @@ function register7(server, store) {
2196
2368
  }
2197
2369
 
2198
2370
  // ../mcp/src/tools/templates.ts
2199
- var import_zod22 = require("zod");
2371
+ var import_zod23 = require("zod");
2200
2372
  function getDoc8(store, id) {
2201
2373
  const doc = store.get(id);
2202
2374
  if (!doc) return { error: `Document "${id}" not found` };
@@ -2213,8 +2385,8 @@ function register8(server, store) {
2213
2385
  "atelier_instantiate_template",
2214
2386
  "Instantiate a template document with variable bindings. Creates a new document in the store.",
2215
2387
  {
2216
- id: import_zod22.z.string().describe("Template document ID"),
2217
- 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 }")
2218
2390
  },
2219
2391
  { readOnlyHint: false, destructiveHint: false },
2220
2392
  async ({ id, bindings }) => {
@@ -2237,7 +2409,7 @@ function register8(server, store) {
2237
2409
  "atelier_find_variables",
2238
2410
  "Scan a document for {{variableName}} patterns. Returns the list of variable references found.",
2239
2411
  {
2240
- id: import_zod22.z.string().describe("Document ID")
2412
+ id: import_zod23.z.string().describe("Document ID")
2241
2413
  },
2242
2414
  { readOnlyHint: true, destructiveHint: false },
2243
2415
  async ({ id }) => {
@@ -2257,7 +2429,7 @@ function register8(server, store) {
2257
2429
  }
2258
2430
 
2259
2431
  // ../mcp/src/tools/layer-effects.ts
2260
- var import_zod23 = require("zod");
2432
+ var import_zod24 = require("zod");
2261
2433
  function getDoc9(store, id) {
2262
2434
  const doc = store.get(id);
2263
2435
  if (!doc) return { error: `Document "${id}" not found` };
@@ -2274,9 +2446,9 @@ function register9(server, store) {
2274
2446
  "atelier_set_blend_mode",
2275
2447
  "Set or clear the blend mode on a layer",
2276
2448
  {
2277
- id: import_zod23.z.string().describe("Document ID"),
2278
- layerId: import_zod23.z.string().describe("Layer ID"),
2279
- 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([
2280
2452
  "normal",
2281
2453
  "multiply",
2282
2454
  "screen",
@@ -2314,13 +2486,13 @@ function register9(server, store) {
2314
2486
  "atelier_set_shadow",
2315
2487
  "Set or remove a drop shadow on a layer",
2316
2488
  {
2317
- id: import_zod23.z.string().describe("Document ID"),
2318
- layerId: import_zod23.z.string().describe("Layer ID"),
2319
- shadow: import_zod23.z.object({
2320
- color: import_zod23.z.unknown().describe("Shadow color (hex string or RGBA/HSLA object)"),
2321
- blur: import_zod23.z.number().min(0).describe("Blur radius in pixels"),
2322
- offsetX: import_zod23.z.number().optional().describe("Horizontal offset in pixels"),
2323
- 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")
2324
2496
  }).nullable().describe("Shadow definition, or null to remove")
2325
2497
  },
2326
2498
  { readOnlyHint: false, destructiveHint: false },
@@ -2342,11 +2514,11 @@ function register9(server, store) {
2342
2514
  "atelier_set_tint",
2343
2515
  "Set or remove a color tint on a layer (works on any visual type)",
2344
2516
  {
2345
- id: import_zod23.z.string().describe("Document ID"),
2346
- layerId: import_zod23.z.string().describe("Layer ID"),
2347
- tint: import_zod23.z.object({
2348
- color: import_zod23.z.string().describe("Tint color (hex string)"),
2349
- 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)")
2350
2522
  }).nullable().describe("Tint definition, or null to remove")
2351
2523
  },
2352
2524
  { readOnlyHint: false, destructiveHint: false },
@@ -2368,18 +2540,18 @@ function register9(server, store) {
2368
2540
  "atelier_set_motion_path",
2369
2541
  "Set or remove a motion path on a layer for path-based position animation",
2370
2542
  {
2371
- id: import_zod23.z.string().describe("Document ID"),
2372
- layerId: import_zod23.z.string().describe("Layer ID"),
2373
- motionPath: import_zod23.z.object({
2374
- points: import_zod23.z.array(import_zod23.z.object({
2375
- x: import_zod23.z.number(),
2376
- y: import_zod23.z.number(),
2377
- in: import_zod23.z.object({ x: import_zod23.z.number(), y: import_zod23.z.number() }).optional(),
2378
- 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()
2379
2551
  })).min(2).describe("Path points (minimum 2)"),
2380
- closed: import_zod23.z.boolean().optional().describe("Whether the path is closed"),
2381
- autoRotate: import_zod23.z.boolean().optional().describe("Auto-rotate layer to follow path tangent"),
2382
- 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")
2383
2555
  }).nullable().describe("Motion path definition, or null to remove")
2384
2556
  },
2385
2557
  { readOnlyHint: false, destructiveHint: false },
@@ -2401,21 +2573,21 @@ function register9(server, store) {
2401
2573
  "atelier_set_clip_path",
2402
2574
  "Set or remove a clip path on a layer to restrict rendering to within a shape",
2403
2575
  {
2404
- id: import_zod23.z.string().describe("Document ID"),
2405
- layerId: import_zod23.z.string().describe("Layer ID"),
2406
- clipPath: import_zod23.z.object({
2407
- type: import_zod23.z.enum(["rect", "ellipse", "path"]).describe("Clip shape type"),
2408
- cornerRadius: import_zod23.z.union([
2409
- import_zod23.z.number(),
2410
- 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()])
2411
2583
  ]).optional().describe("Corner radius (rect only)"),
2412
- points: import_zod23.z.array(import_zod23.z.object({
2413
- x: import_zod23.z.number(),
2414
- y: import_zod23.z.number(),
2415
- in: import_zod23.z.object({ x: import_zod23.z.number(), y: import_zod23.z.number() }).optional(),
2416
- 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()
2417
2589
  })).optional().describe("Path points (path only)"),
2418
- 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)")
2419
2591
  }).nullable().describe("Clip path shape, or null to remove")
2420
2592
  },
2421
2593
  { readOnlyHint: false, destructiveHint: false },
@@ -2437,45 +2609,45 @@ function register9(server, store) {
2437
2609
  "atelier_edit_visual",
2438
2610
  "Edit visual content of an existing layer (text content/style, image src/assetId, shape/fill/stroke)",
2439
2611
  {
2440
- id: import_zod23.z.string().describe("Document ID"),
2441
- 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"),
2442
2614
  // Text fields
2443
- content: import_zod23.z.string().optional().describe("New text content (text layers only)"),
2444
- 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)"),
2445
2617
  // Image fields
2446
- src: import_zod23.z.string().optional().describe("Image source URL (image layers only)"),
2447
- assetId: import_zod23.z.string().optional().describe("Asset ID reference (image layers only)"),
2448
- sourceRect: import_zod23.z.object({
2449
- x: import_zod23.z.number(),
2450
- y: import_zod23.z.number(),
2451
- width: import_zod23.z.number().positive(),
2452
- 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()
2453
2625
  }).nullable().optional().describe("Source rectangle crop (image layers only). Null to remove."),
2454
- spritesheet: import_zod23.z.object({
2455
- columns: import_zod23.z.number().int().positive(),
2456
- rows: import_zod23.z.number().int().positive(),
2457
- frameCount: import_zod23.z.number().int().positive().optional(),
2458
- frameWidth: import_zod23.z.number().positive(),
2459
- 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()
2460
2632
  }).nullable().optional().describe("Spritesheet config (image layers only). Null to remove."),
2461
- 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)"),
2462
2634
  // Shape fields
2463
- shape: import_zod23.z.object({
2464
- type: import_zod23.z.enum(["rect", "ellipse", "path"]),
2465
- cornerRadius: import_zod23.z.union([
2466
- import_zod23.z.number(),
2467
- 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()])
2468
2640
  ]).optional(),
2469
- points: import_zod23.z.array(import_zod23.z.object({
2470
- x: import_zod23.z.number(),
2471
- y: import_zod23.z.number(),
2472
- in: import_zod23.z.object({ x: import_zod23.z.number(), y: import_zod23.z.number() }).optional(),
2473
- 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()
2474
2646
  })).optional(),
2475
- closed: import_zod23.z.boolean().optional()
2647
+ closed: import_zod24.z.boolean().optional()
2476
2648
  }).optional().describe("New shape (shape layers only)"),
2477
- fill: import_zod23.z.record(import_zod23.z.unknown()).optional().describe("New fill definition (shape layers only)"),
2478
- 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)")
2479
2651
  },
2480
2652
  { readOnlyHint: false, destructiveHint: false },
2481
2653
  async ({ id, layerId, content, style, src, assetId, sourceRect, spritesheet, frameIndex, shape, fill, stroke }) => {
@@ -2556,7 +2728,7 @@ function register9(server, store) {
2556
2728
  }
2557
2729
 
2558
2730
  // ../mcp/src/tools/state-config.ts
2559
- var import_zod24 = require("zod");
2731
+ var import_zod25 = require("zod");
2560
2732
  function getDoc10(store, id) {
2561
2733
  const doc = store.get(id);
2562
2734
  if (!doc) return { error: `Document "${id}" not found` };
@@ -2568,27 +2740,27 @@ function ok10(data) {
2568
2740
  function err10(message) {
2569
2741
  return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
2570
2742
  }
2571
- var EasingInputSchema3 = import_zod24.z.union([
2572
- import_zod24.z.enum(["ease-in", "ease-out", "ease-in-out"]),
2573
- import_zod24.z.object({ type: import_zod24.z.literal("linear") }),
2574
- import_zod24.z.object({
2575
- type: import_zod24.z.literal("cubic-bezier"),
2576
- x1: import_zod24.z.number(),
2577
- y1: import_zod24.z.number(),
2578
- x2: import_zod24.z.number(),
2579
- 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()
2580
2752
  }),
2581
- import_zod24.z.object({
2582
- type: import_zod24.z.literal("spring"),
2583
- mass: import_zod24.z.number().optional(),
2584
- stiffness: import_zod24.z.number().optional(),
2585
- damping: import_zod24.z.number().optional(),
2586
- 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()
2587
2759
  }),
2588
- import_zod24.z.object({
2589
- type: import_zod24.z.literal("step"),
2590
- steps: import_zod24.z.number().int().positive(),
2591
- 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()
2592
2764
  })
2593
2765
  ]).describe("Easing function");
2594
2766
  function register10(server, store) {
@@ -2596,14 +2768,14 @@ function register10(server, store) {
2596
2768
  "atelier_set_audio",
2597
2769
  "Set or remove an audio track on a state",
2598
2770
  {
2599
- id: import_zod24.z.string().describe("Document ID"),
2600
- stateName: import_zod24.z.string().describe("State name"),
2601
- audio: import_zod24.z.object({
2602
- src: import_zod24.z.string().describe("Audio source \u2014 asset reference, file path, or URL"),
2603
- offset: import_zod24.z.number().min(0).optional().describe("Skip into audio in seconds (default: 0)"),
2604
- volume: import_zod24.z.number().min(0).max(1).optional().describe("Playback volume 0\u20131 (default: 1)"),
2605
- loop: import_zod24.z.boolean().optional().describe("Loop for state duration (default: false)"),
2606
- 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)")
2607
2779
  }).nullable().describe("Audio configuration, or null to remove")
2608
2780
  },
2609
2781
  { readOnlyHint: false, destructiveHint: false },
@@ -2625,11 +2797,11 @@ function register10(server, store) {
2625
2797
  "atelier_configure_transition",
2626
2798
  "Configure or remove a transition from one state to a target state",
2627
2799
  {
2628
- id: import_zod24.z.string().describe("Document ID"),
2629
- stateName: import_zod24.z.string().describe("Source state name"),
2630
- targetState: import_zod24.z.string().describe("Target state name"),
2631
- transition: import_zod24.z.object({
2632
- 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"),
2633
2805
  easing: EasingInputSchema3.optional()
2634
2806
  }).nullable().describe("Transition config, or null to remove")
2635
2807
  },
@@ -2666,9 +2838,9 @@ function register10(server, store) {
2666
2838
  "atelier_set_state_parent",
2667
2839
  "Set or clear the parent state for hierarchical delta inheritance",
2668
2840
  {
2669
- id: import_zod24.z.string().describe("Document ID"),
2670
- stateName: import_zod24.z.string().describe("State name"),
2671
- 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")
2672
2844
  },
2673
2845
  { readOnlyHint: false, destructiveHint: false },
2674
2846
  async ({ id, stateName, parent }) => {
@@ -2703,7 +2875,7 @@ function register10(server, store) {
2703
2875
  }
2704
2876
 
2705
2877
  // ../mcp/src/tools/export.ts
2706
- var import_zod25 = require("zod");
2878
+ var import_zod26 = require("zod");
2707
2879
 
2708
2880
  // ../canvas/dist/index.js
2709
2881
  function colorToCSS(color) {
@@ -3888,12 +4060,12 @@ function register11(server, store) {
3888
4060
  "atelier_export_svg",
3889
4061
  "Export a frame as an SVG string",
3890
4062
  {
3891
- id: import_zod25.z.string().describe("Document ID"),
3892
- stateName: import_zod25.z.string().optional().describe("State name (defaults to first state)"),
3893
- frame: import_zod25.z.number().int().min(0).optional().describe("Frame number (defaults to 0)"),
3894
- xmlDeclaration: import_zod25.z.boolean().optional().describe("Include XML declaration (default: false)"),
3895
- viewBox: import_zod25.z.boolean().optional().describe("Include viewBox attribute (default: true)"),
3896
- 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)")
3897
4069
  },
3898
4070
  { readOnlyHint: true, destructiveHint: false },
3899
4071
  async ({ id, stateName, frame, xmlDeclaration, viewBox, indent }) => {
@@ -3924,8 +4096,8 @@ function register11(server, store) {
3924
4096
  "atelier_export_lottie",
3925
4097
  "Export a document to Lottie JSON format (lossy, best-effort)",
3926
4098
  {
3927
- id: import_zod25.z.string().describe("Document ID"),
3928
- 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)")
3929
4101
  },
3930
4102
  { readOnlyHint: true, destructiveHint: false },
3931
4103
  async ({ id, stateName }) => {
@@ -3945,7 +4117,7 @@ function register11(server, store) {
3945
4117
  }
3946
4118
 
3947
4119
  // ../mcp/src/tools/assets.ts
3948
- var import_zod26 = require("zod");
4120
+ var import_zod27 = require("zod");
3949
4121
  function getDoc12(store, id) {
3950
4122
  const doc = store.get(id);
3951
4123
  if (!doc) return { error: `Document "${id}" not found` };
@@ -3962,17 +4134,17 @@ function register12(server, store) {
3962
4134
  "atelier_add_asset",
3963
4135
  "Register an external asset (image, SVG, font, animation, audio) on a document",
3964
4136
  {
3965
- id: import_zod26.z.string().describe("Document ID"),
3966
- assetId: import_zod26.z.string().describe("Unique asset identifier"),
3967
- type: import_zod26.z.enum(["image", "svg", "font", "animation", "audio"]).describe("Asset type"),
3968
- src: import_zod26.z.string().describe("File path or URL"),
3969
- description: import_zod26.z.string().optional().describe("Human-readable description"),
3970
- spritesheet: import_zod26.z.object({
3971
- columns: import_zod26.z.number().int().positive().describe("Number of columns in the spritesheet grid"),
3972
- rows: import_zod26.z.number().int().positive().describe("Number of rows in the spritesheet grid"),
3973
- frameCount: import_zod26.z.number().int().positive().optional().describe("Total frame count (defaults to columns \xD7 rows)"),
3974
- frameWidth: import_zod26.z.number().positive().describe("Width of each frame in pixels"),
3975
- 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")
3976
4148
  }).optional().describe("Spritesheet metadata (image assets only)")
3977
4149
  },
3978
4150
  { readOnlyHint: false, destructiveHint: false },
@@ -3995,7 +4167,7 @@ function register12(server, store) {
3995
4167
  "atelier_list_assets",
3996
4168
  "List all registered assets with usage info (which layers/states reference them)",
3997
4169
  {
3998
- id: import_zod26.z.string().describe("Document ID")
4170
+ id: import_zod27.z.string().describe("Document ID")
3999
4171
  },
4000
4172
  { readOnlyHint: true, destructiveHint: false },
4001
4173
  async ({ id }) => {
@@ -4022,8 +4194,8 @@ function register12(server, store) {
4022
4194
  "atelier_remove_asset",
4023
4195
  "Remove a registered asset. Warns if layers or state audio still reference it.",
4024
4196
  {
4025
- id: import_zod26.z.string().describe("Document ID"),
4026
- 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")
4027
4199
  },
4028
4200
  { readOnlyHint: false, destructiveHint: true },
4029
4201
  async ({ id, assetId }) => {
@@ -4047,7 +4219,7 @@ function register12(server, store) {
4047
4219
  }
4048
4220
 
4049
4221
  // ../mcp/src/tools/variables.ts
4050
- var import_zod27 = require("zod");
4222
+ var import_zod28 = require("zod");
4051
4223
  function getDoc13(store, id) {
4052
4224
  const doc = store.get(id);
4053
4225
  if (!doc) return { error: `Document "${id}" not found` };
@@ -4064,11 +4236,11 @@ function register13(server, store) {
4064
4236
  "atelier_add_variable",
4065
4237
  "Define a template variable on a document",
4066
4238
  {
4067
- id: import_zod27.z.string().describe("Document ID"),
4068
- variableName: import_zod27.z.string().describe("Variable name (used in {{variableName}} patterns)"),
4069
- type: import_zod27.z.enum(["string", "number", "color", "asset", "boolean"]).describe("Variable type"),
4070
- description: import_zod27.z.string().optional().describe("Human-readable description"),
4071
- 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")
4072
4244
  },
4073
4245
  { readOnlyHint: false, destructiveHint: false },
4074
4246
  async (args) => {
@@ -4089,7 +4261,7 @@ function register13(server, store) {
4089
4261
  "atelier_list_variables",
4090
4262
  "List all declared variables with usage info (which {{references}} exist in the document)",
4091
4263
  {
4092
- id: import_zod27.z.string().describe("Document ID")
4264
+ id: import_zod28.z.string().describe("Document ID")
4093
4265
  },
4094
4266
  { readOnlyHint: true, destructiveHint: false },
4095
4267
  async ({ id }) => {
@@ -4113,8 +4285,8 @@ function register13(server, store) {
4113
4285
  "atelier_remove_variable",
4114
4286
  "Remove a declared variable. Warns if {{references}} still exist in the document.",
4115
4287
  {
4116
- id: import_zod27.z.string().describe("Document ID"),
4117
- 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")
4118
4290
  },
4119
4291
  { readOnlyHint: false, destructiveHint: true },
4120
4292
  async ({ id, variableName }) => {
@@ -4137,7 +4309,7 @@ function register13(server, store) {
4137
4309
  }
4138
4310
 
4139
4311
  // ../mcp/src/tools/interactions.ts
4140
- var import_zod28 = require("zod");
4312
+ var import_zod29 = require("zod");
4141
4313
  function getDoc14(store, id) {
4142
4314
  const doc = store.get(id);
4143
4315
  if (!doc) return { error: `Document "${id}" not found` };
@@ -4154,23 +4326,23 @@ function register14(server, store) {
4154
4326
  "atelier_add_interaction",
4155
4327
  "Add a trigger-action interaction to a layer (click, hover, timer, signal \u2192 go-to-state, emit-signal, set-variable, toggle-visibility)",
4156
4328
  {
4157
- id: import_zod28.z.string().describe("Document ID"),
4158
- layerId: import_zod28.z.string().describe("Layer to attach the interaction to"),
4159
- interactionId: import_zod28.z.string().describe("Unique interaction ID"),
4160
- trigger: import_zod28.z.object({
4161
- type: import_zod28.z.enum(["click", "hover", "pointerdown", "pointerup", "timer", "signal"]).describe("Trigger type"),
4162
- delay: import_zod28.z.number().optional().describe("Delay in ms (timer trigger only)"),
4163
- 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)")
4164
4336
  }).describe("What triggers the interaction"),
4165
- action: import_zod28.z.object({
4166
- type: import_zod28.z.enum(["go-to-state", "emit-signal", "set-variable", "toggle-visibility"]).describe("Action type"),
4167
- state: import_zod28.z.string().optional().describe("Target state (go-to-state only)"),
4168
- signal: import_zod28.z.string().optional().describe("Signal to emit (emit-signal only)"),
4169
- variable: import_zod28.z.string().optional().describe("Variable name (set-variable only)"),
4170
- value: import_zod28.z.unknown().optional().describe("Value to set (set-variable only)"),
4171
- 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)")
4172
4344
  }).describe("What happens when triggered"),
4173
- description: import_zod28.z.string().optional().describe("Human-readable description")
4345
+ description: import_zod29.z.string().optional().describe("Human-readable description")
4174
4346
  },
4175
4347
  { readOnlyHint: false, destructiveHint: false },
4176
4348
  async ({ id, layerId, interactionId, trigger, action, description }) => {
@@ -4219,8 +4391,8 @@ function register14(server, store) {
4219
4391
  "atelier_list_interactions",
4220
4392
  "List all interactions across all layers or for a specific layer",
4221
4393
  {
4222
- id: import_zod28.z.string().describe("Document ID"),
4223
- 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")
4224
4396
  },
4225
4397
  { readOnlyHint: true, destructiveHint: false },
4226
4398
  async ({ id, layerId }) => {
@@ -4247,9 +4419,9 @@ function register14(server, store) {
4247
4419
  "atelier_remove_interaction",
4248
4420
  "Remove an interaction from a layer",
4249
4421
  {
4250
- id: import_zod28.z.string().describe("Document ID"),
4251
- layerId: import_zod28.z.string().describe("Layer ID"),
4252
- 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")
4253
4425
  },
4254
4426
  { readOnlyHint: false, destructiveHint: true },
4255
4427
  async ({ id, layerId, interactionId }) => {
@@ -4271,7 +4443,7 @@ function register14(server, store) {
4271
4443
  }
4272
4444
 
4273
4445
  // ../mcp/src/tools/refs.ts
4274
- var import_zod29 = require("zod");
4446
+ var import_zod30 = require("zod");
4275
4447
  function getDoc15(store, id) {
4276
4448
  const doc = store.get(id);
4277
4449
  if (!doc) return { error: `Document "${id}" not found` };
@@ -4288,9 +4460,9 @@ function register15(server, store) {
4288
4460
  "atelier_set_ref",
4289
4461
  "Set the source reference on a ref-type layer. Can point to a file path or another in-memory document ID.",
4290
4462
  {
4291
- id: import_zod29.z.string().describe("Document ID"),
4292
- layerId: import_zod29.z.string().describe("Layer ID (must be a ref visual)"),
4293
- 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")
4294
4466
  },
4295
4467
  { readOnlyHint: false, destructiveHint: false },
4296
4468
  async ({ id, layerId, src }) => {
@@ -4310,7 +4482,7 @@ function register15(server, store) {
4310
4482
  "atelier_resolve_refs",
4311
4483
  "Check which ref layers can be resolved (point to valid in-memory documents)",
4312
4484
  {
4313
- id: import_zod29.z.string().describe("Document ID")
4485
+ id: import_zod30.z.string().describe("Document ID")
4314
4486
  },
4315
4487
  { readOnlyHint: true, destructiveHint: false },
4316
4488
  async ({ id }) => {
@@ -4337,7 +4509,7 @@ function register15(server, store) {
4337
4509
  }
4338
4510
 
4339
4511
  // ../mcp/src/tools/performance.ts
4340
- var import_zod30 = require("zod");
4512
+ var import_zod31 = require("zod");
4341
4513
  function getDoc16(store, id) {
4342
4514
  const doc = store.get(id);
4343
4515
  if (!doc) return { error: `Document "${id}" not found` };
@@ -4354,9 +4526,9 @@ function register16(server, store) {
4354
4526
  "atelier_profile",
4355
4527
  "Profile frame resolution performance \u2014 returns timing, layer count, property count, and delta count for a frame",
4356
4528
  {
4357
- id: import_zod30.z.string().describe("Document ID"),
4358
- stateName: import_zod30.z.string().describe("State name to profile"),
4359
- 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")
4360
4532
  },
4361
4533
  { readOnlyHint: true, destructiveHint: false },
4362
4534
  async ({ id, stateName, frame }) => {
@@ -4402,10 +4574,10 @@ function register16(server, store) {
4402
4574
  "atelier_diff_frames",
4403
4575
  "Compare two frames and return only the properties that changed between them",
4404
4576
  {
4405
- id: import_zod30.z.string().describe("Document ID"),
4406
- stateName: import_zod30.z.string().describe("State name"),
4407
- frameA: import_zod30.z.number().int().min(0).describe("First frame number"),
4408
- 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")
4409
4581
  },
4410
4582
  { readOnlyHint: true, destructiveHint: false },
4411
4583
  async ({ id, stateName, frameA, frameB }) => {
@@ -4444,9 +4616,9 @@ function register16(server, store) {
4444
4616
  "atelier_batch_preview",
4445
4617
  "Preview multiple frames at once \u2014 reduces round-trips for scrubbing or timeline inspection",
4446
4618
  {
4447
- id: import_zod30.z.string().describe("Document ID"),
4448
- stateName: import_zod30.z.string().describe("State name to preview"),
4449
- 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)")
4450
4622
  },
4451
4623
  { readOnlyHint: true, destructiveHint: false },
4452
4624
  async ({ id, stateName, frames }) => {
@@ -4482,7 +4654,7 @@ function register16(server, store) {
4482
4654
  "atelier_complexity",
4483
4655
  "Analyze document complexity \u2014 layer count, delta count, expression usage, state hierarchy depth",
4484
4656
  {
4485
- id: import_zod30.z.string().describe("Document ID")
4657
+ id: import_zod31.z.string().describe("Document ID")
4486
4658
  },
4487
4659
  { readOnlyHint: true, destructiveHint: false },
4488
4660
  async ({ id }) => {
@@ -4554,9 +4726,678 @@ function register16(server, store) {
4554
4726
  );
4555
4727
  }
4556
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
+
4557
5399
  // ../mcp/src/index.ts
4558
- function createServer() {
4559
- const store = new DocumentStore();
5400
+ function createServer(store = new DocumentStore()) {
4560
5401
  const server = new import_mcp.McpServer(
4561
5402
  { name: "atelier", version: "0.1.0" },
4562
5403
  { capabilities: { tools: {} } }
@@ -4577,6 +5418,9 @@ function createServer() {
4577
5418
  register14(server, store);
4578
5419
  register15(server, store);
4579
5420
  register16(server, store);
5421
+ register17(server, store);
5422
+ register18(server, store);
5423
+ register19(server, store);
4580
5424
  return { server, store };
4581
5425
  }
4582
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"));
@@ -4590,18 +5434,24 @@ if (isMain) {
4590
5434
  }
4591
5435
  // Annotate the CommonJS export names for ESM import in node:
4592
5436
  0 && (module.exports = {
5437
+ BRIDGE_PROTOCOL_VERSION,
4593
5438
  DocumentStore,
5439
+ WebSocketServerTransport,
4594
5440
  createServer,
5441
+ isBridgeEnvelope,
4595
5442
  registerAssetTools,
4596
5443
  registerDeltaTools,
4597
5444
  registerDocumentTools,
4598
5445
  registerExportTools,
5446
+ registerImportImageTools,
4599
5447
  registerInteractionTools,
4600
5448
  registerLayerEffectTools,
4601
5449
  registerLayerTools,
5450
+ registerOverlayTools,
4602
5451
  registerPerformanceTools,
4603
5452
  registerPresetTools,
4604
5453
  registerPreviewTools,
5454
+ registerRecipeTools,
4605
5455
  registerRefTools,
4606
5456
  registerShapeTools,
4607
5457
  registerStateConfigTools,