@genart-dev/mcp-server 0.1.2 → 0.2.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 +387 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +383 -14
- package/dist/index.js.map +1 -1
- package/dist/lib.cjs +398 -34
- package/dist/lib.cjs.map +1 -1
- package/dist/lib.js +388 -19
- package/dist/lib.js.map +1 -1
- package/package.json +10 -9
package/dist/lib.cjs
CHANGED
|
@@ -470,10 +470,40 @@ async function createSketch(state, input) {
|
|
|
470
470
|
const adapter = registry4.resolve(rendererType);
|
|
471
471
|
algorithm = adapter.getAlgorithmTemplate();
|
|
472
472
|
}
|
|
473
|
+
let resolvedComponents;
|
|
474
|
+
if (input.components && Object.keys(input.components).length > 0) {
|
|
475
|
+
const shorthand = {};
|
|
476
|
+
for (const [name, value] of Object.entries(input.components)) {
|
|
477
|
+
if (typeof value === "string") {
|
|
478
|
+
shorthand[name] = value;
|
|
479
|
+
} else if (value.version) {
|
|
480
|
+
shorthand[name] = value.version;
|
|
481
|
+
} else if (value.code) {
|
|
482
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
483
|
+
resolvedComponents[name] = {
|
|
484
|
+
...value.version ? { version: value.version } : {},
|
|
485
|
+
code: value.code,
|
|
486
|
+
...value.exports ? { exports: value.exports } : {}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (Object.keys(shorthand).length > 0) {
|
|
491
|
+
const resolved = (0, import_core2.resolveComponents)(shorthand, rendererType);
|
|
492
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
493
|
+
for (const rc of resolved) {
|
|
494
|
+
resolvedComponents[rc.name] = {
|
|
495
|
+
version: rc.version,
|
|
496
|
+
code: rc.code,
|
|
497
|
+
exports: [...rc.exports]
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
473
502
|
const seed = input.seed ?? Math.floor(Math.random() * 1e5);
|
|
474
503
|
const ts = now2();
|
|
504
|
+
const hasComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
|
|
475
505
|
const sketch = {
|
|
476
|
-
genart: "1.1",
|
|
506
|
+
genart: hasComponents ? "1.2" : "1.1",
|
|
477
507
|
id: input.id,
|
|
478
508
|
title: input.title,
|
|
479
509
|
created: ts,
|
|
@@ -487,6 +517,7 @@ async function createSketch(state, input) {
|
|
|
487
517
|
...input.philosophy ? { philosophy: input.philosophy } : {},
|
|
488
518
|
...input.themes && input.themes.length > 0 ? { themes: input.themes } : {},
|
|
489
519
|
...input.skills && input.skills.length > 0 ? { skills: input.skills } : {},
|
|
520
|
+
...hasComponents ? { components: resolvedComponents } : {},
|
|
490
521
|
...input.agent ? { agent: input.agent } : {},
|
|
491
522
|
...input.model ? { model: input.model } : {}
|
|
492
523
|
};
|
|
@@ -670,10 +701,44 @@ async function updateAlgorithm(state, input) {
|
|
|
670
701
|
);
|
|
671
702
|
}
|
|
672
703
|
}
|
|
704
|
+
let resolvedComponents;
|
|
705
|
+
if (input.components && Object.keys(input.components).length > 0) {
|
|
706
|
+
const renderer = def.renderer.type;
|
|
707
|
+
const shorthand = {};
|
|
708
|
+
for (const [name, value] of Object.entries(input.components)) {
|
|
709
|
+
if (typeof value === "string") {
|
|
710
|
+
shorthand[name] = value;
|
|
711
|
+
} else if (value.version) {
|
|
712
|
+
shorthand[name] = value.version;
|
|
713
|
+
} else if (value.code) {
|
|
714
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
715
|
+
resolvedComponents[name] = {
|
|
716
|
+
...value.version ? { version: value.version } : {},
|
|
717
|
+
code: value.code,
|
|
718
|
+
...value.exports ? { exports: value.exports } : {}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (Object.keys(shorthand).length > 0) {
|
|
723
|
+
const resolved = (0, import_core2.resolveComponents)(shorthand, renderer);
|
|
724
|
+
if (!resolvedComponents) resolvedComponents = {};
|
|
725
|
+
for (const rc of resolved) {
|
|
726
|
+
resolvedComponents[rc.name] = {
|
|
727
|
+
version: rc.version,
|
|
728
|
+
code: rc.code,
|
|
729
|
+
exports: [...rc.exports]
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
const updated = ["algorithm"];
|
|
735
|
+
const hasNewComponents = resolvedComponents && Object.keys(resolvedComponents).length > 0;
|
|
736
|
+
if (hasNewComponents) updated.push("components");
|
|
673
737
|
const newDef = {
|
|
674
738
|
...def,
|
|
675
739
|
modified: now2(),
|
|
676
740
|
algorithm: input.algorithm,
|
|
741
|
+
...hasNewComponents ? { genart: "1.2", components: resolvedComponents } : {},
|
|
677
742
|
...input.agent ? { agent: input.agent } : {},
|
|
678
743
|
...input.model ? { model: input.model } : {}
|
|
679
744
|
};
|
|
@@ -687,7 +752,7 @@ async function updateAlgorithm(state, input) {
|
|
|
687
752
|
}
|
|
688
753
|
state.emitMutation("sketch:updated", {
|
|
689
754
|
id: input.sketchId,
|
|
690
|
-
updated
|
|
755
|
+
updated
|
|
691
756
|
});
|
|
692
757
|
return {
|
|
693
758
|
success: true,
|
|
@@ -695,6 +760,7 @@ async function updateAlgorithm(state, input) {
|
|
|
695
760
|
renderer: def.renderer.type,
|
|
696
761
|
algorithmLength: input.algorithm.length,
|
|
697
762
|
validationPassed,
|
|
763
|
+
...hasNewComponents ? { componentsUpdated: true } : {},
|
|
698
764
|
fileContent: json
|
|
699
765
|
};
|
|
700
766
|
}
|
|
@@ -1992,9 +2058,216 @@ ${guidelines}`,
|
|
|
1992
2058
|
};
|
|
1993
2059
|
}
|
|
1994
2060
|
|
|
1995
|
-
// src/tools/
|
|
2061
|
+
// src/tools/components.ts
|
|
1996
2062
|
var import_promises5 = require("fs/promises");
|
|
1997
2063
|
var import_core7 = require("@genart-dev/core");
|
|
2064
|
+
var VALID_RENDERERS2 = [
|
|
2065
|
+
"p5",
|
|
2066
|
+
"three",
|
|
2067
|
+
"glsl",
|
|
2068
|
+
"canvas2d",
|
|
2069
|
+
"svg"
|
|
2070
|
+
];
|
|
2071
|
+
var RENDERER_TARGET = {
|
|
2072
|
+
p5: "js",
|
|
2073
|
+
three: "js",
|
|
2074
|
+
canvas2d: "js",
|
|
2075
|
+
svg: "js",
|
|
2076
|
+
glsl: "glsl"
|
|
2077
|
+
};
|
|
2078
|
+
async function listComponents(_state, input) {
|
|
2079
|
+
let entries = Object.values(import_core7.COMPONENT_REGISTRY);
|
|
2080
|
+
if (input.renderer) {
|
|
2081
|
+
const renderer = input.renderer;
|
|
2082
|
+
const target = RENDERER_TARGET[renderer];
|
|
2083
|
+
if (!target) {
|
|
2084
|
+
throw new Error(
|
|
2085
|
+
`Unknown renderer type: '${input.renderer}'. Valid types: ${VALID_RENDERERS2.join(", ")}`
|
|
2086
|
+
);
|
|
2087
|
+
}
|
|
2088
|
+
entries = entries.filter(
|
|
2089
|
+
(e) => e.target === target && (e.renderers.length === 0 || e.renderers.includes(renderer))
|
|
2090
|
+
);
|
|
2091
|
+
}
|
|
2092
|
+
if (input.category) {
|
|
2093
|
+
const cat = input.category;
|
|
2094
|
+
entries = entries.filter((e) => e.category === cat);
|
|
2095
|
+
}
|
|
2096
|
+
entries.sort((a, b) => {
|
|
2097
|
+
const catCmp = a.category.localeCompare(b.category);
|
|
2098
|
+
if (catCmp !== 0) return catCmp;
|
|
2099
|
+
return a.name.localeCompare(b.name);
|
|
2100
|
+
});
|
|
2101
|
+
const components = entries.map((e) => ({
|
|
2102
|
+
name: e.name,
|
|
2103
|
+
version: e.version,
|
|
2104
|
+
category: e.category,
|
|
2105
|
+
target: e.target,
|
|
2106
|
+
exports: [...e.exports],
|
|
2107
|
+
dependencies: [...e.dependencies],
|
|
2108
|
+
description: e.description
|
|
2109
|
+
}));
|
|
2110
|
+
return {
|
|
2111
|
+
count: components.length,
|
|
2112
|
+
components
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
async function addComponent(state, input) {
|
|
2116
|
+
const loaded = state.requireSketch(input.sketchId);
|
|
2117
|
+
const def = loaded.definition;
|
|
2118
|
+
const renderer = def.renderer.type;
|
|
2119
|
+
const entry = import_core7.COMPONENT_REGISTRY[input.component];
|
|
2120
|
+
if (!entry) {
|
|
2121
|
+
throw new Error(`Unknown component: "${input.component}"`);
|
|
2122
|
+
}
|
|
2123
|
+
const target = RENDERER_TARGET[renderer];
|
|
2124
|
+
if (entry.target !== target) {
|
|
2125
|
+
throw new Error(
|
|
2126
|
+
`Component "${input.component}" has target "${entry.target}" but renderer "${renderer}" requires target "${target}"`
|
|
2127
|
+
);
|
|
2128
|
+
}
|
|
2129
|
+
if (entry.renderers.length > 0 && !entry.renderers.includes(renderer)) {
|
|
2130
|
+
throw new Error(
|
|
2131
|
+
`Component "${input.component}" is not compatible with renderer "${renderer}". Compatible: ${entry.renderers.join(", ")}`
|
|
2132
|
+
);
|
|
2133
|
+
}
|
|
2134
|
+
const existingComponents = {};
|
|
2135
|
+
if (def.components) {
|
|
2136
|
+
for (const [name, value] of Object.entries(def.components)) {
|
|
2137
|
+
if (typeof value === "string") {
|
|
2138
|
+
existingComponents[name] = value;
|
|
2139
|
+
} else if (value.version) {
|
|
2140
|
+
existingComponents[name] = value.version;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
if (existingComponents[input.component]) {
|
|
2145
|
+
throw new Error(
|
|
2146
|
+
`Component "${input.component}" is already present in sketch "${input.sketchId}"`
|
|
2147
|
+
);
|
|
2148
|
+
}
|
|
2149
|
+
existingComponents[input.component] = input.version ?? "^1.0.0";
|
|
2150
|
+
const resolved = (0, import_core7.resolveComponents)(existingComponents, renderer);
|
|
2151
|
+
const resolvedRecord = {};
|
|
2152
|
+
for (const rc of resolved) {
|
|
2153
|
+
resolvedRecord[rc.name] = {
|
|
2154
|
+
version: rc.version,
|
|
2155
|
+
code: rc.code,
|
|
2156
|
+
exports: [...rc.exports]
|
|
2157
|
+
};
|
|
2158
|
+
}
|
|
2159
|
+
const previousNames = new Set(
|
|
2160
|
+
def.components ? Object.keys(def.components) : []
|
|
2161
|
+
);
|
|
2162
|
+
const added = resolved.map((rc) => rc.name).filter((name) => !previousNames.has(name));
|
|
2163
|
+
const newDef = {
|
|
2164
|
+
...def,
|
|
2165
|
+
genart: "1.2",
|
|
2166
|
+
modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2167
|
+
components: resolvedRecord
|
|
2168
|
+
};
|
|
2169
|
+
state.sketches.set(input.sketchId, {
|
|
2170
|
+
definition: newDef,
|
|
2171
|
+
path: loaded.path
|
|
2172
|
+
});
|
|
2173
|
+
const json = (0, import_core7.serializeGenart)(newDef);
|
|
2174
|
+
if (!state.remoteMode) {
|
|
2175
|
+
await (0, import_promises5.writeFile)(loaded.path, json, "utf-8");
|
|
2176
|
+
}
|
|
2177
|
+
state.emitMutation("sketch:updated", {
|
|
2178
|
+
id: input.sketchId,
|
|
2179
|
+
updated: ["components"]
|
|
2180
|
+
});
|
|
2181
|
+
return {
|
|
2182
|
+
success: true,
|
|
2183
|
+
sketchId: input.sketchId,
|
|
2184
|
+
components: resolvedRecord,
|
|
2185
|
+
added,
|
|
2186
|
+
fileContent: json
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
async function removeComponent(state, input) {
|
|
2190
|
+
const loaded = state.requireSketch(input.sketchId);
|
|
2191
|
+
const def = loaded.definition;
|
|
2192
|
+
if (!def.components || !def.components[input.component]) {
|
|
2193
|
+
throw new Error(
|
|
2194
|
+
`Component "${input.component}" is not present in sketch "${input.sketchId}"`
|
|
2195
|
+
);
|
|
2196
|
+
}
|
|
2197
|
+
const remaining = { ...def.components };
|
|
2198
|
+
delete remaining[input.component];
|
|
2199
|
+
for (const [name, value] of Object.entries(remaining)) {
|
|
2200
|
+
const entry = import_core7.COMPONENT_REGISTRY[name];
|
|
2201
|
+
if (entry && entry.dependencies.includes(input.component)) {
|
|
2202
|
+
throw new Error(
|
|
2203
|
+
`Cannot remove "${input.component}": component "${name}" depends on it`
|
|
2204
|
+
);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
let warning;
|
|
2208
|
+
const removedValue = def.components[input.component];
|
|
2209
|
+
const exports2 = typeof removedValue === "string" ? import_core7.COMPONENT_REGISTRY[input.component]?.exports ?? [] : removedValue.exports ?? [];
|
|
2210
|
+
const usedExports = exports2.filter((exp) => def.algorithm.includes(exp));
|
|
2211
|
+
if (usedExports.length > 0) {
|
|
2212
|
+
warning = `Algorithm may reference these exports from "${input.component}": ${usedExports.join(", ")}. Review your algorithm after removal.`;
|
|
2213
|
+
}
|
|
2214
|
+
const removed = [input.component];
|
|
2215
|
+
const neededDeps = /* @__PURE__ */ new Set();
|
|
2216
|
+
for (const name of Object.keys(remaining)) {
|
|
2217
|
+
const entry = import_core7.COMPONENT_REGISTRY[name];
|
|
2218
|
+
if (entry) {
|
|
2219
|
+
collectTransitiveDeps(name, neededDeps);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
for (const name of Object.keys(remaining)) {
|
|
2223
|
+
if (!neededDeps.has(name) && !isDirectComponent(name, remaining)) {
|
|
2224
|
+
delete remaining[name];
|
|
2225
|
+
removed.push(name);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
const hasRemaining = Object.keys(remaining).length > 0;
|
|
2229
|
+
const newDef = {
|
|
2230
|
+
...def,
|
|
2231
|
+
modified: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2232
|
+
...hasRemaining ? { components: remaining } : { components: void 0 }
|
|
2233
|
+
};
|
|
2234
|
+
state.sketches.set(input.sketchId, {
|
|
2235
|
+
definition: newDef,
|
|
2236
|
+
path: loaded.path
|
|
2237
|
+
});
|
|
2238
|
+
const json = (0, import_core7.serializeGenart)(newDef);
|
|
2239
|
+
if (!state.remoteMode) {
|
|
2240
|
+
await (0, import_promises5.writeFile)(loaded.path, json, "utf-8");
|
|
2241
|
+
}
|
|
2242
|
+
state.emitMutation("sketch:updated", {
|
|
2243
|
+
id: input.sketchId,
|
|
2244
|
+
updated: ["components"]
|
|
2245
|
+
});
|
|
2246
|
+
return {
|
|
2247
|
+
success: true,
|
|
2248
|
+
sketchId: input.sketchId,
|
|
2249
|
+
removed,
|
|
2250
|
+
...warning ? { warning } : {},
|
|
2251
|
+
fileContent: json
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
function collectTransitiveDeps(name, deps) {
|
|
2255
|
+
const entry = import_core7.COMPONENT_REGISTRY[name];
|
|
2256
|
+
if (!entry) return;
|
|
2257
|
+
deps.add(name);
|
|
2258
|
+
for (const dep of entry.dependencies) {
|
|
2259
|
+
if (!deps.has(dep)) {
|
|
2260
|
+
collectTransitiveDeps(dep, deps);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
function isDirectComponent(name, components) {
|
|
2265
|
+
return name in components;
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
// src/tools/capture.ts
|
|
2269
|
+
var import_promises6 = require("fs/promises");
|
|
2270
|
+
var import_core8 = require("@genart-dev/core");
|
|
1998
2271
|
|
|
1999
2272
|
// src/capture/headless.ts
|
|
2000
2273
|
var cachedModule = null;
|
|
@@ -2088,7 +2361,7 @@ async function captureHtmlMulti(options) {
|
|
|
2088
2361
|
}
|
|
2089
2362
|
|
|
2090
2363
|
// src/tools/capture.ts
|
|
2091
|
-
var registry2 = (0,
|
|
2364
|
+
var registry2 = (0, import_core8.createDefaultRegistry)();
|
|
2092
2365
|
function applyOverrides(sketch, overrides) {
|
|
2093
2366
|
if (overrides.seed === void 0 && overrides.params === void 0) {
|
|
2094
2367
|
return sketch;
|
|
@@ -2169,7 +2442,7 @@ async function buildScreenshotMetadata(state, multi, info) {
|
|
|
2169
2442
|
previewPath: info.previewPath
|
|
2170
2443
|
};
|
|
2171
2444
|
if (!state.remoteMode) {
|
|
2172
|
-
await (0,
|
|
2445
|
+
await (0, import_promises6.writeFile)(info.previewPath, multi.previewPng);
|
|
2173
2446
|
metadata.savedPreviewTo = info.previewPath;
|
|
2174
2447
|
}
|
|
2175
2448
|
return metadata;
|
|
@@ -2230,15 +2503,15 @@ async function captureBatch(state, input) {
|
|
|
2230
2503
|
|
|
2231
2504
|
// src/tools/export.ts
|
|
2232
2505
|
var import_fs = require("fs");
|
|
2233
|
-
var
|
|
2506
|
+
var import_promises7 = require("fs/promises");
|
|
2234
2507
|
var import_path8 = require("path");
|
|
2235
2508
|
var import_archiver = __toESM(require("archiver"), 1);
|
|
2236
|
-
var
|
|
2237
|
-
var registry3 = (0,
|
|
2509
|
+
var import_core9 = require("@genart-dev/core");
|
|
2510
|
+
var registry3 = (0, import_core9.createDefaultRegistry)();
|
|
2238
2511
|
async function validateOutputPath(outputPath) {
|
|
2239
2512
|
const parentDir = (0, import_path8.dirname)(outputPath);
|
|
2240
2513
|
try {
|
|
2241
|
-
const s = await (0,
|
|
2514
|
+
const s = await (0, import_promises7.stat)(parentDir);
|
|
2242
2515
|
if (!s.isDirectory()) {
|
|
2243
2516
|
throw new Error(`Parent directory does not exist: ${parentDir}`);
|
|
2244
2517
|
}
|
|
@@ -2247,7 +2520,7 @@ async function validateOutputPath(outputPath) {
|
|
|
2247
2520
|
throw new Error(`Parent directory does not exist: ${parentDir}`);
|
|
2248
2521
|
}
|
|
2249
2522
|
try {
|
|
2250
|
-
await (0,
|
|
2523
|
+
await (0, import_promises7.stat)(outputPath);
|
|
2251
2524
|
throw new Error(
|
|
2252
2525
|
`File already exists at ${outputPath}. Delete it first or use a different path.`
|
|
2253
2526
|
);
|
|
@@ -2302,7 +2575,7 @@ async function exportHtml(sketch, outputPath) {
|
|
|
2302
2575
|
const adapter = registry3.resolve(sketch.renderer.type);
|
|
2303
2576
|
const html = adapter.generateStandaloneHTML(sketch);
|
|
2304
2577
|
const content = Buffer.from(html, "utf-8");
|
|
2305
|
-
await (0,
|
|
2578
|
+
await (0, import_promises7.writeFile)(outputPath, content);
|
|
2306
2579
|
return {
|
|
2307
2580
|
success: true,
|
|
2308
2581
|
sketchId: sketch.id,
|
|
@@ -2318,7 +2591,7 @@ async function exportPng(sketch, input) {
|
|
|
2318
2591
|
const width = input.width ?? sketch.canvas.width;
|
|
2319
2592
|
const height = input.height ?? sketch.canvas.height;
|
|
2320
2593
|
const result = await captureHtml({ html, width, height });
|
|
2321
|
-
await (0,
|
|
2594
|
+
await (0, import_promises7.writeFile)(input.outputPath, result.bytes);
|
|
2322
2595
|
return {
|
|
2323
2596
|
success: true,
|
|
2324
2597
|
sketchId: sketch.id,
|
|
@@ -2333,7 +2606,7 @@ async function exportSvg(sketch, input) {
|
|
|
2333
2606
|
const height = input.height ?? sketch.canvas.height;
|
|
2334
2607
|
if (sketch.renderer.type === "svg") {
|
|
2335
2608
|
const content2 = Buffer.from(sketch.algorithm, "utf-8");
|
|
2336
|
-
await (0,
|
|
2609
|
+
await (0, import_promises7.writeFile)(input.outputPath, content2);
|
|
2337
2610
|
return {
|
|
2338
2611
|
success: true,
|
|
2339
2612
|
sketchId: sketch.id,
|
|
@@ -2355,7 +2628,7 @@ async function exportSvg(sketch, input) {
|
|
|
2355
2628
|
href="data:image/png;base64,${b64}"/>
|
|
2356
2629
|
</svg>`;
|
|
2357
2630
|
const content = Buffer.from(svg, "utf-8");
|
|
2358
|
-
await (0,
|
|
2631
|
+
await (0, import_promises7.writeFile)(input.outputPath, content);
|
|
2359
2632
|
return {
|
|
2360
2633
|
success: true,
|
|
2361
2634
|
sketchId: sketch.id,
|
|
@@ -2368,7 +2641,7 @@ async function exportSvg(sketch, input) {
|
|
|
2368
2641
|
}
|
|
2369
2642
|
async function exportAlgorithm(sketch, outputPath) {
|
|
2370
2643
|
const content = Buffer.from(sketch.algorithm, "utf-8");
|
|
2371
|
-
await (0,
|
|
2644
|
+
await (0, import_promises7.writeFile)(outputPath, content);
|
|
2372
2645
|
return {
|
|
2373
2646
|
success: true,
|
|
2374
2647
|
sketchId: sketch.id,
|
|
@@ -2383,7 +2656,7 @@ async function exportZip(sketch, input) {
|
|
|
2383
2656
|
const width = input.width ?? sketch.canvas.width;
|
|
2384
2657
|
const height = input.height ?? sketch.canvas.height;
|
|
2385
2658
|
const html = adapter.generateStandaloneHTML(sketch);
|
|
2386
|
-
const genartJson = (0,
|
|
2659
|
+
const genartJson = (0, import_core9.serializeGenart)(sketch);
|
|
2387
2660
|
const algorithm = sketch.algorithm;
|
|
2388
2661
|
const algExt = algorithmExtension(sketch.renderer.type);
|
|
2389
2662
|
const captureResult = await captureHtml({ html, width, height });
|
|
@@ -2400,7 +2673,7 @@ async function exportZip(sketch, input) {
|
|
|
2400
2673
|
archive.append(genartJson, { name: `${sketch.id}.genart` });
|
|
2401
2674
|
await archive.finalize();
|
|
2402
2675
|
await finished;
|
|
2403
|
-
const s = await (0,
|
|
2676
|
+
const s = await (0, import_promises7.stat)(input.outputPath);
|
|
2404
2677
|
return {
|
|
2405
2678
|
success: true,
|
|
2406
2679
|
sketchId: sketch.id,
|
|
@@ -2418,7 +2691,7 @@ async function exportZip(sketch, input) {
|
|
|
2418
2691
|
}
|
|
2419
2692
|
|
|
2420
2693
|
// src/resources/index.ts
|
|
2421
|
-
var
|
|
2694
|
+
var import_core10 = require("@genart-dev/core");
|
|
2422
2695
|
function registerResources(server, state) {
|
|
2423
2696
|
registerSkillsResource(server);
|
|
2424
2697
|
registerCanvasPresetsResource(server);
|
|
@@ -2426,7 +2699,7 @@ function registerResources(server, state) {
|
|
|
2426
2699
|
registerRenderersResource(server);
|
|
2427
2700
|
}
|
|
2428
2701
|
function registerSkillsResource(server) {
|
|
2429
|
-
const skillRegistry = (0,
|
|
2702
|
+
const skillRegistry = (0, import_core10.createDefaultSkillRegistry)();
|
|
2430
2703
|
server.resource(
|
|
2431
2704
|
"skills",
|
|
2432
2705
|
"genart://skills",
|
|
@@ -2477,7 +2750,7 @@ function registerCanvasPresetsResource(server) {
|
|
|
2477
2750
|
mimeType: "application/json",
|
|
2478
2751
|
text: JSON.stringify(
|
|
2479
2752
|
{
|
|
2480
|
-
presets:
|
|
2753
|
+
presets: import_core10.CANVAS_PRESETS.map((p) => ({
|
|
2481
2754
|
id: p.id,
|
|
2482
2755
|
label: p.label,
|
|
2483
2756
|
category: p.category,
|
|
@@ -2534,7 +2807,7 @@ function registerGalleryResource(server, state) {
|
|
|
2534
2807
|
);
|
|
2535
2808
|
}
|
|
2536
2809
|
function registerRenderersResource(server) {
|
|
2537
|
-
const registry4 = (0,
|
|
2810
|
+
const registry4 = (0, import_core10.createDefaultRegistry)();
|
|
2538
2811
|
server.resource(
|
|
2539
2812
|
"renderers",
|
|
2540
2813
|
"genart://renderers",
|
|
@@ -2916,6 +3189,7 @@ function createServer(state) {
|
|
|
2916
3189
|
);
|
|
2917
3190
|
registerWorkspaceTools(server, state);
|
|
2918
3191
|
registerSketchTools(server, state);
|
|
3192
|
+
registerComponentTools(server, state);
|
|
2919
3193
|
registerSelectionTools(server, state);
|
|
2920
3194
|
registerParameterTools(server, state);
|
|
2921
3195
|
registerArrangementTools(server, state);
|
|
@@ -3019,7 +3293,7 @@ function registerWorkspaceTools(server, state) {
|
|
|
3019
3293
|
function registerSketchTools(server, state) {
|
|
3020
3294
|
server.tool(
|
|
3021
3295
|
"create_sketch",
|
|
3022
|
-
|
|
3296
|
+
'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
3297
|
{
|
|
3024
3298
|
id: import_zod2.z.string().describe("URL-safe kebab-case identifier"),
|
|
3025
3299
|
title: import_zod2.z.string().describe("Human-readable title"),
|
|
@@ -3057,6 +3331,16 @@ function registerSketchTools(server, state) {
|
|
|
3057
3331
|
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
3332
|
seed: import_zod2.z.number().optional().describe("Initial random seed (default: random)"),
|
|
3059
3333
|
skills: import_zod2.z.array(import_zod2.z.string()).optional().describe("Design skill references"),
|
|
3334
|
+
components: import_zod2.z.record(
|
|
3335
|
+
import_zod2.z.union([
|
|
3336
|
+
import_zod2.z.string(),
|
|
3337
|
+
import_zod2.z.object({
|
|
3338
|
+
version: import_zod2.z.string().optional(),
|
|
3339
|
+
code: import_zod2.z.string().optional(),
|
|
3340
|
+
exports: import_zod2.z.array(import_zod2.z.string()).optional()
|
|
3341
|
+
})
|
|
3342
|
+
])
|
|
3343
|
+
).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
3344
|
addToWorkspace: import_zod2.z.string().optional().describe("Path to workspace to add sketch to after creation"),
|
|
3061
3345
|
agent: import_zod2.z.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
|
|
3062
3346
|
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 +3420,21 @@ function registerSketchTools(server, state) {
|
|
|
3136
3420
|
);
|
|
3137
3421
|
server.tool(
|
|
3138
3422
|
"update_algorithm",
|
|
3139
|
-
"Replace the algorithm source code of a sketch",
|
|
3423
|
+
"Replace the algorithm source code of a sketch. If adding/changing components, pass them in the components field alongside the algorithm.",
|
|
3140
3424
|
{
|
|
3141
3425
|
sketchId: import_zod2.z.string().describe("ID of the sketch to update"),
|
|
3142
3426
|
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
3427
|
validate: import_zod2.z.boolean().optional().describe("Run renderer-specific validation before saving (default: true)"),
|
|
3428
|
+
components: import_zod2.z.record(
|
|
3429
|
+
import_zod2.z.union([
|
|
3430
|
+
import_zod2.z.string(),
|
|
3431
|
+
import_zod2.z.object({
|
|
3432
|
+
version: import_zod2.z.string().optional(),
|
|
3433
|
+
code: import_zod2.z.string().optional(),
|
|
3434
|
+
exports: import_zod2.z.array(import_zod2.z.string()).optional()
|
|
3435
|
+
})
|
|
3436
|
+
])
|
|
3437
|
+
).optional().describe("Component dependencies to resolve alongside the algorithm update. Use list_components to see available."),
|
|
3144
3438
|
agent: import_zod2.z.string().optional().describe("Your CLI agent name (e.g. 'claude-code', 'codex-cli', 'gemini-cli', 'opencode', 'kiro')"),
|
|
3145
3439
|
model: import_zod2.z.string().optional().describe("Your AI model identifier (e.g. 'claude-opus-4-6', 'gpt-4o', 'gemini-2.5-pro')")
|
|
3146
3440
|
},
|
|
@@ -3236,6 +3530,76 @@ function registerSketchTools(server, state) {
|
|
|
3236
3530
|
}
|
|
3237
3531
|
);
|
|
3238
3532
|
}
|
|
3533
|
+
function registerComponentTools(server, state) {
|
|
3534
|
+
server.tool(
|
|
3535
|
+
"list_components",
|
|
3536
|
+
"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.",
|
|
3537
|
+
{
|
|
3538
|
+
renderer: import_zod2.z.enum(["p5", "three", "glsl", "canvas2d", "svg"]).optional().describe("Filter by renderer compatibility"),
|
|
3539
|
+
category: import_zod2.z.enum([
|
|
3540
|
+
"randomness",
|
|
3541
|
+
"noise",
|
|
3542
|
+
"math",
|
|
3543
|
+
"easing",
|
|
3544
|
+
"color",
|
|
3545
|
+
"vector",
|
|
3546
|
+
"geometry",
|
|
3547
|
+
"grid",
|
|
3548
|
+
"particle",
|
|
3549
|
+
"physics",
|
|
3550
|
+
"distribution",
|
|
3551
|
+
"pattern",
|
|
3552
|
+
"sdf",
|
|
3553
|
+
"transform",
|
|
3554
|
+
"animation",
|
|
3555
|
+
"string",
|
|
3556
|
+
"data-structure",
|
|
3557
|
+
"imaging"
|
|
3558
|
+
]).optional().describe("Filter by component category")
|
|
3559
|
+
},
|
|
3560
|
+
async (args) => {
|
|
3561
|
+
try {
|
|
3562
|
+
const result = await listComponents(state, args);
|
|
3563
|
+
return jsonResult(result);
|
|
3564
|
+
} catch (e) {
|
|
3565
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
);
|
|
3569
|
+
server.tool(
|
|
3570
|
+
"add_component",
|
|
3571
|
+
"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.",
|
|
3572
|
+
{
|
|
3573
|
+
sketchId: import_zod2.z.string().describe("ID of the sketch to add the component to"),
|
|
3574
|
+
component: import_zod2.z.string().describe("Component name (e.g. 'prng', 'noise-2d', 'glsl-noise')"),
|
|
3575
|
+
version: import_zod2.z.string().optional().describe("Version range (default: '^1.0.0')")
|
|
3576
|
+
},
|
|
3577
|
+
async (args) => {
|
|
3578
|
+
try {
|
|
3579
|
+
const result = await addComponent(state, args);
|
|
3580
|
+
return jsonResult(result);
|
|
3581
|
+
} catch (e) {
|
|
3582
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
);
|
|
3586
|
+
server.tool(
|
|
3587
|
+
"remove_component",
|
|
3588
|
+
"Remove a component dependency from a sketch. Checks for dependent components and warns if the algorithm references the component's exports.",
|
|
3589
|
+
{
|
|
3590
|
+
sketchId: import_zod2.z.string().describe("ID of the sketch to remove the component from"),
|
|
3591
|
+
component: import_zod2.z.string().describe("Component name to remove")
|
|
3592
|
+
},
|
|
3593
|
+
async (args) => {
|
|
3594
|
+
try {
|
|
3595
|
+
const result = await removeComponent(state, args);
|
|
3596
|
+
return jsonResult(result);
|
|
3597
|
+
} catch (e) {
|
|
3598
|
+
return toolError(e instanceof Error ? e.message : String(e));
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
);
|
|
3602
|
+
}
|
|
3239
3603
|
function registerSelectionTools(server, state) {
|
|
3240
3604
|
server.tool(
|
|
3241
3605
|
"get_selection",
|
|
@@ -3673,7 +4037,7 @@ function registerKnowledgeTools(server, _state) {
|
|
|
3673
4037
|
|
|
3674
4038
|
// src/state.ts
|
|
3675
4039
|
var import_events = require("events");
|
|
3676
|
-
var
|
|
4040
|
+
var import_promises8 = require("fs/promises");
|
|
3677
4041
|
var import_path9 = require("path");
|
|
3678
4042
|
|
|
3679
4043
|
// src/sidecar.ts
|
|
@@ -3689,8 +4053,8 @@ function notifyMutation(type, payload) {
|
|
|
3689
4053
|
}
|
|
3690
4054
|
|
|
3691
4055
|
// src/state.ts
|
|
3692
|
-
var
|
|
3693
|
-
var
|
|
4056
|
+
var import_core11 = require("@genart-dev/core");
|
|
4057
|
+
var import_promises9 = require("fs/promises");
|
|
3694
4058
|
var EditorState = class extends import_events.EventEmitter {
|
|
3695
4059
|
/** Absolute path to the active .genart-workspace file, or null. */
|
|
3696
4060
|
workspacePath = null;
|
|
@@ -3769,9 +4133,9 @@ var EditorState = class extends import_events.EventEmitter {
|
|
|
3769
4133
|
if (this.basePath && !absPath.startsWith(this.basePath)) {
|
|
3770
4134
|
throw new Error(`Path escapes sandbox: ${absPath}`);
|
|
3771
4135
|
}
|
|
3772
|
-
const raw = await (0,
|
|
4136
|
+
const raw = await (0, import_promises8.readFile)(absPath, "utf-8");
|
|
3773
4137
|
const json = JSON.parse(raw);
|
|
3774
|
-
const ws = (0,
|
|
4138
|
+
const ws = (0, import_core11.parseWorkspace)(json);
|
|
3775
4139
|
this.workspacePath = absPath;
|
|
3776
4140
|
this.workspace = ws;
|
|
3777
4141
|
this.sketches.clear();
|
|
@@ -3787,9 +4151,9 @@ var EditorState = class extends import_events.EventEmitter {
|
|
|
3787
4151
|
if (this.basePath && !absPath.startsWith(this.basePath)) {
|
|
3788
4152
|
throw new Error(`Path escapes sandbox: ${absPath}`);
|
|
3789
4153
|
}
|
|
3790
|
-
const raw = await (0,
|
|
4154
|
+
const raw = await (0, import_promises8.readFile)(absPath, "utf-8");
|
|
3791
4155
|
const json = JSON.parse(raw);
|
|
3792
|
-
const definition = (0,
|
|
4156
|
+
const definition = (0, import_core11.parseGenart)(json);
|
|
3793
4157
|
this.sketches.set(definition.id, { definition, path: absPath });
|
|
3794
4158
|
this.emitMutation("sketch:loaded", { id: definition.id, path: absPath });
|
|
3795
4159
|
return definition;
|
|
@@ -3824,18 +4188,18 @@ var EditorState = class extends import_events.EventEmitter {
|
|
|
3824
4188
|
if (!this.workspace || !this.workspacePath) {
|
|
3825
4189
|
throw new Error("No workspace is currently open");
|
|
3826
4190
|
}
|
|
3827
|
-
const json = (0,
|
|
4191
|
+
const json = (0, import_core11.serializeWorkspace)(this.workspace);
|
|
3828
4192
|
if (!this.remoteMode) {
|
|
3829
|
-
await (0,
|
|
4193
|
+
await (0, import_promises9.writeFile)(this.workspacePath, json, "utf-8");
|
|
3830
4194
|
}
|
|
3831
4195
|
this.emitMutation("workspace:saved", { path: this.workspacePath });
|
|
3832
4196
|
}
|
|
3833
4197
|
/** Save a sketch to disk. */
|
|
3834
4198
|
async saveSketch(id) {
|
|
3835
4199
|
const loaded = this.requireSketch(id);
|
|
3836
|
-
const json = (0,
|
|
4200
|
+
const json = (0, import_core11.serializeGenart)(loaded.definition);
|
|
3837
4201
|
if (!this.remoteMode) {
|
|
3838
|
-
await (0,
|
|
4202
|
+
await (0, import_promises9.writeFile)(loaded.path, json, "utf-8");
|
|
3839
4203
|
}
|
|
3840
4204
|
this.emitMutation("sketch:saved", { id, path: loaded.path });
|
|
3841
4205
|
}
|