@genart-dev/mcp-server 0.1.2 → 0.3.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/index.cjs +1057 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1055 -16
- package/dist/index.js.map +1 -1
- package/dist/lib.cjs +1068 -35
- package/dist/lib.cjs.map +1 -1
- package/dist/lib.d.cts +26 -2
- package/dist/lib.d.ts +26 -2
- package/dist/lib.js +1060 -21
- package/dist/lib.js.map +1 -1
- package/package.json +14 -9
package/dist/lib.cjs
CHANGED
|
@@ -38,6 +38,11 @@ module.exports = __toCommonJS(lib_exports);
|
|
|
38
38
|
// src/server.ts
|
|
39
39
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
40
40
|
var import_zod2 = require("zod");
|
|
41
|
+
var import_core11 = require("@genart-dev/core");
|
|
42
|
+
var import_plugin_typography = __toESM(require("@genart-dev/plugin-typography"), 1);
|
|
43
|
+
var import_plugin_filters = __toESM(require("@genart-dev/plugin-filters"), 1);
|
|
44
|
+
var import_plugin_shapes = __toESM(require("@genart-dev/plugin-shapes"), 1);
|
|
45
|
+
var import_plugin_layout_guides = __toESM(require("@genart-dev/plugin-layout-guides"), 1);
|
|
41
46
|
|
|
42
47
|
// src/tools/workspace.ts
|
|
43
48
|
var import_promises = require("fs/promises");
|
|
@@ -470,10 +475,40 @@ async function createSketch(state, input) {
|
|
|
470
475
|
const adapter = registry4.resolve(rendererType);
|
|
471
476
|
algorithm = adapter.getAlgorithmTemplate();
|
|
472
477
|
}
|
|
478
|
+
let resolvedComponents;
|
|
479
|
+
if (input.components && Object.keys(input.components).length > 0) {
|
|
480
|
+
const shorthand = {};
|
|
481
|
+
for (const [name, value] of Object.entries(input.components)) {
|
|
482
|
+
if (typeof value === "string") {
|
|
483
|
+
shorthand[name] = value;
|
|
484
|
+
} else if (value.version) {
|
|
485
|
+
shorthand[name] = value.version;
|
|
486
|
+
} else if (value.code) {
|
|
487
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
488
|
+
resolvedComponents[name] = {
|
|
489
|
+
...value.version ? { version: value.version } : {},
|
|
490
|
+
code: value.code,
|
|
491
|
+
...value.exports ? { exports: value.exports } : {}
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (Object.keys(shorthand).length > 0) {
|
|
496
|
+
const resolved = (0, import_core2.resolveComponents)(shorthand, rendererType);
|
|
497
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
498
|
+
for (const rc of resolved) {
|
|
499
|
+
resolvedComponents[rc.name] = {
|
|
500
|
+
version: rc.version,
|
|
501
|
+
code: rc.code,
|
|
502
|
+
exports: [...rc.exports]
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
473
507
|
const seed = input.seed ?? Math.floor(Math.random() * 1e5);
|
|
474
508
|
const ts = now2();
|
|
509
|
+
const hasComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
|
|
475
510
|
const sketch = {
|
|
476
|
-
genart: "1.1",
|
|
511
|
+
genart: hasComponents ? "1.2" : "1.1",
|
|
477
512
|
id: input.id,
|
|
478
513
|
title: input.title,
|
|
479
514
|
created: ts,
|
|
@@ -487,6 +522,7 @@ async function createSketch(state, input) {
|
|
|
487
522
|
...input.philosophy ? { philosophy: input.philosophy } : {},
|
|
488
523
|
...input.themes && input.themes.length > 0 ? { themes: input.themes } : {},
|
|
489
524
|
...input.skills && input.skills.length > 0 ? { skills: input.skills } : {},
|
|
525
|
+
...hasComponents ? { components: resolvedComponents } : {},
|
|
490
526
|
...input.agent ? { agent: input.agent } : {},
|
|
491
527
|
...input.model ? { model: input.model } : {}
|
|
492
528
|
};
|
|
@@ -670,10 +706,44 @@ async function updateAlgorithm(state, input) {
|
|
|
670
706
|
);
|
|
671
707
|
}
|
|
672
708
|
}
|
|
709
|
+
let resolvedComponents;
|
|
710
|
+
if (input.components && Object.keys(input.components).length > 0) {
|
|
711
|
+
const renderer = def.renderer.type;
|
|
712
|
+
const shorthand = {};
|
|
713
|
+
for (const [name, value] of Object.entries(input.components)) {
|
|
714
|
+
if (typeof value === "string") {
|
|
715
|
+
shorthand[name] = value;
|
|
716
|
+
} else if (value.version) {
|
|
717
|
+
shorthand[name] = value.version;
|
|
718
|
+
} else if (value.code) {
|
|
719
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
720
|
+
resolvedComponents[name] = {
|
|
721
|
+
...value.version ? { version: value.version } : {},
|
|
722
|
+
code: value.code,
|
|
723
|
+
...value.exports ? { exports: value.exports } : {}
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (Object.keys(shorthand).length > 0) {
|
|
728
|
+
const resolved = (0, import_core2.resolveComponents)(shorthand, renderer);
|
|
729
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
730
|
+
for (const rc of resolved) {
|
|
731
|
+
resolvedComponents[rc.name] = {
|
|
732
|
+
version: rc.version,
|
|
733
|
+
code: rc.code,
|
|
734
|
+
exports: [...rc.exports]
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
const updated = ["algorithm"];
|
|
740
|
+
const hasNewComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
|
|
741
|
+
if (hasNewComponents) updated.push("components");
|
|
673
742
|
const newDef = {
|
|
674
743
|
...def,
|
|
675
744
|
modified: now2(),
|
|
676
745
|
algorithm: input.algorithm,
|
|
746
|
+
...hasNewComponents ? { genart: "1.2", components: resolvedComponents } : {},
|
|
677
747
|
...input.agent ? { agent: input.agent } : {},
|
|
678
748
|
...input.model ? { model: input.model } : {}
|
|
679
749
|
};
|
|
@@ -687,7 +757,7 @@ async function updateAlgorithm(state, input) {
|
|
|
687
757
|
}
|
|
688
758
|
state.emitMutation("sketch:updated", {
|
|
689
759
|
id: input.sketchId,
|
|
690
|
-
updated
|
|
760
|
+
updated
|
|
691
761
|
});
|
|
692
762
|
return {
|
|
693
763
|
success: true,
|
|
@@ -695,6 +765,7 @@ async function updateAlgorithm(state, input) {
|
|
|
695
765
|
renderer: def.renderer.type,
|
|
696
766
|
algorithmLength: input.algorithm.length,
|
|
697
767
|
validationPassed,
|
|
768
|
+
...hasNewComponents ? { componentsUpdated: true } : {},
|
|
698
769
|
fileContent: json
|
|
699
770
|
};
|
|
700
771
|
}
|
|
@@ -1992,9 +2063,216 @@ ${guidelines}`,
|
|
|
1992
2063
|
};
|
|
1993
2064
|
}
|
|
1994
2065
|
|
|
1995
|
-
// src/tools/
|
|
2066
|
+
// src/tools/components.ts
|
|
1996
2067
|
var import_promises5 = require("fs/promises");
|
|
1997
2068
|
var import_core7 = require("@genart-dev/core");
|
|
2069
|
+
var VALID_RENDERERS2 = [
|
|
2070
|
+
"p5",
|
|
2071
|
+
"three",
|
|
2072
|
+
"glsl",
|
|
2073
|
+
"canvas2d",
|
|
2074
|
+
"svg"
|
|
2075
|
+
];
|
|
2076
|
+
var RENDERER_TARGET = {
|
|
2077
|
+
p5: "js",
|
|
2078
|
+
three: "js",
|
|
2079
|
+
canvas2d: "js",
|
|
2080
|
+
svg: "js",
|
|
2081
|
+
glsl: "glsl"
|
|
2082
|
+
};
|
|
2083
|
+
async function listComponents(_state, input) {
|
|
2084
|
+
let entries = Object.values(import_core7.COMPONENT_REGISTRY);
|
|
2085
|
+
if (input.renderer) {
|
|
2086
|
+
const renderer = input.renderer;
|
|
2087
|
+
const target = RENDERER_TARGET[renderer];
|
|
2088
|
+
if (!target) {
|
|
2089
|
+
throw new Error(
|
|
2090
|
+
`Unknown renderer type: '${input.renderer}'. Valid types: ${VALID_RENDERERS2.join(", ")}`
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
entries = entries.filter(
|
|
2094
|
+
(e) => e.target === target && (e.renderers.length === 0 || e.renderers.includes(renderer))
|
|
2095
|
+
);
|
|
2096
|
+
}
|
|
2097
|
+
if (input.category) {
|
|
2098
|
+
const cat = input.category;
|
|
2099
|
+
entries = entries.filter((e) => e.category === cat);
|
|
2100
|
+
}
|
|
2101
|
+
entries.sort((a, b) => {
|
|
2102
|
+
const catCmp = a.category.localeCompare(b.category);
|
|
2103
|
+
if (catCmp !== 0) return catCmp;
|
|
2104
|
+
return a.name.localeCompare(b.name);
|
|
2105
|
+
});
|
|
2106
|
+
const components = entries.map((e) => ({
|
|
2107
|
+
name: e.name,
|
|
2108
|
+
version: e.version,
|
|
2109
|
+
category: e.category,
|
|
2110
|
+
target: e.target,
|
|
2111
|
+
exports: [...e.exports],
|
|
2112
|
+
dependencies: [...e.dependencies],
|
|
2113
|
+
description: e.description
|
|
2114
|
+
}));
|
|
2115
|
+
return {
|
|
2116
|
+
count: components.length,
|
|
2117
|
+
components
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
async function addComponent(state, input) {
|
|
2121
|
+
const loaded = state.requireSketch(input.sketchId);
|
|
2122
|
+
const def = loaded.definition;
|
|
2123
|
+
const renderer = def.renderer.type;
|
|
2124
|
+
const entry = import_core7.COMPONENT_REGISTRY[input.component];
|
|
2125
|
+
if (!entry) {
|
|
2126
|
+
throw new Error(`Unknown component: "${input.component}"`);
|
|
2127
|
+
}
|
|
2128
|
+
const target = RENDERER_TARGET[renderer];
|
|
2129
|
+
if (entry.target !== target) {
|
|
2130
|
+
throw new Error(
|
|
2131
|
+
`Component "${input.component}" has target "${entry.target}" but renderer "${renderer}" requires target "${target}"`
|
|
2132
|
+
);
|
|
2133
|
+
}
|
|
2134
|
+
if (entry.renderers.length > 0 && !entry.renderers.includes(renderer)) {
|
|
2135
|
+
throw new Error(
|
|
2136
|
+
`Component "${input.component}" is not compatible with renderer "${renderer}". Compatible: ${entry.renderers.join(", ")}`
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
const existingComponents = {};
|
|
2140
|
+
if (def.components) {
|
|
2141
|
+
for (const [name, value] of Object.entries(def.components)) {
|
|
2142
|
+
if (typeof value === "string") {
|
|
2143
|
+
existingComponents[name] = value;
|
|
2144
|
+
} else if (value.version) {
|
|
2145
|
+
existingComponents[name] = value.version;
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
if (existingComponents[input.component]) {
|
|
2150
|
+
throw new Error(
|
|
2151
|
+
`Component "${input.component}" is already present in sketch "${input.sketchId}"`
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
2154
|
+
existingComponents[input.component] = input.version ?? "^1.0.0";
|
|
2155
|
+
const resolved = (0, import_core7.resolveComponents)(existingComponents, renderer);
|
|
2156
|
+
const resolvedRecord = {};
|
|
2157
|
+
for (const rc of resolved) {
|
|
2158
|
+
resolvedRecord[rc.name] = {
|
|
2159
|
+
version: rc.version,
|
|
2160
|
+
code: rc.code,
|
|
2161
|
+
exports: [...rc.exports]
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
const previousNames = new Set(
|
|
2165
|
+
def.components ? Object.keys(def.components) : []
|
|
2166
|
+
);
|
|
2167
|
+
const added = resolved.map((rc) => rc.name).filter((name) => !previousNames.has(name));
|
|
2168
|
+
const newDef = {
|
|
2169
|
+
...def,
|
|
2170
|
+
genart: "1.2",
|
|
2171
|
+
modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2172
|
+
components: resolvedRecord
|
|
2173
|
+
};
|
|
2174
|
+
state.sketches.set(input.sketchId, {
|
|
2175
|
+
definition: newDef,
|
|
2176
|
+
path: loaded.path
|
|
2177
|
+
});
|
|
2178
|
+
const json = (0, import_core7.serializeGenart)(newDef);
|
|
2179
|
+
if (!state.remoteMode) {
|
|
2180
|
+
await (0, import_promises5.writeFile)(loaded.path, json, "utf-8");
|
|
2181
|
+
}
|
|
2182
|
+
state.emitMutation("sketch:updated", {
|
|
2183
|
+
id: input.sketchId,
|
|
2184
|
+
updated: ["components"]
|
|
2185
|
+
});
|
|
2186
|
+
return {
|
|
2187
|
+
success: true,
|
|
2188
|
+
sketchId: input.sketchId,
|
|
2189
|
+
components: resolvedRecord,
|
|
2190
|
+
added,
|
|
2191
|
+
fileContent: json
|
|
2192
|
+
};
|
|
2193
|
+
}
|
|
2194
|
+
async function removeComponent(state, input) {
|
|
2195
|
+
const loaded = state.requireSketch(input.sketchId);
|
|
2196
|
+
const def = loaded.definition;
|
|
2197
|
+
if (!def.components || !def.components[input.component]) {
|
|
2198
|
+
throw new Error(
|
|
2199
|
+
`Component "${input.component}" is not present in sketch "${input.sketchId}"`
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2202
|
+
const remaining = { ...def.components };
|
|
2203
|
+
delete remaining[input.component];
|
|
2204
|
+
for (const [name, value] of Object.entries(remaining)) {
|
|
2205
|
+
const entry = import_core7.COMPONENT_REGISTRY[name];
|
|
2206
|
+
if (entry && entry.dependencies.includes(input.component)) {
|
|
2207
|
+
throw new Error(
|
|
2208
|
+
`Cannot remove "${input.component}": component "${name}" depends on it`
|
|
2209
|
+
);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
let warning;
|
|
2213
|
+
const removedValue = def.components[input.component];
|
|
2214
|
+
const exports2 = typeof removedValue === "string" ? import_core7.COMPONENT_REGISTRY[input.component]?.exports ?? [] : removedValue.exports ?? [];
|
|
2215
|
+
const usedExports = exports2.filter((exp) => def.algorithm.includes(exp));
|
|
2216
|
+
if (usedExports.length > 0) {
|
|
2217
|
+
warning = `Algorithm may reference these exports from "${input.component}": ${usedExports.join(", ")}. Review your algorithm after removal.`;
|
|
2218
|
+
}
|
|
2219
|
+
const removed = [input.component];
|
|
2220
|
+
const neededDeps = /* @__PURE__ */ new Set();
|
|
2221
|
+
for (const name of Object.keys(remaining)) {
|
|
2222
|
+
const entry = import_core7.COMPONENT_REGISTRY[name];
|
|
2223
|
+
if (entry) {
|
|
2224
|
+
collectTransitiveDeps(name, neededDeps);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
for (const name of Object.keys(remaining)) {
|
|
2228
|
+
if (!neededDeps.has(name) && !isDirectComponent(name, remaining)) {
|
|
2229
|
+
delete remaining[name];
|
|
2230
|
+
removed.push(name);
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
const hasRemaining = Object.keys(remaining).length > 0;
|
|
2234
|
+
const newDef = {
|
|
2235
|
+
...def,
|
|
2236
|
+
modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2237
|
+
...hasRemaining ? { components: remaining } : { components: void 0 }
|
|
2238
|
+
};
|
|
2239
|
+
state.sketches.set(input.sketchId, {
|
|
2240
|
+
definition: newDef,
|
|
2241
|
+
path: loaded.path
|
|
2242
|
+
});
|
|
2243
|
+
const json = (0, import_core7.serializeGenart)(newDef);
|
|
2244
|
+
if (!state.remoteMode) {
|
|
2245
|
+
await (0, import_promises5.writeFile)(loaded.path, json, "utf-8");
|
|
2246
|
+
}
|
|
2247
|
+
state.emitMutation("sketch:updated", {
|
|
2248
|
+
id: input.sketchId,
|
|
2249
|
+
updated: ["components"]
|
|
2250
|
+
});
|
|
2251
|
+
return {
|
|
2252
|
+
success: true,
|
|
2253
|
+
sketchId: input.sketchId,
|
|
2254
|
+
removed,
|
|
2255
|
+
...warning ? { warning } : {},
|
|
2256
|
+
fileContent: json
|
|
2257
|
+
};
|
|
2258
|
+
}
|
|
2259
|
+
function collectTransitiveDeps(name, deps) {
|
|
2260
|
+
const entry = import_core7.COMPONENT_REGISTRY[name];
|
|
2261
|
+
if (!entry) return;
|
|
2262
|
+
deps.add(name);
|
|
2263
|
+
for (const dep of entry.dependencies) {
|
|
2264
|
+
if (!deps.has(dep)) {
|
|
2265
|
+
collectTransitiveDeps(dep, deps);
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
function isDirectComponent(name, components) {
|
|
2270
|
+
return name in components;
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
// src/tools/capture.ts
|
|
2274
|
+
var import_promises6 = require("fs/promises");
|
|
2275
|
+
var import_core8 = require("@genart-dev/core");
|
|
1998
2276
|
|
|
1999
2277
|
// src/capture/headless.ts
|
|
2000
2278
|
var cachedModule = null;
|
|
@@ -2088,7 +2366,7 @@ async function captureHtmlMulti(options) {
|
|
|
2088
2366
|
}
|
|
2089
2367
|
|
|
2090
2368
|
// src/tools/capture.ts
|
|
2091
|
-
var registry2 = (0,
|
|
2369
|
+
var registry2 = (0, import_core8.createDefaultRegistry)();
|
|
2092
2370
|
function applyOverrides(sketch, overrides) {
|
|
2093
2371
|
if (overrides.seed === void 0 && overrides.params === void 0) {
|
|
2094
2372
|
return sketch;
|
|
@@ -2169,7 +2447,7 @@ async function buildScreenshotMetadata(state, multi, info) {
|
|
|
2169
2447
|
previewPath: info.previewPath
|
|
2170
2448
|
};
|
|
2171
2449
|
if (!state.remoteMode) {
|
|
2172
|
-
await (0,
|
|
2450
|
+
await (0, import_promises6.writeFile)(info.previewPath, multi.previewPng);
|
|
2173
2451
|
metadata.savedPreviewTo = info.previewPath;
|
|
2174
2452
|
}
|
|
2175
2453
|
return metadata;
|
|
@@ -2230,15 +2508,15 @@ async function captureBatch(state, input) {
|
|
|
2230
2508
|
|
|
2231
2509
|
// src/tools/export.ts
|
|
2232
2510
|
var import_fs = require("fs");
|
|
2233
|
-
var
|
|
2511
|
+
var import_promises7 = require("fs/promises");
|
|
2234
2512
|
var import_path8 = require("path");
|
|
2235
2513
|
var import_archiver = __toESM(require("archiver"), 1);
|
|
2236
|
-
var
|
|
2237
|
-
var registry3 = (0,
|
|
2514
|
+
var import_core9 = require("@genart-dev/core");
|
|
2515
|
+
var registry3 = (0, import_core9.createDefaultRegistry)();
|
|
2238
2516
|
async function validateOutputPath(outputPath) {
|
|
2239
2517
|
const parentDir = (0, import_path8.dirname)(outputPath);
|
|
2240
2518
|
try {
|
|
2241
|
-
const s = await (0,
|
|
2519
|
+
const s = await (0, import_promises7.stat)(parentDir);
|
|
2242
2520
|
if (!s.isDirectory()) {
|
|
2243
2521
|
throw new Error(`Parent directory does not exist: ${parentDir}`);
|
|
2244
2522
|
}
|
|
@@ -2247,7 +2525,7 @@ async function validateOutputPath(outputPath) {
|
|
|
2247
2525
|
throw new Error(`Parent directory does not exist: ${parentDir}`);
|
|
2248
2526
|
}
|
|
2249
2527
|
try {
|
|
2250
|
-
await (0,
|
|
2528
|
+
await (0, import_promises7.stat)(outputPath);
|
|
2251
2529
|
throw new Error(
|
|
2252
2530
|
`File already exists at ${outputPath}. Delete it first or use a different path.`
|
|
2253
2531
|
);
|
|
@@ -2302,7 +2580,7 @@ async function exportHtml(sketch, outputPath) {
|
|
|
2302
2580
|
const adapter = registry3.resolve(sketch.renderer.type);
|
|
2303
2581
|
const html = adapter.generateStandaloneHTML(sketch);
|
|
2304
2582
|
const content = Buffer.from(html, "utf-8");
|
|
2305
|
-
await (0,
|
|
2583
|
+
await (0, import_promises7.writeFile)(outputPath, content);
|
|
2306
2584
|
return {
|
|
2307
2585
|
success: true,
|
|
2308
2586
|
sketchId: sketch.id,
|
|
@@ -2318,7 +2596,7 @@ async function exportPng(sketch, input) {
|
|
|
2318
2596
|
const width = input.width ?? sketch.canvas.width;
|
|
2319
2597
|
const height = input.height ?? sketch.canvas.height;
|
|
2320
2598
|
const result = await captureHtml({ html, width, height });
|
|
2321
|
-
await (0,
|
|
2599
|
+
await (0, import_promises7.writeFile)(input.outputPath, result.bytes);
|
|
2322
2600
|
return {
|
|
2323
2601
|
success: true,
|
|
2324
2602
|
sketchId: sketch.id,
|
|
@@ -2333,7 +2611,7 @@ async function exportSvg(sketch, input) {
|
|
|
2333
2611
|
const height = input.height ?? sketch.canvas.height;
|
|
2334
2612
|
if (sketch.renderer.type === "svg") {
|
|
2335
2613
|
const content2 = Buffer.from(sketch.algorithm, "utf-8");
|
|
2336
|
-
await (0,
|
|
2614
|
+
await (0, import_promises7.writeFile)(input.outputPath, content2);
|
|
2337
2615
|
return {
|
|
2338
2616
|
success: true,
|
|
2339
2617
|
sketchId: sketch.id,
|
|
@@ -2355,7 +2633,7 @@ async function exportSvg(sketch, input) {
|
|
|
2355
2633
|
href="data:image/png;base64,${b64}"/>
|
|
2356
2634
|
</svg>`;
|
|
2357
2635
|
const content = Buffer.from(svg, "utf-8");
|
|
2358
|
-
await (0,
|
|
2636
|
+
await (0, import_promises7.writeFile)(input.outputPath, content);
|
|
2359
2637
|
return {
|
|
2360
2638
|
success: true,
|
|
2361
2639
|
sketchId: sketch.id,
|
|
@@ -2368,7 +2646,7 @@ async function exportSvg(sketch, input) {
|
|
|
2368
2646
|
}
|
|
2369
2647
|
async function exportAlgorithm(sketch, outputPath) {
|
|
2370
2648
|
const content = Buffer.from(sketch.algorithm, "utf-8");
|
|
2371
|
-
await (0,
|
|
2649
|
+
await (0, import_promises7.writeFile)(outputPath, content);
|
|
2372
2650
|
return {
|
|
2373
2651
|
success: true,
|
|
2374
2652
|
sketchId: sketch.id,
|
|
@@ -2383,7 +2661,7 @@ async function exportZip(sketch, input) {
|
|
|
2383
2661
|
const width = input.width ?? sketch.canvas.width;
|
|
2384
2662
|
const height = input.height ?? sketch.canvas.height;
|
|
2385
2663
|
const html = adapter.generateStandaloneHTML(sketch);
|
|
2386
|
-
const genartJson = (0,
|
|
2664
|
+
const genartJson = (0, import_core9.serializeGenart)(sketch);
|
|
2387
2665
|
const algorithm = sketch.algorithm;
|
|
2388
2666
|
const algExt = algorithmExtension(sketch.renderer.type);
|
|
2389
2667
|
const captureResult = await captureHtml({ html, width, height });
|
|
@@ -2400,7 +2678,7 @@ async function exportZip(sketch, input) {
|
|
|
2400
2678
|
archive.append(genartJson, { name: `${sketch.id}.genart` });
|
|
2401
2679
|
await archive.finalize();
|
|
2402
2680
|
await finished;
|
|
2403
|
-
const s = await (0,
|
|
2681
|
+
const s = await (0, import_promises7.stat)(input.outputPath);
|
|
2404
2682
|
return {
|
|
2405
2683
|
success: true,
|
|
2406
2684
|
sketchId: sketch.id,
|
|
@@ -2417,8 +2695,332 @@ async function exportZip(sketch, input) {
|
|
|
2417
2695
|
};
|
|
2418
2696
|
}
|
|
2419
2697
|
|
|
2698
|
+
// src/tools/design.ts
|
|
2699
|
+
function requireSketchId(state, args) {
|
|
2700
|
+
return args.sketchId ?? state.requireSelectedSketchId();
|
|
2701
|
+
}
|
|
2702
|
+
var BLEND_MODES = [
|
|
2703
|
+
"normal",
|
|
2704
|
+
"multiply",
|
|
2705
|
+
"screen",
|
|
2706
|
+
"overlay",
|
|
2707
|
+
"darken",
|
|
2708
|
+
"lighten",
|
|
2709
|
+
"color-dodge",
|
|
2710
|
+
"color-burn",
|
|
2711
|
+
"hard-light",
|
|
2712
|
+
"soft-light",
|
|
2713
|
+
"difference",
|
|
2714
|
+
"exclusion",
|
|
2715
|
+
"hue",
|
|
2716
|
+
"saturation",
|
|
2717
|
+
"color",
|
|
2718
|
+
"luminosity"
|
|
2719
|
+
];
|
|
2720
|
+
function generateLayerId() {
|
|
2721
|
+
return `layer-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2722
|
+
}
|
|
2723
|
+
async function designAddLayer(state, args) {
|
|
2724
|
+
const sketchId = requireSketchId(state, args);
|
|
2725
|
+
const loaded = state.requireSketch(sketchId);
|
|
2726
|
+
const stack = state.getLayerStack(sketchId);
|
|
2727
|
+
const registry4 = state.pluginRegistry;
|
|
2728
|
+
const layerTypeDef = registry4?.resolveLayerType(args.type);
|
|
2729
|
+
if (!layerTypeDef) {
|
|
2730
|
+
throw new Error(
|
|
2731
|
+
`Unknown layer type: '${args.type}'. Use design_list_layers types from registered plugins.`
|
|
2732
|
+
);
|
|
2733
|
+
}
|
|
2734
|
+
const defaults = layerTypeDef.createDefault();
|
|
2735
|
+
const id = generateLayerId();
|
|
2736
|
+
const { width, height } = loaded.definition.canvas;
|
|
2737
|
+
const layer = {
|
|
2738
|
+
id,
|
|
2739
|
+
type: args.type,
|
|
2740
|
+
name: args.name ?? layerTypeDef.displayName,
|
|
2741
|
+
visible: true,
|
|
2742
|
+
locked: false,
|
|
2743
|
+
opacity: args.opacity ?? 1,
|
|
2744
|
+
blendMode: args.blendMode ?? "normal",
|
|
2745
|
+
transform: {
|
|
2746
|
+
x: 0,
|
|
2747
|
+
y: 0,
|
|
2748
|
+
width,
|
|
2749
|
+
height,
|
|
2750
|
+
rotation: 0,
|
|
2751
|
+
scaleX: 1,
|
|
2752
|
+
scaleY: 1,
|
|
2753
|
+
anchorX: 0.5,
|
|
2754
|
+
anchorY: 0.5,
|
|
2755
|
+
...args.transform
|
|
2756
|
+
},
|
|
2757
|
+
properties: { ...defaults, ...args.properties }
|
|
2758
|
+
};
|
|
2759
|
+
stack.add(layer, args.index);
|
|
2760
|
+
await state.saveSketch(sketchId);
|
|
2761
|
+
return {
|
|
2762
|
+
layerId: id,
|
|
2763
|
+
type: args.type,
|
|
2764
|
+
name: layer.name,
|
|
2765
|
+
index: args.index ?? stack.count - 1,
|
|
2766
|
+
sketchId
|
|
2767
|
+
};
|
|
2768
|
+
}
|
|
2769
|
+
async function designRemoveLayer(state, args) {
|
|
2770
|
+
const sketchId = requireSketchId(state, args);
|
|
2771
|
+
const stack = state.getLayerStack(sketchId);
|
|
2772
|
+
const removed = stack.remove(args.layerId);
|
|
2773
|
+
if (!removed) {
|
|
2774
|
+
throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
|
|
2775
|
+
}
|
|
2776
|
+
await state.saveSketch(sketchId);
|
|
2777
|
+
return { removed: true, layerId: args.layerId, sketchId };
|
|
2778
|
+
}
|
|
2779
|
+
async function designListLayers(state, args) {
|
|
2780
|
+
const sketchId = requireSketchId(state, args);
|
|
2781
|
+
const stack = state.getLayerStack(sketchId);
|
|
2782
|
+
const layers = stack.getAll();
|
|
2783
|
+
return {
|
|
2784
|
+
sketchId,
|
|
2785
|
+
count: layers.length,
|
|
2786
|
+
layers: layers.map((l, i) => ({
|
|
2787
|
+
index: i,
|
|
2788
|
+
id: l.id,
|
|
2789
|
+
type: l.type,
|
|
2790
|
+
name: l.name,
|
|
2791
|
+
visible: l.visible,
|
|
2792
|
+
locked: l.locked,
|
|
2793
|
+
opacity: l.opacity,
|
|
2794
|
+
blendMode: l.blendMode
|
|
2795
|
+
}))
|
|
2796
|
+
};
|
|
2797
|
+
}
|
|
2798
|
+
async function designGetLayer(state, args) {
|
|
2799
|
+
const sketchId = requireSketchId(state, args);
|
|
2800
|
+
const stack = state.getLayerStack(sketchId);
|
|
2801
|
+
const layer = stack.get(args.layerId);
|
|
2802
|
+
if (!layer) {
|
|
2803
|
+
throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
|
|
2804
|
+
}
|
|
2805
|
+
return {
|
|
2806
|
+
sketchId,
|
|
2807
|
+
layer: {
|
|
2808
|
+
id: layer.id,
|
|
2809
|
+
type: layer.type,
|
|
2810
|
+
name: layer.name,
|
|
2811
|
+
visible: layer.visible,
|
|
2812
|
+
locked: layer.locked,
|
|
2813
|
+
opacity: layer.opacity,
|
|
2814
|
+
blendMode: layer.blendMode,
|
|
2815
|
+
transform: layer.transform,
|
|
2816
|
+
properties: layer.properties
|
|
2817
|
+
}
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
async function designUpdateLayer(state, args) {
|
|
2821
|
+
const sketchId = requireSketchId(state, args);
|
|
2822
|
+
const stack = state.getLayerStack(sketchId);
|
|
2823
|
+
const layer = stack.get(args.layerId);
|
|
2824
|
+
if (!layer) {
|
|
2825
|
+
throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
|
|
2826
|
+
}
|
|
2827
|
+
const updates = {};
|
|
2828
|
+
if (args.properties) {
|
|
2829
|
+
Object.assign(updates, args.properties);
|
|
2830
|
+
}
|
|
2831
|
+
if (Object.keys(updates).length > 0) {
|
|
2832
|
+
stack.updateProperties(args.layerId, updates);
|
|
2833
|
+
}
|
|
2834
|
+
if (args.name !== void 0) {
|
|
2835
|
+
const current = stack.get(args.layerId);
|
|
2836
|
+
stack.updateProperties(args.layerId, { ...current.properties });
|
|
2837
|
+
const mutableLayer = stack.get(args.layerId);
|
|
2838
|
+
mutableLayer.name = args.name;
|
|
2839
|
+
}
|
|
2840
|
+
await state.saveSketch(sketchId);
|
|
2841
|
+
return { updated: true, layerId: args.layerId, sketchId };
|
|
2842
|
+
}
|
|
2843
|
+
async function designSetTransform(state, args) {
|
|
2844
|
+
const sketchId = requireSketchId(state, args);
|
|
2845
|
+
const stack = state.getLayerStack(sketchId);
|
|
2846
|
+
const layer = stack.get(args.layerId);
|
|
2847
|
+
if (!layer) {
|
|
2848
|
+
throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
|
|
2849
|
+
}
|
|
2850
|
+
const partial = {};
|
|
2851
|
+
if (args.x !== void 0) partial.x = args.x;
|
|
2852
|
+
if (args.y !== void 0) partial.y = args.y;
|
|
2853
|
+
if (args.width !== void 0) partial.width = args.width;
|
|
2854
|
+
if (args.height !== void 0) partial.height = args.height;
|
|
2855
|
+
if (args.rotation !== void 0) partial.rotation = args.rotation;
|
|
2856
|
+
if (args.scaleX !== void 0) partial.scaleX = args.scaleX;
|
|
2857
|
+
if (args.scaleY !== void 0) partial.scaleY = args.scaleY;
|
|
2858
|
+
if (args.anchorX !== void 0) partial.anchorX = args.anchorX;
|
|
2859
|
+
if (args.anchorY !== void 0) partial.anchorY = args.anchorY;
|
|
2860
|
+
stack.updateTransform(args.layerId, partial);
|
|
2861
|
+
await state.saveSketch(sketchId);
|
|
2862
|
+
return {
|
|
2863
|
+
updated: true,
|
|
2864
|
+
layerId: args.layerId,
|
|
2865
|
+
transform: stack.get(args.layerId).transform,
|
|
2866
|
+
sketchId
|
|
2867
|
+
};
|
|
2868
|
+
}
|
|
2869
|
+
async function designSetBlend(state, args) {
|
|
2870
|
+
const sketchId = requireSketchId(state, args);
|
|
2871
|
+
const stack = state.getLayerStack(sketchId);
|
|
2872
|
+
const layer = stack.get(args.layerId);
|
|
2873
|
+
if (!layer) {
|
|
2874
|
+
throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
|
|
2875
|
+
}
|
|
2876
|
+
if (args.blendMode && !BLEND_MODES.includes(args.blendMode)) {
|
|
2877
|
+
throw new Error(
|
|
2878
|
+
`Invalid blend mode '${args.blendMode}'. Must be one of: ${BLEND_MODES.join(", ")}`
|
|
2879
|
+
);
|
|
2880
|
+
}
|
|
2881
|
+
stack.updateBlend(
|
|
2882
|
+
args.layerId,
|
|
2883
|
+
args.blendMode,
|
|
2884
|
+
args.opacity
|
|
2885
|
+
);
|
|
2886
|
+
await state.saveSketch(sketchId);
|
|
2887
|
+
const updated = stack.get(args.layerId);
|
|
2888
|
+
return {
|
|
2889
|
+
updated: true,
|
|
2890
|
+
layerId: args.layerId,
|
|
2891
|
+
blendMode: updated.blendMode,
|
|
2892
|
+
opacity: updated.opacity,
|
|
2893
|
+
sketchId
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
async function designReorderLayers(state, args) {
|
|
2897
|
+
const sketchId = requireSketchId(state, args);
|
|
2898
|
+
const stack = state.getLayerStack(sketchId);
|
|
2899
|
+
const layer = stack.get(args.layerId);
|
|
2900
|
+
if (!layer) {
|
|
2901
|
+
throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
|
|
2902
|
+
}
|
|
2903
|
+
stack.reorder(args.layerId, args.newIndex);
|
|
2904
|
+
await state.saveSketch(sketchId);
|
|
2905
|
+
return {
|
|
2906
|
+
reordered: true,
|
|
2907
|
+
layerId: args.layerId,
|
|
2908
|
+
newIndex: args.newIndex,
|
|
2909
|
+
sketchId
|
|
2910
|
+
};
|
|
2911
|
+
}
|
|
2912
|
+
async function designDuplicateLayer(state, args) {
|
|
2913
|
+
const sketchId = requireSketchId(state, args);
|
|
2914
|
+
const stack = state.getLayerStack(sketchId);
|
|
2915
|
+
const layer = stack.get(args.layerId);
|
|
2916
|
+
if (!layer) {
|
|
2917
|
+
throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
|
|
2918
|
+
}
|
|
2919
|
+
const newId = stack.duplicate(args.layerId);
|
|
2920
|
+
await state.saveSketch(sketchId);
|
|
2921
|
+
return {
|
|
2922
|
+
duplicated: true,
|
|
2923
|
+
sourceLayerId: args.layerId,
|
|
2924
|
+
newLayerId: newId,
|
|
2925
|
+
sketchId
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
2928
|
+
async function designToggleVisibility(state, args) {
|
|
2929
|
+
const sketchId = requireSketchId(state, args);
|
|
2930
|
+
const stack = state.getLayerStack(sketchId);
|
|
2931
|
+
const layer = stack.get(args.layerId);
|
|
2932
|
+
if (!layer) {
|
|
2933
|
+
throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
|
|
2934
|
+
}
|
|
2935
|
+
const newVisible = args.visible ?? !layer.visible;
|
|
2936
|
+
const mutableLayer = layer;
|
|
2937
|
+
mutableLayer.visible = newVisible;
|
|
2938
|
+
stack.updateProperties(args.layerId, { ...layer.properties });
|
|
2939
|
+
await state.saveSketch(sketchId);
|
|
2940
|
+
return {
|
|
2941
|
+
layerId: args.layerId,
|
|
2942
|
+
visible: newVisible,
|
|
2943
|
+
sketchId
|
|
2944
|
+
};
|
|
2945
|
+
}
|
|
2946
|
+
async function designLockLayer(state, args) {
|
|
2947
|
+
const sketchId = requireSketchId(state, args);
|
|
2948
|
+
const stack = state.getLayerStack(sketchId);
|
|
2949
|
+
const layer = stack.get(args.layerId);
|
|
2950
|
+
if (!layer) {
|
|
2951
|
+
throw new Error(`Layer '${args.layerId}' not found in sketch '${sketchId}'.`);
|
|
2952
|
+
}
|
|
2953
|
+
const newLocked = args.locked ?? !layer.locked;
|
|
2954
|
+
const mutableLayer = layer;
|
|
2955
|
+
mutableLayer.locked = newLocked;
|
|
2956
|
+
stack.updateProperties(args.layerId, { ...layer.properties });
|
|
2957
|
+
await state.saveSketch(sketchId);
|
|
2958
|
+
return {
|
|
2959
|
+
layerId: args.layerId,
|
|
2960
|
+
locked: newLocked,
|
|
2961
|
+
sketchId
|
|
2962
|
+
};
|
|
2963
|
+
}
|
|
2964
|
+
async function designCaptureComposite(state, args) {
|
|
2965
|
+
const sketchId = requireSketchId(state, args);
|
|
2966
|
+
const stack = state.getLayerStack(sketchId);
|
|
2967
|
+
const layers = stack.getAll();
|
|
2968
|
+
return {
|
|
2969
|
+
sketchId,
|
|
2970
|
+
layerCount: layers.length,
|
|
2971
|
+
visibleCount: layers.filter((l) => l.visible).length,
|
|
2972
|
+
message: "Composite capture requires a rendering surface. Use capture_screenshot to get a rasterized preview of the sketch, then use design_list_layers to see the design layer stack."
|
|
2973
|
+
};
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2976
|
+
// src/tools/design-plugins.ts
|
|
2977
|
+
function registerPluginMcpTools(server, registry4, state) {
|
|
2978
|
+
for (const tool of registry4.getMcpTools()) {
|
|
2979
|
+
const inputSchema = tool.definition.inputSchema;
|
|
2980
|
+
server.tool(
|
|
2981
|
+
tool.name,
|
|
2982
|
+
tool.definition.description,
|
|
2983
|
+
// Pass raw JSON schema — MCP SDK accepts this alongside Zod
|
|
2984
|
+
inputSchema,
|
|
2985
|
+
async (args) => {
|
|
2986
|
+
try {
|
|
2987
|
+
const sketchId = args.sketchId ?? state.requireSelectedSketchId();
|
|
2988
|
+
const context = state.createMcpToolContext(sketchId);
|
|
2989
|
+
const result = await tool.definition.handler(args, context);
|
|
2990
|
+
await state.saveSketch(sketchId);
|
|
2991
|
+
return {
|
|
2992
|
+
content: result.content.map((c) => {
|
|
2993
|
+
if (c.type === "text") {
|
|
2994
|
+
return { type: "text", text: c.text };
|
|
2995
|
+
}
|
|
2996
|
+
return {
|
|
2997
|
+
type: "image",
|
|
2998
|
+
data: c.data,
|
|
2999
|
+
mimeType: c.mimeType
|
|
3000
|
+
};
|
|
3001
|
+
}),
|
|
3002
|
+
isError: result.isError
|
|
3003
|
+
};
|
|
3004
|
+
} catch (e) {
|
|
3005
|
+
return {
|
|
3006
|
+
content: [
|
|
3007
|
+
{
|
|
3008
|
+
type: "text",
|
|
3009
|
+
text: JSON.stringify({
|
|
3010
|
+
error: e instanceof Error ? e.message : String(e)
|
|
3011
|
+
})
|
|
3012
|
+
}
|
|
3013
|
+
],
|
|
3014
|
+
isError: true
|
|
3015
|
+
};
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
);
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
|
|
2420
3022
|
// src/resources/index.ts
|
|
2421
|
-
var
|
|
3023
|
+
var import_core10 = require("@genart-dev/core");
|
|
2422
3024
|
function registerResources(server, state) {
|
|
2423
3025
|
registerSkillsResource(server);
|
|
2424
3026
|
registerCanvasPresetsResource(server);
|
|
@@ -2426,7 +3028,7 @@ function registerResources(server, state) {
|
|
|
2426
3028
|
registerRenderersResource(server);
|
|
2427
3029
|
}
|
|
2428
3030
|
function registerSkillsResource(server) {
|
|
2429
|
-
const skillRegistry = (0,
|
|
3031
|
+
const skillRegistry = (0, import_core10.createDefaultSkillRegistry)();
|
|
2430
3032
|
server.resource(
|
|
2431
3033
|
"skills",
|
|
2432
3034
|
"genart://skills",
|
|
@@ -2477,7 +3079,7 @@ function registerCanvasPresetsResource(server) {
|
|
|
2477
3079
|
mimeType: "application/json",
|
|
2478
3080
|
text: JSON.stringify(
|
|
2479
3081
|
{
|
|
2480
|
-
presets:
|
|
3082
|
+
presets: import_core10.CANVAS_PRESETS.map((p) => ({
|
|
2481
3083
|
id: p.id,
|
|
2482
3084
|
label: p.label,
|
|
2483
3085
|
category: p.category,
|
|
@@ -2534,7 +3136,7 @@ function registerGalleryResource(server, state) {
|
|
|
2534
3136
|
);
|
|
2535
3137
|
}
|
|
2536
3138
|
function registerRenderersResource(server) {
|
|
2537
|
-
const registry4 = (0,
|
|
3139
|
+
const registry4 = (0, import_core10.createDefaultRegistry)();
|
|
2538
3140
|
server.resource(
|
|
2539
3141
|
"renderers",
|
|
2540
3142
|
"genart://renderers",
|
|
@@ -2900,11 +3502,23 @@ function toolError(message) {
|
|
|
2900
3502
|
isError: true
|
|
2901
3503
|
};
|
|
2902
3504
|
}
|
|
3505
|
+
async function initializePluginRegistry() {
|
|
3506
|
+
const registry4 = (0, import_core11.createPluginRegistry)({
|
|
3507
|
+
surface: "mcp",
|
|
3508
|
+
supportsInteractiveTools: false,
|
|
3509
|
+
supportsRendering: false
|
|
3510
|
+
});
|
|
3511
|
+
await registry4.register(import_plugin_typography.default);
|
|
3512
|
+
await registry4.register(import_plugin_filters.default);
|
|
3513
|
+
await registry4.register(import_plugin_shapes.default);
|
|
3514
|
+
await registry4.register(import_plugin_layout_guides.default);
|
|
3515
|
+
return registry4;
|
|
3516
|
+
}
|
|
2903
3517
|
function createServer(state) {
|
|
2904
3518
|
const server = new import_mcp.McpServer(
|
|
2905
3519
|
{
|
|
2906
3520
|
name: "@genart/mcp-server",
|
|
2907
|
-
version: "0.0
|
|
3521
|
+
version: "0.3.0"
|
|
2908
3522
|
},
|
|
2909
3523
|
{
|
|
2910
3524
|
capabilities: {
|
|
@@ -2914,8 +3528,14 @@ function createServer(state) {
|
|
|
2914
3528
|
}
|
|
2915
3529
|
}
|
|
2916
3530
|
);
|
|
3531
|
+
const registryReady = initializePluginRegistry().then((registry4) => {
|
|
3532
|
+
state.pluginRegistry = registry4;
|
|
3533
|
+
registerPluginMcpTools(server, registry4, state);
|
|
3534
|
+
});
|
|
3535
|
+
server._pluginsReady = registryReady;
|
|
2917
3536
|
registerWorkspaceTools(server, state);
|
|
2918
3537
|
registerSketchTools(server, state);
|
|
3538
|
+
registerComponentTools(server, state);
|
|
2919
3539
|
registerSelectionTools(server, state);
|
|
2920
3540
|
registerParameterTools(server, state);
|
|
2921
3541
|
registerArrangementTools(server, state);
|
|
@@ -2923,6 +3543,7 @@ function createServer(state) {
|
|
|
2923
3543
|
registerMergeTools(server, state);
|
|
2924
3544
|
registerSnapshotTools(server, state);
|
|
2925
3545
|
registerKnowledgeTools(server, state);
|
|
3546
|
+
registerDesignTools(server, state);
|
|
2926
3547
|
registerCaptureTools(server, state);
|
|
2927
3548
|
registerExportTools(server, state);
|
|
2928
3549
|
registerResources(server, state);
|
|
@@ -3019,7 +3640,7 @@ function registerWorkspaceTools(server, state) {
|
|
|
3019
3640
|
function registerSketchTools(server, state) {
|
|
3020
3641
|
server.tool(
|
|
3021
3642
|
"create_sketch",
|
|
3022
|
-
|
|
3643
|
+
'Create a new .genart sketch file from metadata, parameters, and algorithm. IMPORTANT: Do not embed common utilities (PRNG, noise, easing, color math, vector ops) inline in the algorithm. Instead, declare them as components: { "prng": "^1.0.0", "noise-2d": "^1.0.0" }. Then use the exported functions directly in your algorithm (e.g., mulberry32, fbm2D). Use list_components to see all available components for the current renderer.',
|
|
3023
3644
|
{
|
|
3024
3645
|
id: import_zod2.z.string().describe("URL-safe kebab-case identifier"),
|
|
3025
3646
|
title: import_zod2.z.string().describe("Human-readable title"),
|
|
@@ -3057,6 +3678,16 @@ function registerSketchTools(server, state) {
|
|
|
3057
3678
|
algorithm: import_zod2.z.string().optional().describe("Algorithm source code (default: renderer template). For p5: must be `function sketch(p, state) { ... }` in instance mode. State provides: state.WIDTH, state.HEIGHT, state.SEED (number), state.PARAMS (keyed by param key), state.COLORS (keyed by color key, hex strings). Use p5 instance methods (p.createCanvas, p.background, etc)."),
|
|
3058
3679
|
seed: import_zod2.z.number().optional().describe("Initial random seed (default: random)"),
|
|
3059
3680
|
skills: import_zod2.z.array(import_zod2.z.string()).optional().describe("Design skill references"),
|
|
3681
|
+
components: import_zod2.z.record(
|
|
3682
|
+
import_zod2.z.union([
|
|
3683
|
+
import_zod2.z.string(),
|
|
3684
|
+
import_zod2.z.object({
|
|
3685
|
+
version: import_zod2.z.string().optional(),
|
|
3686
|
+
code: import_zod2.z.string().optional(),
|
|
3687
|
+
exports: import_zod2.z.array(import_zod2.z.string()).optional()
|
|
3688
|
+
})
|
|
3689
|
+
])
|
|
3690
|
+
).optional().describe('Component dependencies. Use list_components to see available. Keys are component names, values are semver ranges (e.g. "^1.0.0") or objects with version/code/exports.'),
|
|
3060
3691
|
addToWorkspace: import_zod2.z.string().optional().describe("Path to workspace to add sketch to after creation"),
|
|
3061
3692
|
agent: import_zod2.z.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
|
|
3062
3693
|
model: import_zod2.z.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
|
|
@@ -3136,11 +3767,21 @@ function registerSketchTools(server, state) {
|
|
|
3136
3767
|
);
|
|
3137
3768
|
server.tool(
|
|
3138
3769
|
"update_algorithm",
|
|
3139
|
-
"Replace the algorithm source code of a sketch",
|
|
3770
|
+
"Replace the algorithm source code of a sketch. If adding/changing components, pass them in the components field alongside the algorithm.",
|
|
3140
3771
|
{
|
|
3141
3772
|
sketchId: import_zod2.z.string().describe("ID of the sketch to update"),
|
|
3142
3773
|
algorithm: import_zod2.z.string().describe("New algorithm source code. For p5: must be `function sketch(p, state) { ... }` in instance mode. State provides: state.WIDTH, state.HEIGHT, state.SEED, state.PARAMS (keyed by param key), state.COLORS (keyed by color key)."),
|
|
3143
3774
|
validate: import_zod2.z.boolean().optional().describe("Run renderer-specific validation before saving (default: true)"),
|
|
3775
|
+
components: import_zod2.z.record(
|
|
3776
|
+
import_zod2.z.union([
|
|
3777
|
+
import_zod2.z.string(),
|
|
3778
|
+
import_zod2.z.object({
|
|
3779
|
+
version: import_zod2.z.string().optional(),
|
|
3780
|
+
code: import_zod2.z.string().optional(),
|
|
3781
|
+
exports: import_zod2.z.array(import_zod2.z.string()).optional()
|
|
3782
|
+
})
|
|
3783
|
+
])
|
|
3784
|
+
).optional().describe("Component dependencies to resolve alongside the algorithm update. Use list_components to see available."),
|
|
3144
3785
|
agent: import_zod2.z.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
|
|
3145
3786
|
model: import_zod2.z.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
|
|
3146
3787
|
},
|
|
@@ -3236,6 +3877,76 @@ function registerSketchTools(server, state) {
|
|
|
3236
3877
|
}
|
|
3237
3878
|
);
|
|
3238
3879
|
}
|
|
3880
|
+
function registerComponentTools(server, state) {
|
|
3881
|
+
server.tool(
|
|
3882
|
+
"list_components",
|
|
3883
|
+
"List available reusable components from the registry, filtered by renderer and/or category. Components provide common utilities (PRNG, noise, easing, color math, etc.) that can be declared as dependencies instead of inlining code in the algorithm.",
|
|
3884
|
+
{
|
|
3885
|
+
renderer: import_zod2.z.enum(["p5", "three", "glsl", "canvas2d", "svg"]).optional().describe("Filter by renderer compatibility"),
|
|
3886
|
+
category: import_zod2.z.enum([
|
|
3887
|
+
"randomness",
|
|
3888
|
+
"noise",
|
|
3889
|
+
"math",
|
|
3890
|
+
"easing",
|
|
3891
|
+
"color",
|
|
3892
|
+
"vector",
|
|
3893
|
+
"geometry",
|
|
3894
|
+
"grid",
|
|
3895
|
+
"particle",
|
|
3896
|
+
"physics",
|
|
3897
|
+
"distribution",
|
|
3898
|
+
"pattern",
|
|
3899
|
+
"sdf",
|
|
3900
|
+
"transform",
|
|
3901
|
+
"animation",
|
|
3902
|
+
"string",
|
|
3903
|
+
"data-structure",
|
|
3904
|
+
"imaging"
|
|
3905
|
+
]).optional().describe("Filter by component category")
|
|
3906
|
+
},
|
|
3907
|
+
async (args) => {
|
|
3908
|
+
try {
|
|
3909
|
+
const result = await listComponents(state, args);
|
|
3910
|
+
return jsonResult(result);
|
|
3911
|
+
} catch (e) {
|
|
3912
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
);
|
|
3916
|
+
server.tool(
|
|
3917
|
+
"add_component",
|
|
3918
|
+
"Add a component dependency to an existing sketch. Resolves the component and any transitive dependencies from the registry, validates renderer compatibility, and writes the resolved form to the sketch file.",
|
|
3919
|
+
{
|
|
3920
|
+
sketchId: import_zod2.z.string().describe("ID of the sketch to add the component to"),
|
|
3921
|
+
component: import_zod2.z.string().describe("Component name (e.g. 'prng', 'noise-2d', 'glsl-noise')"),
|
|
3922
|
+
version: import_zod2.z.string().optional().describe("Version range (default: '^1.0.0')")
|
|
3923
|
+
},
|
|
3924
|
+
async (args) => {
|
|
3925
|
+
try {
|
|
3926
|
+
const result = await addComponent(state, args);
|
|
3927
|
+
return jsonResult(result);
|
|
3928
|
+
} catch (e) {
|
|
3929
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
);
|
|
3933
|
+
server.tool(
|
|
3934
|
+
"remove_component",
|
|
3935
|
+
"Remove a component dependency from a sketch. Checks for dependent components and warns if the algorithm references the component's exports.",
|
|
3936
|
+
{
|
|
3937
|
+
sketchId: import_zod2.z.string().describe("ID of the sketch to remove the component from"),
|
|
3938
|
+
component: import_zod2.z.string().describe("Component name to remove")
|
|
3939
|
+
},
|
|
3940
|
+
async (args) => {
|
|
3941
|
+
try {
|
|
3942
|
+
const result = await removeComponent(state, args);
|
|
3943
|
+
return jsonResult(result);
|
|
3944
|
+
} catch (e) {
|
|
3945
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
);
|
|
3949
|
+
}
|
|
3239
3950
|
function registerSelectionTools(server, state) {
|
|
3240
3951
|
server.tool(
|
|
3241
3952
|
"get_selection",
|
|
@@ -3621,6 +4332,247 @@ function registerExportTools(server, state) {
|
|
|
3621
4332
|
}
|
|
3622
4333
|
);
|
|
3623
4334
|
}
|
|
4335
|
+
function registerDesignTools(server, state) {
|
|
4336
|
+
server.tool(
|
|
4337
|
+
"design_add_layer",
|
|
4338
|
+
"Add a new design layer of a given type to the active sketch. Layer types come from registered plugins (e.g. 'typography:text', 'filter:grain', 'shapes:rect', 'guides:thirds').",
|
|
4339
|
+
{
|
|
4340
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
|
|
4341
|
+
type: import_zod2.z.string().describe("Layer type ID (e.g. 'typography:text', 'filter:grain', 'shapes:rect')"),
|
|
4342
|
+
name: import_zod2.z.string().optional().describe("Layer display name (default: type's display name)"),
|
|
4343
|
+
properties: import_zod2.z.record(import_zod2.z.unknown()).optional().describe("Initial layer properties (merged with type defaults)"),
|
|
4344
|
+
transform: import_zod2.z.object({
|
|
4345
|
+
x: import_zod2.z.number().optional(),
|
|
4346
|
+
y: import_zod2.z.number().optional(),
|
|
4347
|
+
width: import_zod2.z.number().optional(),
|
|
4348
|
+
height: import_zod2.z.number().optional(),
|
|
4349
|
+
rotation: import_zod2.z.number().optional(),
|
|
4350
|
+
scaleX: import_zod2.z.number().optional(),
|
|
4351
|
+
scaleY: import_zod2.z.number().optional(),
|
|
4352
|
+
anchorX: import_zod2.z.number().optional(),
|
|
4353
|
+
anchorY: import_zod2.z.number().optional()
|
|
4354
|
+
}).optional().describe("Layer transform (default: full canvas)"),
|
|
4355
|
+
opacity: import_zod2.z.number().optional().describe("Layer opacity 0\u20131 (default: 1)"),
|
|
4356
|
+
blendMode: import_zod2.z.string().optional().describe("Blend mode (default: 'normal')"),
|
|
4357
|
+
index: import_zod2.z.number().optional().describe("Insert position in layer stack (default: top)")
|
|
4358
|
+
},
|
|
4359
|
+
async (args) => {
|
|
4360
|
+
try {
|
|
4361
|
+
const result = await designAddLayer(state, args);
|
|
4362
|
+
return jsonResult(result);
|
|
4363
|
+
} catch (e) {
|
|
4364
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4365
|
+
}
|
|
4366
|
+
}
|
|
4367
|
+
);
|
|
4368
|
+
server.tool(
|
|
4369
|
+
"design_remove_layer",
|
|
4370
|
+
"Remove a design layer from the active sketch",
|
|
4371
|
+
{
|
|
4372
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
|
|
4373
|
+
layerId: import_zod2.z.string().describe("ID of the layer to remove")
|
|
4374
|
+
},
|
|
4375
|
+
async (args) => {
|
|
4376
|
+
try {
|
|
4377
|
+
const result = await designRemoveLayer(state, args);
|
|
4378
|
+
return jsonResult(result);
|
|
4379
|
+
} catch (e) {
|
|
4380
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4381
|
+
}
|
|
4382
|
+
}
|
|
4383
|
+
);
|
|
4384
|
+
server.tool(
|
|
4385
|
+
"design_list_layers",
|
|
4386
|
+
"List all design layers in the active sketch with their types, visibility, and key properties",
|
|
4387
|
+
{
|
|
4388
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)")
|
|
4389
|
+
},
|
|
4390
|
+
async (args) => {
|
|
4391
|
+
try {
|
|
4392
|
+
const result = await designListLayers(state, args);
|
|
4393
|
+
return jsonResult(result);
|
|
4394
|
+
} catch (e) {
|
|
4395
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
);
|
|
4399
|
+
server.tool(
|
|
4400
|
+
"design_get_layer",
|
|
4401
|
+
"Get full details of a single design layer including all properties and transform",
|
|
4402
|
+
{
|
|
4403
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
|
|
4404
|
+
layerId: import_zod2.z.string().describe("ID of the layer to inspect")
|
|
4405
|
+
},
|
|
4406
|
+
async (args) => {
|
|
4407
|
+
try {
|
|
4408
|
+
const result = await designGetLayer(state, args);
|
|
4409
|
+
return jsonResult(result);
|
|
4410
|
+
} catch (e) {
|
|
4411
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
);
|
|
4415
|
+
server.tool(
|
|
4416
|
+
"design_update_layer",
|
|
4417
|
+
"Update properties on a design layer (e.g. text content, filter intensity, shape fill color)",
|
|
4418
|
+
{
|
|
4419
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
|
|
4420
|
+
layerId: import_zod2.z.string().describe("ID of the layer to update"),
|
|
4421
|
+
name: import_zod2.z.string().optional().describe("New display name"),
|
|
4422
|
+
properties: import_zod2.z.record(import_zod2.z.unknown()).optional().describe("Property key-value pairs to set")
|
|
4423
|
+
},
|
|
4424
|
+
async (args) => {
|
|
4425
|
+
try {
|
|
4426
|
+
const result = await designUpdateLayer(state, args);
|
|
4427
|
+
return jsonResult(result);
|
|
4428
|
+
} catch (e) {
|
|
4429
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
);
|
|
4433
|
+
server.tool(
|
|
4434
|
+
"design_set_transform",
|
|
4435
|
+
"Set the position, size, rotation, and scale of a design layer",
|
|
4436
|
+
{
|
|
4437
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
|
|
4438
|
+
layerId: import_zod2.z.string().describe("ID of the layer to transform"),
|
|
4439
|
+
x: import_zod2.z.number().optional().describe("X position"),
|
|
4440
|
+
y: import_zod2.z.number().optional().describe("Y position"),
|
|
4441
|
+
width: import_zod2.z.number().optional().describe("Width"),
|
|
4442
|
+
height: import_zod2.z.number().optional().describe("Height"),
|
|
4443
|
+
rotation: import_zod2.z.number().optional().describe("Rotation in degrees"),
|
|
4444
|
+
scaleX: import_zod2.z.number().optional().describe("Horizontal scale"),
|
|
4445
|
+
scaleY: import_zod2.z.number().optional().describe("Vertical scale"),
|
|
4446
|
+
anchorX: import_zod2.z.number().optional().describe("Anchor X (0\u20131)"),
|
|
4447
|
+
anchorY: import_zod2.z.number().optional().describe("Anchor Y (0\u20131)")
|
|
4448
|
+
},
|
|
4449
|
+
async (args) => {
|
|
4450
|
+
try {
|
|
4451
|
+
const result = await designSetTransform(state, args);
|
|
4452
|
+
return jsonResult(result);
|
|
4453
|
+
} catch (e) {
|
|
4454
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
);
|
|
4458
|
+
server.tool(
|
|
4459
|
+
"design_set_blend",
|
|
4460
|
+
"Set blend mode and/or opacity on a design layer",
|
|
4461
|
+
{
|
|
4462
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
|
|
4463
|
+
layerId: import_zod2.z.string().describe("ID of the layer"),
|
|
4464
|
+
blendMode: import_zod2.z.enum([
|
|
4465
|
+
"normal",
|
|
4466
|
+
"multiply",
|
|
4467
|
+
"screen",
|
|
4468
|
+
"overlay",
|
|
4469
|
+
"darken",
|
|
4470
|
+
"lighten",
|
|
4471
|
+
"color-dodge",
|
|
4472
|
+
"color-burn",
|
|
4473
|
+
"hard-light",
|
|
4474
|
+
"soft-light",
|
|
4475
|
+
"difference",
|
|
4476
|
+
"exclusion",
|
|
4477
|
+
"hue",
|
|
4478
|
+
"saturation",
|
|
4479
|
+
"color",
|
|
4480
|
+
"luminosity"
|
|
4481
|
+
]).optional().describe("CSS blend mode"),
|
|
4482
|
+
opacity: import_zod2.z.number().optional().describe("Layer opacity 0\u20131")
|
|
4483
|
+
},
|
|
4484
|
+
async (args) => {
|
|
4485
|
+
try {
|
|
4486
|
+
const result = await designSetBlend(state, args);
|
|
4487
|
+
return jsonResult(result);
|
|
4488
|
+
} catch (e) {
|
|
4489
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
);
|
|
4493
|
+
server.tool(
|
|
4494
|
+
"design_reorder_layers",
|
|
4495
|
+
"Move a design layer to a new position in the z-order stack",
|
|
4496
|
+
{
|
|
4497
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
|
|
4498
|
+
layerId: import_zod2.z.string().describe("ID of the layer to move"),
|
|
4499
|
+
newIndex: import_zod2.z.number().describe("New position (0 = bottom, n-1 = top)")
|
|
4500
|
+
},
|
|
4501
|
+
async (args) => {
|
|
4502
|
+
try {
|
|
4503
|
+
const result = await designReorderLayers(state, args);
|
|
4504
|
+
return jsonResult(result);
|
|
4505
|
+
} catch (e) {
|
|
4506
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
);
|
|
4510
|
+
server.tool(
|
|
4511
|
+
"design_duplicate_layer",
|
|
4512
|
+
"Clone a design layer with a new ID, inserted directly above the source",
|
|
4513
|
+
{
|
|
4514
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
|
|
4515
|
+
layerId: import_zod2.z.string().describe("ID of the layer to duplicate")
|
|
4516
|
+
},
|
|
4517
|
+
async (args) => {
|
|
4518
|
+
try {
|
|
4519
|
+
const result = await designDuplicateLayer(state, args);
|
|
4520
|
+
return jsonResult(result);
|
|
4521
|
+
} catch (e) {
|
|
4522
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
);
|
|
4526
|
+
server.tool(
|
|
4527
|
+
"design_toggle_visibility",
|
|
4528
|
+
"Show or hide a design layer",
|
|
4529
|
+
{
|
|
4530
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
|
|
4531
|
+
layerId: import_zod2.z.string().describe("ID of the layer"),
|
|
4532
|
+
visible: import_zod2.z.boolean().optional().describe("Set visibility (default: toggle)")
|
|
4533
|
+
},
|
|
4534
|
+
async (args) => {
|
|
4535
|
+
try {
|
|
4536
|
+
const result = await designToggleVisibility(state, args);
|
|
4537
|
+
return jsonResult(result);
|
|
4538
|
+
} catch (e) {
|
|
4539
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4540
|
+
}
|
|
4541
|
+
}
|
|
4542
|
+
);
|
|
4543
|
+
server.tool(
|
|
4544
|
+
"design_lock_layer",
|
|
4545
|
+
"Lock or unlock a design layer to prevent accidental edits",
|
|
4546
|
+
{
|
|
4547
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)"),
|
|
4548
|
+
layerId: import_zod2.z.string().describe("ID of the layer"),
|
|
4549
|
+
locked: import_zod2.z.boolean().optional().describe("Set lock state (default: toggle)")
|
|
4550
|
+
},
|
|
4551
|
+
async (args) => {
|
|
4552
|
+
try {
|
|
4553
|
+
const result = await designLockLayer(state, args);
|
|
4554
|
+
return jsonResult(result);
|
|
4555
|
+
} catch (e) {
|
|
4556
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
);
|
|
4560
|
+
server.tool(
|
|
4561
|
+
"design_capture_composite",
|
|
4562
|
+
"Get info about the design layer composite for a sketch. For full visual capture use capture_screenshot.",
|
|
4563
|
+
{
|
|
4564
|
+
sketchId: import_zod2.z.string().optional().describe("Target sketch ID (default: selected sketch)")
|
|
4565
|
+
},
|
|
4566
|
+
async (args) => {
|
|
4567
|
+
try {
|
|
4568
|
+
const result = await designCaptureComposite(state, args);
|
|
4569
|
+
return jsonResult(result);
|
|
4570
|
+
} catch (e) {
|
|
4571
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
4572
|
+
}
|
|
4573
|
+
}
|
|
4574
|
+
);
|
|
4575
|
+
}
|
|
3624
4576
|
function registerKnowledgeTools(server, _state) {
|
|
3625
4577
|
server.tool(
|
|
3626
4578
|
"list_skills",
|
|
@@ -3673,7 +4625,7 @@ function registerKnowledgeTools(server, _state) {
|
|
|
3673
4625
|
|
|
3674
4626
|
// src/state.ts
|
|
3675
4627
|
var import_events = require("events");
|
|
3676
|
-
var
|
|
4628
|
+
var import_promises8 = require("fs/promises");
|
|
3677
4629
|
var import_path9 = require("path");
|
|
3678
4630
|
|
|
3679
4631
|
// src/sidecar.ts
|
|
@@ -3689,8 +4641,8 @@ function notifyMutation(type, payload) {
|
|
|
3689
4641
|
}
|
|
3690
4642
|
|
|
3691
4643
|
// src/state.ts
|
|
3692
|
-
var
|
|
3693
|
-
var
|
|
4644
|
+
var import_core12 = require("@genart-dev/core");
|
|
4645
|
+
var import_promises9 = require("fs/promises");
|
|
3694
4646
|
var EditorState = class extends import_events.EventEmitter {
|
|
3695
4647
|
/** Absolute path to the active .genart-workspace file, or null. */
|
|
3696
4648
|
workspacePath = null;
|
|
@@ -3712,6 +4664,10 @@ var EditorState = class extends import_events.EventEmitter {
|
|
|
3712
4664
|
* instead of writing to disk. Set by mcp-host for HTTP-based sessions.
|
|
3713
4665
|
*/
|
|
3714
4666
|
remoteMode = false;
|
|
4667
|
+
/** Plugin registry for design mode. Set during server initialization. */
|
|
4668
|
+
pluginRegistry = null;
|
|
4669
|
+
/** Layer stacks keyed by sketch ID. Created lazily when design tools are used. */
|
|
4670
|
+
layerStacks = /* @__PURE__ */ new Map();
|
|
3715
4671
|
constructor(options) {
|
|
3716
4672
|
super();
|
|
3717
4673
|
if (options?.basePath) {
|
|
@@ -3769,13 +4725,14 @@ var EditorState = class extends import_events.EventEmitter {
|
|
|
3769
4725
|
if (this.basePath && !absPath.startsWith(this.basePath)) {
|
|
3770
4726
|
throw new Error(`Path escapes sandbox: ${absPath}`);
|
|
3771
4727
|
}
|
|
3772
|
-
const raw = await (0,
|
|
4728
|
+
const raw = await (0, import_promises8.readFile)(absPath, "utf-8");
|
|
3773
4729
|
const json = JSON.parse(raw);
|
|
3774
|
-
const ws = (0,
|
|
4730
|
+
const ws = (0, import_core12.parseWorkspace)(json);
|
|
3775
4731
|
this.workspacePath = absPath;
|
|
3776
4732
|
this.workspace = ws;
|
|
3777
4733
|
this.sketches.clear();
|
|
3778
4734
|
this.selection.clear();
|
|
4735
|
+
this.layerStacks.clear();
|
|
3779
4736
|
for (const ref of ws.sketches) {
|
|
3780
4737
|
const sketchPath = this.resolveSketchPath(ref.file);
|
|
3781
4738
|
await this.loadSketch(sketchPath);
|
|
@@ -3787,9 +4744,9 @@ var EditorState = class extends import_events.EventEmitter {
|
|
|
3787
4744
|
if (this.basePath && !absPath.startsWith(this.basePath)) {
|
|
3788
4745
|
throw new Error(`Path escapes sandbox: ${absPath}`);
|
|
3789
4746
|
}
|
|
3790
|
-
const raw = await (0,
|
|
4747
|
+
const raw = await (0, import_promises8.readFile)(absPath, "utf-8");
|
|
3791
4748
|
const json = JSON.parse(raw);
|
|
3792
|
-
const definition = (0,
|
|
4749
|
+
const definition = (0, import_core12.parseGenart)(json);
|
|
3793
4750
|
this.sketches.set(definition.id, { definition, path: absPath });
|
|
3794
4751
|
this.emitMutation("sketch:loaded", { id: definition.id, path: absPath });
|
|
3795
4752
|
return definition;
|
|
@@ -3817,6 +4774,7 @@ var EditorState = class extends import_events.EventEmitter {
|
|
|
3817
4774
|
removeSketch(id) {
|
|
3818
4775
|
this.sketches.delete(id);
|
|
3819
4776
|
this.selection.delete(id);
|
|
4777
|
+
this.layerStacks.delete(id);
|
|
3820
4778
|
this.emitMutation("sketch:removed", { id });
|
|
3821
4779
|
}
|
|
3822
4780
|
/** Save the active workspace to disk. */
|
|
@@ -3824,18 +4782,18 @@ var EditorState = class extends import_events.EventEmitter {
|
|
|
3824
4782
|
if (!this.workspace || !this.workspacePath) {
|
|
3825
4783
|
throw new Error("No workspace is currently open");
|
|
3826
4784
|
}
|
|
3827
|
-
const json = (0,
|
|
4785
|
+
const json = (0, import_core12.serializeWorkspace)(this.workspace);
|
|
3828
4786
|
if (!this.remoteMode) {
|
|
3829
|
-
await (0,
|
|
4787
|
+
await (0, import_promises9.writeFile)(this.workspacePath, json, "utf-8");
|
|
3830
4788
|
}
|
|
3831
4789
|
this.emitMutation("workspace:saved", { path: this.workspacePath });
|
|
3832
4790
|
}
|
|
3833
4791
|
/** Save a sketch to disk. */
|
|
3834
4792
|
async saveSketch(id) {
|
|
3835
4793
|
const loaded = this.requireSketch(id);
|
|
3836
|
-
const json = (0,
|
|
4794
|
+
const json = (0, import_core12.serializeGenart)(loaded.definition);
|
|
3837
4795
|
if (!this.remoteMode) {
|
|
3838
|
-
await (0,
|
|
4796
|
+
await (0, import_promises9.writeFile)(loaded.path, json, "utf-8");
|
|
3839
4797
|
}
|
|
3840
4798
|
this.emitMutation("sketch:saved", { id, path: loaded.path });
|
|
3841
4799
|
}
|
|
@@ -3860,6 +4818,81 @@ var EditorState = class extends import_events.EventEmitter {
|
|
|
3860
4818
|
selection: Array.from(this.selection)
|
|
3861
4819
|
};
|
|
3862
4820
|
}
|
|
4821
|
+
/**
|
|
4822
|
+
* Get or create a LayerStackAccessor for a sketch.
|
|
4823
|
+
* Initializes from the sketch's persisted design layers.
|
|
4824
|
+
*/
|
|
4825
|
+
getLayerStack(sketchId) {
|
|
4826
|
+
let stack = this.layerStacks.get(sketchId);
|
|
4827
|
+
if (stack) return stack;
|
|
4828
|
+
const loaded = this.requireSketch(sketchId);
|
|
4829
|
+
const initialLayers = loaded.definition.layers ?? [];
|
|
4830
|
+
stack = (0, import_core12.createLayerStack)(initialLayers, (changeType) => {
|
|
4831
|
+
this.syncLayersToDefinition(sketchId);
|
|
4832
|
+
const mutationType = `design:${changeType}`;
|
|
4833
|
+
this.emitMutation(mutationType, { sketchId, changeType });
|
|
4834
|
+
});
|
|
4835
|
+
this.layerStacks.set(sketchId, stack);
|
|
4836
|
+
return stack;
|
|
4837
|
+
}
|
|
4838
|
+
/**
|
|
4839
|
+
* Sync the layer stack's current state back to the sketch definition.
|
|
4840
|
+
* Called automatically on every layer mutation.
|
|
4841
|
+
*/
|
|
4842
|
+
syncLayersToDefinition(sketchId) {
|
|
4843
|
+
const loaded = this.sketches.get(sketchId);
|
|
4844
|
+
const stack = this.layerStacks.get(sketchId);
|
|
4845
|
+
if (!loaded || !stack) return;
|
|
4846
|
+
const layers = stack.getAll();
|
|
4847
|
+
loaded.definition = {
|
|
4848
|
+
...loaded.definition,
|
|
4849
|
+
layers: layers.length > 0 ? layers : void 0
|
|
4850
|
+
};
|
|
4851
|
+
}
|
|
4852
|
+
/**
|
|
4853
|
+
* Create an McpToolContext for a plugin's MCP tool handler.
|
|
4854
|
+
* Provides access to the layer stack, sketch state, and change notifications.
|
|
4855
|
+
*/
|
|
4856
|
+
createMcpToolContext(sketchId) {
|
|
4857
|
+
const loaded = this.requireSketch(sketchId);
|
|
4858
|
+
const layerStack = this.getLayerStack(sketchId);
|
|
4859
|
+
const def = loaded.definition;
|
|
4860
|
+
const sketchState = {
|
|
4861
|
+
seed: def.state.seed,
|
|
4862
|
+
params: def.state.params,
|
|
4863
|
+
colorPalette: def.state.colorPalette,
|
|
4864
|
+
canvasWidth: def.canvas.width,
|
|
4865
|
+
canvasHeight: def.canvas.height,
|
|
4866
|
+
rendererId: def.renderer.type
|
|
4867
|
+
};
|
|
4868
|
+
return {
|
|
4869
|
+
layers: layerStack,
|
|
4870
|
+
sketchState,
|
|
4871
|
+
canvasWidth: def.canvas.width,
|
|
4872
|
+
canvasHeight: def.canvas.height,
|
|
4873
|
+
async resolveAsset(_assetId) {
|
|
4874
|
+
return null;
|
|
4875
|
+
},
|
|
4876
|
+
async captureComposite(_format) {
|
|
4877
|
+
throw new Error("captureComposite is not available in headless MCP mode");
|
|
4878
|
+
},
|
|
4879
|
+
emitChange(_changeType) {
|
|
4880
|
+
}
|
|
4881
|
+
};
|
|
4882
|
+
}
|
|
4883
|
+
/**
|
|
4884
|
+
* Get the currently selected sketch ID for design operations.
|
|
4885
|
+
* Returns the single selected sketch, or throws if none/multiple selected.
|
|
4886
|
+
*/
|
|
4887
|
+
requireSelectedSketchId() {
|
|
4888
|
+
if (this.selection.size === 0) {
|
|
4889
|
+
throw new Error("No sketch is selected. Use select_sketch or open_sketch first.");
|
|
4890
|
+
}
|
|
4891
|
+
if (this.selection.size > 1) {
|
|
4892
|
+
throw new Error("Multiple sketches are selected. Design operations require a single sketch.");
|
|
4893
|
+
}
|
|
4894
|
+
return this.selection.values().next().value;
|
|
4895
|
+
}
|
|
3863
4896
|
/** Emit a mutation event for external listeners (WebSocket broadcast, sidecar IPC). */
|
|
3864
4897
|
emitMutation(type, payload) {
|
|
3865
4898
|
this.emit("mutation", { type, payload });
|