@a-company/atelier 0.29.0 → 0.37.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/chunk-5QQESXI6.js +4432 -0
- package/dist/chunk-5QQESXI6.js.map +1 -0
- package/dist/cli.cjs +2391 -530
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +301 -429
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +2233 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +584 -2
- package/dist/index.d.ts +584 -2
- package/dist/index.js +111 -3
- package/dist/mcp.cjs +1215 -365
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +1209 -365
- package/dist/mcp.js.map +1 -1
- package/package.json +20 -9
- package/src/web/inline-app.ts +867 -0
- package/src/web/tsconfig.json +9 -0
- package/templates/welcome.atelier +67 -0
- package/university/content/notes/N-atel-001-first-render.md +114 -0
- package/university/content/notes/N-atel-001-install-and-launch.md +84 -0
- package/university/content/notes/N-atel-001-what-is-atelier.md +51 -0
- package/university/content/notes/N-atel-101-easings.md +97 -0
- package/university/content/notes/N-atel-101-layers.md +106 -0
- package/university/content/notes/N-atel-101-states-and-deltas.md +94 -0
- package/university/content/notes/N-atel-101-the-atelier-format.md +72 -0
- package/university/content/notes/N-atel-201-authoring-tools.md +141 -0
- package/university/content/notes/N-atel-201-mcp-overview.md +86 -0
- package/university/content/notes/N-atel-201-patterns.md +108 -0
- package/university/content/notes/N-atel-201-visual-and-effects.md +125 -0
- package/university/content/notes/N-atel-301-composition-and-overlays.md +141 -0
- package/university/content/notes/N-atel-301-effects.md +136 -0
- package/university/content/notes/N-atel-301-images-and-video.md +126 -0
- package/university/content/notes/N-atel-301-shapes-and-text.md +118 -0
- package/university/content/notes/N-atel-401-hierarchical-states.md +71 -0
- package/university/content/notes/N-atel-401-motion-deep-dive.md +106 -0
- package/university/content/notes/N-atel-401-presets-and-templates.md +98 -0
- package/university/content/notes/N-atel-401-transitions.md +94 -0
- package/university/content/notes/N-atel-501-detected-vs-user-edited.md +76 -0
- package/university/content/notes/N-atel-501-layer-tag-isolation.md +62 -0
- package/university/content/notes/N-atel-501-silence-trim.md +98 -0
- package/university/content/notes/N-atel-501-transcribe-and-captions.md +98 -0
- package/university/content/notes/N-atel-601-carousel.md +71 -0
- package/university/content/notes/N-atel-601-overlay-rules.md +96 -0
- package/university/content/notes/N-atel-601-recipe-tools-and-apply.md +84 -0
- package/university/content/notes/N-atel-601-studio-recipe.md +103 -0
- package/university/content/notes/N-atel-701-choosing-output.md +68 -0
- package/university/content/notes/N-atel-701-png-and-frames.md +84 -0
- package/university/content/notes/N-atel-701-vector.md +85 -0
- package/university/content/notes/N-atel-701-video.md +88 -0
- package/university/content/notes/N-atel-801-editing-surface.md +69 -0
- package/university/content/notes/N-atel-801-live-bridge.md +84 -0
- package/university/content/notes/N-atel-801-studio-app.md +72 -0
- package/university/content/notes/N-atel-801-symbiotic-loop.md +56 -0
- package/university/content/paths/LP-atel-001.yaml +21 -0
- package/university/content/paths/LP-atel-101.yaml +22 -0
- package/university/content/paths/LP-atel-201.yaml +23 -0
- package/university/content/paths/LP-atel-301.yaml +22 -0
- package/university/content/paths/LP-atel-401.yaml +22 -0
- package/university/content/paths/LP-atel-501.yaml +22 -0
- package/university/content/paths/LP-atel-601.yaml +22 -0
- package/university/content/paths/LP-atel-701.yaml +22 -0
- package/university/content/paths/LP-atel-801.yaml +22 -0
- package/university/content/quizzes/Q-atel-001-orientation.yaml +66 -0
- package/university/content/quizzes/Q-atel-101-document-model.yaml +66 -0
- package/university/content/quizzes/Q-atel-201-mcp-authoring.yaml +66 -0
- package/university/content/quizzes/Q-atel-301-visual-system.yaml +66 -0
- package/university/content/quizzes/Q-atel-401-state-machines.yaml +66 -0
- package/university/content/quizzes/Q-atel-501-video-pipeline.yaml +66 -0
- package/university/content/quizzes/Q-atel-601-recipes.yaml +66 -0
- package/university/content/quizzes/Q-atel-701-export.yaml +66 -0
- package/university/content/quizzes/Q-atel-801-studio-loop.yaml +66 -0
- package/university/index.yaml +720 -0
- package/university/pack.yaml +21 -0
- package/dist/chunk-JV7RGETS.js +0 -2292
- package/dist/chunk-JV7RGETS.js.map +0 -1
package/dist/mcp.js
CHANGED
|
@@ -12,13 +12,15 @@ function generateId() {
|
|
|
12
12
|
}
|
|
13
13
|
var DocumentStore = class {
|
|
14
14
|
docs = /* @__PURE__ */ new Map();
|
|
15
|
+
listeners = [];
|
|
15
16
|
/** Create a new document entry. Returns the assigned ID. */
|
|
16
|
-
create(doc, id) {
|
|
17
|
+
create(doc, id, source = "system") {
|
|
17
18
|
const docId = id ?? generateId();
|
|
18
19
|
if (this.docs.has(docId)) {
|
|
19
20
|
throw new Error(`Document "${docId}" already exists`);
|
|
20
21
|
}
|
|
21
22
|
this.docs.set(docId, doc);
|
|
23
|
+
this.emit(docId, doc, source);
|
|
22
24
|
return docId;
|
|
23
25
|
}
|
|
24
26
|
/** Get a document by ID. */
|
|
@@ -26,12 +28,15 @@ var DocumentStore = class {
|
|
|
26
28
|
return this.docs.get(id);
|
|
27
29
|
}
|
|
28
30
|
/** Set (overwrite) a document by ID. */
|
|
29
|
-
set(id, doc) {
|
|
31
|
+
set(id, doc, source = "system") {
|
|
30
32
|
this.docs.set(id, doc);
|
|
33
|
+
this.emit(id, doc, source);
|
|
31
34
|
}
|
|
32
35
|
/** Delete a document by ID. Returns true if it existed. */
|
|
33
|
-
delete(id) {
|
|
34
|
-
|
|
36
|
+
delete(id, source = "system") {
|
|
37
|
+
const existed = this.docs.delete(id);
|
|
38
|
+
if (existed) this.emit(id, null, source);
|
|
39
|
+
return existed;
|
|
35
40
|
}
|
|
36
41
|
/** Check if a document exists. */
|
|
37
42
|
has(id) {
|
|
@@ -45,14 +50,43 @@ var DocumentStore = class {
|
|
|
45
50
|
}
|
|
46
51
|
return result;
|
|
47
52
|
}
|
|
48
|
-
/** Clear all documents (useful for testing). */
|
|
53
|
+
/** Clear all documents (useful for testing). Does NOT fire onChange. */
|
|
49
54
|
clear() {
|
|
50
55
|
this.docs.clear();
|
|
51
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Subscribe to mutation events. The listener is invoked after every
|
|
59
|
+
* create()/set()/delete() with the changed document ID, the new value
|
|
60
|
+
* (or `null` when deleted), and the source tag.
|
|
61
|
+
*
|
|
62
|
+
* Returns an unsubscribe function. Multiple listeners are supported and
|
|
63
|
+
* delivered in registration order. A listener that throws is isolated —
|
|
64
|
+
* the error is swallowed and remaining listeners still fire.
|
|
65
|
+
*
|
|
66
|
+
* TODO(#10): the live WS bridge will subscribe here to forward LLM-side
|
|
67
|
+
* mutations into the in-browser Studio (which exposes the symmetric
|
|
68
|
+
* `AtelierStudio.applyMutation()` API).
|
|
69
|
+
*/
|
|
70
|
+
onChange(listener) {
|
|
71
|
+
this.listeners.push(listener);
|
|
72
|
+
return () => {
|
|
73
|
+
const idx = this.listeners.indexOf(listener);
|
|
74
|
+
if (idx !== -1) this.listeners.splice(idx, 1);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
emit(id, doc, source) {
|
|
78
|
+
const snapshot = this.listeners.slice();
|
|
79
|
+
for (const listener of snapshot) {
|
|
80
|
+
try {
|
|
81
|
+
listener(id, doc, source);
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
52
86
|
};
|
|
53
87
|
|
|
54
88
|
// ../mcp/src/tools/document.ts
|
|
55
|
-
import { z as
|
|
89
|
+
import { z as z16 } from "zod";
|
|
56
90
|
|
|
57
91
|
// ../schema/dist/index.js
|
|
58
92
|
import { z } from "zod";
|
|
@@ -69,6 +103,7 @@ import { z as z11 } from "zod";
|
|
|
69
103
|
import { z as z12 } from "zod";
|
|
70
104
|
import { z as z13 } from "zod";
|
|
71
105
|
import { z as z14 } from "zod";
|
|
106
|
+
import { z as z15 } from "zod";
|
|
72
107
|
import { parse as yamlParse, stringify as yamlStringify } from "yaml";
|
|
73
108
|
var PixelSchema = z.number();
|
|
74
109
|
var PercentageSchema = z.string().regex(/^-?\d+(\.\d+)?%$/, {
|
|
@@ -492,6 +527,121 @@ var AtelierDocumentSchema = z14.object({
|
|
|
492
527
|
layers: z14.array(LayerSchema),
|
|
493
528
|
states: z14.record(z14.string(), StateSchema)
|
|
494
529
|
});
|
|
530
|
+
var SilencePolicySchema = z15.object({
|
|
531
|
+
noise: z15.string().optional(),
|
|
532
|
+
min_silence: z15.number().nonnegative().optional(),
|
|
533
|
+
default_padding_pre: z15.number().nonnegative().optional(),
|
|
534
|
+
default_padding_post: z15.number().nonnegative().optional(),
|
|
535
|
+
match_tolerance: z15.number().nonnegative().optional()
|
|
536
|
+
}).strict();
|
|
537
|
+
var CaptionStyleSchema = z15.object({
|
|
538
|
+
font_family: z15.string().optional(),
|
|
539
|
+
font_size: z15.number().positive().optional(),
|
|
540
|
+
font_weight: z15.union([z15.literal("normal"), z15.literal("bold"), z15.number()]).optional(),
|
|
541
|
+
text_align: z15.enum(["left", "center", "right"]).optional(),
|
|
542
|
+
color: z15.string().optional(),
|
|
543
|
+
y_ratio: z15.number().min(0).max(1).optional(),
|
|
544
|
+
width_ratio: z15.number().min(0).max(1).optional(),
|
|
545
|
+
fade_seconds: z15.number().nonnegative().optional()
|
|
546
|
+
}).strict();
|
|
547
|
+
var CaptionGroupingSchema = z15.object({
|
|
548
|
+
max_words: z15.number().int().positive().optional(),
|
|
549
|
+
pause_gap: z15.number().nonnegative().optional()
|
|
550
|
+
}).strict();
|
|
551
|
+
var OverlayAnchorSchema = z15.enum([
|
|
552
|
+
"top-left",
|
|
553
|
+
"top-right",
|
|
554
|
+
"bottom-left",
|
|
555
|
+
"bottom-right"
|
|
556
|
+
]);
|
|
557
|
+
var OverlayTextStyleSchema = z15.object({
|
|
558
|
+
font_family: z15.string().optional(),
|
|
559
|
+
font_size: z15.number().positive().optional(),
|
|
560
|
+
font_weight: z15.union([z15.literal("normal"), z15.literal("bold"), z15.number()]).optional(),
|
|
561
|
+
color: z15.string().optional()
|
|
562
|
+
}).strict();
|
|
563
|
+
function validatePageNumberFormat(format, ctx) {
|
|
564
|
+
if (format.length === 0) {
|
|
565
|
+
ctx.addIssue({
|
|
566
|
+
code: z15.ZodIssueCode.custom,
|
|
567
|
+
message: "format must be a non-empty string"
|
|
568
|
+
});
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const open = (format.match(/\{/g) ?? []).length;
|
|
572
|
+
const close = (format.match(/\}/g) ?? []).length;
|
|
573
|
+
if (open !== close) {
|
|
574
|
+
ctx.addIssue({
|
|
575
|
+
code: z15.ZodIssueCode.custom,
|
|
576
|
+
message: `format has unbalanced braces (${open} '{' vs ${close} '}')`
|
|
577
|
+
});
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
const groupRe = /\{([^{}]*)\}/g;
|
|
581
|
+
const groupRule = /^(current|total)(:0\d+d)?$/;
|
|
582
|
+
let m;
|
|
583
|
+
let sawCurrent = false;
|
|
584
|
+
let sawTotal = false;
|
|
585
|
+
while ((m = groupRe.exec(format)) !== null) {
|
|
586
|
+
const inner = m[1];
|
|
587
|
+
if (!groupRule.test(inner)) {
|
|
588
|
+
ctx.addIssue({
|
|
589
|
+
code: z15.ZodIssueCode.custom,
|
|
590
|
+
message: `format placeholder "{${inner}}" is not recognized \u2014 expected {current}, {total}, {current:0Nd}, or {total:0Nd}`
|
|
591
|
+
});
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
if (inner.startsWith("current")) sawCurrent = true;
|
|
595
|
+
if (inner.startsWith("total")) sawTotal = true;
|
|
596
|
+
}
|
|
597
|
+
if (!sawCurrent && !sawTotal) {
|
|
598
|
+
ctx.addIssue({
|
|
599
|
+
code: z15.ZodIssueCode.custom,
|
|
600
|
+
message: "format must contain at least one {current} or {total} placeholder"
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
var OverlayHandleRuleSchema = z15.object({
|
|
605
|
+
text: z15.string().min(1),
|
|
606
|
+
anchor: OverlayAnchorSchema,
|
|
607
|
+
margin: z15.number().nonnegative().optional(),
|
|
608
|
+
style: OverlayTextStyleSchema.optional()
|
|
609
|
+
}).strict();
|
|
610
|
+
var OverlayPageNumberRuleSchema = z15.object({
|
|
611
|
+
format: z15.string().superRefine(validatePageNumberFormat),
|
|
612
|
+
anchor: OverlayAnchorSchema,
|
|
613
|
+
margin: z15.number().nonnegative().optional(),
|
|
614
|
+
style: OverlayTextStyleSchema.optional()
|
|
615
|
+
}).strict();
|
|
616
|
+
var OverlayRulesSchema = z15.object({
|
|
617
|
+
handle: OverlayHandleRuleSchema.optional(),
|
|
618
|
+
page_number: OverlayPageNumberRuleSchema.optional()
|
|
619
|
+
}).strict();
|
|
620
|
+
var StudioRecipeSchema = z15.object({
|
|
621
|
+
version: z15.string(),
|
|
622
|
+
name: z15.string(),
|
|
623
|
+
description: z15.string().optional(),
|
|
624
|
+
author: z15.string().optional(),
|
|
625
|
+
tags: z15.array(z15.string()).optional(),
|
|
626
|
+
silence_policy: SilencePolicySchema.optional(),
|
|
627
|
+
caption_style: CaptionStyleSchema.optional(),
|
|
628
|
+
caption_grouping: CaptionGroupingSchema.optional(),
|
|
629
|
+
// Phase 1.5 — first-class overlay rules
|
|
630
|
+
overlay_rules: OverlayRulesSchema.optional(),
|
|
631
|
+
// Reserved — Phase 3 (parse-opaque)
|
|
632
|
+
caption_highlight: z15.unknown().optional(),
|
|
633
|
+
transition_kit: z15.unknown().optional(),
|
|
634
|
+
palette: z15.unknown().optional(),
|
|
635
|
+
audio_policy: z15.unknown().optional(),
|
|
636
|
+
aspect_targets: z15.array(z15.unknown()).optional()
|
|
637
|
+
}).strict();
|
|
638
|
+
var RESERVED_RECIPE_FIELDS = [
|
|
639
|
+
"caption_highlight",
|
|
640
|
+
"transition_kit",
|
|
641
|
+
"palette",
|
|
642
|
+
"audio_policy",
|
|
643
|
+
"aspect_targets"
|
|
644
|
+
];
|
|
495
645
|
function formatErrors(error) {
|
|
496
646
|
return error.issues.map((issue) => ({
|
|
497
647
|
path: issue.path.join(".") || "(root)",
|
|
@@ -505,14 +655,30 @@ function validateDocument(input) {
|
|
|
505
655
|
}
|
|
506
656
|
return { success: false, errors: formatErrors(result.error) };
|
|
507
657
|
}
|
|
658
|
+
function validateRecipe(recipe) {
|
|
659
|
+
const parsed = StudioRecipeSchema.safeParse(recipe);
|
|
660
|
+
if (!parsed.success) {
|
|
661
|
+
return { success: false, errors: formatErrors(parsed.error) };
|
|
662
|
+
}
|
|
663
|
+
const warnings = [];
|
|
664
|
+
const data = parsed.data;
|
|
665
|
+
for (const field of RESERVED_RECIPE_FIELDS) {
|
|
666
|
+
if (data[field] !== void 0) {
|
|
667
|
+
warnings.push(
|
|
668
|
+
`${field} is reserved for Phase 3 and currently has no effect.`
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return { success: true, data, ...warnings.length > 0 && { warnings } };
|
|
673
|
+
}
|
|
508
674
|
function parseAtelier(yamlString) {
|
|
509
675
|
let parsed;
|
|
510
676
|
try {
|
|
511
677
|
parsed = yamlParse(yamlString);
|
|
512
|
-
} catch (
|
|
678
|
+
} catch (err20) {
|
|
513
679
|
return {
|
|
514
680
|
success: false,
|
|
515
|
-
errors: [{ path: "(yaml)", message: `YAML parse error: ${
|
|
681
|
+
errors: [{ path: "(yaml)", message: `YAML parse error: ${err20.message}` }]
|
|
516
682
|
};
|
|
517
683
|
}
|
|
518
684
|
return validateDocument(parsed);
|
|
@@ -538,13 +704,13 @@ function register(server, store) {
|
|
|
538
704
|
"atelier_create",
|
|
539
705
|
"Create a new Atelier animation document with canvas settings",
|
|
540
706
|
{
|
|
541
|
-
name:
|
|
542
|
-
width:
|
|
543
|
-
height:
|
|
544
|
-
fps:
|
|
545
|
-
background:
|
|
546
|
-
description:
|
|
547
|
-
tags:
|
|
707
|
+
name: z16.string().describe("Animation name"),
|
|
708
|
+
width: z16.number().positive().describe("Canvas width in pixels"),
|
|
709
|
+
height: z16.number().positive().describe("Canvas height in pixels"),
|
|
710
|
+
fps: z16.number().positive().int().describe("Frames per second"),
|
|
711
|
+
background: z16.string().optional().describe("Background color (hex string)"),
|
|
712
|
+
description: z16.string().optional().describe("Animation description"),
|
|
713
|
+
tags: z16.array(z16.string()).optional().describe("Tags for categorization")
|
|
548
714
|
},
|
|
549
715
|
{ readOnlyHint: false, destructiveHint: false },
|
|
550
716
|
async ({ name, width, height, fps, background, description, tags }) => {
|
|
@@ -569,7 +735,7 @@ function register(server, store) {
|
|
|
569
735
|
"atelier_info",
|
|
570
736
|
"Get summary information about an Atelier document",
|
|
571
737
|
{
|
|
572
|
-
id:
|
|
738
|
+
id: z16.string().describe("Document ID")
|
|
573
739
|
},
|
|
574
740
|
{ readOnlyHint: true, destructiveHint: false },
|
|
575
741
|
async ({ id }) => {
|
|
@@ -597,8 +763,8 @@ function register(server, store) {
|
|
|
597
763
|
"atelier_load",
|
|
598
764
|
"Load an Atelier document from a YAML string",
|
|
599
765
|
{
|
|
600
|
-
id:
|
|
601
|
-
yaml:
|
|
766
|
+
id: z16.string().optional().describe("Custom document ID (auto-generated if omitted)"),
|
|
767
|
+
yaml: z16.string().describe("YAML string representing an Atelier document")
|
|
602
768
|
},
|
|
603
769
|
{ readOnlyHint: false, destructiveHint: false },
|
|
604
770
|
async ({ id, yaml }) => {
|
|
@@ -614,7 +780,7 @@ function register(server, store) {
|
|
|
614
780
|
"atelier_export",
|
|
615
781
|
"Export an Atelier document as a YAML string",
|
|
616
782
|
{
|
|
617
|
-
id:
|
|
783
|
+
id: z16.string().describe("Document ID")
|
|
618
784
|
},
|
|
619
785
|
{ readOnlyHint: true, destructiveHint: false },
|
|
620
786
|
async ({ id }) => {
|
|
@@ -637,7 +803,7 @@ function register(server, store) {
|
|
|
637
803
|
}
|
|
638
804
|
|
|
639
805
|
// ../mcp/src/tools/layers.ts
|
|
640
|
-
import { z as
|
|
806
|
+
import { z as z17 } from "zod";
|
|
641
807
|
function getDoc2(store, id) {
|
|
642
808
|
const doc = store.get(id);
|
|
643
809
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -649,69 +815,69 @@ function ok2(data) {
|
|
|
649
815
|
function err2(message) {
|
|
650
816
|
return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
|
|
651
817
|
}
|
|
652
|
-
var VisualInputSchema =
|
|
653
|
-
type:
|
|
818
|
+
var VisualInputSchema = z17.object({
|
|
819
|
+
type: z17.enum(["shape", "text", "image", "group", "ref"]).describe("Visual type"),
|
|
654
820
|
// shape visual fields
|
|
655
|
-
shape:
|
|
656
|
-
type:
|
|
657
|
-
cornerRadius:
|
|
658
|
-
points:
|
|
659
|
-
x:
|
|
660
|
-
y:
|
|
661
|
-
in:
|
|
662
|
-
out:
|
|
821
|
+
shape: z17.object({
|
|
822
|
+
type: z17.enum(["rect", "ellipse", "path"]),
|
|
823
|
+
cornerRadius: z17.union([z17.number(), z17.tuple([z17.number(), z17.number(), z17.number(), z17.number()])]).optional(),
|
|
824
|
+
points: z17.array(z17.object({
|
|
825
|
+
x: z17.number(),
|
|
826
|
+
y: z17.number(),
|
|
827
|
+
in: z17.object({ x: z17.number(), y: z17.number() }).optional(),
|
|
828
|
+
out: z17.object({ x: z17.number(), y: z17.number() }).optional()
|
|
663
829
|
})).optional(),
|
|
664
|
-
closed:
|
|
830
|
+
closed: z17.boolean().optional()
|
|
665
831
|
}).optional(),
|
|
666
|
-
fill:
|
|
667
|
-
stroke:
|
|
832
|
+
fill: z17.record(z17.unknown()).optional(),
|
|
833
|
+
stroke: z17.record(z17.unknown()).optional(),
|
|
668
834
|
// text visual fields
|
|
669
|
-
content:
|
|
670
|
-
style:
|
|
835
|
+
content: z17.string().optional(),
|
|
836
|
+
style: z17.record(z17.unknown()).optional(),
|
|
671
837
|
// image visual fields
|
|
672
|
-
assetId:
|
|
673
|
-
sourceRect:
|
|
674
|
-
x:
|
|
675
|
-
y:
|
|
676
|
-
width:
|
|
677
|
-
height:
|
|
838
|
+
assetId: z17.string().optional(),
|
|
839
|
+
sourceRect: z17.object({
|
|
840
|
+
x: z17.number(),
|
|
841
|
+
y: z17.number(),
|
|
842
|
+
width: z17.number().positive(),
|
|
843
|
+
height: z17.number().positive()
|
|
678
844
|
}).optional(),
|
|
679
|
-
spritesheet:
|
|
680
|
-
columns:
|
|
681
|
-
rows:
|
|
682
|
-
frameCount:
|
|
683
|
-
frameWidth:
|
|
684
|
-
frameHeight:
|
|
845
|
+
spritesheet: z17.object({
|
|
846
|
+
columns: z17.number().int().positive(),
|
|
847
|
+
rows: z17.number().int().positive(),
|
|
848
|
+
frameCount: z17.number().int().positive().optional(),
|
|
849
|
+
frameWidth: z17.number().positive(),
|
|
850
|
+
frameHeight: z17.number().positive()
|
|
685
851
|
}).optional(),
|
|
686
|
-
frameIndex:
|
|
852
|
+
frameIndex: z17.number().int().min(0).optional(),
|
|
687
853
|
// ref visual fields
|
|
688
|
-
src:
|
|
689
|
-
state:
|
|
690
|
-
frame:
|
|
854
|
+
src: z17.string().optional(),
|
|
855
|
+
state: z17.string().optional(),
|
|
856
|
+
frame: z17.number().int().min(0).optional()
|
|
691
857
|
}).describe("Visual content definition");
|
|
692
858
|
function register2(server, store) {
|
|
693
859
|
server.tool(
|
|
694
860
|
"atelier_add_layer",
|
|
695
861
|
"Add a new layer to an Atelier document",
|
|
696
862
|
{
|
|
697
|
-
id:
|
|
698
|
-
layerId:
|
|
863
|
+
id: z17.string().describe("Document ID"),
|
|
864
|
+
layerId: z17.string().describe("Unique layer ID"),
|
|
699
865
|
visual: VisualInputSchema.describe("Visual content definition"),
|
|
700
|
-
x:
|
|
701
|
-
y:
|
|
702
|
-
width:
|
|
703
|
-
height:
|
|
704
|
-
description:
|
|
705
|
-
tags:
|
|
706
|
-
opacity:
|
|
707
|
-
rotation:
|
|
708
|
-
parentId:
|
|
709
|
-
anchorPoint:
|
|
710
|
-
scale:
|
|
711
|
-
visible:
|
|
712
|
-
tint:
|
|
713
|
-
color:
|
|
714
|
-
amount:
|
|
866
|
+
x: z17.union([z17.number(), z17.string()]).describe("X position (pixels or percentage)"),
|
|
867
|
+
y: z17.union([z17.number(), z17.string()]).describe("Y position (pixels or percentage)"),
|
|
868
|
+
width: z17.union([z17.number(), z17.string()]).describe("Width (pixels or percentage)"),
|
|
869
|
+
height: z17.union([z17.number(), z17.string()]).describe("Height (pixels or percentage)"),
|
|
870
|
+
description: z17.string().optional().describe("Layer description"),
|
|
871
|
+
tags: z17.array(z17.string()).optional().describe("Tags"),
|
|
872
|
+
opacity: z17.number().min(0).max(1).optional().describe("Opacity 0-1"),
|
|
873
|
+
rotation: z17.number().optional().describe("Rotation in degrees"),
|
|
874
|
+
parentId: z17.string().optional().describe("Parent layer ID for transform inheritance"),
|
|
875
|
+
anchorPoint: z17.object({ x: z17.number(), y: z17.number() }).optional().describe("Anchor point (0-1 normalized)"),
|
|
876
|
+
scale: z17.object({ x: z17.number(), y: z17.number() }).optional().describe("Scale factors"),
|
|
877
|
+
visible: z17.boolean().optional().describe("Whether layer is visible"),
|
|
878
|
+
tint: z17.object({
|
|
879
|
+
color: z17.string().describe("Tint color (hex string)"),
|
|
880
|
+
amount: z17.number().min(0).max(1).describe("Tint amount (0 = none, 1 = full)")
|
|
715
881
|
}).optional().describe("Color tint overlay")
|
|
716
882
|
},
|
|
717
883
|
{ readOnlyHint: false, destructiveHint: false },
|
|
@@ -748,20 +914,20 @@ function register2(server, store) {
|
|
|
748
914
|
"atelier_edit_layer",
|
|
749
915
|
"Edit properties of an existing layer",
|
|
750
916
|
{
|
|
751
|
-
id:
|
|
752
|
-
layerId:
|
|
753
|
-
x:
|
|
754
|
-
y:
|
|
755
|
-
width:
|
|
756
|
-
height:
|
|
757
|
-
description:
|
|
758
|
-
tags:
|
|
759
|
-
opacity:
|
|
760
|
-
rotation:
|
|
761
|
-
parentId:
|
|
762
|
-
anchorPoint:
|
|
763
|
-
scale:
|
|
764
|
-
visible:
|
|
917
|
+
id: z17.string().describe("Document ID"),
|
|
918
|
+
layerId: z17.string().describe("Layer ID to edit"),
|
|
919
|
+
x: z17.union([z17.number(), z17.string()]).optional().describe("New X position"),
|
|
920
|
+
y: z17.union([z17.number(), z17.string()]).optional().describe("New Y position"),
|
|
921
|
+
width: z17.union([z17.number(), z17.string()]).optional().describe("New width"),
|
|
922
|
+
height: z17.union([z17.number(), z17.string()]).optional().describe("New height"),
|
|
923
|
+
description: z17.string().optional().describe("New description"),
|
|
924
|
+
tags: z17.array(z17.string()).optional().describe("New tags"),
|
|
925
|
+
opacity: z17.number().min(0).max(1).optional().describe("New opacity"),
|
|
926
|
+
rotation: z17.number().optional().describe("New rotation"),
|
|
927
|
+
parentId: z17.string().nullable().optional().describe("New parent layer ID (null to clear)"),
|
|
928
|
+
anchorPoint: z17.object({ x: z17.number(), y: z17.number() }).optional().describe("New anchor point"),
|
|
929
|
+
scale: z17.object({ x: z17.number(), y: z17.number() }).optional().describe("New scale"),
|
|
930
|
+
visible: z17.boolean().optional().describe("New visibility")
|
|
765
931
|
},
|
|
766
932
|
{ readOnlyHint: false, destructiveHint: false },
|
|
767
933
|
async ({ id, layerId, x, y, width, height, description, tags, opacity, rotation, parentId, anchorPoint, scale, visible }) => {
|
|
@@ -796,8 +962,8 @@ function register2(server, store) {
|
|
|
796
962
|
"atelier_remove_layer",
|
|
797
963
|
"Remove a layer from an Atelier document",
|
|
798
964
|
{
|
|
799
|
-
id:
|
|
800
|
-
layerId:
|
|
965
|
+
id: z17.string().describe("Document ID"),
|
|
966
|
+
layerId: z17.string().describe("Layer ID to remove")
|
|
801
967
|
},
|
|
802
968
|
{ readOnlyHint: false, destructiveHint: true },
|
|
803
969
|
async ({ id, layerId }) => {
|
|
@@ -825,7 +991,7 @@ function register2(server, store) {
|
|
|
825
991
|
"atelier_list_layers",
|
|
826
992
|
"List all layers in an Atelier document",
|
|
827
993
|
{
|
|
828
|
-
id:
|
|
994
|
+
id: z17.string().describe("Document ID")
|
|
829
995
|
},
|
|
830
996
|
{ readOnlyHint: true, destructiveHint: false },
|
|
831
997
|
async ({ id }) => {
|
|
@@ -852,9 +1018,9 @@ function register2(server, store) {
|
|
|
852
1018
|
"atelier_reorder",
|
|
853
1019
|
"Move a layer to a new position in the layer stack",
|
|
854
1020
|
{
|
|
855
|
-
id:
|
|
856
|
-
layerId:
|
|
857
|
-
position:
|
|
1021
|
+
id: z17.string().describe("Document ID"),
|
|
1022
|
+
layerId: z17.string().describe("Layer ID to move"),
|
|
1023
|
+
position: z17.number().int().min(0).describe("Target position index (0-based)")
|
|
858
1024
|
},
|
|
859
1025
|
{ readOnlyHint: false, destructiveHint: false },
|
|
860
1026
|
async ({ id, layerId, position }) => {
|
|
@@ -872,7 +1038,7 @@ function register2(server, store) {
|
|
|
872
1038
|
}
|
|
873
1039
|
|
|
874
1040
|
// ../mcp/src/tools/shapes.ts
|
|
875
|
-
import { z as
|
|
1041
|
+
import { z as z18 } from "zod";
|
|
876
1042
|
function getDoc3(store, id) {
|
|
877
1043
|
const doc = store.get(id);
|
|
878
1044
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -889,21 +1055,21 @@ function register3(server, store) {
|
|
|
889
1055
|
"atelier_set_shape",
|
|
890
1056
|
"Set the shape on a shape-type layer",
|
|
891
1057
|
{
|
|
892
|
-
id:
|
|
893
|
-
layerId:
|
|
894
|
-
shape:
|
|
895
|
-
type:
|
|
896
|
-
cornerRadius:
|
|
897
|
-
|
|
898
|
-
|
|
1058
|
+
id: z18.string().describe("Document ID"),
|
|
1059
|
+
layerId: z18.string().describe("Layer ID (must be a shape visual)"),
|
|
1060
|
+
shape: z18.object({
|
|
1061
|
+
type: z18.enum(["rect", "ellipse", "path"]).describe("Shape type"),
|
|
1062
|
+
cornerRadius: z18.union([
|
|
1063
|
+
z18.number(),
|
|
1064
|
+
z18.tuple([z18.number(), z18.number(), z18.number(), z18.number()])
|
|
899
1065
|
]).optional().describe("Corner radius (rect only)"),
|
|
900
|
-
points:
|
|
901
|
-
x:
|
|
902
|
-
y:
|
|
903
|
-
in:
|
|
904
|
-
out:
|
|
1066
|
+
points: z18.array(z18.object({
|
|
1067
|
+
x: z18.number(),
|
|
1068
|
+
y: z18.number(),
|
|
1069
|
+
in: z18.object({ x: z18.number(), y: z18.number() }).optional(),
|
|
1070
|
+
out: z18.object({ x: z18.number(), y: z18.number() }).optional()
|
|
905
1071
|
})).optional().describe("Path points (path only)"),
|
|
906
|
-
closed:
|
|
1072
|
+
closed: z18.boolean().optional().describe("Whether path is closed (path only)")
|
|
907
1073
|
}).describe("Shape definition")
|
|
908
1074
|
},
|
|
909
1075
|
{ readOnlyHint: false, destructiveHint: false },
|
|
@@ -922,20 +1088,20 @@ function register3(server, store) {
|
|
|
922
1088
|
"atelier_set_fill",
|
|
923
1089
|
"Set the fill on a shape-type layer",
|
|
924
1090
|
{
|
|
925
|
-
id:
|
|
926
|
-
layerId:
|
|
927
|
-
fill:
|
|
928
|
-
type:
|
|
929
|
-
color:
|
|
930
|
-
angle:
|
|
931
|
-
center:
|
|
932
|
-
x:
|
|
933
|
-
y:
|
|
1091
|
+
id: z18.string().describe("Document ID"),
|
|
1092
|
+
layerId: z18.string().describe("Layer ID (must be a shape visual)"),
|
|
1093
|
+
fill: z18.object({
|
|
1094
|
+
type: z18.enum(["solid", "linear-gradient", "radial-gradient"]).describe("Fill type"),
|
|
1095
|
+
color: z18.unknown().optional().describe("Color for solid fill (hex string or RGBA/HSLA object)"),
|
|
1096
|
+
angle: z18.number().optional().describe("Angle in degrees (linear-gradient only)"),
|
|
1097
|
+
center: z18.object({
|
|
1098
|
+
x: z18.union([z18.number(), z18.string()]),
|
|
1099
|
+
y: z18.union([z18.number(), z18.string()])
|
|
934
1100
|
}).optional().describe("Center point (radial-gradient only)"),
|
|
935
|
-
radius:
|
|
936
|
-
stops:
|
|
937
|
-
offset:
|
|
938
|
-
color:
|
|
1101
|
+
radius: z18.union([z18.number(), z18.string()]).optional().describe("Radius (radial-gradient only)"),
|
|
1102
|
+
stops: z18.array(z18.object({
|
|
1103
|
+
offset: z18.number().min(0).max(1),
|
|
1104
|
+
color: z18.unknown()
|
|
939
1105
|
})).optional().describe("Gradient stops")
|
|
940
1106
|
}).describe("Fill definition")
|
|
941
1107
|
},
|
|
@@ -955,14 +1121,14 @@ function register3(server, store) {
|
|
|
955
1121
|
"atelier_set_stroke",
|
|
956
1122
|
"Set the stroke on a shape-type layer",
|
|
957
1123
|
{
|
|
958
|
-
id:
|
|
959
|
-
layerId:
|
|
960
|
-
stroke:
|
|
961
|
-
color:
|
|
962
|
-
width:
|
|
963
|
-
dash:
|
|
964
|
-
lineCap:
|
|
965
|
-
lineJoin:
|
|
1124
|
+
id: z18.string().describe("Document ID"),
|
|
1125
|
+
layerId: z18.string().describe("Layer ID (must be a shape visual)"),
|
|
1126
|
+
stroke: z18.object({
|
|
1127
|
+
color: z18.unknown().describe("Stroke color (hex string or RGBA/HSLA object)"),
|
|
1128
|
+
width: z18.number().positive().describe("Stroke width in pixels"),
|
|
1129
|
+
dash: z18.array(z18.number()).optional().describe("Dash pattern"),
|
|
1130
|
+
lineCap: z18.enum(["butt", "round", "square"]).optional().describe("Line cap style"),
|
|
1131
|
+
lineJoin: z18.enum(["miter", "round", "bevel"]).optional().describe("Line join style")
|
|
966
1132
|
}).describe("Stroke definition")
|
|
967
1133
|
},
|
|
968
1134
|
{ readOnlyHint: false, destructiveHint: false },
|
|
@@ -980,7 +1146,7 @@ function register3(server, store) {
|
|
|
980
1146
|
}
|
|
981
1147
|
|
|
982
1148
|
// ../mcp/src/tools/states.ts
|
|
983
|
-
import { z as
|
|
1149
|
+
import { z as z19 } from "zod";
|
|
984
1150
|
function getDoc4(store, id) {
|
|
985
1151
|
const doc = store.get(id);
|
|
986
1152
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -997,11 +1163,11 @@ function register4(server, store) {
|
|
|
997
1163
|
"atelier_add_state",
|
|
998
1164
|
"Add a named animation state to a document",
|
|
999
1165
|
{
|
|
1000
|
-
id:
|
|
1001
|
-
stateName:
|
|
1002
|
-
duration:
|
|
1003
|
-
description:
|
|
1004
|
-
tags:
|
|
1166
|
+
id: z19.string().describe("Document ID"),
|
|
1167
|
+
stateName: z19.string().describe("State name (e.g. 'intro', 'hover', 'exit')"),
|
|
1168
|
+
duration: z19.number().positive().int().describe("Duration in frames"),
|
|
1169
|
+
description: z19.string().optional().describe("State description"),
|
|
1170
|
+
tags: z19.array(z19.string()).optional().describe("Tags")
|
|
1005
1171
|
},
|
|
1006
1172
|
{ readOnlyHint: false, destructiveHint: false },
|
|
1007
1173
|
async ({ id, stateName, duration, description, tags }) => {
|
|
@@ -1024,11 +1190,11 @@ function register4(server, store) {
|
|
|
1024
1190
|
"atelier_edit_state",
|
|
1025
1191
|
"Edit metadata of an existing animation state",
|
|
1026
1192
|
{
|
|
1027
|
-
id:
|
|
1028
|
-
stateName:
|
|
1029
|
-
duration:
|
|
1030
|
-
description:
|
|
1031
|
-
tags:
|
|
1193
|
+
id: z19.string().describe("Document ID"),
|
|
1194
|
+
stateName: z19.string().describe("State name to edit"),
|
|
1195
|
+
duration: z19.number().positive().int().optional().describe("New duration in frames"),
|
|
1196
|
+
description: z19.string().optional().describe("New description"),
|
|
1197
|
+
tags: z19.array(z19.string()).optional().describe("New tags")
|
|
1032
1198
|
},
|
|
1033
1199
|
{ readOnlyHint: false, destructiveHint: false },
|
|
1034
1200
|
async ({ id, stateName, duration, description, tags }) => {
|
|
@@ -1047,8 +1213,8 @@ function register4(server, store) {
|
|
|
1047
1213
|
"atelier_remove_state",
|
|
1048
1214
|
"Remove an animation state and all its deltas",
|
|
1049
1215
|
{
|
|
1050
|
-
id:
|
|
1051
|
-
stateName:
|
|
1216
|
+
id: z19.string().describe("Document ID"),
|
|
1217
|
+
stateName: z19.string().describe("State name to remove")
|
|
1052
1218
|
},
|
|
1053
1219
|
{ readOnlyHint: false, destructiveHint: true },
|
|
1054
1220
|
async ({ id, stateName }) => {
|
|
@@ -1065,7 +1231,7 @@ function register4(server, store) {
|
|
|
1065
1231
|
"atelier_list_states",
|
|
1066
1232
|
"List all animation states in a document",
|
|
1067
1233
|
{
|
|
1068
|
-
id:
|
|
1234
|
+
id: z19.string().describe("Document ID")
|
|
1069
1235
|
},
|
|
1070
1236
|
{ readOnlyHint: true, destructiveHint: false },
|
|
1071
1237
|
async ({ id }) => {
|
|
@@ -1085,7 +1251,7 @@ function register4(server, store) {
|
|
|
1085
1251
|
}
|
|
1086
1252
|
|
|
1087
1253
|
// ../mcp/src/tools/deltas.ts
|
|
1088
|
-
import { z as
|
|
1254
|
+
import { z as z20 } from "zod";
|
|
1089
1255
|
|
|
1090
1256
|
// ../math/dist/index.js
|
|
1091
1257
|
function linear(t) {
|
|
@@ -1824,17 +1990,17 @@ function register5(server, store) {
|
|
|
1824
1990
|
"atelier_add_delta",
|
|
1825
1991
|
"Add an animation delta (keyframe transition) to a state",
|
|
1826
1992
|
{
|
|
1827
|
-
id:
|
|
1828
|
-
stateName:
|
|
1829
|
-
layer:
|
|
1993
|
+
id: z20.string().describe("Document ID"),
|
|
1994
|
+
stateName: z20.string().describe("State name"),
|
|
1995
|
+
layer: z20.string().describe("Target layer ID"),
|
|
1830
1996
|
property: AnimatablePropertyEnum.describe("Property to animate"),
|
|
1831
|
-
range:
|
|
1832
|
-
from:
|
|
1833
|
-
to:
|
|
1997
|
+
range: z20.tuple([z20.number().int().min(0), z20.number().int().min(0)]).describe("Frame range [start, end] inclusive"),
|
|
1998
|
+
from: z20.unknown().describe("Starting value"),
|
|
1999
|
+
to: z20.unknown().describe("Ending value"),
|
|
1834
2000
|
easing: EasingInputSchema.optional().describe("Easing function"),
|
|
1835
|
-
description:
|
|
1836
|
-
tags:
|
|
1837
|
-
deltaId:
|
|
2001
|
+
description: z20.string().optional().describe("Delta description"),
|
|
2002
|
+
tags: z20.array(z20.string()).optional().describe("Tags"),
|
|
2003
|
+
deltaId: z20.string().optional().describe("Custom delta ID")
|
|
1838
2004
|
},
|
|
1839
2005
|
{ readOnlyHint: false, destructiveHint: false },
|
|
1840
2006
|
async ({ id, stateName, layer, property, range, from, to, easing, description, tags, deltaId }) => {
|
|
@@ -1872,17 +2038,17 @@ function register5(server, store) {
|
|
|
1872
2038
|
"atelier_edit_delta",
|
|
1873
2039
|
"Edit an existing delta by index within a state",
|
|
1874
2040
|
{
|
|
1875
|
-
id:
|
|
1876
|
-
stateName:
|
|
1877
|
-
deltaIndex:
|
|
1878
|
-
layer:
|
|
2041
|
+
id: z20.string().describe("Document ID"),
|
|
2042
|
+
stateName: z20.string().describe("State name"),
|
|
2043
|
+
deltaIndex: z20.number().int().min(0).describe("Delta index within the state"),
|
|
2044
|
+
layer: z20.string().optional().describe("New target layer ID"),
|
|
1879
2045
|
property: AnimatablePropertyEnum.optional().describe("New property to animate"),
|
|
1880
|
-
range:
|
|
1881
|
-
from:
|
|
1882
|
-
to:
|
|
2046
|
+
range: z20.tuple([z20.number().int().min(0), z20.number().int().min(0)]).optional().describe("New frame range [start, end]"),
|
|
2047
|
+
from: z20.unknown().optional().describe("New starting value"),
|
|
2048
|
+
to: z20.unknown().optional().describe("New ending value"),
|
|
1883
2049
|
easing: EasingInputSchema.optional().describe("New easing function"),
|
|
1884
|
-
description:
|
|
1885
|
-
tags:
|
|
2050
|
+
description: z20.string().optional().describe("New description"),
|
|
2051
|
+
tags: z20.array(z20.string()).optional().describe("New tags")
|
|
1886
2052
|
},
|
|
1887
2053
|
{ readOnlyHint: false, destructiveHint: false },
|
|
1888
2054
|
async ({ id, stateName, deltaIndex, layer, property, range, from, to, easing, description, tags }) => {
|
|
@@ -1921,9 +2087,9 @@ function register5(server, store) {
|
|
|
1921
2087
|
"atelier_remove_delta",
|
|
1922
2088
|
"Remove a delta by index from a state",
|
|
1923
2089
|
{
|
|
1924
|
-
id:
|
|
1925
|
-
stateName:
|
|
1926
|
-
deltaIndex:
|
|
2090
|
+
id: z20.string().describe("Document ID"),
|
|
2091
|
+
stateName: z20.string().describe("State name"),
|
|
2092
|
+
deltaIndex: z20.number().int().min(0).describe("Delta index to remove")
|
|
1927
2093
|
},
|
|
1928
2094
|
{ readOnlyHint: false, destructiveHint: true },
|
|
1929
2095
|
async ({ id, stateName, deltaIndex }) => {
|
|
@@ -1951,12 +2117,12 @@ function register5(server, store) {
|
|
|
1951
2117
|
"atelier_apply_preset",
|
|
1952
2118
|
"Apply a preset to a layer, expanding it into concrete deltas",
|
|
1953
2119
|
{
|
|
1954
|
-
id:
|
|
1955
|
-
stateName:
|
|
1956
|
-
presetName:
|
|
1957
|
-
layerId:
|
|
1958
|
-
startFrame:
|
|
1959
|
-
duration:
|
|
2120
|
+
id: z20.string().describe("Document ID"),
|
|
2121
|
+
stateName: z20.string().describe("State name to add deltas to"),
|
|
2122
|
+
presetName: z20.string().describe("Preset name defined in the document"),
|
|
2123
|
+
layerId: z20.string().describe("Target layer ID"),
|
|
2124
|
+
startFrame: z20.number().int().min(0).optional().describe("Start frame (default: 0)"),
|
|
2125
|
+
duration: z20.number().positive().int().optional().describe("Duration for preset (default: state duration)")
|
|
1960
2126
|
},
|
|
1961
2127
|
{ readOnlyHint: false, destructiveHint: false },
|
|
1962
2128
|
async ({ id, stateName, presetName, layerId, startFrame, duration }) => {
|
|
@@ -1999,7 +2165,7 @@ ${errors.join("\n")}`);
|
|
|
1999
2165
|
}
|
|
2000
2166
|
|
|
2001
2167
|
// ../mcp/src/tools/presets.ts
|
|
2002
|
-
import { z as
|
|
2168
|
+
import { z as z21 } from "zod";
|
|
2003
2169
|
function getDoc6(store, id) {
|
|
2004
2170
|
const doc = store.get(id);
|
|
2005
2171
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -2013,11 +2179,11 @@ function err6(message) {
|
|
|
2013
2179
|
}
|
|
2014
2180
|
var AnimatablePropertyEnum2 = AnimatablePropertySchema;
|
|
2015
2181
|
var EasingInputSchema2 = EasingSchema;
|
|
2016
|
-
var PresetDeltaSchema2 =
|
|
2182
|
+
var PresetDeltaSchema2 = z21.object({
|
|
2017
2183
|
property: AnimatablePropertyEnum2.describe("Animatable property"),
|
|
2018
|
-
offset:
|
|
2019
|
-
from:
|
|
2020
|
-
to:
|
|
2184
|
+
offset: z21.tuple([z21.number().int().min(0), z21.number().int().min(0)]).optional().describe("Relative frame offset [start, end]"),
|
|
2185
|
+
from: z21.unknown().describe("Starting value"),
|
|
2186
|
+
to: z21.unknown().describe("Ending value"),
|
|
2021
2187
|
easing: EasingInputSchema2.optional().describe("Easing function")
|
|
2022
2188
|
});
|
|
2023
2189
|
function register6(server, store) {
|
|
@@ -2025,11 +2191,11 @@ function register6(server, store) {
|
|
|
2025
2191
|
"atelier_define_preset",
|
|
2026
2192
|
"Define a reusable animation preset on a document",
|
|
2027
2193
|
{
|
|
2028
|
-
id:
|
|
2029
|
-
presetName:
|
|
2030
|
-
description:
|
|
2031
|
-
tags:
|
|
2032
|
-
deltas:
|
|
2194
|
+
id: z21.string().describe("Document ID"),
|
|
2195
|
+
presetName: z21.string().describe("Preset name"),
|
|
2196
|
+
description: z21.string().optional().describe("Preset description"),
|
|
2197
|
+
tags: z21.array(z21.string()).optional().describe("Tags"),
|
|
2198
|
+
deltas: z21.array(PresetDeltaSchema2).min(1).describe("Array of preset delta definitions")
|
|
2033
2199
|
},
|
|
2034
2200
|
{ readOnlyHint: false, destructiveHint: false },
|
|
2035
2201
|
async ({ id, presetName, description, tags, deltas }) => {
|
|
@@ -2050,7 +2216,7 @@ function register6(server, store) {
|
|
|
2050
2216
|
"atelier_list_presets",
|
|
2051
2217
|
"List all presets defined on a document",
|
|
2052
2218
|
{
|
|
2053
|
-
id:
|
|
2219
|
+
id: z21.string().describe("Document ID")
|
|
2054
2220
|
},
|
|
2055
2221
|
{ readOnlyHint: true, destructiveHint: false },
|
|
2056
2222
|
async ({ id }) => {
|
|
@@ -2073,7 +2239,7 @@ function register6(server, store) {
|
|
|
2073
2239
|
}
|
|
2074
2240
|
|
|
2075
2241
|
// ../mcp/src/tools/preview.ts
|
|
2076
|
-
import { z as
|
|
2242
|
+
import { z as z22 } from "zod";
|
|
2077
2243
|
function getDoc7(store, id) {
|
|
2078
2244
|
const doc = store.get(id);
|
|
2079
2245
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -2090,7 +2256,7 @@ function register7(server, store) {
|
|
|
2090
2256
|
"atelier_validate",
|
|
2091
2257
|
"Validate an Atelier document for schema correctness and delta overlaps",
|
|
2092
2258
|
{
|
|
2093
|
-
id:
|
|
2259
|
+
id: z22.string().describe("Document ID")
|
|
2094
2260
|
},
|
|
2095
2261
|
{ readOnlyHint: true, destructiveHint: false },
|
|
2096
2262
|
async ({ id }) => {
|
|
@@ -2119,9 +2285,9 @@ function register7(server, store) {
|
|
|
2119
2285
|
"atelier_preview",
|
|
2120
2286
|
"Preview the resolved state of all layers at a specific frame",
|
|
2121
2287
|
{
|
|
2122
|
-
id:
|
|
2123
|
-
stateName:
|
|
2124
|
-
frame:
|
|
2288
|
+
id: z22.string().describe("Document ID"),
|
|
2289
|
+
stateName: z22.string().describe("State name to preview"),
|
|
2290
|
+
frame: z22.number().int().min(0).describe("Frame number to resolve")
|
|
2125
2291
|
},
|
|
2126
2292
|
{ readOnlyHint: true, destructiveHint: false },
|
|
2127
2293
|
async ({ id, stateName, frame }) => {
|
|
@@ -2156,7 +2322,7 @@ function register7(server, store) {
|
|
|
2156
2322
|
}
|
|
2157
2323
|
|
|
2158
2324
|
// ../mcp/src/tools/templates.ts
|
|
2159
|
-
import { z as
|
|
2325
|
+
import { z as z23 } from "zod";
|
|
2160
2326
|
function getDoc8(store, id) {
|
|
2161
2327
|
const doc = store.get(id);
|
|
2162
2328
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -2173,8 +2339,8 @@ function register8(server, store) {
|
|
|
2173
2339
|
"atelier_instantiate_template",
|
|
2174
2340
|
"Instantiate a template document with variable bindings. Creates a new document in the store.",
|
|
2175
2341
|
{
|
|
2176
|
-
id:
|
|
2177
|
-
bindings:
|
|
2342
|
+
id: z23.string().describe("Template document ID"),
|
|
2343
|
+
bindings: z23.record(z23.unknown()).describe("Variable bindings: { variableName: value }")
|
|
2178
2344
|
},
|
|
2179
2345
|
{ readOnlyHint: false, destructiveHint: false },
|
|
2180
2346
|
async ({ id, bindings }) => {
|
|
@@ -2197,7 +2363,7 @@ function register8(server, store) {
|
|
|
2197
2363
|
"atelier_find_variables",
|
|
2198
2364
|
"Scan a document for {{variableName}} patterns. Returns the list of variable references found.",
|
|
2199
2365
|
{
|
|
2200
|
-
id:
|
|
2366
|
+
id: z23.string().describe("Document ID")
|
|
2201
2367
|
},
|
|
2202
2368
|
{ readOnlyHint: true, destructiveHint: false },
|
|
2203
2369
|
async ({ id }) => {
|
|
@@ -2217,7 +2383,7 @@ function register8(server, store) {
|
|
|
2217
2383
|
}
|
|
2218
2384
|
|
|
2219
2385
|
// ../mcp/src/tools/layer-effects.ts
|
|
2220
|
-
import { z as
|
|
2386
|
+
import { z as z24 } from "zod";
|
|
2221
2387
|
function getDoc9(store, id) {
|
|
2222
2388
|
const doc = store.get(id);
|
|
2223
2389
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -2234,9 +2400,9 @@ function register9(server, store) {
|
|
|
2234
2400
|
"atelier_set_blend_mode",
|
|
2235
2401
|
"Set or clear the blend mode on a layer",
|
|
2236
2402
|
{
|
|
2237
|
-
id:
|
|
2238
|
-
layerId:
|
|
2239
|
-
blendMode:
|
|
2403
|
+
id: z24.string().describe("Document ID"),
|
|
2404
|
+
layerId: z24.string().describe("Layer ID"),
|
|
2405
|
+
blendMode: z24.enum([
|
|
2240
2406
|
"normal",
|
|
2241
2407
|
"multiply",
|
|
2242
2408
|
"screen",
|
|
@@ -2274,13 +2440,13 @@ function register9(server, store) {
|
|
|
2274
2440
|
"atelier_set_shadow",
|
|
2275
2441
|
"Set or remove a drop shadow on a layer",
|
|
2276
2442
|
{
|
|
2277
|
-
id:
|
|
2278
|
-
layerId:
|
|
2279
|
-
shadow:
|
|
2280
|
-
color:
|
|
2281
|
-
blur:
|
|
2282
|
-
offsetX:
|
|
2283
|
-
offsetY:
|
|
2443
|
+
id: z24.string().describe("Document ID"),
|
|
2444
|
+
layerId: z24.string().describe("Layer ID"),
|
|
2445
|
+
shadow: z24.object({
|
|
2446
|
+
color: z24.unknown().describe("Shadow color (hex string or RGBA/HSLA object)"),
|
|
2447
|
+
blur: z24.number().min(0).describe("Blur radius in pixels"),
|
|
2448
|
+
offsetX: z24.number().optional().describe("Horizontal offset in pixels"),
|
|
2449
|
+
offsetY: z24.number().optional().describe("Vertical offset in pixels")
|
|
2284
2450
|
}).nullable().describe("Shadow definition, or null to remove")
|
|
2285
2451
|
},
|
|
2286
2452
|
{ readOnlyHint: false, destructiveHint: false },
|
|
@@ -2302,11 +2468,11 @@ function register9(server, store) {
|
|
|
2302
2468
|
"atelier_set_tint",
|
|
2303
2469
|
"Set or remove a color tint on a layer (works on any visual type)",
|
|
2304
2470
|
{
|
|
2305
|
-
id:
|
|
2306
|
-
layerId:
|
|
2307
|
-
tint:
|
|
2308
|
-
color:
|
|
2309
|
-
amount:
|
|
2471
|
+
id: z24.string().describe("Document ID"),
|
|
2472
|
+
layerId: z24.string().describe("Layer ID"),
|
|
2473
|
+
tint: z24.object({
|
|
2474
|
+
color: z24.string().describe("Tint color (hex string)"),
|
|
2475
|
+
amount: z24.number().min(0).max(1).describe("Tint amount (0 = none, 1 = full)")
|
|
2310
2476
|
}).nullable().describe("Tint definition, or null to remove")
|
|
2311
2477
|
},
|
|
2312
2478
|
{ readOnlyHint: false, destructiveHint: false },
|
|
@@ -2328,18 +2494,18 @@ function register9(server, store) {
|
|
|
2328
2494
|
"atelier_set_motion_path",
|
|
2329
2495
|
"Set or remove a motion path on a layer for path-based position animation",
|
|
2330
2496
|
{
|
|
2331
|
-
id:
|
|
2332
|
-
layerId:
|
|
2333
|
-
motionPath:
|
|
2334
|
-
points:
|
|
2335
|
-
x:
|
|
2336
|
-
y:
|
|
2337
|
-
in:
|
|
2338
|
-
out:
|
|
2497
|
+
id: z24.string().describe("Document ID"),
|
|
2498
|
+
layerId: z24.string().describe("Layer ID"),
|
|
2499
|
+
motionPath: z24.object({
|
|
2500
|
+
points: z24.array(z24.object({
|
|
2501
|
+
x: z24.number(),
|
|
2502
|
+
y: z24.number(),
|
|
2503
|
+
in: z24.object({ x: z24.number(), y: z24.number() }).optional(),
|
|
2504
|
+
out: z24.object({ x: z24.number(), y: z24.number() }).optional()
|
|
2339
2505
|
})).min(2).describe("Path points (minimum 2)"),
|
|
2340
|
-
closed:
|
|
2341
|
-
autoRotate:
|
|
2342
|
-
autoRotateOffset:
|
|
2506
|
+
closed: z24.boolean().optional().describe("Whether the path is closed"),
|
|
2507
|
+
autoRotate: z24.boolean().optional().describe("Auto-rotate layer to follow path tangent"),
|
|
2508
|
+
autoRotateOffset: z24.number().optional().describe("Rotation offset in degrees when autoRotate is enabled")
|
|
2343
2509
|
}).nullable().describe("Motion path definition, or null to remove")
|
|
2344
2510
|
},
|
|
2345
2511
|
{ readOnlyHint: false, destructiveHint: false },
|
|
@@ -2361,21 +2527,21 @@ function register9(server, store) {
|
|
|
2361
2527
|
"atelier_set_clip_path",
|
|
2362
2528
|
"Set or remove a clip path on a layer to restrict rendering to within a shape",
|
|
2363
2529
|
{
|
|
2364
|
-
id:
|
|
2365
|
-
layerId:
|
|
2366
|
-
clipPath:
|
|
2367
|
-
type:
|
|
2368
|
-
cornerRadius:
|
|
2369
|
-
|
|
2370
|
-
|
|
2530
|
+
id: z24.string().describe("Document ID"),
|
|
2531
|
+
layerId: z24.string().describe("Layer ID"),
|
|
2532
|
+
clipPath: z24.object({
|
|
2533
|
+
type: z24.enum(["rect", "ellipse", "path"]).describe("Clip shape type"),
|
|
2534
|
+
cornerRadius: z24.union([
|
|
2535
|
+
z24.number(),
|
|
2536
|
+
z24.tuple([z24.number(), z24.number(), z24.number(), z24.number()])
|
|
2371
2537
|
]).optional().describe("Corner radius (rect only)"),
|
|
2372
|
-
points:
|
|
2373
|
-
x:
|
|
2374
|
-
y:
|
|
2375
|
-
in:
|
|
2376
|
-
out:
|
|
2538
|
+
points: z24.array(z24.object({
|
|
2539
|
+
x: z24.number(),
|
|
2540
|
+
y: z24.number(),
|
|
2541
|
+
in: z24.object({ x: z24.number(), y: z24.number() }).optional(),
|
|
2542
|
+
out: z24.object({ x: z24.number(), y: z24.number() }).optional()
|
|
2377
2543
|
})).optional().describe("Path points (path only)"),
|
|
2378
|
-
closed:
|
|
2544
|
+
closed: z24.boolean().optional().describe("Whether path is closed (path only)")
|
|
2379
2545
|
}).nullable().describe("Clip path shape, or null to remove")
|
|
2380
2546
|
},
|
|
2381
2547
|
{ readOnlyHint: false, destructiveHint: false },
|
|
@@ -2397,45 +2563,45 @@ function register9(server, store) {
|
|
|
2397
2563
|
"atelier_edit_visual",
|
|
2398
2564
|
"Edit visual content of an existing layer (text content/style, image src/assetId, shape/fill/stroke)",
|
|
2399
2565
|
{
|
|
2400
|
-
id:
|
|
2401
|
-
layerId:
|
|
2566
|
+
id: z24.string().describe("Document ID"),
|
|
2567
|
+
layerId: z24.string().describe("Layer ID"),
|
|
2402
2568
|
// Text fields
|
|
2403
|
-
content:
|
|
2404
|
-
style:
|
|
2569
|
+
content: z24.string().optional().describe("New text content (text layers only)"),
|
|
2570
|
+
style: z24.record(z24.unknown()).optional().describe("Partial style update \u2014 only provided keys are overwritten (text layers only)"),
|
|
2405
2571
|
// Image fields
|
|
2406
|
-
src:
|
|
2407
|
-
assetId:
|
|
2408
|
-
sourceRect:
|
|
2409
|
-
x:
|
|
2410
|
-
y:
|
|
2411
|
-
width:
|
|
2412
|
-
height:
|
|
2572
|
+
src: z24.string().optional().describe("Image source URL (image layers only)"),
|
|
2573
|
+
assetId: z24.string().optional().describe("Asset ID reference (image layers only)"),
|
|
2574
|
+
sourceRect: z24.object({
|
|
2575
|
+
x: z24.number(),
|
|
2576
|
+
y: z24.number(),
|
|
2577
|
+
width: z24.number().positive(),
|
|
2578
|
+
height: z24.number().positive()
|
|
2413
2579
|
}).nullable().optional().describe("Source rectangle crop (image layers only). Null to remove."),
|
|
2414
|
-
spritesheet:
|
|
2415
|
-
columns:
|
|
2416
|
-
rows:
|
|
2417
|
-
frameCount:
|
|
2418
|
-
frameWidth:
|
|
2419
|
-
frameHeight:
|
|
2580
|
+
spritesheet: z24.object({
|
|
2581
|
+
columns: z24.number().int().positive(),
|
|
2582
|
+
rows: z24.number().int().positive(),
|
|
2583
|
+
frameCount: z24.number().int().positive().optional(),
|
|
2584
|
+
frameWidth: z24.number().positive(),
|
|
2585
|
+
frameHeight: z24.number().positive()
|
|
2420
2586
|
}).nullable().optional().describe("Spritesheet config (image layers only). Null to remove."),
|
|
2421
|
-
frameIndex:
|
|
2587
|
+
frameIndex: z24.number().int().min(0).optional().describe("Spritesheet frame index (image layers only)"),
|
|
2422
2588
|
// Shape fields
|
|
2423
|
-
shape:
|
|
2424
|
-
type:
|
|
2425
|
-
cornerRadius:
|
|
2426
|
-
|
|
2427
|
-
|
|
2589
|
+
shape: z24.object({
|
|
2590
|
+
type: z24.enum(["rect", "ellipse", "path"]),
|
|
2591
|
+
cornerRadius: z24.union([
|
|
2592
|
+
z24.number(),
|
|
2593
|
+
z24.tuple([z24.number(), z24.number(), z24.number(), z24.number()])
|
|
2428
2594
|
]).optional(),
|
|
2429
|
-
points:
|
|
2430
|
-
x:
|
|
2431
|
-
y:
|
|
2432
|
-
in:
|
|
2433
|
-
out:
|
|
2595
|
+
points: z24.array(z24.object({
|
|
2596
|
+
x: z24.number(),
|
|
2597
|
+
y: z24.number(),
|
|
2598
|
+
in: z24.object({ x: z24.number(), y: z24.number() }).optional(),
|
|
2599
|
+
out: z24.object({ x: z24.number(), y: z24.number() }).optional()
|
|
2434
2600
|
})).optional(),
|
|
2435
|
-
closed:
|
|
2601
|
+
closed: z24.boolean().optional()
|
|
2436
2602
|
}).optional().describe("New shape (shape layers only)"),
|
|
2437
|
-
fill:
|
|
2438
|
-
stroke:
|
|
2603
|
+
fill: z24.record(z24.unknown()).optional().describe("New fill definition (shape layers only)"),
|
|
2604
|
+
stroke: z24.record(z24.unknown()).optional().describe("New stroke definition (shape layers only)")
|
|
2439
2605
|
},
|
|
2440
2606
|
{ readOnlyHint: false, destructiveHint: false },
|
|
2441
2607
|
async ({ id, layerId, content, style, src, assetId, sourceRect, spritesheet, frameIndex, shape, fill, stroke }) => {
|
|
@@ -2516,7 +2682,7 @@ function register9(server, store) {
|
|
|
2516
2682
|
}
|
|
2517
2683
|
|
|
2518
2684
|
// ../mcp/src/tools/state-config.ts
|
|
2519
|
-
import { z as
|
|
2685
|
+
import { z as z25 } from "zod";
|
|
2520
2686
|
function getDoc10(store, id) {
|
|
2521
2687
|
const doc = store.get(id);
|
|
2522
2688
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -2528,27 +2694,27 @@ function ok10(data) {
|
|
|
2528
2694
|
function err10(message) {
|
|
2529
2695
|
return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
|
|
2530
2696
|
}
|
|
2531
|
-
var EasingInputSchema3 =
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
type:
|
|
2536
|
-
x1:
|
|
2537
|
-
y1:
|
|
2538
|
-
x2:
|
|
2539
|
-
y2:
|
|
2697
|
+
var EasingInputSchema3 = z25.union([
|
|
2698
|
+
z25.enum(["ease-in", "ease-out", "ease-in-out"]),
|
|
2699
|
+
z25.object({ type: z25.literal("linear") }),
|
|
2700
|
+
z25.object({
|
|
2701
|
+
type: z25.literal("cubic-bezier"),
|
|
2702
|
+
x1: z25.number(),
|
|
2703
|
+
y1: z25.number(),
|
|
2704
|
+
x2: z25.number(),
|
|
2705
|
+
y2: z25.number()
|
|
2540
2706
|
}),
|
|
2541
|
-
|
|
2542
|
-
type:
|
|
2543
|
-
mass:
|
|
2544
|
-
stiffness:
|
|
2545
|
-
damping:
|
|
2546
|
-
velocity:
|
|
2707
|
+
z25.object({
|
|
2708
|
+
type: z25.literal("spring"),
|
|
2709
|
+
mass: z25.number().optional(),
|
|
2710
|
+
stiffness: z25.number().optional(),
|
|
2711
|
+
damping: z25.number().optional(),
|
|
2712
|
+
velocity: z25.number().optional()
|
|
2547
2713
|
}),
|
|
2548
|
-
|
|
2549
|
-
type:
|
|
2550
|
-
steps:
|
|
2551
|
-
position:
|
|
2714
|
+
z25.object({
|
|
2715
|
+
type: z25.literal("step"),
|
|
2716
|
+
steps: z25.number().int().positive(),
|
|
2717
|
+
position: z25.enum(["start", "end"]).optional()
|
|
2552
2718
|
})
|
|
2553
2719
|
]).describe("Easing function");
|
|
2554
2720
|
function register10(server, store) {
|
|
@@ -2556,14 +2722,14 @@ function register10(server, store) {
|
|
|
2556
2722
|
"atelier_set_audio",
|
|
2557
2723
|
"Set or remove an audio track on a state",
|
|
2558
2724
|
{
|
|
2559
|
-
id:
|
|
2560
|
-
stateName:
|
|
2561
|
-
audio:
|
|
2562
|
-
src:
|
|
2563
|
-
offset:
|
|
2564
|
-
volume:
|
|
2565
|
-
loop:
|
|
2566
|
-
startFrame:
|
|
2725
|
+
id: z25.string().describe("Document ID"),
|
|
2726
|
+
stateName: z25.string().describe("State name"),
|
|
2727
|
+
audio: z25.object({
|
|
2728
|
+
src: z25.string().describe("Audio source \u2014 asset reference, file path, or URL"),
|
|
2729
|
+
offset: z25.number().min(0).optional().describe("Skip into audio in seconds (default: 0)"),
|
|
2730
|
+
volume: z25.number().min(0).max(1).optional().describe("Playback volume 0\u20131 (default: 1)"),
|
|
2731
|
+
loop: z25.boolean().optional().describe("Loop for state duration (default: false)"),
|
|
2732
|
+
startFrame: z25.number().int().min(0).optional().describe("Frame to start playing (default: 0)")
|
|
2567
2733
|
}).nullable().describe("Audio configuration, or null to remove")
|
|
2568
2734
|
},
|
|
2569
2735
|
{ readOnlyHint: false, destructiveHint: false },
|
|
@@ -2585,11 +2751,11 @@ function register10(server, store) {
|
|
|
2585
2751
|
"atelier_configure_transition",
|
|
2586
2752
|
"Configure or remove a transition from one state to a target state",
|
|
2587
2753
|
{
|
|
2588
|
-
id:
|
|
2589
|
-
stateName:
|
|
2590
|
-
targetState:
|
|
2591
|
-
transition:
|
|
2592
|
-
duration:
|
|
2754
|
+
id: z25.string().describe("Document ID"),
|
|
2755
|
+
stateName: z25.string().describe("Source state name"),
|
|
2756
|
+
targetState: z25.string().describe("Target state name"),
|
|
2757
|
+
transition: z25.object({
|
|
2758
|
+
duration: z25.number().positive().int().describe("Transition duration in frames"),
|
|
2593
2759
|
easing: EasingInputSchema3.optional()
|
|
2594
2760
|
}).nullable().describe("Transition config, or null to remove")
|
|
2595
2761
|
},
|
|
@@ -2626,9 +2792,9 @@ function register10(server, store) {
|
|
|
2626
2792
|
"atelier_set_state_parent",
|
|
2627
2793
|
"Set or clear the parent state for hierarchical delta inheritance",
|
|
2628
2794
|
{
|
|
2629
|
-
id:
|
|
2630
|
-
stateName:
|
|
2631
|
-
parent:
|
|
2795
|
+
id: z25.string().describe("Document ID"),
|
|
2796
|
+
stateName: z25.string().describe("State name"),
|
|
2797
|
+
parent: z25.string().nullable().describe("Parent state name, or null to clear")
|
|
2632
2798
|
},
|
|
2633
2799
|
{ readOnlyHint: false, destructiveHint: false },
|
|
2634
2800
|
async ({ id, stateName, parent }) => {
|
|
@@ -2663,7 +2829,7 @@ function register10(server, store) {
|
|
|
2663
2829
|
}
|
|
2664
2830
|
|
|
2665
2831
|
// ../mcp/src/tools/export.ts
|
|
2666
|
-
import { z as
|
|
2832
|
+
import { z as z26 } from "zod";
|
|
2667
2833
|
|
|
2668
2834
|
// ../canvas/dist/index.js
|
|
2669
2835
|
function colorToCSS(color) {
|
|
@@ -3848,12 +4014,12 @@ function register11(server, store) {
|
|
|
3848
4014
|
"atelier_export_svg",
|
|
3849
4015
|
"Export a frame as an SVG string",
|
|
3850
4016
|
{
|
|
3851
|
-
id:
|
|
3852
|
-
stateName:
|
|
3853
|
-
frame:
|
|
3854
|
-
xmlDeclaration:
|
|
3855
|
-
viewBox:
|
|
3856
|
-
indent:
|
|
4017
|
+
id: z26.string().describe("Document ID"),
|
|
4018
|
+
stateName: z26.string().optional().describe("State name (defaults to first state)"),
|
|
4019
|
+
frame: z26.number().int().min(0).optional().describe("Frame number (defaults to 0)"),
|
|
4020
|
+
xmlDeclaration: z26.boolean().optional().describe("Include XML declaration (default: false)"),
|
|
4021
|
+
viewBox: z26.boolean().optional().describe("Include viewBox attribute (default: true)"),
|
|
4022
|
+
indent: z26.number().int().min(0).optional().describe("Indent size in spaces (default: 2)")
|
|
3857
4023
|
},
|
|
3858
4024
|
{ readOnlyHint: true, destructiveHint: false },
|
|
3859
4025
|
async ({ id, stateName, frame, xmlDeclaration, viewBox, indent }) => {
|
|
@@ -3884,8 +4050,8 @@ function register11(server, store) {
|
|
|
3884
4050
|
"atelier_export_lottie",
|
|
3885
4051
|
"Export a document to Lottie JSON format (lossy, best-effort)",
|
|
3886
4052
|
{
|
|
3887
|
-
id:
|
|
3888
|
-
stateName:
|
|
4053
|
+
id: z26.string().describe("Document ID"),
|
|
4054
|
+
stateName: z26.string().optional().describe("State to export (defaults to first state)")
|
|
3889
4055
|
},
|
|
3890
4056
|
{ readOnlyHint: true, destructiveHint: false },
|
|
3891
4057
|
async ({ id, stateName }) => {
|
|
@@ -3905,7 +4071,7 @@ function register11(server, store) {
|
|
|
3905
4071
|
}
|
|
3906
4072
|
|
|
3907
4073
|
// ../mcp/src/tools/assets.ts
|
|
3908
|
-
import { z as
|
|
4074
|
+
import { z as z27 } from "zod";
|
|
3909
4075
|
function getDoc12(store, id) {
|
|
3910
4076
|
const doc = store.get(id);
|
|
3911
4077
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -3922,17 +4088,17 @@ function register12(server, store) {
|
|
|
3922
4088
|
"atelier_add_asset",
|
|
3923
4089
|
"Register an external asset (image, SVG, font, animation, audio) on a document",
|
|
3924
4090
|
{
|
|
3925
|
-
id:
|
|
3926
|
-
assetId:
|
|
3927
|
-
type:
|
|
3928
|
-
src:
|
|
3929
|
-
description:
|
|
3930
|
-
spritesheet:
|
|
3931
|
-
columns:
|
|
3932
|
-
rows:
|
|
3933
|
-
frameCount:
|
|
3934
|
-
frameWidth:
|
|
3935
|
-
frameHeight:
|
|
4091
|
+
id: z27.string().describe("Document ID"),
|
|
4092
|
+
assetId: z27.string().describe("Unique asset identifier"),
|
|
4093
|
+
type: z27.enum(["image", "svg", "font", "animation", "audio"]).describe("Asset type"),
|
|
4094
|
+
src: z27.string().describe("File path or URL"),
|
|
4095
|
+
description: z27.string().optional().describe("Human-readable description"),
|
|
4096
|
+
spritesheet: z27.object({
|
|
4097
|
+
columns: z27.number().int().positive().describe("Number of columns in the spritesheet grid"),
|
|
4098
|
+
rows: z27.number().int().positive().describe("Number of rows in the spritesheet grid"),
|
|
4099
|
+
frameCount: z27.number().int().positive().optional().describe("Total frame count (defaults to columns \xD7 rows)"),
|
|
4100
|
+
frameWidth: z27.number().positive().describe("Width of each frame in pixels"),
|
|
4101
|
+
frameHeight: z27.number().positive().describe("Height of each frame in pixels")
|
|
3936
4102
|
}).optional().describe("Spritesheet metadata (image assets only)")
|
|
3937
4103
|
},
|
|
3938
4104
|
{ readOnlyHint: false, destructiveHint: false },
|
|
@@ -3955,7 +4121,7 @@ function register12(server, store) {
|
|
|
3955
4121
|
"atelier_list_assets",
|
|
3956
4122
|
"List all registered assets with usage info (which layers/states reference them)",
|
|
3957
4123
|
{
|
|
3958
|
-
id:
|
|
4124
|
+
id: z27.string().describe("Document ID")
|
|
3959
4125
|
},
|
|
3960
4126
|
{ readOnlyHint: true, destructiveHint: false },
|
|
3961
4127
|
async ({ id }) => {
|
|
@@ -3982,8 +4148,8 @@ function register12(server, store) {
|
|
|
3982
4148
|
"atelier_remove_asset",
|
|
3983
4149
|
"Remove a registered asset. Warns if layers or state audio still reference it.",
|
|
3984
4150
|
{
|
|
3985
|
-
id:
|
|
3986
|
-
assetId:
|
|
4151
|
+
id: z27.string().describe("Document ID"),
|
|
4152
|
+
assetId: z27.string().describe("Asset ID to remove")
|
|
3987
4153
|
},
|
|
3988
4154
|
{ readOnlyHint: false, destructiveHint: true },
|
|
3989
4155
|
async ({ id, assetId }) => {
|
|
@@ -4007,7 +4173,7 @@ function register12(server, store) {
|
|
|
4007
4173
|
}
|
|
4008
4174
|
|
|
4009
4175
|
// ../mcp/src/tools/variables.ts
|
|
4010
|
-
import { z as
|
|
4176
|
+
import { z as z28 } from "zod";
|
|
4011
4177
|
function getDoc13(store, id) {
|
|
4012
4178
|
const doc = store.get(id);
|
|
4013
4179
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -4024,11 +4190,11 @@ function register13(server, store) {
|
|
|
4024
4190
|
"atelier_add_variable",
|
|
4025
4191
|
"Define a template variable on a document",
|
|
4026
4192
|
{
|
|
4027
|
-
id:
|
|
4028
|
-
variableName:
|
|
4029
|
-
type:
|
|
4030
|
-
description:
|
|
4031
|
-
default:
|
|
4193
|
+
id: z28.string().describe("Document ID"),
|
|
4194
|
+
variableName: z28.string().describe("Variable name (used in {{variableName}} patterns)"),
|
|
4195
|
+
type: z28.enum(["string", "number", "color", "asset", "boolean"]).describe("Variable type"),
|
|
4196
|
+
description: z28.string().optional().describe("Human-readable description"),
|
|
4197
|
+
default: z28.unknown().optional().describe("Default value")
|
|
4032
4198
|
},
|
|
4033
4199
|
{ readOnlyHint: false, destructiveHint: false },
|
|
4034
4200
|
async (args) => {
|
|
@@ -4049,7 +4215,7 @@ function register13(server, store) {
|
|
|
4049
4215
|
"atelier_list_variables",
|
|
4050
4216
|
"List all declared variables with usage info (which {{references}} exist in the document)",
|
|
4051
4217
|
{
|
|
4052
|
-
id:
|
|
4218
|
+
id: z28.string().describe("Document ID")
|
|
4053
4219
|
},
|
|
4054
4220
|
{ readOnlyHint: true, destructiveHint: false },
|
|
4055
4221
|
async ({ id }) => {
|
|
@@ -4073,8 +4239,8 @@ function register13(server, store) {
|
|
|
4073
4239
|
"atelier_remove_variable",
|
|
4074
4240
|
"Remove a declared variable. Warns if {{references}} still exist in the document.",
|
|
4075
4241
|
{
|
|
4076
|
-
id:
|
|
4077
|
-
variableName:
|
|
4242
|
+
id: z28.string().describe("Document ID"),
|
|
4243
|
+
variableName: z28.string().describe("Variable name to remove")
|
|
4078
4244
|
},
|
|
4079
4245
|
{ readOnlyHint: false, destructiveHint: true },
|
|
4080
4246
|
async ({ id, variableName }) => {
|
|
@@ -4097,7 +4263,7 @@ function register13(server, store) {
|
|
|
4097
4263
|
}
|
|
4098
4264
|
|
|
4099
4265
|
// ../mcp/src/tools/interactions.ts
|
|
4100
|
-
import { z as
|
|
4266
|
+
import { z as z29 } from "zod";
|
|
4101
4267
|
function getDoc14(store, id) {
|
|
4102
4268
|
const doc = store.get(id);
|
|
4103
4269
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -4114,23 +4280,23 @@ function register14(server, store) {
|
|
|
4114
4280
|
"atelier_add_interaction",
|
|
4115
4281
|
"Add a trigger-action interaction to a layer (click, hover, timer, signal \u2192 go-to-state, emit-signal, set-variable, toggle-visibility)",
|
|
4116
4282
|
{
|
|
4117
|
-
id:
|
|
4118
|
-
layerId:
|
|
4119
|
-
interactionId:
|
|
4120
|
-
trigger:
|
|
4121
|
-
type:
|
|
4122
|
-
delay:
|
|
4123
|
-
signal:
|
|
4283
|
+
id: z29.string().describe("Document ID"),
|
|
4284
|
+
layerId: z29.string().describe("Layer to attach the interaction to"),
|
|
4285
|
+
interactionId: z29.string().describe("Unique interaction ID"),
|
|
4286
|
+
trigger: z29.object({
|
|
4287
|
+
type: z29.enum(["click", "hover", "pointerdown", "pointerup", "timer", "signal"]).describe("Trigger type"),
|
|
4288
|
+
delay: z29.number().optional().describe("Delay in ms (timer trigger only)"),
|
|
4289
|
+
signal: z29.string().optional().describe("Signal name (signal trigger only)")
|
|
4124
4290
|
}).describe("What triggers the interaction"),
|
|
4125
|
-
action:
|
|
4126
|
-
type:
|
|
4127
|
-
state:
|
|
4128
|
-
signal:
|
|
4129
|
-
variable:
|
|
4130
|
-
value:
|
|
4131
|
-
targetLayer:
|
|
4291
|
+
action: z29.object({
|
|
4292
|
+
type: z29.enum(["go-to-state", "emit-signal", "set-variable", "toggle-visibility"]).describe("Action type"),
|
|
4293
|
+
state: z29.string().optional().describe("Target state (go-to-state only)"),
|
|
4294
|
+
signal: z29.string().optional().describe("Signal to emit (emit-signal only)"),
|
|
4295
|
+
variable: z29.string().optional().describe("Variable name (set-variable only)"),
|
|
4296
|
+
value: z29.unknown().optional().describe("Value to set (set-variable only)"),
|
|
4297
|
+
targetLayer: z29.string().optional().describe("Target layer ID (toggle-visibility, defaults to self)")
|
|
4132
4298
|
}).describe("What happens when triggered"),
|
|
4133
|
-
description:
|
|
4299
|
+
description: z29.string().optional().describe("Human-readable description")
|
|
4134
4300
|
},
|
|
4135
4301
|
{ readOnlyHint: false, destructiveHint: false },
|
|
4136
4302
|
async ({ id, layerId, interactionId, trigger, action, description }) => {
|
|
@@ -4179,8 +4345,8 @@ function register14(server, store) {
|
|
|
4179
4345
|
"atelier_list_interactions",
|
|
4180
4346
|
"List all interactions across all layers or for a specific layer",
|
|
4181
4347
|
{
|
|
4182
|
-
id:
|
|
4183
|
-
layerId:
|
|
4348
|
+
id: z29.string().describe("Document ID"),
|
|
4349
|
+
layerId: z29.string().optional().describe("Optional: filter to a specific layer")
|
|
4184
4350
|
},
|
|
4185
4351
|
{ readOnlyHint: true, destructiveHint: false },
|
|
4186
4352
|
async ({ id, layerId }) => {
|
|
@@ -4207,9 +4373,9 @@ function register14(server, store) {
|
|
|
4207
4373
|
"atelier_remove_interaction",
|
|
4208
4374
|
"Remove an interaction from a layer",
|
|
4209
4375
|
{
|
|
4210
|
-
id:
|
|
4211
|
-
layerId:
|
|
4212
|
-
interactionId:
|
|
4376
|
+
id: z29.string().describe("Document ID"),
|
|
4377
|
+
layerId: z29.string().describe("Layer ID"),
|
|
4378
|
+
interactionId: z29.string().describe("Interaction ID to remove")
|
|
4213
4379
|
},
|
|
4214
4380
|
{ readOnlyHint: false, destructiveHint: true },
|
|
4215
4381
|
async ({ id, layerId, interactionId }) => {
|
|
@@ -4231,7 +4397,7 @@ function register14(server, store) {
|
|
|
4231
4397
|
}
|
|
4232
4398
|
|
|
4233
4399
|
// ../mcp/src/tools/refs.ts
|
|
4234
|
-
import { z as
|
|
4400
|
+
import { z as z30 } from "zod";
|
|
4235
4401
|
function getDoc15(store, id) {
|
|
4236
4402
|
const doc = store.get(id);
|
|
4237
4403
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -4248,9 +4414,9 @@ function register15(server, store) {
|
|
|
4248
4414
|
"atelier_set_ref",
|
|
4249
4415
|
"Set the source reference on a ref-type layer. Can point to a file path or another in-memory document ID.",
|
|
4250
4416
|
{
|
|
4251
|
-
id:
|
|
4252
|
-
layerId:
|
|
4253
|
-
src:
|
|
4417
|
+
id: z30.string().describe("Document ID"),
|
|
4418
|
+
layerId: z30.string().describe("Layer ID (must be a ref visual)"),
|
|
4419
|
+
src: z30.string().describe("File path, URL, or in-memory document ID")
|
|
4254
4420
|
},
|
|
4255
4421
|
{ readOnlyHint: false, destructiveHint: false },
|
|
4256
4422
|
async ({ id, layerId, src }) => {
|
|
@@ -4270,7 +4436,7 @@ function register15(server, store) {
|
|
|
4270
4436
|
"atelier_resolve_refs",
|
|
4271
4437
|
"Check which ref layers can be resolved (point to valid in-memory documents)",
|
|
4272
4438
|
{
|
|
4273
|
-
id:
|
|
4439
|
+
id: z30.string().describe("Document ID")
|
|
4274
4440
|
},
|
|
4275
4441
|
{ readOnlyHint: true, destructiveHint: false },
|
|
4276
4442
|
async ({ id }) => {
|
|
@@ -4297,7 +4463,7 @@ function register15(server, store) {
|
|
|
4297
4463
|
}
|
|
4298
4464
|
|
|
4299
4465
|
// ../mcp/src/tools/performance.ts
|
|
4300
|
-
import { z as
|
|
4466
|
+
import { z as z31 } from "zod";
|
|
4301
4467
|
function getDoc16(store, id) {
|
|
4302
4468
|
const doc = store.get(id);
|
|
4303
4469
|
if (!doc) return { error: `Document "${id}" not found` };
|
|
@@ -4314,9 +4480,9 @@ function register16(server, store) {
|
|
|
4314
4480
|
"atelier_profile",
|
|
4315
4481
|
"Profile frame resolution performance \u2014 returns timing, layer count, property count, and delta count for a frame",
|
|
4316
4482
|
{
|
|
4317
|
-
id:
|
|
4318
|
-
stateName:
|
|
4319
|
-
frame:
|
|
4483
|
+
id: z31.string().describe("Document ID"),
|
|
4484
|
+
stateName: z31.string().describe("State name to profile"),
|
|
4485
|
+
frame: z31.number().int().min(0).describe("Frame number to resolve")
|
|
4320
4486
|
},
|
|
4321
4487
|
{ readOnlyHint: true, destructiveHint: false },
|
|
4322
4488
|
async ({ id, stateName, frame }) => {
|
|
@@ -4362,10 +4528,10 @@ function register16(server, store) {
|
|
|
4362
4528
|
"atelier_diff_frames",
|
|
4363
4529
|
"Compare two frames and return only the properties that changed between them",
|
|
4364
4530
|
{
|
|
4365
|
-
id:
|
|
4366
|
-
stateName:
|
|
4367
|
-
frameA:
|
|
4368
|
-
frameB:
|
|
4531
|
+
id: z31.string().describe("Document ID"),
|
|
4532
|
+
stateName: z31.string().describe("State name"),
|
|
4533
|
+
frameA: z31.number().int().min(0).describe("First frame number"),
|
|
4534
|
+
frameB: z31.number().int().min(0).describe("Second frame number")
|
|
4369
4535
|
},
|
|
4370
4536
|
{ readOnlyHint: true, destructiveHint: false },
|
|
4371
4537
|
async ({ id, stateName, frameA, frameB }) => {
|
|
@@ -4404,9 +4570,9 @@ function register16(server, store) {
|
|
|
4404
4570
|
"atelier_batch_preview",
|
|
4405
4571
|
"Preview multiple frames at once \u2014 reduces round-trips for scrubbing or timeline inspection",
|
|
4406
4572
|
{
|
|
4407
|
-
id:
|
|
4408
|
-
stateName:
|
|
4409
|
-
frames:
|
|
4573
|
+
id: z31.string().describe("Document ID"),
|
|
4574
|
+
stateName: z31.string().describe("State name to preview"),
|
|
4575
|
+
frames: z31.array(z31.number().int().min(0)).min(1).max(60).describe("Array of frame numbers to resolve (max 60)")
|
|
4410
4576
|
},
|
|
4411
4577
|
{ readOnlyHint: true, destructiveHint: false },
|
|
4412
4578
|
async ({ id, stateName, frames }) => {
|
|
@@ -4442,7 +4608,7 @@ function register16(server, store) {
|
|
|
4442
4608
|
"atelier_complexity",
|
|
4443
4609
|
"Analyze document complexity \u2014 layer count, delta count, expression usage, state hierarchy depth",
|
|
4444
4610
|
{
|
|
4445
|
-
id:
|
|
4611
|
+
id: z31.string().describe("Document ID")
|
|
4446
4612
|
},
|
|
4447
4613
|
{ readOnlyHint: true, destructiveHint: false },
|
|
4448
4614
|
async ({ id }) => {
|
|
@@ -4514,9 +4680,678 @@ function register16(server, store) {
|
|
|
4514
4680
|
);
|
|
4515
4681
|
}
|
|
4516
4682
|
|
|
4683
|
+
// ../mcp/src/tools/overlays.ts
|
|
4684
|
+
import { z as z32 } from "zod";
|
|
4685
|
+
function getDoc17(store, id) {
|
|
4686
|
+
const doc = store.get(id);
|
|
4687
|
+
if (!doc) return { error: `Document "${id}" not found` };
|
|
4688
|
+
return { doc };
|
|
4689
|
+
}
|
|
4690
|
+
function ok17(data) {
|
|
4691
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true, ...data }) }] };
|
|
4692
|
+
}
|
|
4693
|
+
function err17(message) {
|
|
4694
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
|
|
4695
|
+
}
|
|
4696
|
+
var AnchorEnum = z32.enum(["top-left", "top-right", "bottom-left", "bottom-right"]);
|
|
4697
|
+
var OverlayStyleSchema = z32.object({
|
|
4698
|
+
font_family: z32.string().optional(),
|
|
4699
|
+
font_size: z32.number().positive().optional(),
|
|
4700
|
+
font_weight: z32.union([z32.number(), z32.enum(["normal", "bold"])]).optional(),
|
|
4701
|
+
color: z32.string().optional()
|
|
4702
|
+
}).optional();
|
|
4703
|
+
var DEFAULT_OVERLAY_MARGIN = 24;
|
|
4704
|
+
var DEFAULT_TEXT_STYLE = {
|
|
4705
|
+
fontFamily: "Inter",
|
|
4706
|
+
fontSize: 24,
|
|
4707
|
+
fontWeight: 600,
|
|
4708
|
+
color: "#F5F5F7"
|
|
4709
|
+
};
|
|
4710
|
+
function anchorToFrame(anchor, canvas, margin) {
|
|
4711
|
+
switch (anchor) {
|
|
4712
|
+
case "top-left":
|
|
4713
|
+
return { frame: { x: margin, y: margin }, anchorPoint: { x: 0, y: 0 } };
|
|
4714
|
+
case "top-right":
|
|
4715
|
+
return { frame: { x: canvas.width - margin, y: margin }, anchorPoint: { x: 1, y: 0 } };
|
|
4716
|
+
case "bottom-left":
|
|
4717
|
+
return { frame: { x: margin, y: canvas.height - margin }, anchorPoint: { x: 0, y: 1 } };
|
|
4718
|
+
case "bottom-right":
|
|
4719
|
+
return {
|
|
4720
|
+
frame: { x: canvas.width - margin, y: canvas.height - margin },
|
|
4721
|
+
anchorPoint: { x: 1, y: 1 }
|
|
4722
|
+
};
|
|
4723
|
+
}
|
|
4724
|
+
}
|
|
4725
|
+
function mergeStyle(style) {
|
|
4726
|
+
return {
|
|
4727
|
+
fontFamily: style?.font_family ?? DEFAULT_TEXT_STYLE.fontFamily,
|
|
4728
|
+
fontSize: style?.font_size ?? DEFAULT_TEXT_STYLE.fontSize,
|
|
4729
|
+
fontWeight: style?.font_weight ?? DEFAULT_TEXT_STYLE.fontWeight,
|
|
4730
|
+
color: style?.color ?? DEFAULT_TEXT_STYLE.color
|
|
4731
|
+
};
|
|
4732
|
+
}
|
|
4733
|
+
function renderPageNumberFormat(format, currentIndex, totalCount) {
|
|
4734
|
+
return format.replace(
|
|
4735
|
+
/\{(current|total)(?::0(\d+)d)?\}/g,
|
|
4736
|
+
(_, name, padWidth) => {
|
|
4737
|
+
const value = name === "current" ? currentIndex : totalCount;
|
|
4738
|
+
const str = String(value);
|
|
4739
|
+
if (padWidth) {
|
|
4740
|
+
const width = parseInt(padWidth, 10);
|
|
4741
|
+
return str.padStart(width, "0");
|
|
4742
|
+
}
|
|
4743
|
+
return str;
|
|
4744
|
+
}
|
|
4745
|
+
);
|
|
4746
|
+
}
|
|
4747
|
+
function register17(server, store) {
|
|
4748
|
+
server.tool(
|
|
4749
|
+
"atelier_add_handle",
|
|
4750
|
+
"Add an anchored creator-handle TextVisual overlay layer (tag: overlay)",
|
|
4751
|
+
{
|
|
4752
|
+
id: z32.string().describe("Document ID"),
|
|
4753
|
+
text: z32.string().min(1).describe('Handle text, e.g. "@username"'),
|
|
4754
|
+
anchor: AnchorEnum.describe("Anchored corner"),
|
|
4755
|
+
margin: z32.number().nonnegative().optional().describe("Px from anchored edges (default 24)"),
|
|
4756
|
+
style: OverlayStyleSchema.describe("Text style overrides (snake_case to match recipe)"),
|
|
4757
|
+
layerId: z32.string().optional().describe("Layer ID (default: overlay-handle)")
|
|
4758
|
+
},
|
|
4759
|
+
{ readOnlyHint: false, destructiveHint: false },
|
|
4760
|
+
async ({ id, text, anchor, margin, style, layerId }) => {
|
|
4761
|
+
const result = getDoc17(store, id);
|
|
4762
|
+
if ("error" in result) return err17(result.error);
|
|
4763
|
+
const { doc } = result;
|
|
4764
|
+
const targetId = layerId ?? "overlay-handle";
|
|
4765
|
+
if (doc.layers.some((l) => l.id === targetId)) {
|
|
4766
|
+
return err17(`Layer "${targetId}" already exists in document "${id}"`);
|
|
4767
|
+
}
|
|
4768
|
+
const m = margin ?? DEFAULT_OVERLAY_MARGIN;
|
|
4769
|
+
const { frame, anchorPoint } = anchorToFrame(anchor, doc.canvas, m);
|
|
4770
|
+
const layer = {
|
|
4771
|
+
id: targetId,
|
|
4772
|
+
tags: ["overlay"],
|
|
4773
|
+
visual: { type: "text", content: text, style: mergeStyle(style) },
|
|
4774
|
+
frame,
|
|
4775
|
+
bounds: { width: 600, height: 80 },
|
|
4776
|
+
anchorPoint
|
|
4777
|
+
};
|
|
4778
|
+
doc.layers.push(layer);
|
|
4779
|
+
store.set(id, doc);
|
|
4780
|
+
return ok17({ layerId: targetId, layer });
|
|
4781
|
+
}
|
|
4782
|
+
);
|
|
4783
|
+
server.tool(
|
|
4784
|
+
"atelier_add_page_number",
|
|
4785
|
+
"Add an anchored page-number TextVisual overlay layer (tag: overlay). Substitutes {current} / {total} (and zero-pad forms {current:02d} / {total:02d}) into format.",
|
|
4786
|
+
{
|
|
4787
|
+
id: z32.string().describe("Document ID"),
|
|
4788
|
+
format: z32.string().min(1).describe('Format string, e.g. "{current}/{total}" or "{current:02d}/{total:02d}"'),
|
|
4789
|
+
anchor: AnchorEnum.describe("Anchored corner"),
|
|
4790
|
+
currentIndex: z32.number().int().nonnegative().describe("1-based current index"),
|
|
4791
|
+
totalCount: z32.number().int().positive().describe("Total count"),
|
|
4792
|
+
margin: z32.number().nonnegative().optional().describe("Px from anchored edges (default 24)"),
|
|
4793
|
+
style: OverlayStyleSchema.describe("Text style overrides (snake_case to match recipe)"),
|
|
4794
|
+
layerId: z32.string().optional().describe("Layer ID (default: overlay-page-number)")
|
|
4795
|
+
},
|
|
4796
|
+
{ readOnlyHint: false, destructiveHint: false },
|
|
4797
|
+
async ({ id, format, anchor, currentIndex, totalCount, margin, style, layerId }) => {
|
|
4798
|
+
const result = getDoc17(store, id);
|
|
4799
|
+
if ("error" in result) return err17(result.error);
|
|
4800
|
+
const { doc } = result;
|
|
4801
|
+
const targetId = layerId ?? "overlay-page-number";
|
|
4802
|
+
if (doc.layers.some((l) => l.id === targetId)) {
|
|
4803
|
+
return err17(`Layer "${targetId}" already exists in document "${id}"`);
|
|
4804
|
+
}
|
|
4805
|
+
const content = renderPageNumberFormat(format, currentIndex, totalCount);
|
|
4806
|
+
const m = margin ?? DEFAULT_OVERLAY_MARGIN;
|
|
4807
|
+
const { frame, anchorPoint } = anchorToFrame(anchor, doc.canvas, m);
|
|
4808
|
+
const layer = {
|
|
4809
|
+
id: targetId,
|
|
4810
|
+
tags: ["overlay"],
|
|
4811
|
+
visual: { type: "text", content, style: mergeStyle(style) },
|
|
4812
|
+
frame,
|
|
4813
|
+
bounds: { width: 200, height: 80 },
|
|
4814
|
+
anchorPoint
|
|
4815
|
+
};
|
|
4816
|
+
doc.layers.push(layer);
|
|
4817
|
+
store.set(id, doc);
|
|
4818
|
+
return ok17({ layerId: targetId, layer, rendered: content });
|
|
4819
|
+
}
|
|
4820
|
+
);
|
|
4821
|
+
}
|
|
4822
|
+
|
|
4823
|
+
// ../mcp/src/tools/recipes.ts
|
|
4824
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync } from "fs";
|
|
4825
|
+
import { join, resolve, isAbsolute, dirname } from "path";
|
|
4826
|
+
import { homedir } from "os";
|
|
4827
|
+
import { z as z33 } from "zod";
|
|
4828
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
4829
|
+
function getDoc18(store, id) {
|
|
4830
|
+
const doc = store.get(id);
|
|
4831
|
+
if (!doc) return { error: `Document "${id}" not found` };
|
|
4832
|
+
return { doc };
|
|
4833
|
+
}
|
|
4834
|
+
function ok18(data) {
|
|
4835
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true, ...data }) }] };
|
|
4836
|
+
}
|
|
4837
|
+
function err18(message, extra) {
|
|
4838
|
+
return {
|
|
4839
|
+
content: [{ type: "text", text: JSON.stringify({ error: message, ...extra }) }],
|
|
4840
|
+
isError: true
|
|
4841
|
+
};
|
|
4842
|
+
}
|
|
4843
|
+
function resolveRecipePath(pathOrName, projectDir) {
|
|
4844
|
+
if (isAbsolute(pathOrName) || pathOrName.includes("/") || pathOrName.includes("\\")) {
|
|
4845
|
+
return resolve(pathOrName);
|
|
4846
|
+
}
|
|
4847
|
+
const candidates = [];
|
|
4848
|
+
const exts = [".recipe.yaml", ".recipe.json", ".yaml", ".yml", ".json"];
|
|
4849
|
+
const projectRecipesDir = join(resolve(projectDir), ".atelier", "recipes");
|
|
4850
|
+
for (const ext of exts) candidates.push(join(projectRecipesDir, `${pathOrName}${ext}`));
|
|
4851
|
+
const userRecipesDir = join(homedir(), ".atelier", "recipes");
|
|
4852
|
+
for (const ext of exts) candidates.push(join(userRecipesDir, `${pathOrName}${ext}`));
|
|
4853
|
+
for (const candidate of candidates) {
|
|
4854
|
+
if (existsSync(candidate)) return candidate;
|
|
4855
|
+
}
|
|
4856
|
+
throw new Error(
|
|
4857
|
+
`Recipe "${pathOrName}" not found. Looked in:
|
|
4858
|
+
${candidates.map((c) => ` ${c}`).join("\n")}`
|
|
4859
|
+
);
|
|
4860
|
+
}
|
|
4861
|
+
function parseRecipeText(raw, path) {
|
|
4862
|
+
if (path.endsWith(".json")) return JSON.parse(raw);
|
|
4863
|
+
return parseYaml(raw);
|
|
4864
|
+
}
|
|
4865
|
+
function loadRecipe(pathOrName, projectDir) {
|
|
4866
|
+
const path = resolveRecipePath(pathOrName, projectDir);
|
|
4867
|
+
const raw = readFileSync(path, "utf-8");
|
|
4868
|
+
const parsed = parseRecipeText(raw, path);
|
|
4869
|
+
const result = validateRecipe(parsed);
|
|
4870
|
+
if (!result.success) {
|
|
4871
|
+
const msg = result.errors.map((e) => ` ${e.path}: ${e.message}`).join("\n");
|
|
4872
|
+
throw new Error(`Invalid recipe at ${path}:
|
|
4873
|
+
${msg}`);
|
|
4874
|
+
}
|
|
4875
|
+
return { recipe: result.data, path, warnings: result.warnings ?? [] };
|
|
4876
|
+
}
|
|
4877
|
+
function recipeToYaml(recipe) {
|
|
4878
|
+
return stringifyYaml(recipe);
|
|
4879
|
+
}
|
|
4880
|
+
var DEFAULT_OVERLAY_MARGIN2 = 24;
|
|
4881
|
+
var DEFAULT_OVERLAY_TEXT_STYLE = {
|
|
4882
|
+
fontFamily: "Inter",
|
|
4883
|
+
fontSize: 24,
|
|
4884
|
+
fontWeight: 600,
|
|
4885
|
+
color: "#F5F5F7"
|
|
4886
|
+
};
|
|
4887
|
+
var HANDLE_LAYER_ID = "overlay-handle";
|
|
4888
|
+
var PAGE_NUMBER_LAYER_ID = "overlay-page-number";
|
|
4889
|
+
function applyRecipeToOverlay(doc, recipe, ctx) {
|
|
4890
|
+
const preserved = doc.layers.filter((l) => !(l.tags ?? []).includes("overlay"));
|
|
4891
|
+
const overlayLayers = [];
|
|
4892
|
+
const warnings = [];
|
|
4893
|
+
const rules = recipe.overlay_rules;
|
|
4894
|
+
if (!rules) {
|
|
4895
|
+
return { doc: { ...doc, layers: preserved }, warnings };
|
|
4896
|
+
}
|
|
4897
|
+
if (rules.handle) {
|
|
4898
|
+
overlayLayers.push(buildHandleLayer(rules.handle, doc.canvas));
|
|
4899
|
+
}
|
|
4900
|
+
if (rules.page_number) {
|
|
4901
|
+
if (ctx?.currentIndex != null && ctx?.totalCount != null) {
|
|
4902
|
+
overlayLayers.push(
|
|
4903
|
+
buildPageNumberLayer(rules.page_number, doc.canvas, ctx.currentIndex, ctx.totalCount)
|
|
4904
|
+
);
|
|
4905
|
+
} else {
|
|
4906
|
+
warnings.push(
|
|
4907
|
+
"recipe.overlay_rules.page_number is present but currentIndex/totalCount were not provided \u2014 the page_number layer was skipped."
|
|
4908
|
+
);
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
return { doc: { ...doc, layers: [...preserved, ...overlayLayers] }, warnings };
|
|
4912
|
+
}
|
|
4913
|
+
function buildHandleLayer(rule, canvas) {
|
|
4914
|
+
const margin = rule.margin ?? DEFAULT_OVERLAY_MARGIN2;
|
|
4915
|
+
const { frame, anchorPoint } = anchorToFrame2(rule.anchor, canvas, margin);
|
|
4916
|
+
return {
|
|
4917
|
+
id: HANDLE_LAYER_ID,
|
|
4918
|
+
tags: ["overlay"],
|
|
4919
|
+
visual: { type: "text", content: rule.text, style: mergeOverlayStyle(rule.style) },
|
|
4920
|
+
frame,
|
|
4921
|
+
bounds: { width: 600, height: 80 },
|
|
4922
|
+
anchorPoint
|
|
4923
|
+
};
|
|
4924
|
+
}
|
|
4925
|
+
function buildPageNumberLayer(rule, canvas, currentIndex, totalCount) {
|
|
4926
|
+
const margin = rule.margin ?? DEFAULT_OVERLAY_MARGIN2;
|
|
4927
|
+
const { frame, anchorPoint } = anchorToFrame2(rule.anchor, canvas, margin);
|
|
4928
|
+
return {
|
|
4929
|
+
id: PAGE_NUMBER_LAYER_ID,
|
|
4930
|
+
tags: ["overlay"],
|
|
4931
|
+
visual: {
|
|
4932
|
+
type: "text",
|
|
4933
|
+
content: renderPageNumberFormat2(rule.format, currentIndex, totalCount),
|
|
4934
|
+
style: mergeOverlayStyle(rule.style)
|
|
4935
|
+
},
|
|
4936
|
+
frame,
|
|
4937
|
+
bounds: { width: 200, height: 80 },
|
|
4938
|
+
anchorPoint
|
|
4939
|
+
};
|
|
4940
|
+
}
|
|
4941
|
+
function anchorToFrame2(anchor, canvas, margin) {
|
|
4942
|
+
switch (anchor) {
|
|
4943
|
+
case "top-left":
|
|
4944
|
+
return { frame: { x: margin, y: margin }, anchorPoint: { x: 0, y: 0 } };
|
|
4945
|
+
case "top-right":
|
|
4946
|
+
return { frame: { x: canvas.width - margin, y: margin }, anchorPoint: { x: 1, y: 0 } };
|
|
4947
|
+
case "bottom-left":
|
|
4948
|
+
return { frame: { x: margin, y: canvas.height - margin }, anchorPoint: { x: 0, y: 1 } };
|
|
4949
|
+
case "bottom-right":
|
|
4950
|
+
return {
|
|
4951
|
+
frame: { x: canvas.width - margin, y: canvas.height - margin },
|
|
4952
|
+
anchorPoint: { x: 1, y: 1 }
|
|
4953
|
+
};
|
|
4954
|
+
}
|
|
4955
|
+
}
|
|
4956
|
+
function mergeOverlayStyle(style) {
|
|
4957
|
+
return {
|
|
4958
|
+
fontFamily: style?.font_family ?? DEFAULT_OVERLAY_TEXT_STYLE.fontFamily,
|
|
4959
|
+
fontSize: style?.font_size ?? DEFAULT_OVERLAY_TEXT_STYLE.fontSize,
|
|
4960
|
+
fontWeight: style?.font_weight ?? DEFAULT_OVERLAY_TEXT_STYLE.fontWeight,
|
|
4961
|
+
color: style?.color ?? DEFAULT_OVERLAY_TEXT_STYLE.color
|
|
4962
|
+
};
|
|
4963
|
+
}
|
|
4964
|
+
function renderPageNumberFormat2(format, currentIndex, totalCount) {
|
|
4965
|
+
return format.replace(
|
|
4966
|
+
/\{(current|total)(?::0(\d+)d)?\}/g,
|
|
4967
|
+
(_, name, padWidth) => {
|
|
4968
|
+
const value = name === "current" ? currentIndex : totalCount;
|
|
4969
|
+
const str = String(value);
|
|
4970
|
+
if (padWidth) return str.padStart(parseInt(padWidth, 10), "0");
|
|
4971
|
+
return str;
|
|
4972
|
+
}
|
|
4973
|
+
);
|
|
4974
|
+
}
|
|
4975
|
+
var RECIPE_EXTS = [".recipe.yaml", ".recipe.yml", ".recipe.json"];
|
|
4976
|
+
function isRecipeFile(name) {
|
|
4977
|
+
return RECIPE_EXTS.some((ext) => name.endsWith(ext));
|
|
4978
|
+
}
|
|
4979
|
+
function listRecipesIn(dir) {
|
|
4980
|
+
if (!existsSync(dir)) return [];
|
|
4981
|
+
let entries;
|
|
4982
|
+
try {
|
|
4983
|
+
entries = readdirSync(dir);
|
|
4984
|
+
} catch {
|
|
4985
|
+
return [];
|
|
4986
|
+
}
|
|
4987
|
+
return entries.filter(isRecipeFile).map((name) => ({ name, path: join(dir, name) })).sort((a, b) => a.name.localeCompare(b.name));
|
|
4988
|
+
}
|
|
4989
|
+
function register18(server, store) {
|
|
4990
|
+
server.tool(
|
|
4991
|
+
"atelier_recipe_list",
|
|
4992
|
+
"List discoverable Studio Recipes \u2014 project-local <projectDir>/.atelier/recipes/*.recipe.* plus ~/.atelier/recipes/*.recipe.*. Returns { name, path } entries with absolute paths.",
|
|
4993
|
+
{
|
|
4994
|
+
projectDir: z33.string().optional().describe("Project root to search (default: process.cwd())")
|
|
4995
|
+
},
|
|
4996
|
+
{ readOnlyHint: true, destructiveHint: false },
|
|
4997
|
+
async ({ projectDir }) => {
|
|
4998
|
+
const root = resolve(projectDir ?? process.cwd());
|
|
4999
|
+
const projectRecipes = listRecipesIn(join(root, ".atelier", "recipes"));
|
|
5000
|
+
const userRecipes = listRecipesIn(join(homedir(), ".atelier", "recipes"));
|
|
5001
|
+
const seen = new Set(projectRecipes.map((r) => r.name));
|
|
5002
|
+
const recipes = [...projectRecipes, ...userRecipes.filter((r) => !seen.has(r.name))];
|
|
5003
|
+
return ok18({ count: recipes.length, recipes });
|
|
5004
|
+
}
|
|
5005
|
+
);
|
|
5006
|
+
server.tool(
|
|
5007
|
+
"atelier_recipe_get",
|
|
5008
|
+
"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.",
|
|
5009
|
+
{
|
|
5010
|
+
path: z33.string().describe("Recipe path or bare name (resolved like the CLI)"),
|
|
5011
|
+
projectDir: z33.string().optional().describe("Project root for name resolution (default: process.cwd())")
|
|
5012
|
+
},
|
|
5013
|
+
{ readOnlyHint: true, destructiveHint: false },
|
|
5014
|
+
async ({ path, projectDir }) => {
|
|
5015
|
+
const root = resolve(projectDir ?? process.cwd());
|
|
5016
|
+
try {
|
|
5017
|
+
const loaded = loadRecipe(path, root);
|
|
5018
|
+
return ok18({
|
|
5019
|
+
path: loaded.path,
|
|
5020
|
+
recipe: loaded.recipe,
|
|
5021
|
+
yaml: recipeToYaml(loaded.recipe),
|
|
5022
|
+
warnings: loaded.warnings
|
|
5023
|
+
});
|
|
5024
|
+
} catch (e) {
|
|
5025
|
+
return err18(e.message);
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
);
|
|
5029
|
+
server.tool(
|
|
5030
|
+
"atelier_recipe_validate",
|
|
5031
|
+
"Validate a recipe from a path OR inline YAML text. Returns { success, warnings, errors }.",
|
|
5032
|
+
{
|
|
5033
|
+
path: z33.string().optional().describe("Recipe path or bare name to validate"),
|
|
5034
|
+
yaml: z33.string().optional().describe("Inline YAML text to validate (alternative to path)"),
|
|
5035
|
+
projectDir: z33.string().optional().describe("Project root for name resolution (default: process.cwd())")
|
|
5036
|
+
},
|
|
5037
|
+
{ readOnlyHint: true, destructiveHint: false },
|
|
5038
|
+
async ({ path, yaml, projectDir }) => {
|
|
5039
|
+
if (!path && yaml === void 0) {
|
|
5040
|
+
return err18("Provide either `path` or `yaml` to validate");
|
|
5041
|
+
}
|
|
5042
|
+
const root = resolve(projectDir ?? process.cwd());
|
|
5043
|
+
let parsed;
|
|
5044
|
+
try {
|
|
5045
|
+
if (yaml !== void 0) {
|
|
5046
|
+
parsed = parseYaml(yaml);
|
|
5047
|
+
} else {
|
|
5048
|
+
const resolved = resolveRecipePath(path, root);
|
|
5049
|
+
parsed = parseRecipeText(readFileSync(resolved, "utf-8"), resolved);
|
|
5050
|
+
}
|
|
5051
|
+
} catch (e) {
|
|
5052
|
+
return ok18({ valid: false, warnings: [], errors: [{ path: "(yaml)", message: e.message }] });
|
|
5053
|
+
}
|
|
5054
|
+
const result = validateRecipe(parsed);
|
|
5055
|
+
if (!result.success) {
|
|
5056
|
+
return ok18({ valid: false, warnings: [], errors: result.errors });
|
|
5057
|
+
}
|
|
5058
|
+
return ok18({ valid: true, warnings: result.warnings ?? [], errors: [] });
|
|
5059
|
+
}
|
|
5060
|
+
);
|
|
5061
|
+
server.tool(
|
|
5062
|
+
"atelier_recipe_save",
|
|
5063
|
+
"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.",
|
|
5064
|
+
{
|
|
5065
|
+
path: z33.string().describe("Absolute or relative file path to write the recipe to"),
|
|
5066
|
+
recipe: z33.record(z33.unknown()).optional().describe("Recipe object (will be serialized to YAML)"),
|
|
5067
|
+
yaml: z33.string().optional().describe("Raw YAML text (alternative to `recipe`)")
|
|
5068
|
+
},
|
|
5069
|
+
{ readOnlyHint: false, destructiveHint: true },
|
|
5070
|
+
async ({ path, recipe, yaml }) => {
|
|
5071
|
+
if (recipe === void 0 && yaml === void 0) {
|
|
5072
|
+
return err18("Provide either `recipe` (object) or `yaml` (text) to save");
|
|
5073
|
+
}
|
|
5074
|
+
let parsed;
|
|
5075
|
+
let yamlText;
|
|
5076
|
+
if (yaml !== void 0) {
|
|
5077
|
+
yamlText = yaml;
|
|
5078
|
+
try {
|
|
5079
|
+
parsed = parseYaml(yaml);
|
|
5080
|
+
} catch (e) {
|
|
5081
|
+
return err18(`YAML parse error: ${e.message}`);
|
|
5082
|
+
}
|
|
5083
|
+
} else {
|
|
5084
|
+
parsed = recipe;
|
|
5085
|
+
yamlText = stringifyYaml(recipe);
|
|
5086
|
+
}
|
|
5087
|
+
const result = validateRecipe(parsed);
|
|
5088
|
+
if (!result.success) {
|
|
5089
|
+
return err18("Refusing to write an invalid recipe", { errors: result.errors });
|
|
5090
|
+
}
|
|
5091
|
+
const absPath = resolve(path);
|
|
5092
|
+
try {
|
|
5093
|
+
mkdirSync(dirname(absPath), { recursive: true });
|
|
5094
|
+
writeFileSync(absPath, yamlText, "utf-8");
|
|
5095
|
+
} catch (e) {
|
|
5096
|
+
return err18(`Failed to write recipe: ${e.message}`);
|
|
5097
|
+
}
|
|
5098
|
+
return ok18({ path: absPath, warnings: result.warnings ?? [] });
|
|
5099
|
+
}
|
|
5100
|
+
);
|
|
5101
|
+
server.tool(
|
|
5102
|
+
"atelier_recipe_apply",
|
|
5103
|
+
`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.`,
|
|
5104
|
+
{
|
|
5105
|
+
documentId: z33.string().describe("In-store document ID to apply the recipe to"),
|
|
5106
|
+
path: z33.string().optional().describe("Recipe path or bare name (alternative to `recipe`)"),
|
|
5107
|
+
recipe: z33.record(z33.unknown()).optional().describe("Recipe object (alternative to `path`)"),
|
|
5108
|
+
projectDir: z33.string().optional().describe("Project root for name resolution (default: process.cwd())"),
|
|
5109
|
+
currentIndex: z33.number().int().nonnegative().optional().describe("1-based carousel index for page_number"),
|
|
5110
|
+
totalCount: z33.number().int().positive().optional().describe("Total carousel size for page_number")
|
|
5111
|
+
},
|
|
5112
|
+
{ readOnlyHint: false, destructiveHint: false },
|
|
5113
|
+
async ({ documentId, path, recipe, projectDir, currentIndex, totalCount }) => {
|
|
5114
|
+
const result = getDoc18(store, documentId);
|
|
5115
|
+
if ("error" in result) return err18(result.error);
|
|
5116
|
+
const { doc } = result;
|
|
5117
|
+
if (path === void 0 && recipe === void 0) {
|
|
5118
|
+
return err18("Provide either `path` or `recipe` to apply");
|
|
5119
|
+
}
|
|
5120
|
+
let resolvedRecipe;
|
|
5121
|
+
if (recipe !== void 0) {
|
|
5122
|
+
const validated = validateRecipe(recipe);
|
|
5123
|
+
if (!validated.success) {
|
|
5124
|
+
return err18("Invalid recipe", { errors: validated.errors });
|
|
5125
|
+
}
|
|
5126
|
+
resolvedRecipe = validated.data;
|
|
5127
|
+
} else {
|
|
5128
|
+
const root = resolve(projectDir ?? process.cwd());
|
|
5129
|
+
try {
|
|
5130
|
+
resolvedRecipe = loadRecipe(path, root).recipe;
|
|
5131
|
+
} catch (e) {
|
|
5132
|
+
return err18(e.message);
|
|
5133
|
+
}
|
|
5134
|
+
}
|
|
5135
|
+
const { doc: updated, warnings } = applyRecipeToOverlay(doc, resolvedRecipe, {
|
|
5136
|
+
currentIndex,
|
|
5137
|
+
totalCount
|
|
5138
|
+
});
|
|
5139
|
+
store.set(documentId, updated, "llm");
|
|
5140
|
+
return ok18({
|
|
5141
|
+
documentId,
|
|
5142
|
+
warnings,
|
|
5143
|
+
layerCount: updated.layers.length,
|
|
5144
|
+
document: updated
|
|
5145
|
+
});
|
|
5146
|
+
}
|
|
5147
|
+
);
|
|
5148
|
+
}
|
|
5149
|
+
|
|
5150
|
+
// ../mcp/src/tools/import-images.ts
|
|
5151
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, statSync } from "fs";
|
|
5152
|
+
import { resolve as resolve2, join as join2, basename, extname } from "path";
|
|
5153
|
+
import { z as z34 } from "zod";
|
|
5154
|
+
function getDoc19(store, id) {
|
|
5155
|
+
const doc = store.get(id);
|
|
5156
|
+
if (!doc) return { error: `Document "${id}" not found` };
|
|
5157
|
+
return { doc };
|
|
5158
|
+
}
|
|
5159
|
+
function ok19(data) {
|
|
5160
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true, ...data }) }] };
|
|
5161
|
+
}
|
|
5162
|
+
function err19(message) {
|
|
5163
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
|
|
5164
|
+
}
|
|
5165
|
+
var IMAGE_EXTS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".webp", ".gif", ".svg"]);
|
|
5166
|
+
function isImageFile(name) {
|
|
5167
|
+
return IMAGE_EXTS.has(extname(name).toLowerCase());
|
|
5168
|
+
}
|
|
5169
|
+
function matchesGlob(name, glob) {
|
|
5170
|
+
if (!glob) return true;
|
|
5171
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
5172
|
+
return new RegExp(`^${escaped}$`, "i").test(name);
|
|
5173
|
+
}
|
|
5174
|
+
function deriveAssetId(filename, used) {
|
|
5175
|
+
const stem = basename(filename, extname(filename));
|
|
5176
|
+
const base = stem.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "image";
|
|
5177
|
+
let candidate = base;
|
|
5178
|
+
let n = 2;
|
|
5179
|
+
while (used.has(candidate)) {
|
|
5180
|
+
candidate = `${base}-${n}`;
|
|
5181
|
+
n++;
|
|
5182
|
+
}
|
|
5183
|
+
used.add(candidate);
|
|
5184
|
+
return candidate;
|
|
5185
|
+
}
|
|
5186
|
+
function nextImageLayerId(existing, seq) {
|
|
5187
|
+
let candidate = `image-${seq}`;
|
|
5188
|
+
let n = seq;
|
|
5189
|
+
while (existing.has(candidate)) {
|
|
5190
|
+
n++;
|
|
5191
|
+
candidate = `image-${n}`;
|
|
5192
|
+
}
|
|
5193
|
+
existing.add(candidate);
|
|
5194
|
+
return candidate;
|
|
5195
|
+
}
|
|
5196
|
+
function register19(server, store) {
|
|
5197
|
+
server.tool(
|
|
5198
|
+
"atelier_import_images",
|
|
5199
|
+
`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).`,
|
|
5200
|
+
{
|
|
5201
|
+
documentId: z34.string().describe("In-store document ID to import into"),
|
|
5202
|
+
dir: z34.string().describe("Directory containing the images"),
|
|
5203
|
+
glob: z34.string().optional().describe('Optional filename glob, e.g. "*.png" (default: all images)'),
|
|
5204
|
+
asLayers: z34.boolean().optional().describe("Create layers per image (default: true). false = register assets only.")
|
|
5205
|
+
},
|
|
5206
|
+
{ readOnlyHint: false, destructiveHint: false },
|
|
5207
|
+
async ({ documentId, dir, glob, asLayers }) => {
|
|
5208
|
+
const result = getDoc19(store, documentId);
|
|
5209
|
+
if ("error" in result) return err19(result.error);
|
|
5210
|
+
const { doc } = result;
|
|
5211
|
+
const absDir = resolve2(dir);
|
|
5212
|
+
if (!existsSync2(absDir)) return err19(`Directory "${absDir}" does not exist`);
|
|
5213
|
+
if (!statSync(absDir).isDirectory()) return err19(`Path "${absDir}" is not a directory`);
|
|
5214
|
+
const files = readdirSync2(absDir).filter((name) => isImageFile(name) && matchesGlob(name, glob)).sort((a, b) => a.localeCompare(b));
|
|
5215
|
+
if (files.length === 0) {
|
|
5216
|
+
return err19(`No image files matched in "${absDir}"${glob ? ` (glob: ${glob})` : ""}`);
|
|
5217
|
+
}
|
|
5218
|
+
const makeLayers = asLayers !== false;
|
|
5219
|
+
const canvas = doc.canvas;
|
|
5220
|
+
const square = Math.min(canvas.width, canvas.height);
|
|
5221
|
+
const allowBackgroundSwap = files.length === 1 && doc.layers[0]?.id === "background";
|
|
5222
|
+
const newAssets = { ...doc.assets ?? {} };
|
|
5223
|
+
const newLayers = [...doc.layers];
|
|
5224
|
+
const usedAssetIds = new Set(Object.keys(newAssets));
|
|
5225
|
+
const existingLayerIds = new Set(newLayers.map((l) => l.id));
|
|
5226
|
+
const assetIds = [];
|
|
5227
|
+
const layerIds = [];
|
|
5228
|
+
let layerSeq = newLayers.length + 1;
|
|
5229
|
+
for (const file of files) {
|
|
5230
|
+
const absPath = join2(absDir, file);
|
|
5231
|
+
const assetId = deriveAssetId(file, usedAssetIds);
|
|
5232
|
+
newAssets[assetId] = { type: "image", src: absPath, description: file };
|
|
5233
|
+
assetIds.push(assetId);
|
|
5234
|
+
if (!makeLayers) continue;
|
|
5235
|
+
const layerId = nextImageLayerId(existingLayerIds, layerSeq);
|
|
5236
|
+
layerSeq++;
|
|
5237
|
+
const layer = {
|
|
5238
|
+
id: layerId,
|
|
5239
|
+
visual: { type: "image", assetId, src: absPath },
|
|
5240
|
+
frame: { x: canvas.width / 2, y: canvas.height / 2 },
|
|
5241
|
+
bounds: { width: square, height: square },
|
|
5242
|
+
anchorPoint: { x: 0.5, y: 0.5 },
|
|
5243
|
+
opacity: 1
|
|
5244
|
+
};
|
|
5245
|
+
if (allowBackgroundSwap) {
|
|
5246
|
+
newLayers[0] = layer;
|
|
5247
|
+
} else {
|
|
5248
|
+
newLayers.unshift(layer);
|
|
5249
|
+
}
|
|
5250
|
+
layerIds.push(layerId);
|
|
5251
|
+
}
|
|
5252
|
+
const newDoc = { ...doc, assets: newAssets, layers: newLayers };
|
|
5253
|
+
store.set(documentId, newDoc, "llm");
|
|
5254
|
+
return ok19({
|
|
5255
|
+
imported: files.length,
|
|
5256
|
+
assetIds,
|
|
5257
|
+
layerIds,
|
|
5258
|
+
asLayers: makeLayers,
|
|
5259
|
+
note: "Layer bounds default to a centered square (min canvas dimension) \u2014 natural image dimensions are not measured."
|
|
5260
|
+
});
|
|
5261
|
+
}
|
|
5262
|
+
);
|
|
5263
|
+
}
|
|
5264
|
+
|
|
5265
|
+
// ../mcp/src/bridge-protocol.ts
|
|
5266
|
+
var BRIDGE_PROTOCOL_VERSION = 1;
|
|
5267
|
+
function isBridgeEnvelope(value) {
|
|
5268
|
+
if (!value || typeof value !== "object") return false;
|
|
5269
|
+
const t = value.type;
|
|
5270
|
+
return t === "hello" || t === "doc:load" || t === "doc:loaded" || t === "doc:patch" || t === "llm:mutation" || t === "error";
|
|
5271
|
+
}
|
|
5272
|
+
|
|
5273
|
+
// ../mcp/src/transports/ws-transport.ts
|
|
5274
|
+
var WebSocketServerTransport = class {
|
|
5275
|
+
constructor(ws) {
|
|
5276
|
+
this.ws = ws;
|
|
5277
|
+
}
|
|
5278
|
+
onclose;
|
|
5279
|
+
onerror;
|
|
5280
|
+
onmessage;
|
|
5281
|
+
sessionId;
|
|
5282
|
+
started = false;
|
|
5283
|
+
closed = false;
|
|
5284
|
+
async start() {
|
|
5285
|
+
if (this.started) {
|
|
5286
|
+
throw new Error(
|
|
5287
|
+
"WebSocketServerTransport already started \u2014 the MCP SDK calls start() automatically; do not call it again"
|
|
5288
|
+
);
|
|
5289
|
+
}
|
|
5290
|
+
this.started = true;
|
|
5291
|
+
this.ws.on("message", (data) => {
|
|
5292
|
+
let text;
|
|
5293
|
+
try {
|
|
5294
|
+
if (typeof data === "string") {
|
|
5295
|
+
text = data;
|
|
5296
|
+
} else if (data instanceof Buffer) {
|
|
5297
|
+
text = data.toString("utf-8");
|
|
5298
|
+
} else if (Array.isArray(data)) {
|
|
5299
|
+
text = Buffer.concat(data).toString("utf-8");
|
|
5300
|
+
} else if (data instanceof ArrayBuffer) {
|
|
5301
|
+
text = Buffer.from(data).toString("utf-8");
|
|
5302
|
+
} else {
|
|
5303
|
+
text = String(data);
|
|
5304
|
+
}
|
|
5305
|
+
} catch (err20) {
|
|
5306
|
+
this.onerror?.(err20 instanceof Error ? err20 : new Error(String(err20)));
|
|
5307
|
+
return;
|
|
5308
|
+
}
|
|
5309
|
+
let parsed;
|
|
5310
|
+
try {
|
|
5311
|
+
parsed = JSON.parse(text);
|
|
5312
|
+
} catch (err20) {
|
|
5313
|
+
this.onerror?.(
|
|
5314
|
+
new Error(
|
|
5315
|
+
`WebSocketServerTransport: failed to parse JSON-RPC message: ${err20 instanceof Error ? err20.message : String(err20)}`
|
|
5316
|
+
)
|
|
5317
|
+
);
|
|
5318
|
+
return;
|
|
5319
|
+
}
|
|
5320
|
+
this.onmessage?.(parsed);
|
|
5321
|
+
});
|
|
5322
|
+
this.ws.on("close", () => {
|
|
5323
|
+
if (this.closed) return;
|
|
5324
|
+
this.closed = true;
|
|
5325
|
+
this.onclose?.();
|
|
5326
|
+
});
|
|
5327
|
+
this.ws.on("error", (err20) => {
|
|
5328
|
+
this.onerror?.(err20);
|
|
5329
|
+
});
|
|
5330
|
+
}
|
|
5331
|
+
async send(message) {
|
|
5332
|
+
if (this.closed) {
|
|
5333
|
+
throw new Error("WebSocketServerTransport: cannot send after close");
|
|
5334
|
+
}
|
|
5335
|
+
if (this.ws.readyState !== 1) {
|
|
5336
|
+
throw new Error(
|
|
5337
|
+
`WebSocketServerTransport: WebSocket is not OPEN (readyState=${this.ws.readyState})`
|
|
5338
|
+
);
|
|
5339
|
+
}
|
|
5340
|
+
this.ws.send(JSON.stringify(message));
|
|
5341
|
+
}
|
|
5342
|
+
async close() {
|
|
5343
|
+
if (this.closed) return;
|
|
5344
|
+
this.closed = true;
|
|
5345
|
+
try {
|
|
5346
|
+
this.ws.close();
|
|
5347
|
+
} catch {
|
|
5348
|
+
}
|
|
5349
|
+
this.onclose?.();
|
|
5350
|
+
}
|
|
5351
|
+
};
|
|
5352
|
+
|
|
4517
5353
|
// ../mcp/src/index.ts
|
|
4518
|
-
function createServer() {
|
|
4519
|
-
const store = new DocumentStore();
|
|
5354
|
+
function createServer(store = new DocumentStore()) {
|
|
4520
5355
|
const server = new McpServer(
|
|
4521
5356
|
{ name: "atelier", version: "0.1.0" },
|
|
4522
5357
|
{ capabilities: { tools: {} } }
|
|
@@ -4537,6 +5372,9 @@ function createServer() {
|
|
|
4537
5372
|
register14(server, store);
|
|
4538
5373
|
register15(server, store);
|
|
4539
5374
|
register16(server, store);
|
|
5375
|
+
register17(server, store);
|
|
5376
|
+
register18(server, store);
|
|
5377
|
+
register19(server, store);
|
|
4540
5378
|
return { server, store };
|
|
4541
5379
|
}
|
|
4542
5380
|
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"));
|
|
@@ -4549,18 +5387,24 @@ if (isMain) {
|
|
|
4549
5387
|
});
|
|
4550
5388
|
}
|
|
4551
5389
|
export {
|
|
5390
|
+
BRIDGE_PROTOCOL_VERSION,
|
|
4552
5391
|
DocumentStore,
|
|
5392
|
+
WebSocketServerTransport,
|
|
4553
5393
|
createServer,
|
|
5394
|
+
isBridgeEnvelope,
|
|
4554
5395
|
register12 as registerAssetTools,
|
|
4555
5396
|
register5 as registerDeltaTools,
|
|
4556
5397
|
register as registerDocumentTools,
|
|
4557
5398
|
register11 as registerExportTools,
|
|
5399
|
+
register19 as registerImportImageTools,
|
|
4558
5400
|
register14 as registerInteractionTools,
|
|
4559
5401
|
register9 as registerLayerEffectTools,
|
|
4560
5402
|
register2 as registerLayerTools,
|
|
5403
|
+
register17 as registerOverlayTools,
|
|
4561
5404
|
register16 as registerPerformanceTools,
|
|
4562
5405
|
register6 as registerPresetTools,
|
|
4563
5406
|
register7 as registerPreviewTools,
|
|
5407
|
+
register18 as registerRecipeTools,
|
|
4564
5408
|
register15 as registerRefTools,
|
|
4565
5409
|
register3 as registerShapeTools,
|
|
4566
5410
|
register10 as registerStateConfigTools,
|