@zseven-w/openpencil 0.5.2 → 0.6.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/openpencil-cli.cjs +860 -279
- package/dist/openpencil-cli.cjs.map +4 -4
- package/package.json +1 -1
package/dist/openpencil-cli.cjs
CHANGED
|
@@ -99,6 +99,24 @@ var init_output = __esm({
|
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
// ../cli/src/connection.ts
|
|
102
|
+
async function getReachableAppUrl(port) {
|
|
103
|
+
for (const baseUrl of APP_BASE_URLS) {
|
|
104
|
+
const url = `${baseUrl}:${port}/api/mcp/server`;
|
|
105
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
106
|
+
try {
|
|
107
|
+
const res = await fetch(url, {
|
|
108
|
+
signal: AbortSignal.timeout(500)
|
|
109
|
+
});
|
|
110
|
+
if (res.ok) return `${baseUrl}:${port}`;
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
if (attempt < 4) {
|
|
114
|
+
await new Promise((resolve4) => setTimeout(resolve4, 200));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
102
120
|
function isPidAlive(pid) {
|
|
103
121
|
try {
|
|
104
122
|
process.kill(pid, 0);
|
|
@@ -111,13 +129,23 @@ async function getAppInfo() {
|
|
|
111
129
|
try {
|
|
112
130
|
const raw = await (0, import_promises2.readFile)(PORT_FILE_PATH, "utf-8");
|
|
113
131
|
const { port, pid, timestamp } = JSON.parse(raw);
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
const url = await getReachableAppUrl(port);
|
|
133
|
+
if (url) {
|
|
134
|
+
return { port, pid, timestamp, url };
|
|
135
|
+
}
|
|
136
|
+
if (!isPidAlive(pid)) {
|
|
137
|
+
try {
|
|
138
|
+
await (0, import_promises2.unlink)(PORT_FILE_PATH);
|
|
139
|
+
} catch {
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
116
144
|
} catch {
|
|
117
145
|
return null;
|
|
118
146
|
}
|
|
119
147
|
}
|
|
120
|
-
var import_promises2, import_node_path, import_node_os, PORT_FILE_DIR, PORT_FILE_NAME, PORT_FILE_PATH;
|
|
148
|
+
var import_promises2, import_node_path, import_node_os, PORT_FILE_DIR, PORT_FILE_NAME, PORT_FILE_PATH, APP_BASE_URLS;
|
|
121
149
|
var init_connection = __esm({
|
|
122
150
|
"../cli/src/connection.ts"() {
|
|
123
151
|
"use strict";
|
|
@@ -128,6 +156,7 @@ var init_connection = __esm({
|
|
|
128
156
|
PORT_FILE_DIR = ".openpencil";
|
|
129
157
|
PORT_FILE_NAME = ".port";
|
|
130
158
|
PORT_FILE_PATH = (0, import_node_path.join)((0, import_node_os.homedir)(), PORT_FILE_DIR, PORT_FILE_NAME);
|
|
159
|
+
APP_BASE_URLS = ["http://127.0.0.1", "http://localhost"];
|
|
131
160
|
}
|
|
132
161
|
});
|
|
133
162
|
|
|
@@ -150,12 +179,8 @@ function getFreePort() {
|
|
|
150
179
|
async function waitForPortFile(timeoutMs = 15e3) {
|
|
151
180
|
const start = Date.now();
|
|
152
181
|
while (Date.now() - start < timeoutMs) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const data = JSON.parse(raw);
|
|
156
|
-
if (data.port && data.pid) return data;
|
|
157
|
-
} catch {
|
|
158
|
-
}
|
|
182
|
+
const info = await getAppInfo();
|
|
183
|
+
if (info) return { port: info.port, pid: info.pid };
|
|
159
184
|
await new Promise((r2) => setTimeout(r2, 300));
|
|
160
185
|
}
|
|
161
186
|
throw new Error("Timeout waiting for OpenPencil to start");
|
|
@@ -396,6 +421,67 @@ function resolveDocPath(filePath) {
|
|
|
396
421
|
if (!filePath || filePath === LIVE_CANVAS_PATH) return LIVE_CANVAS_PATH;
|
|
397
422
|
return (0, import_node_path3.resolve)(filePath);
|
|
398
423
|
}
|
|
424
|
+
function clearSyncUrl() {
|
|
425
|
+
_cachedSyncUrl = null;
|
|
426
|
+
_cachedSyncUrlTime = 0;
|
|
427
|
+
}
|
|
428
|
+
async function getReachableSyncUrl(port) {
|
|
429
|
+
for (const baseUrl of SYNC_BASE_URLS) {
|
|
430
|
+
const url = `${baseUrl}:${port}/api/mcp/server`;
|
|
431
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
432
|
+
try {
|
|
433
|
+
const res = await fetch(url, {
|
|
434
|
+
signal: AbortSignal.timeout(500)
|
|
435
|
+
});
|
|
436
|
+
if (res.ok) return `${baseUrl}:${port}`;
|
|
437
|
+
} catch {
|
|
438
|
+
}
|
|
439
|
+
if (attempt < 4) {
|
|
440
|
+
await new Promise((resolve4) => setTimeout(resolve4, 200));
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
async function probeLiveSyncUrl(baseUrl) {
|
|
447
|
+
try {
|
|
448
|
+
const docRes = await fetch(`${baseUrl}/api/mcp/document`, {
|
|
449
|
+
signal: AbortSignal.timeout(500)
|
|
450
|
+
});
|
|
451
|
+
if (docRes.ok) return "connected";
|
|
452
|
+
if (docRes.status === 404) return "no-document";
|
|
453
|
+
} catch {
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
const selectionRes = await fetch(`${baseUrl}/api/mcp/selection`, {
|
|
457
|
+
signal: AbortSignal.timeout(500)
|
|
458
|
+
});
|
|
459
|
+
if (selectionRes.ok) return "no-document";
|
|
460
|
+
} catch {
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
const serverRes = await fetch(`${baseUrl}/api/mcp/server`, {
|
|
464
|
+
signal: AbortSignal.timeout(500)
|
|
465
|
+
});
|
|
466
|
+
if (serverRes.ok) return "no-document";
|
|
467
|
+
} catch {
|
|
468
|
+
}
|
|
469
|
+
return "unreachable";
|
|
470
|
+
}
|
|
471
|
+
function buildLiveSyncMessage(status, port) {
|
|
472
|
+
const portHint = port ? ` (port ${port})` : "";
|
|
473
|
+
switch (status) {
|
|
474
|
+
case "connected":
|
|
475
|
+
return `Connected to OpenPencil live canvas${portHint}.`;
|
|
476
|
+
case "no-document":
|
|
477
|
+
return `OpenPencil is running${portHint}, but no live document is loaded in the editor yet. Open the editor page and wait for sync.`;
|
|
478
|
+
case "unreachable":
|
|
479
|
+
return `Found an OpenPencil port file${portHint}, but the live sync server is unreachable. Restart OpenPencil and try again.`;
|
|
480
|
+
case "missing-port-file":
|
|
481
|
+
default:
|
|
482
|
+
return "No running OpenPencil instance found. Start the Electron app or dev server first.";
|
|
483
|
+
}
|
|
484
|
+
}
|
|
399
485
|
function isPidAlive2(pid) {
|
|
400
486
|
try {
|
|
401
487
|
process.kill(pid, 0);
|
|
@@ -405,23 +491,86 @@ function isPidAlive2(pid) {
|
|
|
405
491
|
}
|
|
406
492
|
}
|
|
407
493
|
async function getSyncUrl() {
|
|
494
|
+
if (_cachedSyncUrl && Date.now() - _cachedSyncUrlTime < SYNC_URL_TTL) {
|
|
495
|
+
return _cachedSyncUrl;
|
|
496
|
+
}
|
|
497
|
+
const state = await getLiveSyncState();
|
|
498
|
+
if ((state.status === "connected" || state.status === "no-document") && state.url) {
|
|
499
|
+
_cachedSyncUrl = state.url;
|
|
500
|
+
_cachedSyncUrlTime = Date.now();
|
|
501
|
+
return state.url;
|
|
502
|
+
}
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
async function getLiveSyncState() {
|
|
408
506
|
try {
|
|
409
507
|
const raw = await (0, import_promises4.readFile)(PORT_FILE_PATH3, "utf-8");
|
|
410
508
|
const { port, pid } = JSON.parse(raw);
|
|
411
|
-
|
|
412
|
-
|
|
509
|
+
const url = await getReachableSyncUrl(port);
|
|
510
|
+
if (url) {
|
|
511
|
+
const status = await probeLiveSyncUrl(url);
|
|
512
|
+
if (status === "unreachable") {
|
|
513
|
+
return {
|
|
514
|
+
status,
|
|
515
|
+
url: null,
|
|
516
|
+
port,
|
|
517
|
+
message: buildLiveSyncMessage(status, port)
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
status,
|
|
522
|
+
url,
|
|
523
|
+
port,
|
|
524
|
+
message: buildLiveSyncMessage(status, port)
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
if (!isPidAlive2(pid)) {
|
|
528
|
+
try {
|
|
529
|
+
await (0, import_promises4.unlink)(PORT_FILE_PATH3);
|
|
530
|
+
} catch {
|
|
531
|
+
}
|
|
532
|
+
return {
|
|
533
|
+
status: "missing-port-file",
|
|
534
|
+
url: null,
|
|
535
|
+
port: null,
|
|
536
|
+
message: buildLiveSyncMessage("missing-port-file")
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
status: "unreachable",
|
|
541
|
+
url: null,
|
|
542
|
+
port,
|
|
543
|
+
message: buildLiveSyncMessage("unreachable", port)
|
|
544
|
+
};
|
|
413
545
|
} catch {
|
|
414
|
-
return
|
|
546
|
+
return {
|
|
547
|
+
status: "missing-port-file",
|
|
548
|
+
url: null,
|
|
549
|
+
port: null,
|
|
550
|
+
message: buildLiveSyncMessage("missing-port-file")
|
|
551
|
+
};
|
|
415
552
|
}
|
|
416
553
|
}
|
|
417
554
|
async function fetchLiveDocument() {
|
|
418
|
-
const
|
|
419
|
-
if (
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
555
|
+
const cachedUrl = _cachedSyncUrl && Date.now() - _cachedSyncUrlTime < SYNC_URL_TTL ? _cachedSyncUrl : null;
|
|
556
|
+
if (cachedUrl) {
|
|
557
|
+
try {
|
|
558
|
+
const res2 = await fetch(`${cachedUrl}/api/mcp/document`);
|
|
559
|
+
if (res2.ok) {
|
|
560
|
+
const data2 = await res2.json();
|
|
561
|
+
return data2.document;
|
|
562
|
+
}
|
|
563
|
+
} catch {
|
|
564
|
+
clearSyncUrl();
|
|
565
|
+
}
|
|
423
566
|
}
|
|
424
|
-
const
|
|
567
|
+
const sync = await getLiveSyncState();
|
|
568
|
+
if (!sync.url || sync.status !== "connected") {
|
|
569
|
+
throw new Error(sync.message);
|
|
570
|
+
}
|
|
571
|
+
_cachedSyncUrl = sync.url;
|
|
572
|
+
_cachedSyncUrlTime = Date.now();
|
|
573
|
+
const res = await fetch(`${sync.url}/api/mcp/document`);
|
|
425
574
|
if (!res.ok) {
|
|
426
575
|
const body = await res.json().catch(() => ({}));
|
|
427
576
|
throw new Error(
|
|
@@ -432,7 +581,8 @@ async function fetchLiveDocument() {
|
|
|
432
581
|
return data.document;
|
|
433
582
|
}
|
|
434
583
|
async function pushLiveDocument(doc) {
|
|
435
|
-
const
|
|
584
|
+
const cachedUrl = _cachedSyncUrl && Date.now() - _cachedSyncUrlTime < SYNC_URL_TTL ? _cachedSyncUrl : null;
|
|
585
|
+
const syncUrl = cachedUrl ?? await getSyncUrl();
|
|
436
586
|
if (!syncUrl) return;
|
|
437
587
|
try {
|
|
438
588
|
await fetch(`${syncUrl}/api/mcp/document`, {
|
|
@@ -441,6 +591,7 @@ async function pushLiveDocument(doc) {
|
|
|
441
591
|
body: JSON.stringify({ document: doc })
|
|
442
592
|
});
|
|
443
593
|
} catch {
|
|
594
|
+
clearSyncUrl();
|
|
444
595
|
}
|
|
445
596
|
}
|
|
446
597
|
function validate(doc) {
|
|
@@ -492,19 +643,33 @@ async function fileExists(filePath) {
|
|
|
492
643
|
}
|
|
493
644
|
}
|
|
494
645
|
async function fetchLiveSelection() {
|
|
495
|
-
const
|
|
496
|
-
if (
|
|
646
|
+
const cachedUrl = _cachedSyncUrl && Date.now() - _cachedSyncUrlTime < SYNC_URL_TTL ? _cachedSyncUrl : null;
|
|
647
|
+
if (cachedUrl) {
|
|
648
|
+
try {
|
|
649
|
+
const res = await fetch(`${cachedUrl}/api/mcp/selection`);
|
|
650
|
+
if (res.ok) return await res.json();
|
|
651
|
+
} catch {
|
|
652
|
+
clearSyncUrl();
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const sync = await getLiveSyncState();
|
|
656
|
+
if (!sync.url) {
|
|
657
|
+
throw new Error(sync.message);
|
|
658
|
+
}
|
|
659
|
+
if (sync.status === "no-document") {
|
|
497
660
|
return { selectedIds: [], activePageId: null };
|
|
498
661
|
}
|
|
662
|
+
_cachedSyncUrl = sync.url;
|
|
663
|
+
_cachedSyncUrlTime = Date.now();
|
|
499
664
|
try {
|
|
500
|
-
const res = await fetch(`${
|
|
665
|
+
const res = await fetch(`${sync.url}/api/mcp/selection`);
|
|
501
666
|
if (!res.ok) return { selectedIds: [], activePageId: null };
|
|
502
667
|
return await res.json();
|
|
503
668
|
} catch {
|
|
504
|
-
|
|
669
|
+
throw new Error(buildLiveSyncMessage("unreachable", sync.port));
|
|
505
670
|
}
|
|
506
671
|
}
|
|
507
|
-
var import_promises4, import_node_fs2, import_node_os3, import_node_path3, cache, LIVE_CANVAS_PATH, PORT_FILE_PATH3;
|
|
672
|
+
var import_promises4, import_node_fs2, import_node_os3, import_node_path3, cache, LIVE_CANVAS_PATH, _cachedSyncUrl, _cachedSyncUrlTime, SYNC_URL_TTL, PORT_FILE_PATH3, SYNC_BASE_URLS;
|
|
508
673
|
var init_document_manager = __esm({
|
|
509
674
|
"src/mcp/document-manager.ts"() {
|
|
510
675
|
"use strict";
|
|
@@ -517,7 +682,11 @@ var init_document_manager = __esm({
|
|
|
517
682
|
init_app2();
|
|
518
683
|
cache = /* @__PURE__ */ new Map();
|
|
519
684
|
LIVE_CANVAS_PATH = "live://canvas";
|
|
685
|
+
_cachedSyncUrl = null;
|
|
686
|
+
_cachedSyncUrlTime = 0;
|
|
687
|
+
SYNC_URL_TTL = 3e4;
|
|
520
688
|
PORT_FILE_PATH3 = (0, import_node_path3.join)((0, import_node_os3.homedir)(), PORT_FILE_DIR_NAME, PORT_FILE_NAME2);
|
|
689
|
+
SYNC_BASE_URLS = ["http://127.0.0.1", "http://localhost"];
|
|
521
690
|
}
|
|
522
691
|
});
|
|
523
692
|
|
|
@@ -1004,6 +1173,168 @@ var init_replace_refs = __esm({
|
|
|
1004
1173
|
});
|
|
1005
1174
|
|
|
1006
1175
|
// ../../packages/pen-core/src/normalize.ts
|
|
1176
|
+
function normalizePenDocument(doc) {
|
|
1177
|
+
const normalized = {
|
|
1178
|
+
...doc,
|
|
1179
|
+
children: doc.children.map((n) => normalizeNode(n))
|
|
1180
|
+
};
|
|
1181
|
+
if (normalized.pages && normalized.pages.length > 0) {
|
|
1182
|
+
normalized.pages = normalized.pages.map((p) => ({
|
|
1183
|
+
...p,
|
|
1184
|
+
children: p.children.map((n) => normalizeNode(n))
|
|
1185
|
+
}));
|
|
1186
|
+
}
|
|
1187
|
+
return normalized;
|
|
1188
|
+
}
|
|
1189
|
+
function normalizeNode(node) {
|
|
1190
|
+
const out = { ...node };
|
|
1191
|
+
if ("fill" in out && out.fill !== void 0) {
|
|
1192
|
+
out.fill = normalizeFills(out.fill);
|
|
1193
|
+
}
|
|
1194
|
+
if ("stroke" in out && out.stroke != null) {
|
|
1195
|
+
out.stroke = normalizeStroke(out.stroke);
|
|
1196
|
+
}
|
|
1197
|
+
if ("width" in out) out.width = normalizeSizing(out.width);
|
|
1198
|
+
if ("height" in out) out.height = normalizeSizing(out.height);
|
|
1199
|
+
if ("padding" in out) out.padding = normalizePadding(out.padding);
|
|
1200
|
+
if (out.type === "text" && !("content" in out) && typeof out.text === "string") {
|
|
1201
|
+
out.content = out.text;
|
|
1202
|
+
delete out.text;
|
|
1203
|
+
}
|
|
1204
|
+
if (out.type === "icon_font" && !out.iconFontFamily) {
|
|
1205
|
+
out.iconFontFamily = "lucide";
|
|
1206
|
+
}
|
|
1207
|
+
if ("children" in out && Array.isArray(out.children)) {
|
|
1208
|
+
out.children = out.children.map((c) => normalizeNode(c));
|
|
1209
|
+
}
|
|
1210
|
+
return out;
|
|
1211
|
+
}
|
|
1212
|
+
function normalizeFills(raw) {
|
|
1213
|
+
if (!raw) return [];
|
|
1214
|
+
if (typeof raw === "string") {
|
|
1215
|
+
return [{ type: "solid", color: raw }];
|
|
1216
|
+
}
|
|
1217
|
+
if (Array.isArray(raw)) {
|
|
1218
|
+
return raw.map((f3) => normalizeSingleFill(f3)).filter(Boolean);
|
|
1219
|
+
}
|
|
1220
|
+
if (typeof raw === "object") {
|
|
1221
|
+
const f3 = normalizeSingleFill(raw);
|
|
1222
|
+
return f3 ? [f3] : [];
|
|
1223
|
+
}
|
|
1224
|
+
return [];
|
|
1225
|
+
}
|
|
1226
|
+
function normalizeSingleFill(raw) {
|
|
1227
|
+
if (typeof raw === "string") {
|
|
1228
|
+
return raw ? { type: "solid", color: raw } : null;
|
|
1229
|
+
}
|
|
1230
|
+
if (!raw || typeof raw !== "object") return null;
|
|
1231
|
+
const t = raw.type;
|
|
1232
|
+
if (t === "color" || t === "solid") {
|
|
1233
|
+
return {
|
|
1234
|
+
type: "solid",
|
|
1235
|
+
color: typeof raw.color === "string" ? raw.color : "#000000"
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
if (t === "gradient") {
|
|
1239
|
+
const gt = raw.gradientType ?? "linear";
|
|
1240
|
+
const stops = normalizeGradientStops(raw.colors);
|
|
1241
|
+
if (gt === "radial") {
|
|
1242
|
+
const center = raw.center;
|
|
1243
|
+
return {
|
|
1244
|
+
type: "radial_gradient",
|
|
1245
|
+
cx: typeof center?.x === "number" ? center.x : 0.5,
|
|
1246
|
+
cy: typeof center?.y === "number" ? center.y : 0.5,
|
|
1247
|
+
radius: 0.5,
|
|
1248
|
+
stops
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
return {
|
|
1252
|
+
type: "linear_gradient",
|
|
1253
|
+
angle: typeof raw.rotation === "number" ? raw.rotation : 0,
|
|
1254
|
+
stops
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
if (t === "linear_gradient" || t === "radial_gradient") {
|
|
1258
|
+
const stops = "stops" in raw ? normalizeGradientStops(raw.stops) : "colors" in raw ? normalizeGradientStops(raw.colors) : [];
|
|
1259
|
+
return { ...raw, stops };
|
|
1260
|
+
}
|
|
1261
|
+
if (t === "image") return raw;
|
|
1262
|
+
if ("color" in raw) {
|
|
1263
|
+
return {
|
|
1264
|
+
type: "solid",
|
|
1265
|
+
color: typeof raw.color === "string" ? raw.color : "#000000"
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
function normalizeGradientStops(raw) {
|
|
1271
|
+
if (!Array.isArray(raw) || raw.length === 0) return [];
|
|
1272
|
+
const parsed = raw.map((s2) => {
|
|
1273
|
+
const stop = s2;
|
|
1274
|
+
const rawOffset = typeof stop.offset === "number" && Number.isFinite(stop.offset) ? stop.offset : typeof stop.position === "number" && Number.isFinite(stop.position) ? stop.position : null;
|
|
1275
|
+
const offset = rawOffset !== null && rawOffset > 1 ? rawOffset / 100 : rawOffset;
|
|
1276
|
+
return {
|
|
1277
|
+
offset,
|
|
1278
|
+
color: typeof stop.color === "string" ? stop.color : "#000000"
|
|
1279
|
+
};
|
|
1280
|
+
});
|
|
1281
|
+
const n = parsed.length;
|
|
1282
|
+
return parsed.map((s2, i) => ({
|
|
1283
|
+
color: s2.color,
|
|
1284
|
+
offset: s2.offset !== null ? Math.max(0, Math.min(1, s2.offset)) : i / Math.max(n - 1, 1)
|
|
1285
|
+
}));
|
|
1286
|
+
}
|
|
1287
|
+
function normalizeStroke(raw) {
|
|
1288
|
+
if (!raw) return void 0;
|
|
1289
|
+
const out = { ...raw };
|
|
1290
|
+
if ("fill" in out) {
|
|
1291
|
+
out.fill = normalizeFills(out.fill);
|
|
1292
|
+
}
|
|
1293
|
+
if ("color" in out && typeof out.color === "string") {
|
|
1294
|
+
out.fill = [{ type: "solid", color: out.color }];
|
|
1295
|
+
delete out.color;
|
|
1296
|
+
}
|
|
1297
|
+
if (typeof out.thickness === "string") {
|
|
1298
|
+
const str = out.thickness;
|
|
1299
|
+
if (!str.startsWith("$")) {
|
|
1300
|
+
const num = parseFloat(str);
|
|
1301
|
+
out.thickness = isNaN(num) ? 1 : num;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
return out;
|
|
1305
|
+
}
|
|
1306
|
+
function normalizeSizing(value) {
|
|
1307
|
+
if (typeof value === "number") return value;
|
|
1308
|
+
if (typeof value !== "string") return 0;
|
|
1309
|
+
if (value.startsWith("$")) return value;
|
|
1310
|
+
if (value.startsWith("fill_container")) return "fill_container";
|
|
1311
|
+
if (value.startsWith("fit_content")) {
|
|
1312
|
+
const match = value.match(/\((\d+(?:\.\d+)?)\)/);
|
|
1313
|
+
if (match) return parseFloat(match[1]);
|
|
1314
|
+
return "fit_content";
|
|
1315
|
+
}
|
|
1316
|
+
const num = parseFloat(value);
|
|
1317
|
+
return isNaN(num) ? 0 : num;
|
|
1318
|
+
}
|
|
1319
|
+
function normalizePadding(value) {
|
|
1320
|
+
if (typeof value === "number") return value;
|
|
1321
|
+
if (typeof value === "string") {
|
|
1322
|
+
if (value.startsWith("$")) return value;
|
|
1323
|
+
const num = parseFloat(value);
|
|
1324
|
+
return isNaN(num) ? 0 : num;
|
|
1325
|
+
}
|
|
1326
|
+
if (Array.isArray(value)) {
|
|
1327
|
+
return value.map((v) => {
|
|
1328
|
+
if (typeof v === "number") return v;
|
|
1329
|
+
if (typeof v === "string") {
|
|
1330
|
+
const num = parseFloat(v);
|
|
1331
|
+
return isNaN(num) ? 0 : num;
|
|
1332
|
+
}
|
|
1333
|
+
return 0;
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
return void 0;
|
|
1337
|
+
}
|
|
1007
1338
|
var init_normalize = __esm({
|
|
1008
1339
|
"../../packages/pen-core/src/normalize.ts"() {
|
|
1009
1340
|
"use strict";
|
|
@@ -1192,12 +1523,10 @@ var init_arc_path = __esm({
|
|
|
1192
1523
|
});
|
|
1193
1524
|
|
|
1194
1525
|
// ../../packages/pen-core/src/boolean-ops.ts
|
|
1195
|
-
var import_paper;
|
|
1196
1526
|
var init_boolean_ops = __esm({
|
|
1197
1527
|
"../../packages/pen-core/src/boolean-ops.ts"() {
|
|
1198
1528
|
"use strict";
|
|
1199
1529
|
init_define_import_meta_env();
|
|
1200
|
-
import_paper = __toESM(require("paper"), 1);
|
|
1201
1530
|
}
|
|
1202
1531
|
});
|
|
1203
1532
|
|
|
@@ -1374,65 +1703,6 @@ var init_skill_registry = __esm({
|
|
|
1374
1703
|
"use strict";
|
|
1375
1704
|
init_define_import_meta_env();
|
|
1376
1705
|
skillRegistry = [
|
|
1377
|
-
{
|
|
1378
|
-
"meta": {
|
|
1379
|
-
"name": "jsonl-format-simplified",
|
|
1380
|
-
"description": "Simplified nested JSON format for basic tier models",
|
|
1381
|
-
"phase": [
|
|
1382
|
-
"generation"
|
|
1383
|
-
],
|
|
1384
|
-
"trigger": {
|
|
1385
|
-
"flags": [
|
|
1386
|
-
"isBasicTier"
|
|
1387
|
-
]
|
|
1388
|
-
},
|
|
1389
|
-
"priority": 0,
|
|
1390
|
-
"budget": 1500,
|
|
1391
|
-
"category": "base"
|
|
1392
|
-
},
|
|
1393
|
-
"content": 'Generate a UI section as a nested JSON tree. Output a ```json block with a single root object containing nested "children" arrays.\n\nTYPES:\nframe (width,height,layout,gap,padding,justifyContent,alignItems,cornerRadius,fill,children), rectangle (width,height,cornerRadius,fill), text (content,fontFamily,fontSize,fontWeight,fill,width,textAlign), icon_font (iconFontName,width,height,fill)\nSHARED: id, type, name\n\nRULES:\n- Root: type="frame", width="fill_container", height="fit_content", layout="vertical".\n- Children go in "children" arrays. No x/y on layout children.\n- width/height: number | "fill_container" | "fit_content".\n- fill: [{"type":"solid","color":"#hex"}].\n- Text: never set height. Use width="fill_container" for wrapping text.\n- Icons: use icon_font with iconFontName (lucide names: search, bell, user, heart, star, plus, x, check, chevron-right, settings). Sizes: 16/20/24px.\n- Buttons: frame with padding=[12,24] containing a text child.\n- No emoji characters. No markdown. No explanation. No tool calls.\n\nEXAMPLE:\n```json\n{\n "id": "root",\n "type": "frame",\n "name": "Hero",\n "width": "fill_container",\n "height": "fit_content",\n "layout": "vertical",\n "gap": 24,\n "padding": [48, 24],\n "fill": [{"type": "solid", "color": "#F8FAFC"}],\n "children": [\n {"id": "title", "type": "text", "name": "Headline", "content": "Learn Smarter", "fontSize": 48, "fontWeight": 700, "fontFamily": "Space Grotesk", "fill": [{"type": "solid", "color": "#0F172A"}]},\n {"id": "desc", "type": "text", "name": "Description", "content": "AI-powered learning", "fontSize": 16, "width": "fill_container", "fill": [{"type": "solid", "color": "#64748B"}]},\n {"id": "cta", "type": "frame", "name": "CTA", "padding": [14, 28], "cornerRadius": 10, "justifyContent": "center", "fill": [{"type": "solid", "color": "#2563EB"}], "children": [\n {"id": "cta-text", "type": "text", "content": "Get Started", "fontSize": 16, "fontWeight": 600, "fill": [{"type": "solid", "color": "#FFFFFF"}]}\n ]}\n ]\n}\n```\n\nCRITICAL: You are a JSON generator, NOT a code assistant. Output ONLY the ```json block. Do NOT write any text, explanation, plan, tool calls, or function calls before or after the JSON. Do NOT use [TOOL_CALL], {tool => ...}, or any tool/function invocation syntax. Start your response with ```json immediately.'
|
|
1394
|
-
},
|
|
1395
|
-
{
|
|
1396
|
-
"meta": {
|
|
1397
|
-
"name": "schema",
|
|
1398
|
-
"description": "PenNode type definitions and property schemas",
|
|
1399
|
-
"phase": [
|
|
1400
|
-
"generation"
|
|
1401
|
-
],
|
|
1402
|
-
"trigger": null,
|
|
1403
|
-
"priority": 0,
|
|
1404
|
-
"budget": 2e3,
|
|
1405
|
-
"category": "base"
|
|
1406
|
-
},
|
|
1407
|
-
"content": `PenNode types (the ONLY format you output for designs):
|
|
1408
|
-
- frame: Container. Props: width, height, layout ('none'|'vertical'|'horizontal'), gap, padding, justifyContent ('start'|'center'|'end'|'space_between'|'space_around'), alignItems ('start'|'center'|'end'), clipContent (boolean), children[], cornerRadius, fill, stroke, effects
|
|
1409
|
-
- rectangle: Props: width, height, cornerRadius, fill, stroke, effects
|
|
1410
|
-
- ellipse: Props: width, height, fill, stroke, effects
|
|
1411
|
-
- text: Props: content, fontFamily, fontSize, fontWeight, fontStyle ('normal'|'italic'), fill, width, height, textAlign, textGrowth ('auto'|'fixed-width'|'fixed-width-height'), lineHeight (multiplier), letterSpacing (px), textAlignVertical ('top'|'middle'|'bottom')
|
|
1412
|
-
- path: SVG icon. Props: d (SVG path), width, height, fill, stroke, effects
|
|
1413
|
-
- image: Props: width, height, cornerRadius, effects, imageSearchQuery (2-3 English keywords)
|
|
1414
|
-
|
|
1415
|
-
All nodes share: id, type, name, role, x, y, rotation, opacity
|
|
1416
|
-
Fill = [{ type: "solid", color: "#hex" }] or [{ type: "linear_gradient", angle, stops: [{ offset, color }] }]
|
|
1417
|
-
Stroke = { thickness, fill: [...] } Effects = [{ type: "shadow", offsetX, offsetY, blur, spread, color }]
|
|
1418
|
-
SIZING: width/height accept number (px), "fill_container", or "fit_content".
|
|
1419
|
-
PADDING: number (uniform), [v, h], or [top, right, bottom, left].
|
|
1420
|
-
cornerRadius is a number. fill is ALWAYS an array. Do NOT set x/y on children inside layout frames.`
|
|
1421
|
-
},
|
|
1422
|
-
{
|
|
1423
|
-
"meta": {
|
|
1424
|
-
"name": "jsonl-format",
|
|
1425
|
-
"description": "Sub-agent flat JSONL output format with node types and rules",
|
|
1426
|
-
"phase": [
|
|
1427
|
-
"generation"
|
|
1428
|
-
],
|
|
1429
|
-
"trigger": null,
|
|
1430
|
-
"priority": 0,
|
|
1431
|
-
"budget": 1500,
|
|
1432
|
-
"category": "base"
|
|
1433
|
-
},
|
|
1434
|
-
"content": 'PenNode flat JSONL engine. Output a ```json block with ONE node per line.\n\nTYPES:\nframe (width,height,layout,gap,padding,justifyContent,alignItems,clipContent,cornerRadius,fill,stroke,effects), rectangle, ellipse, text (content,fontFamily,fontSize,fontWeight,fontStyle,fill,width,textAlign,textGrowth,lineHeight,letterSpacing), icon_font (iconFontName,width,height,fill), path (d,width,height,fill,stroke), image (width,height,imageSearchQuery,imagePrompt). imagePrompt: describe subject+scene+style, NEVER mention background type (transparent/white/plain). Match composition to aspect ratio.\nSHARED: id, type, name, role, x, y, opacity\nROLES: section, row, column, divider | navbar, button, icon-button, badge, input, search-bar | card, stat-card, pricing-card, feature-card | heading, subheading, body-text, caption, label | table, table-row, table-header\nwidth/height: number | "fill_container" | "fit_content". padding: number | [v,h] | [T,R,B,L]. Fill=[{"type":"solid","color":"#hex"}].\nStroke: {"thickness":N,"fill":[{"type":"solid","color":"#hex"}]}. Directional: {"thickness":{"bottom":1},"fill":[...]}.\n\nRULES:\n- Section root: width="fill_container", height="fit_content", layout="vertical".\n- No x/y on children in layout frames. All nodes descend from section root.\n- Width consistency: siblings in vertical layout use the SAME width strategy.\n- Never "fill_container" inside "fit_content" parent.\n- clipContent: true on cards with cornerRadius + image children.\n- Text: NEVER set height. Short text (titles, labels, buttons) \u2014 omit textGrowth. Long text (>15 chars wrapping) \u2014 textGrowth="fixed-width", width="fill_container", lineHeight=1.4-1.6.\n- lineHeight: Display 40-56px - 0.9-1.0. Heading 20-36px - 1.0-1.2. Body - 1.4-1.6. letterSpacing: -0.5 to -1 for headlines, 1-3 for uppercase.\n- Icons: ALWAYS use icon_font nodes with iconFontName (lucide names: search, bell, user, heart, star, plus, x, check, chevron-right, settings, etc). Sizes: 14/20/24px. NEVER use emoji characters as icon substitutes \u2014 they cannot render on canvas.\n- CJK fonts: "Noto Sans SC"/"Noto Sans JP"/"Noto Sans KR" for headings. CJK lineHeight: 1.3-1.4 headings, 1.6-1.8 body.\n- Buttons: frame(padding=[12,24], justifyContent="center") > text. Icon+text: frame(layout="horizontal", gap=8, alignItems="center", padding=[8,16]).\n- Card rows: ALL cards width="fill_container" + height="fill_container".\n- FORMS: ALL inputs AND button use width="fill_container". gap=16-20.\n- Phone mockup: ONE frame, w=260-300, h=520-580, cornerRadius=32, solid fill + 1px stroke.\n- Z-order: Earlier siblings render on top. Overlay elements (badges, indicators, floating buttons) MUST come BEFORE the content they overlap.\n\nFORMAT: _parent (null=root, else parent-id). Parent before children.\n```json\n{"_parent":null,"id":"root","type":"frame","name":"Hero","width":"fill_container","height":"fit_content","layout":"vertical","gap":24,"padding":[48,24],"fill":[{"type":"solid","color":"#F8FAFC"}]}\n{"_parent":"root","id":"header","type":"frame","name":"Header","justifyContent":"space_between","alignItems":"center","width":"fill_container"}\n{"_parent":"header","id":"logo","type":"text","name":"Logo","content":"ACME","fontSize":18,"fontWeight":600,"fontFamily":"Space Grotesk","fill":[{"type":"solid","color":"#0D0D0D"}]}\n{"_parent":"header","id":"notifBtn","type":"frame","name":"Notification","width":44,"height":44}\n{"_parent":"notifBtn","id":"notifIcon","type":"icon_font","name":"Bell","iconFontName":"bell","width":20,"height":20,"fill":"#0D0D0D","x":12,"y":12}\n{"_parent":"root","id":"title","type":"text","name":"Headline","content":"Learn Smarter","fontSize":48,"fontWeight":700,"fontFamily":"Space Grotesk","lineHeight":0.95,"fill":[{"type":"solid","color":"#0F172A"}]}\n{"_parent":"root","id":"desc","type":"text","name":"Description","content":"AI-powered vocabulary learning that adapts to your pace","fontSize":16,"textGrowth":"fixed-width","width":"fill_container","lineHeight":1.5,"fill":[{"type":"solid","color":"#64748B"}]}\n{"_parent":"root","id":"cta","type":"frame","name":"CTA Button","padding":[14,28],"cornerRadius":10,"justifyContent":"center","fill":[{"type":"solid","color":"#2563EB"}]}\n{"_parent":"cta","id":"cta-text","type":"text","name":"CTA Label","content":"Get Started","fontSize":16,"fontWeight":600,"fill":[{"type":"solid","color":"#FFFFFF"}]}\n```\n\nCRITICAL: Output ONLY the ```json block. Do NOT write any text, explanation, plan, tool calls, or function calls. Do NOT use [TOOL_CALL] or {tool => ...} syntax. Start your response with ```json immediately.'
|
|
1435
|
-
},
|
|
1436
1706
|
{
|
|
1437
1707
|
"meta": {
|
|
1438
1708
|
"name": "local-edit",
|
|
@@ -1505,6 +1775,65 @@ RULES:
|
|
|
1505
1775
|
- WIDTH SELECTION: Single-task screens (type 2 above) - ALWAYS width=375, height=812 (mobile). Multi-section pages and data-rich workspaces (types 1 & 3) - width=1200, height=0 (desktop). This is mandatory.
|
|
1506
1776
|
- MULTI-SCREEN APPS: When the request involves multiple distinct screens/pages (e.g. "\u767B\u5F55\u9875+\u4E2A\u4EBA\u4E2D\u5FC3", "login and profile"), add "screen":"<name>" to each subtask to group sections that belong to the same page. Use a concise page name (e.g. "\u767B\u5F55", "Profile"). Subtasks sharing the same "screen" are placed in one root frame. Single-screen requests don't need "screen". Example: [{"id":"brand","label":"Brand Area","screen":"Login","region":{...}},{"id":"form","label":"Login Form","screen":"Login","region":{...}},{"id":"card","label":"User Card","screen":"Profile","region":{...}}]
|
|
1507
1777
|
- NO explanation. NO markdown. NO tool calls. NO function calls. NO [TOOL_CALL]. JUST the JSON object. Start with {.`
|
|
1778
|
+
},
|
|
1779
|
+
{
|
|
1780
|
+
"meta": {
|
|
1781
|
+
"name": "jsonl-format",
|
|
1782
|
+
"description": "Sub-agent flat JSONL output format with node types and rules",
|
|
1783
|
+
"phase": [
|
|
1784
|
+
"generation"
|
|
1785
|
+
],
|
|
1786
|
+
"trigger": null,
|
|
1787
|
+
"priority": 0,
|
|
1788
|
+
"budget": 1500,
|
|
1789
|
+
"category": "base"
|
|
1790
|
+
},
|
|
1791
|
+
"content": 'PenNode flat JSONL engine. Output a ```json block with ONE node per line.\n\nTYPES:\nframe (width,height,layout,gap,padding,justifyContent,alignItems,clipContent,cornerRadius,fill,stroke,effects), rectangle, ellipse, text (content,fontFamily,fontSize,fontWeight,fontStyle,fill,width,textAlign,textGrowth,lineHeight,letterSpacing), icon_font (iconFontName,width,height,fill), path (d,width,height,fill,stroke), image (width,height,imageSearchQuery,imagePrompt). imagePrompt: describe subject+scene+style, NEVER mention background type (transparent/white/plain). Match composition to aspect ratio.\nSHARED: id, type, name, role, x, y, opacity\nROLES: section, row, column, divider | navbar, button, icon-button, badge, input, search-bar | card, stat-card, pricing-card, feature-card | heading, subheading, body-text, caption, label | table, table-row, table-header\nwidth/height: number | "fill_container" | "fit_content". padding: number | [v,h] | [T,R,B,L]. Fill=[{"type":"solid","color":"#hex"}].\nStroke: {"thickness":N,"fill":[{"type":"solid","color":"#hex"}]}. Directional: {"thickness":{"bottom":1},"fill":[...]}.\n\nRULES:\n- Section root: width="fill_container", height="fit_content", layout="vertical".\n- No x/y on children in layout frames. All nodes descend from section root.\n- Width consistency: siblings in vertical layout use the SAME width strategy.\n- Never "fill_container" inside "fit_content" parent.\n- clipContent: true on cards with cornerRadius + image children.\n- Text: NEVER set height. Short text (titles, labels, buttons) \u2014 omit textGrowth. Long text (>15 chars wrapping) \u2014 textGrowth="fixed-width", width="fill_container", lineHeight=1.4-1.6.\n- lineHeight: Display 40-56px - 0.9-1.0. Heading 20-36px - 1.0-1.2. Body - 1.4-1.6. letterSpacing: -0.5 to -1 for headlines, 1-3 for uppercase.\n- Icons: ALWAYS use icon_font nodes with iconFontName (lucide names: search, bell, user, heart, star, plus, x, check, chevron-right, settings, etc). Sizes: 14/20/24px. NEVER use emoji characters as icon substitutes \u2014 they cannot render on canvas.\n- CJK fonts: "Noto Sans SC"/"Noto Sans JP"/"Noto Sans KR" for headings. CJK lineHeight: 1.3-1.4 headings, 1.6-1.8 body.\n- Buttons: frame(padding=[12,24], justifyContent="center") > text. Icon+text: frame(layout="horizontal", gap=8, alignItems="center", padding=[8,16]).\n- Card rows: ALL cards width="fill_container" + height="fill_container".\n- FORMS: ALL inputs AND button use width="fill_container". gap=16-20.\n- Phone mockup: ONE frame, w=260-300, h=520-580, cornerRadius=32, solid fill + 1px stroke.\n- Z-order: Earlier siblings render on top. Overlay elements (badges, indicators, floating buttons) MUST come BEFORE the content they overlap.\n\nFORMAT: _parent (null=root, else parent-id). Parent before children.\n```json\n{"_parent":null,"id":"root","type":"frame","name":"Hero","width":"fill_container","height":"fit_content","layout":"vertical","gap":24,"padding":[48,24],"fill":[{"type":"solid","color":"#F8FAFC"}]}\n{"_parent":"root","id":"header","type":"frame","name":"Header","justifyContent":"space_between","alignItems":"center","width":"fill_container"}\n{"_parent":"header","id":"logo","type":"text","name":"Logo","content":"ACME","fontSize":18,"fontWeight":600,"fontFamily":"Space Grotesk","fill":[{"type":"solid","color":"#0D0D0D"}]}\n{"_parent":"header","id":"notifBtn","type":"frame","name":"Notification","width":44,"height":44}\n{"_parent":"notifBtn","id":"notifIcon","type":"icon_font","name":"Bell","iconFontName":"bell","width":20,"height":20,"fill":"#0D0D0D","x":12,"y":12}\n{"_parent":"root","id":"title","type":"text","name":"Headline","content":"Learn Smarter","fontSize":48,"fontWeight":700,"fontFamily":"Space Grotesk","lineHeight":0.95,"fill":[{"type":"solid","color":"#0F172A"}]}\n{"_parent":"root","id":"desc","type":"text","name":"Description","content":"AI-powered vocabulary learning that adapts to your pace","fontSize":16,"textGrowth":"fixed-width","width":"fill_container","lineHeight":1.5,"fill":[{"type":"solid","color":"#64748B"}]}\n{"_parent":"root","id":"cta","type":"frame","name":"CTA Button","padding":[14,28],"cornerRadius":10,"justifyContent":"center","fill":[{"type":"solid","color":"#2563EB"}]}\n{"_parent":"cta","id":"cta-text","type":"text","name":"CTA Label","content":"Get Started","fontSize":16,"fontWeight":600,"fill":[{"type":"solid","color":"#FFFFFF"}]}\n```\n\nCRITICAL: Output ONLY the ```json block. Do NOT write any text, explanation, plan, tool calls, or function calls. Do NOT use [TOOL_CALL] or {tool => ...} syntax. Start your response with ```json immediately.'
|
|
1792
|
+
},
|
|
1793
|
+
{
|
|
1794
|
+
"meta": {
|
|
1795
|
+
"name": "jsonl-format-simplified",
|
|
1796
|
+
"description": "Simplified nested JSON format for basic tier models",
|
|
1797
|
+
"phase": [
|
|
1798
|
+
"generation"
|
|
1799
|
+
],
|
|
1800
|
+
"trigger": {
|
|
1801
|
+
"flags": [
|
|
1802
|
+
"isBasicTier"
|
|
1803
|
+
]
|
|
1804
|
+
},
|
|
1805
|
+
"priority": 0,
|
|
1806
|
+
"budget": 1500,
|
|
1807
|
+
"category": "base"
|
|
1808
|
+
},
|
|
1809
|
+
"content": 'Generate a UI section as a nested JSON tree. Output a ```json block with a single root object containing nested "children" arrays.\n\nTYPES:\nframe (width,height,layout,gap,padding,justifyContent,alignItems,cornerRadius,fill,children), rectangle (width,height,cornerRadius,fill), text (content,fontFamily,fontSize,fontWeight,fill,width,textAlign), icon_font (iconFontName,width,height,fill)\nSHARED: id, type, name\n\nRULES:\n- Root: type="frame", width="fill_container", height="fit_content", layout="vertical".\n- Children go in "children" arrays. No x/y on layout children.\n- width/height: number | "fill_container" | "fit_content".\n- fill: [{"type":"solid","color":"#hex"}].\n- Text: never set height. Use width="fill_container" for wrapping text.\n- Icons: use icon_font with iconFontName (lucide names: search, bell, user, heart, star, plus, x, check, chevron-right, settings). Sizes: 16/20/24px.\n- Buttons: frame with padding=[12,24] containing a text child.\n- No emoji characters. No markdown. No explanation. No tool calls.\n\nEXAMPLE:\n```json\n{\n "id": "root",\n "type": "frame",\n "name": "Hero",\n "width": "fill_container",\n "height": "fit_content",\n "layout": "vertical",\n "gap": 24,\n "padding": [48, 24],\n "fill": [{"type": "solid", "color": "#F8FAFC"}],\n "children": [\n {"id": "title", "type": "text", "name": "Headline", "content": "Learn Smarter", "fontSize": 48, "fontWeight": 700, "fontFamily": "Space Grotesk", "fill": [{"type": "solid", "color": "#0F172A"}]},\n {"id": "desc", "type": "text", "name": "Description", "content": "AI-powered learning", "fontSize": 16, "width": "fill_container", "fill": [{"type": "solid", "color": "#64748B"}]},\n {"id": "cta", "type": "frame", "name": "CTA", "padding": [14, 28], "cornerRadius": 10, "justifyContent": "center", "fill": [{"type": "solid", "color": "#2563EB"}], "children": [\n {"id": "cta-text", "type": "text", "content": "Get Started", "fontSize": 16, "fontWeight": 600, "fill": [{"type": "solid", "color": "#FFFFFF"}]}\n ]}\n ]\n}\n```\n\nCRITICAL: You are a JSON generator, NOT a code assistant. Output ONLY the ```json block. Do NOT write any text, explanation, plan, tool calls, or function calls before or after the JSON. Do NOT use [TOOL_CALL], {tool => ...}, or any tool/function invocation syntax. Start your response with ```json immediately.'
|
|
1810
|
+
},
|
|
1811
|
+
{
|
|
1812
|
+
"meta": {
|
|
1813
|
+
"name": "schema",
|
|
1814
|
+
"description": "PenNode type definitions and property schemas",
|
|
1815
|
+
"phase": [
|
|
1816
|
+
"generation"
|
|
1817
|
+
],
|
|
1818
|
+
"trigger": null,
|
|
1819
|
+
"priority": 0,
|
|
1820
|
+
"budget": 2e3,
|
|
1821
|
+
"category": "base"
|
|
1822
|
+
},
|
|
1823
|
+
"content": `PenNode types (the ONLY format you output for designs):
|
|
1824
|
+
- frame: Container. Props: width, height, layout ('none'|'vertical'|'horizontal'), gap, padding, justifyContent ('start'|'center'|'end'|'space_between'|'space_around'), alignItems ('start'|'center'|'end'), clipContent (boolean), children[], cornerRadius, fill, stroke, effects
|
|
1825
|
+
- rectangle: Props: width, height, cornerRadius, fill, stroke, effects
|
|
1826
|
+
- ellipse: Props: width, height, fill, stroke, effects
|
|
1827
|
+
- text: Props: content, fontFamily, fontSize, fontWeight, fontStyle ('normal'|'italic'), fill, width, height, textAlign, textGrowth ('auto'|'fixed-width'|'fixed-width-height'), lineHeight (multiplier), letterSpacing (px), textAlignVertical ('top'|'middle'|'bottom')
|
|
1828
|
+
- path: SVG icon. Props: d (SVG path), width, height, fill, stroke, effects
|
|
1829
|
+
- image: Props: width, height, cornerRadius, effects, imageSearchQuery (2-3 English keywords)
|
|
1830
|
+
|
|
1831
|
+
All nodes share: id, type, name, role, x, y, rotation, opacity
|
|
1832
|
+
Fill = [{ type: "solid", color: "#hex" }] or [{ type: "linear_gradient", angle, stops: [{ offset, color }] }]
|
|
1833
|
+
Stroke = { thickness, fill: [...] } Effects = [{ type: "shadow", offsetX, offsetY, blur, spread, color }]
|
|
1834
|
+
SIZING: width/height accept number (px), "fill_container", or "fit_content".
|
|
1835
|
+
PADDING: number (uniform), [v, h], or [top, right, bottom, left].
|
|
1836
|
+
cornerRadius is a number. fill is ALWAYS an array. Do NOT set x/y on children inside layout frames.`
|
|
1508
1837
|
},
|
|
1509
1838
|
{
|
|
1510
1839
|
"meta": {
|
|
@@ -1592,7 +1921,43 @@ IMPORTANT:
|
|
|
1592
1921
|
},
|
|
1593
1922
|
{
|
|
1594
1923
|
"meta": {
|
|
1595
|
-
"name": "design-
|
|
1924
|
+
"name": "design-type",
|
|
1925
|
+
"description": "Design type detection and classification rules",
|
|
1926
|
+
"phase": [
|
|
1927
|
+
"planning"
|
|
1928
|
+
],
|
|
1929
|
+
"trigger": null,
|
|
1930
|
+
"priority": 5,
|
|
1931
|
+
"budget": 1e3,
|
|
1932
|
+
"category": "base"
|
|
1933
|
+
},
|
|
1934
|
+
"content": `DESIGN TYPE DETECTION:
|
|
1935
|
+
Classify by the design's PURPOSE \u2014 reason about intent, do not keyword-match:
|
|
1936
|
+
|
|
1937
|
+
1. Multi-section page \u2014 marketing, promotional, or informational content designed to be scrolled (e.g. product sites, portfolios, company pages):
|
|
1938
|
+
- Desktop: width=1200, height=0 (scrollable), 6-10 subtasks
|
|
1939
|
+
- Structure: navigation - hero - content sections - CTA - footer
|
|
1940
|
+
|
|
1941
|
+
2. Single-task screen \u2014 functional UI focused on one user task (e.g. authentication, forms, settings, profiles, modals, onboarding):
|
|
1942
|
+
- Mobile: width=375, height=812 (fixed viewport), 1-5 subtasks
|
|
1943
|
+
- Structure: header + focused content area only, no navigation/hero/footer
|
|
1944
|
+
|
|
1945
|
+
3. Data-rich workspace \u2014 overview screens with metrics, tables, or management panels (e.g. dashboards, admin consoles, analytics):
|
|
1946
|
+
- Desktop: width=1200, height=0, 2-5 subtasks
|
|
1947
|
+
- Structure: sidebar or topbar + content panels
|
|
1948
|
+
|
|
1949
|
+
WIDTH SELECTION RULES:
|
|
1950
|
+
- Single-task screens (type 2) - ALWAYS width=375, height=812 (mobile).
|
|
1951
|
+
- Multi-section pages and data-rich workspaces (types 1 & 3) - width=1200, height=0 (desktop).
|
|
1952
|
+
- This mapping is mandatory.
|
|
1953
|
+
|
|
1954
|
+
MOBILE vs MOCKUP:
|
|
1955
|
+
- "mobile"/"\u79FB\u52A8\u7AEF"/"\u624B\u673A" + screen type (login, profile, settings) = ACTUAL mobile screen (375x812), NOT a desktop page with phone mockup.
|
|
1956
|
+
- Phone mockups are ONLY for app showcase/marketing sections when the user explicitly asks for a "mockup"/"\u5C55\u793A"/"showcase"/"preview".`
|
|
1957
|
+
},
|
|
1958
|
+
{
|
|
1959
|
+
"meta": {
|
|
1960
|
+
"name": "design-md",
|
|
1596
1961
|
"description": "Custom design system from design.md specification",
|
|
1597
1962
|
"phase": [
|
|
1598
1963
|
"generation"
|
|
@@ -1624,39 +1989,71 @@ IMPORTANT:
|
|
|
1624
1989
|
},
|
|
1625
1990
|
{
|
|
1626
1991
|
"meta": {
|
|
1627
|
-
"name": "
|
|
1628
|
-
"description": "
|
|
1992
|
+
"name": "style-consistency",
|
|
1993
|
+
"description": "Preserve visual consistency when modifying existing designs",
|
|
1629
1994
|
"phase": [
|
|
1630
|
-
"
|
|
1995
|
+
"maintenance"
|
|
1631
1996
|
],
|
|
1632
1997
|
"trigger": null,
|
|
1633
|
-
"priority":
|
|
1998
|
+
"priority": 10,
|
|
1634
1999
|
"budget": 1e3,
|
|
1635
2000
|
"category": "base"
|
|
1636
2001
|
},
|
|
1637
|
-
"content":
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
2002
|
+
"content": "STYLE CONSISTENCY RULES:\n\nWhen modifying an existing design, preserve visual coherence:\n\nCOLOR PALETTE:\n- Extract the existing palette from context nodes before making changes.\n- New elements MUST use colors from the existing palette unless the user explicitly requests new colors.\n- Maintain the same accent color usage pattern (primary for CTAs, secondary for highlights).\n\nTYPOGRAPHY:\n- Match existing font families \u2014 do not introduce new fonts unless requested.\n- Maintain the same type scale (heading sizes, body sizes, caption sizes).\n- Preserve lineHeight and letterSpacing patterns from existing text nodes.\n\nSPACING:\n- Match existing padding and gap values when adding new sections or elements.\n- Section padding should be consistent across the design.\n- Card internal padding should match sibling cards.\n\nVISUAL TREATMENT:\n- cornerRadius should be consistent across similar element types.\n- Shadow styles should match existing elements of the same category.\n- Border/stroke styles should be consistent (same color, same thickness).\n- clipContent should match sibling containers.\n\nHIERARCHY:\n- Maintain the same depth of nesting \u2014 do not add unnecessary wrapper frames.\n- Keep the same layout pattern (vertical sections with horizontal content rows).\n- Width strategies (fill_container vs fixed) should match siblings."
|
|
2003
|
+
},
|
|
2004
|
+
{
|
|
2005
|
+
"meta": {
|
|
2006
|
+
"name": "codegen-planning",
|
|
2007
|
+
"description": "Analyze PenNode tree and split into code generation chunks with component boundaries and dependencies",
|
|
2008
|
+
"phase": [
|
|
2009
|
+
"generation"
|
|
2010
|
+
],
|
|
2011
|
+
"trigger": {
|
|
2012
|
+
"flags": [
|
|
2013
|
+
"isCodeGen"
|
|
2014
|
+
]
|
|
2015
|
+
},
|
|
2016
|
+
"priority": 10,
|
|
2017
|
+
"budget": 2e3,
|
|
2018
|
+
"category": "base"
|
|
2019
|
+
},
|
|
2020
|
+
"content": '# Code Generation Planning\n\nYou are a code generation planner. Given a PenNode tree summary and a target framework, decompose the design into code generation chunks.\n\n## Input\n\nYou receive:\n1. A text summary of the PenNode tree. Each line includes: `[nodeId]`, type, name, dimensions, role, and child count. The `nodeId` values are stable identifiers \u2014 use them in your `nodeIds` arrays.\n2. The target framework name\n\n## Output\n\nRespond with ONLY valid JSON matching this schema:\n\n```json\n{\n "chunks": [\n {\n "id": "chunk-1",\n "name": "navbar",\n "nodeIds": ["node-id-1", "node-id-2"],\n "role": "navbar",\n "suggestedComponentName": "NavBar",\n "dependencies": [],\n "exposedSlots": ["logo", "nav-links"]\n }\n ],\n "sharedStyles": [\n { "name": "card-shadow", "description": "Shared drop shadow used by card components" }\n ],\n "rootLayout": {\n "direction": "vertical",\n "gap": 0,\n "responsive": true\n }\n}\n```\n\n## Chunking Rules\n\n1. **Top-level frames with roles** \u2192 each becomes a chunk (navbar, hero, footer, sidebar, etc.)\n2. **Repeated sibling structures** (3+ similar frames at the same level) \u2192 single chunk with iteration hint in the name (e.g. "card-list")\n3. **Deep nested frames without roles** \u2192 fold into their nearest ancestor chunk\n4. **Root layout** \u2192 derive from the top-level container\'s layout properties (direction, gap)\n5. **Dependencies** \u2192 if chunk B is visually nested inside chunk A, B depends on A\n6. **Shared styles** \u2192 identify fill colors, effects, or typography patterns used by 2+ chunks\n\n## Naming Conventions\n\n- `id`: `chunk-{index}` starting from 1\n- `name`: kebab-case descriptive name derived from the node name or role\n- `suggestedComponentName`: PascalCase version of name (e.g. "hero-section" \u2192 "HeroSection")\n\n## Constraints\n\n- Each nodeId must reference an actual node from the input tree\n- Every node in the input should appear in exactly one chunk\'s nodeIds\n- A chunk should contain between 1 and 20 nodes (split large subtrees)\n- Keep the total number of chunks under 15 for any design'
|
|
2021
|
+
},
|
|
2022
|
+
{
|
|
2023
|
+
"meta": {
|
|
2024
|
+
"name": "codegen-assembly",
|
|
2025
|
+
"description": "Merge generated code chunks into a single production-grade file with deduplication and responsive design",
|
|
2026
|
+
"phase": [
|
|
2027
|
+
"generation"
|
|
2028
|
+
],
|
|
2029
|
+
"trigger": {
|
|
2030
|
+
"flags": [
|
|
2031
|
+
"isCodeGen"
|
|
2032
|
+
]
|
|
2033
|
+
},
|
|
2034
|
+
"priority": 10,
|
|
2035
|
+
"budget": 2e3,
|
|
2036
|
+
"category": "base"
|
|
2037
|
+
},
|
|
2038
|
+
"content": '# Code Assembly\n\nYou assemble multiple code chunks into a single production-ready file.\n\n## Input\n\n1. An array of chunk results, each containing:\n - `chunkId` and `name`\n - Generated `code`\n - `contract` (may be missing for degraded chunks \u2014 infer from code in that case)\n - Status: `successful`, `degraded` (no contract), or `failed` (code missing)\n2. The `CodePlanFromAI` with rootLayout and sharedStyles\n3. Design variables and theme definitions\n4. Target framework name\n\n## Output\n\nA single, complete, production-ready source file that:\n1. Imports all dependencies (deduplicated)\n2. Defines all chunk components\n3. Exports a root component that composes all chunks according to rootLayout\n4. Includes CSS variable definitions for design variables\n\n## Assembly Rules\n\n### Import Deduplication\n- Merge imports from the same source: `{ source: "react", specifiers: ["useState"] }` + `{ source: "react", specifiers: ["useEffect"] }` \u2192 `import { useState, useEffect } from \'react\'`\n- Remove duplicate specifiers\n- Order: framework imports first, then external libraries, then local components\n\n### Root Component\n- Name: use the page/document name or default to "Design"\n- Layout: apply `rootLayout.direction` and `rootLayout.gap` to arrange chunk components\n- If `responsive: true`: add responsive breakpoints (mobile-first)\n\n### Shared Styles\n- Extract shared styles described in the plan into reusable CSS classes or styled components\n- Reference them in chunk components instead of duplicating\n\n### Design Variables\n- Generate CSS custom property definitions (`:root { --name: value }`) from the provided variables\n- Include theme variants if themes are defined\n\n### Handling Degraded/Failed Chunks\n- For **degraded** chunks (code present, no contract): infer component names and imports from the raw code\n- For **failed** chunks: insert a placeholder comment: `/* TODO: {chunkName} \u2014 generation failed */`\n- Always note which chunks were degraded in a comment at the top of the file\n\n### Quality Rules\n- Replace absolute pixel positioning with flex/grid layout where possible\n- Use semantic HTML elements (nav, header, main, section, footer, article)\n- Ensure all text is readable (sufficient contrast, reasonable font sizes)\n- Add responsive breakpoints for common widths (640px, 768px, 1024px, 1280px)'
|
|
2039
|
+
},
|
|
2040
|
+
{
|
|
2041
|
+
"meta": {
|
|
2042
|
+
"name": "codegen-chunk",
|
|
2043
|
+
"description": "Universal rules for generating code from a PenNode chunk \u2014 layout semantics, naming, property mapping",
|
|
2044
|
+
"phase": [
|
|
2045
|
+
"generation"
|
|
2046
|
+
],
|
|
2047
|
+
"trigger": {
|
|
2048
|
+
"flags": [
|
|
2049
|
+
"isCodeGen"
|
|
2050
|
+
]
|
|
2051
|
+
},
|
|
2052
|
+
"priority": 10,
|
|
2053
|
+
"budget": 3e3,
|
|
2054
|
+
"category": "base"
|
|
2055
|
+
},
|
|
2056
|
+
"content": '# Code Chunk Generation\n\nYou generate code for a single chunk of a design. You receive local PenNode data + a framework-specific skill.\n\n## Input\n\n1. An array of PenNode objects (the chunk\'s nodes with full properties)\n2. The target framework name and its framework-specific rules\n3. The chunk\'s suggested component name\n4. Contracts from dependency chunks (if any)\n\n## Output\n\nYou MUST output TWO things separated by a line containing only `---CONTRACT---`:\n\n1. The generated code (complete, compilable component)\n2. A JSON contract block\n\nExample output:\n```\nimport React from \'react\'\n\nexport function NavBar() {\n return (\n <nav className="flex items-center justify-between px-6 py-4">\n <div className="text-xl font-bold">Logo</div>\n <div className="flex gap-4">\n <a href="#">Home</a>\n <a href="#">About</a>\n </div>\n </nav>\n )\n}\n---CONTRACT---\n{\n "chunkId": "chunk-1",\n "componentName": "NavBar",\n "exportedProps": [],\n "slots": [],\n "cssClasses": [],\n "cssVariables": [],\n "imports": [{ "source": "react", "specifiers": ["default"] }]\n}\n```\n\n## Node-to-Code Mapping Rules\n\n### Layout Nodes (type: "frame" with layout property)\n- `layout: "vertical"` \u2192 vertical stack (flexbox column, VStack, Column, etc.)\n- `layout: "horizontal"` \u2192 horizontal stack (flexbox row, HStack, Row, etc.)\n- `layout: "none"` or absent \u2192 absolute/relative positioning\n- `gap` \u2192 spacing between children\n- `padding` \u2192 internal padding (can be uniform or per-side: top/right/bottom/left)\n- `justifyContent` / `alignItems` \u2192 alignment within the stack\n- `clipContent: true` \u2192 overflow hidden\n\n### Dimension Handling\n- Fixed `width`/`height` in pixels \u2192 use exact values\n- `width: "fill_container"` \u2192 stretch to fill parent (width: 100%, flex: 1, etc.)\n- `height: "fill_container"` \u2192 stretch to fill parent height\n- Root component: use the frame\'s actual dimensions as max-width with responsive scaling\n\n### Text Nodes (type: "text")\n- `characters` \u2192 text content\n- `fontSize`, `fontWeight`, `fontFamily` \u2192 typography\n- `lineHeight` \u2192 line spacing\n- `textAlign` \u2192 text alignment\n- `fill` \u2192 text color\n- Use semantic HTML tags when appropriate (h1-h6 for headings, p for body text)\n\n### Shape Nodes (type: "rectangle", "ellipse", "polygon", "line", "path")\n- Convert to CSS shapes where possible (border-radius for ellipse, etc.)\n- `fill` \u2192 background color/gradient\n- `stroke` \u2192 border\n- `cornerRadius` \u2192 border-radius (can be uniform or per-corner)\n- `effects` \u2192 box-shadow (for drop shadows), filter (for blur)\n- `opacity` \u2192 opacity\n- `rotation` \u2192 transform: rotate()\n\n### Image Nodes (type: "image")\n- `src` \u2192 image source URL\n- `objectFit` \u2192 object-fit CSS property\n- Use `<img>` with proper alt text derived from node name\n\n### Variable References\n- Values starting with `$` are variable references\n- Web frameworks: output as `var(--variable-name)` using CSS custom properties\n- Mobile frameworks: output as literal value with `/* var(--name) */` comment\n\n### Naming\n- Component name: use the chunk\'s `suggestedComponentName`\n- CSS classes/variable names: derive from node names, kebab-case\n- Internal variables: camelCase, descriptive\n\n### Using Dependency Contracts\n- If a dependency chunk exported a component, import and use it by its `componentName`\n- Respect the dependency\'s `exportedProps` \u2014 pass required props\n- Use dependency\'s `slots` as children/content areas'
|
|
1660
2057
|
},
|
|
1661
2058
|
{
|
|
1662
2059
|
"meta": {
|
|
@@ -1686,186 +2083,322 @@ MOBILE vs MOCKUP:
|
|
|
1686
2083
|
},
|
|
1687
2084
|
{
|
|
1688
2085
|
"meta": {
|
|
1689
|
-
"name": "
|
|
1690
|
-
"description": "
|
|
2086
|
+
"name": "text-rules",
|
|
2087
|
+
"description": "Text sizing, typography, and wrapping rules",
|
|
2088
|
+
"phase": [
|
|
2089
|
+
"generation"
|
|
2090
|
+
],
|
|
2091
|
+
"trigger": null,
|
|
2092
|
+
"priority": 15,
|
|
2093
|
+
"budget": 1e3,
|
|
2094
|
+
"category": "base"
|
|
2095
|
+
},
|
|
2096
|
+
"content": 'TEXT RULES:\n- Body/description in vertical layout: width="fill_container" + textGrowth="fixed-width" (wraps text, auto-sizes height).\n- Short labels in horizontal rows: width="fit_content" + textGrowth="auto". Prevents squeezing siblings.\n- NEVER fixed pixel width on text inside layout frames \u2014 causes overflow.\n- Text >15 chars MUST have textGrowth="fixed-width". NEVER set explicit pixel height on text nodes \u2014 OMIT height.\n- Typography: Display 40-56px, Heading 28-36px, Subheading 20-24px, Body 16-18px, Caption 13-14px.\n- lineHeight: headings 1.1-1.2, body 1.4-1.6. letterSpacing: -0.5 for headlines, 0.5-2 for uppercase.'
|
|
2097
|
+
},
|
|
2098
|
+
{
|
|
2099
|
+
"meta": {
|
|
2100
|
+
"name": "overflow",
|
|
2101
|
+
"description": "Overflow prevention rules for text and child sizing",
|
|
2102
|
+
"phase": [
|
|
2103
|
+
"generation"
|
|
2104
|
+
],
|
|
2105
|
+
"trigger": null,
|
|
2106
|
+
"priority": 16,
|
|
2107
|
+
"budget": 500,
|
|
2108
|
+
"category": "base"
|
|
2109
|
+
},
|
|
2110
|
+
"content": 'OVERFLOW PREVENTION (CRITICAL):\n- Text in vertical layout: width="fill_container" + textGrowth="fixed-width". In horizontal: width="fit_content".\n- NEVER set fixed pixel width on text inside layout frames (e.g. width:378 in 195px card - overflows!).\n- Fixed-width children must be <= parent content area (parent width - padding).\n- Badges: short labels only (CJK <=8 chars / Latin <=16 chars).'
|
|
2111
|
+
},
|
|
2112
|
+
{
|
|
2113
|
+
"meta": {
|
|
2114
|
+
"name": "incremental-add",
|
|
2115
|
+
"description": "Rules for adding new elements to existing designs",
|
|
1691
2116
|
"phase": [
|
|
1692
2117
|
"maintenance"
|
|
1693
2118
|
],
|
|
2119
|
+
"trigger": {
|
|
2120
|
+
"keywords": [
|
|
2121
|
+
"add",
|
|
2122
|
+
"insert",
|
|
2123
|
+
"new section",
|
|
2124
|
+
"append"
|
|
2125
|
+
]
|
|
2126
|
+
},
|
|
2127
|
+
"priority": 20,
|
|
2128
|
+
"budget": 1500,
|
|
2129
|
+
"category": "domain"
|
|
2130
|
+
},
|
|
2131
|
+
"content": `INCREMENTAL ADDITION RULES:
|
|
2132
|
+
|
|
2133
|
+
When adding new elements to an existing design:
|
|
2134
|
+
|
|
2135
|
+
CONTEXT AWARENESS:
|
|
2136
|
+
- Analyze the existing design structure before adding new elements.
|
|
2137
|
+
- Match the visual style (colors, fonts, spacing, cornerRadius) of existing siblings.
|
|
2138
|
+
- Place new elements in logical positions within the hierarchy.
|
|
2139
|
+
|
|
2140
|
+
SIBLING CONSISTENCY:
|
|
2141
|
+
- New cards in a card row MUST match existing cards' width/height strategy (typically fill_container).
|
|
2142
|
+
- New inputs in a form MUST match existing inputs' width and height.
|
|
2143
|
+
- New sections MUST use the same padding and gap patterns as existing sections.
|
|
2144
|
+
|
|
2145
|
+
INSERTION RULES:
|
|
2146
|
+
- Use "_parent" to specify where the new node belongs in the tree.
|
|
2147
|
+
- New sections append after the last existing section by default.
|
|
2148
|
+
- New items within a list/grid append after the last existing item.
|
|
2149
|
+
- Preserve z-order: overlay elements (badges, indicators) come BEFORE content.
|
|
2150
|
+
|
|
2151
|
+
COMMON PATTERNS:
|
|
2152
|
+
- "Add a section" -> new frame with width="fill_container", height="fit_content", layout="vertical", matching section padding.
|
|
2153
|
+
- "Add a card" -> new frame matching sibling card structure (same children pattern, same styles).
|
|
2154
|
+
- "Add an input" -> new frame with role="input" or "form-input", width="fill_container", matching sibling inputs.
|
|
2155
|
+
- "Add a button" -> new frame with role="button", matching existing button style.
|
|
2156
|
+
- "Add a row" -> new frame with layout="horizontal", appropriate gap and alignment.
|
|
2157
|
+
|
|
2158
|
+
ID GENERATION:
|
|
2159
|
+
- Use unique descriptive IDs for new nodes (e.g. "new-feature-card", "contact-section").
|
|
2160
|
+
- Never reuse existing IDs.`
|
|
2161
|
+
},
|
|
2162
|
+
{
|
|
2163
|
+
"meta": {
|
|
2164
|
+
"name": "design-code",
|
|
2165
|
+
"description": "HTML/CSS design code generation for visual reference",
|
|
2166
|
+
"phase": [
|
|
2167
|
+
"generation"
|
|
2168
|
+
],
|
|
1694
2169
|
"trigger": null,
|
|
1695
|
-
"priority":
|
|
2170
|
+
"priority": 20,
|
|
1696
2171
|
"budget": 1e3,
|
|
1697
2172
|
"category": "base"
|
|
1698
2173
|
},
|
|
1699
|
-
"content":
|
|
2174
|
+
"content": `You are a world-class frontend designer. Generate a SINGLE self-contained HTML file that looks production-grade.
|
|
2175
|
+
|
|
2176
|
+
OUTPUT RULES:
|
|
2177
|
+
- Output ONLY the complete HTML file, starting with <!DOCTYPE html>. No explanation.
|
|
2178
|
+
- ALL CSS must be inline in a <style> block. No external stylesheets except Google Fonts.
|
|
2179
|
+
- Use modern CSS: flexbox, gap, custom properties, clamp().
|
|
2180
|
+
- The page must render correctly at the specified viewport dimensions.
|
|
2181
|
+
- All images use colored placeholder rectangles with labels (no external images).
|
|
2182
|
+
- Icons use simple inline SVG shapes (geometric, not complex).
|
|
2183
|
+
- Include Google Fonts via <link> in the <head> if non-system fonts are specified.
|
|
2184
|
+
|
|
2185
|
+
DESIGN QUALITY:
|
|
2186
|
+
- This is a visual reference for a design tool \u2014 every pixel matters.
|
|
2187
|
+
- Create clear visual hierarchy: one dominant element per section, everything else subordinate.
|
|
2188
|
+
- Use whitespace generously \u2014 premium designs breathe.
|
|
2189
|
+
- Avoid template-ish layouts: don't put everything in the center, explore asymmetry.
|
|
2190
|
+
- Color should guide the eye: accent color on CTAs and key elements, neutral everywhere else.
|
|
2191
|
+
- Typography should create rhythm: vary size, weight, and color across the type scale.
|
|
2192
|
+
- Shadows should be subtle (0 2px 8px rgba(0,0,0,0.08)) \u2014 never heavy drop shadows.
|
|
2193
|
+
- Corner radius should be consistent across the design (8-12px for modern, 16px+ for friendly).
|
|
2194
|
+
- Sections should flow naturally: alternate background tints, use generous vertical padding (80-120px).
|
|
2195
|
+
|
|
2196
|
+
ANTI-PATTERNS TO AVOID:
|
|
2197
|
+
- Every card looking identical with blue icon + black title + gray text (the "AI template" look).
|
|
2198
|
+
- Centered everything \u2014 real designs use left-alignment and asymmetric layouts.
|
|
2199
|
+
- Too many things competing for attention \u2014 ruthlessly prioritize.
|
|
2200
|
+
- Decorative elements that serve no purpose \u2014 every element must earn its place.
|
|
2201
|
+
- Generic stock-photo-style image placeholders \u2014 use branded colored rectangles.
|
|
2202
|
+
- All buttons the same size and color \u2014 create a button hierarchy.
|
|
2203
|
+
|
|
2204
|
+
TEXT CONTENT:
|
|
2205
|
+
- Headlines: 2-6 words, punchy and specific to the product.
|
|
2206
|
+
- Subtitles: 1 sentence, max 15 words.
|
|
2207
|
+
- Feature descriptions: 1 sentence, max 20 words.
|
|
2208
|
+
- Button text: 1-3 words.
|
|
2209
|
+
- Never use lorem ipsum or generic "Your text here" placeholders.`
|
|
1700
2210
|
},
|
|
1701
2211
|
{
|
|
1702
2212
|
"meta": {
|
|
1703
|
-
"name": "
|
|
1704
|
-
"description": "
|
|
2213
|
+
"name": "design-system",
|
|
2214
|
+
"description": "Design system token generation from product descriptions",
|
|
1705
2215
|
"phase": [
|
|
1706
2216
|
"generation"
|
|
1707
2217
|
],
|
|
1708
2218
|
"trigger": null,
|
|
1709
|
-
"priority":
|
|
2219
|
+
"priority": 20,
|
|
1710
2220
|
"budget": 1e3,
|
|
1711
2221
|
"category": "base"
|
|
1712
2222
|
},
|
|
1713
|
-
"content": '
|
|
2223
|
+
"content": 'You are a design system architect. Given a product description, create a cohesive design token system.\nOutput ONLY a JSON object, no explanation.\n\n{\n "palette": {\n "background": "#hex (page bg, slightly tinted \u2014 never pure white)",\n "surface": "#hex (card/container bg)",\n "text": "#hex (primary text, dark but not black)",\n "textSecondary": "#hex (body/secondary text, muted)",\n "primary": "#hex (main action color)",\n "primaryLight": "#hex (lighter tint for hover/subtle backgrounds)",\n "accent": "#hex (secondary accent, complementary to primary)",\n "border": "#hex (subtle dividers)"\n },\n "typography": {\n "headingFont": "font name (display/personality font)",\n "bodyFont": "font name (readable/neutral font)",\n "scale": [14, 16, 20, 28, 40, 56]\n },\n "spacing": {\n "unit": 8,\n "scale": [4, 8, 12, 16, 24, 32, 48, 64, 80, 96]\n },\n "radius": [4, 8, 12, 16],\n "aesthetic": "2-5 word style description"\n}\n\nRULES:\n- Match colors to the product personality: tech/SaaS - cool blue/indigo, creative - warm amber/coral, finance - deep navy/emerald, health - sage/teal, education - violet/sky.\n- Ensure WCAG AA contrast (4.5:1) between text and background, primary and surface.\n- Font pairing: heading should be distinctive (Space Grotesk, Outfit, Sora, Plus Jakarta Sans, Clash Display), body should be readable (Inter, DM Sans, Satoshi). Max 2 families.\n- CJK content: if the request is in Chinese/Japanese/Korean, use "Noto Sans SC"/"Noto Sans JP"/"Noto Sans KR" for heading, "Inter" for body. Never use display fonts without CJK glyphs.\n- Dark theme: when request mentions dark/cyber/terminal/neon/\u6697\u9ED1/\u6DF1\u8272, use dark background (#0F172A or #18181B), light text, brighter accents.\n- Default to light theme unless explicitly asked for dark.\n- Radius: 0-4 for sharp/professional, 8-12 for modern, 16+ for playful/friendly.\n- Scale should have clear size jumps: [14, 16, 20, 28, 40, 56] not [14, 15, 16, 17, 18].\n- Aesthetic description guides the overall feel: "clean minimal blue tech", "warm editorial amber", "bold dark neon gaming".'
|
|
2224
|
+
},
|
|
2225
|
+
{
|
|
2226
|
+
"meta": {
|
|
2227
|
+
"name": "codegen-html",
|
|
2228
|
+
"description": "HTML + CSS code generation rules \u2014 semantic HTML5 with CSS classes in style block",
|
|
2229
|
+
"phase": [
|
|
2230
|
+
"generation"
|
|
2231
|
+
],
|
|
2232
|
+
"trigger": {
|
|
2233
|
+
"keywords": [
|
|
2234
|
+
"html",
|
|
2235
|
+
"css",
|
|
2236
|
+
"vanilla",
|
|
2237
|
+
"static"
|
|
2238
|
+
]
|
|
2239
|
+
},
|
|
2240
|
+
"priority": 20,
|
|
2241
|
+
"budget": 2e3,
|
|
2242
|
+
"category": "knowledge"
|
|
2243
|
+
},
|
|
2244
|
+
"content": '# HTML + CSS Code Generation\n\nGenerate semantic HTML5 markup with CSS classes defined in a `<style>` block. No build tools, no framework dependencies.\n\n## Output Format\n- HTML5 (`.html`)\n- Semantic HTML elements\n- All styling via CSS classes in a `<style>` block\n- CSS custom properties for design variables\n- No inline styles, no framework, no build tools\n- Each node gets a unique, descriptive CSS class name derived from `node.name`\n\n## Layout Mapping\n- `layout: "vertical"` \u2192 `display: flex; flex-direction: column`\n- `layout: "horizontal"` \u2192 `display: flex; flex-direction: row`\n- `gap: N` \u2192 `gap: Npx`\n- `padding: N` \u2192 `padding: Npx`\n- `padding: [t, r, b, l]` \u2192 `padding: Tpx Rpx Bpx Lpx`\n- `justifyContent: "start"` \u2192 `justify-content: flex-start`\n- `justifyContent: "center"` \u2192 `justify-content: center`\n- `justifyContent: "end"` \u2192 `justify-content: flex-end`\n- `justifyContent: "space_between"` \u2192 `justify-content: space-between`\n- `justifyContent: "space_around"` \u2192 `justify-content: space-around`\n- `alignItems: "start"` \u2192 `align-items: flex-start`\n- `alignItems: "center"` \u2192 `align-items: center`\n- `alignItems: "end"` \u2192 `align-items: flex-end`\n- `clipContent: true` \u2192 `overflow: hidden`\n\n## Color & Fill Mapping\n- Solid fill `#hex` \u2192 `background: #hex`\n- Variable ref `$name` \u2192 `background: var(--name)`\n- Text fill \u2192 `color: #hex` or `color: var(--name)`\n- Linear gradient \u2192 `background: linear-gradient(Ndeg, color1 0%, color2 100%)`\n- Radial gradient \u2192 `background: radial-gradient(circle, color1 0%, color2 100%)`\n\n## Border & Stroke Mapping\n- `stroke.thickness` \u2192 `border-width: Npx; border-style: solid`\n- `stroke.color` \u2192 `border-color: #hex`\n- Variable ref \u2192 `border-width: var(--name)`, `border-color: var(--name)`\n\n## Corner Radius\n- Uniform \u2192 `border-radius: Npx`\n- Per-corner `[tl, tr, br, bl]` \u2192 `border-radius: TLpx TRpx BRpx BLpx`\n- Ellipse \u2192 `border-radius: 50%`\n\n## Effects\n- Drop shadow \u2192 `box-shadow: offsetXpx offsetYpx blurpx spreadpx color`\n- Inner shadow \u2192 `box-shadow: inset offsetXpx offsetYpx blurpx spreadpx color`\n- Multiple shadows comma-separated\n\n## Typography\n- `fontSize` \u2192 `font-size: Npx`\n- `fontWeight` \u2192 `font-weight: N`\n- `fontStyle: "italic"` \u2192 `font-style: italic`\n- `fontFamily` \u2192 `font-family: \'Name\', sans-serif`\n- `lineHeight` \u2192 `line-height: value`\n- `letterSpacing` \u2192 `letter-spacing: Npx`\n- `textAlign` \u2192 `text-align: left|center|right`\n- `textAlignVertical: "middle"` \u2192 `vertical-align: middle`\n- `textGrowth: "auto"` \u2192 `white-space: nowrap`\n- `textGrowth: "fixed-width-height"` \u2192 `overflow: hidden`\n- `underline` \u2192 `text-decoration: underline`\n- `strikethrough` \u2192 `text-decoration: line-through`\n\n## Dimensions\n- Fixed \u2192 `width: Npx; height: Npx`\n- `fill_container` \u2192 `width: 100%` or `height: 100%`\n- Root container \u2192 `max-width: Npx; width: 100%; margin: 0 auto` for responsive centering\n\n## Image Handling\n- `<img class="className" src="src" alt="name" />`\n- `object-fit: contain|cover|fill` based on `objectFit` property:\n - `objectFit: "fit"` \u2192 `object-fit: contain`\n - `objectFit: "crop"` \u2192 `object-fit: cover`\n - default \u2192 `object-fit: fill`\n- Corner radius applied via CSS class\n\n## Opacity & Transform\n- `opacity: N` \u2192 `opacity: N`\n- `rotation: N` \u2192 `transform: rotate(Ndeg)`\n\n## Positioning\n- Absolute children \u2192 `position: absolute; left: Xpx; top: Ypx`\n- Container \u2192 `position: relative`\n\n## Semantic HTML Tags\n- Font size >= 32 \u2192 `<h1>`\n- Font size >= 24 \u2192 `<h2>`\n- Font size >= 20 \u2192 `<h3>`\n- Other text \u2192 `<p>`\n- Lines \u2192 `<hr>`\n- Use `<nav>`, `<header>`, `<main>`, `<section>`, `<footer>`, `<article>` appropriately\n\n## Icon Handling\n- Icon font nodes \u2192 `<i class="className" data-lucide="icon-name"></i>`\n- Set `width`, `height`, and `color` via CSS class\n- Include Lucide CDN script for icon rendering\n\n## SVG Elements\n- Path nodes \u2192 inline `<svg>` with `<path d="..." fill="color" />`\n- Set `viewBox`, `width`, `height` on SVG element\n\n## Variable References\n- `$variable` refs \u2192 `var(--variable-name)` CSS custom properties\n- Define variables in `:root { --name: value; }` block\n- Background: `background: var(--name)`\n- Text color: `color: var(--name)`\n- Border: `border-color: var(--name)`\n\n## Responsive Design\n- Use `max-width` with `width: 100%` for fluid containers\n- Media queries at common breakpoints: `@media (min-width: 640px)`, `768px`, `1024px`, `1280px`\n- Use relative units where appropriate (`em`, `rem`, `%`)'
|
|
2245
|
+
},
|
|
2246
|
+
{
|
|
2247
|
+
"meta": {
|
|
2248
|
+
"name": "codegen-compose",
|
|
2249
|
+
"description": "Jetpack Compose (Kotlin) code generation rules \u2014 composable functions with Modifier chains",
|
|
2250
|
+
"phase": [
|
|
2251
|
+
"generation"
|
|
2252
|
+
],
|
|
2253
|
+
"trigger": {
|
|
2254
|
+
"keywords": [
|
|
2255
|
+
"compose",
|
|
2256
|
+
"jetpack",
|
|
2257
|
+
"kotlin",
|
|
2258
|
+
"android"
|
|
2259
|
+
]
|
|
2260
|
+
},
|
|
2261
|
+
"priority": 20,
|
|
2262
|
+
"budget": 2e3,
|
|
2263
|
+
"category": "knowledge"
|
|
2264
|
+
},
|
|
2265
|
+
"content": '# Jetpack Compose (Kotlin) Code Generation\n\nGenerate Kotlin composable functions using Jetpack Compose UI toolkit.\n\n## Output Format\n- Kotlin file (`.kt`)\n- `@Composable fun ComponentName() { ... }`\n- Standard Compose imports: `androidx.compose.foundation.*`, `androidx.compose.material3.*`, `androidx.compose.ui.*`\n- Use `dp` for dimensions, `sp` for font sizes\n\n## Layout Mapping\n- `layout: "vertical"` \u2192 `Column(modifier, verticalArrangement, horizontalAlignment) { ... }`\n- `layout: "horizontal"` \u2192 `Row(modifier, horizontalArrangement, verticalAlignment) { ... }`\n- No layout / stacked children \u2192 `Box(modifier) { ... }`\n- `gap: N` \u2192 `verticalArrangement = Arrangement.spacedBy(N.dp)` (Column) or `horizontalArrangement = Arrangement.spacedBy(N.dp)` (Row)\n- `alignItems` in Column (horizontal alignment):\n - `"start"` \u2192 `horizontalAlignment = Alignment.Start`\n - `"center"` \u2192 `horizontalAlignment = Alignment.CenterHorizontally`\n - `"end"` \u2192 `horizontalAlignment = Alignment.End`\n- `alignItems` in Row (vertical alignment):\n - `"start"` \u2192 `verticalAlignment = Alignment.Top`\n - `"center"` \u2192 `verticalAlignment = Alignment.CenterVertically`\n - `"end"` \u2192 `verticalAlignment = Alignment.Bottom`\n\n## Modifier Chain Pattern\n- Compose uses `Modifier` chains: `Modifier.size().background().border().padding()`\n- Order matters: modifiers apply outside-in\n- Size before background: `.size(width = N.dp, height = N.dp)`\n- Width only: `.width(N.dp)`, height only: `.height(N.dp)`\n- `clipContent: true` \u2192 `.clipToBounds()`\n- Position offset: `.offset(x = N.dp, y = N.dp)`\n- Rotation: `.rotate(Nf)`\n- Opacity: `.alpha(Nf)`\n\n## Color & Fill Mapping\n- Solid fill `#RRGGBB` \u2192 `Color(0xFFRRGGBB)` (uppercase hex, FF alpha prefix)\n- 8-digit hex `#RRGGBBAA` \u2192 `Color(0xAARRGGBB)` (alpha moved to front)\n- Variable ref `$name` \u2192 `Color.Unspecified /* var(--name) */` placeholder\n- Background with shape: `.background(Color(...), RoundedCornerShape(N.dp))`\n- Background without shape: `.background(Color(...))`\n- Text color \u2192 `color = Color(0xFFhex)` parameter on `Text()`\n- Linear gradient \u2192 `Brush.linearGradient(listOf(Color(...), Color(...)))` as background\n- Radial gradient \u2192 `Brush.radialGradient(listOf(Color(...), Color(...)))` as background\n\n## Border & Stroke Mapping\n- `stroke` \u2192 `.border(N.dp, Color(...), shape)`\n- Shape defaults to `RectangleShape` if no corner radius\n- With corner radius \u2192 `.border(N.dp, Color(...), RoundedCornerShape(N.dp))`\n- Variable ref thickness \u2192 `/* var(--name) */ 1.dp` placeholder\n\n## Corner Radius\n- Uniform \u2192 `RoundedCornerShape(N.dp)`\n- Per-corner \u2192 `RoundedCornerShape(topStart = TL.dp, topEnd = TR.dp, bottomEnd = BR.dp, bottomStart = BL.dp)`\n- Applied via `.clip(shape)` or as parameter in `.background(color, shape)` / `.border()`\n\n## Effects\n- Shadow \u2192 `.shadow(elevation = N.dp, shape = RoundedCornerShape(0.dp))`\n- Blur \u2192 `// .blur(radius = N.dp) \u2014 requires custom implementation` (not natively supported as modifier)\n\n## Typography\n- Text nodes \u2192 `Text(text = "content", fontSize, fontWeight, color, ...)`\n- `fontSize` \u2192 `fontSize = N.sp`\n- `fontWeight` \u2192 `fontWeight = FontWeight.Thin|ExtraLight|Light|Normal|Medium|SemiBold|Bold|ExtraBold|Black`\n- `fontStyle: "italic"` \u2192 `fontStyle = FontStyle.Italic`\n- `fontFamily` \u2192 `fontFamily = FontFamily(Font(R.font.name))`\n- `letterSpacing` \u2192 `letterSpacing = N.sp`\n- `lineHeight` \u2192 `lineHeight = (lineHeight * fontSize).sp`\n- `textAlign` \u2192 `textAlign = TextAlign.Start|Center|End|Justify`\n- `underline` \u2192 `textDecoration = TextDecoration.Underline`\n- `strikethrough` \u2192 `textDecoration = TextDecoration.LineThrough`\n- Combined \u2192 `textDecoration = TextDecoration.combine(listOf(TextDecoration.Underline, TextDecoration.LineThrough))`\n- Short param list (2 or fewer) \u2192 inline single-line `Text(text, fontSize)`\n- Long param list \u2192 multi-line with indentation\n\n## Padding\n- Uniform \u2192 `.padding(N.dp)`\n- Symmetric \u2192 `.padding(vertical = V.dp, horizontal = H.dp)`\n- Per-side \u2192 `.padding(start = L.dp, top = T.dp, end = R.dp, bottom = B.dp)`\n- Variable ref \u2192 `.padding(/* var(--name) */ 0.dp)` placeholder\n\n## Dimensions\n- Both \u2192 `.size(width = N.dp, height = N.dp)`\n- Width only \u2192 `.width(N.dp)`\n- Height only \u2192 `.height(N.dp)`\n\n## Image Handling\n- Network URL \u2192 `AsyncImage(model = "url", contentDescription = "name", modifier, contentScale)`\n- Local resource \u2192 `Image(painter = painterResource(id = R.drawable.name), contentDescription, modifier, contentScale)`\n- Data URI \u2192 decode at runtime with `BitmapFactory.decodeByteArray()` \u2192 `Image(bitmap = bitmap.asImageBitmap())`\n- `objectFit: "fit"` \u2192 `contentScale = ContentScale.Fit`\n- `objectFit: "crop"` \u2192 `contentScale = ContentScale.Crop`\n- `objectFit: "fill"` \u2192 `contentScale = ContentScale.FillBounds`\n- Corner radius on images \u2192 `.clip(RoundedCornerShape(N.dp))`\n\n## Ellipse\n- Ellipse node \u2192 `Box(modifier = Modifier.size(...).clip(CircleShape).background(color))`\n\n## Icon Handling\n- Icon font nodes \u2192 `Icon(LucideIcons.IconName, contentDescription = "name", modifier = Modifier.size(N.dp), tint = Color(...))`\n- Icon name: kebab-case converted to PascalCase\n\n## Line Nodes\n- Line \u2192 `Divider(color = Color(...), thickness = N.dp, modifier = Modifier.width(W.dp))`\n\n## Path & Polygon\n- Path nodes \u2192 `Canvas(modifier) { drawPath(PathParser().parsePathString(data).toPath(), color) }`\n- Polygon nodes \u2192 `Canvas(modifier) { ... }` with polygon path calculation using trigonometry\n\n## Responsive Design\n- Use `BoxWithConstraints { ... }` for parent-relative sizing\n- `maxWidth` and `maxHeight` constraints available\n- Use `Modifier.fillMaxWidth()` / `.fillMaxHeight()` for full-size containers'
|
|
2266
|
+
},
|
|
2267
|
+
{
|
|
2268
|
+
"meta": {
|
|
2269
|
+
"name": "codegen-react-native",
|
|
2270
|
+
"description": "React Native code generation rules \u2014 View/Text/Image with StyleSheet.create()",
|
|
2271
|
+
"phase": [
|
|
2272
|
+
"generation"
|
|
2273
|
+
],
|
|
2274
|
+
"trigger": {
|
|
2275
|
+
"keywords": [
|
|
2276
|
+
"react-native",
|
|
2277
|
+
"rn",
|
|
2278
|
+
"react native",
|
|
2279
|
+
"mobile"
|
|
2280
|
+
]
|
|
2281
|
+
},
|
|
2282
|
+
"priority": 20,
|
|
2283
|
+
"budget": 2e3,
|
|
2284
|
+
"category": "knowledge"
|
|
2285
|
+
},
|
|
2286
|
+
"content": "# React Native Code Generation\n\nGenerate React Native components with `StyleSheet.create()` style objects. No CSS, no web-specific styling.\n\n## Output Format\n- TypeScript/JavaScript (`.tsx` or `.jsx`)\n- Functional components with `export function ComponentName()`\n- Import from `react-native`: `View`, `Text`, `Image`, `StyleSheet`\n- Import from `react-native-svg` when path/polygon nodes exist: `Svg`, `Path as SvgPath`, `Polygon as SvgPolygon`\n- All styling via inline style objects or `StyleSheet.create()` for performance\n\n## Layout Mapping\n- `layout: \"vertical\"` \u2192 `flexDirection: 'column'`\n- `layout: \"horizontal\"` \u2192 `flexDirection: 'row'`\n- No layout \u2192 default column (React Native default)\n- `gap: N` \u2192 `gap: N` (numeric, no units)\n- `padding: N` \u2192 `padding: N`\n- `padding: [vertical, horizontal]` \u2192 `paddingVertical: V, paddingHorizontal: H`\n- `padding: [top, right, bottom, left]` \u2192 `paddingTop: T, paddingRight: R, paddingBottom: B, paddingLeft: L`\n- `justifyContent: \"start\"` \u2192 `justifyContent: 'flex-start'`\n- `justifyContent: \"center\"` \u2192 `justifyContent: 'center'`\n- `justifyContent: \"end\"` \u2192 `justifyContent: 'flex-end'`\n- `justifyContent: \"space_between\"` \u2192 `justifyContent: 'space-between'`\n- `justifyContent: \"space_around\"` \u2192 `justifyContent: 'space-around'`\n- `alignItems: \"start\"` \u2192 `alignItems: 'flex-start'`\n- `alignItems: \"center\"` \u2192 `alignItems: 'center'`\n- `alignItems: \"end\"` \u2192 `alignItems: 'flex-end'`\n- `clipContent: true` \u2192 `overflow: 'hidden'`\n\n## Components\n- Container/frame/rectangle/group \u2192 `<View style={...} />`\n- Text \u2192 `<Text style={...}>content</Text>`\n- Image \u2192 `<Image source={...} style={...} />`\n- Line \u2192 `<View style={{ width: N, height: thickness, backgroundColor: color }} />`\n- Ellipse \u2192 `<View style={{ borderRadius: min(w,h)/2, ...fill }} />`\n\n## Color & Fill Mapping\n- Solid fill `#hex` \u2192 `backgroundColor: '#hex'`\n- Variable ref `$name` \u2192 `/* var(--name) */ backgroundColor: '#000000'` (placeholder with comment)\n- Text fill \u2192 `color: '#hex'`\n- Gradients: not natively supported in React Native \u2014 use `react-native-linear-gradient` library or note as comment\n\n## Border & Stroke Mapping\n- `stroke.thickness` \u2192 `borderWidth: N` (numeric, no units)\n- `stroke.color` \u2192 `borderColor: '#hex'`\n- Variable ref \u2192 `/* var(--name) */ borderWidth: 1` placeholder\n\n## Corner Radius\n- Uniform \u2192 `borderRadius: N`\n- Per-corner \u2192 `borderTopLeftRadius: TL, borderTopRightRadius: TR, borderBottomRightRadius: BR, borderBottomLeftRadius: BL`\n- Ellipse \u2192 `borderRadius: min(width, height) / 2`\n\n## Effects (Shadows)\n- Shadow color \u2192 `shadowColor: '#color'`\n- Shadow offset \u2192 `shadowOffset: { width: X, height: Y }`\n- Shadow opacity \u2192 `shadowOpacity: 1`\n- Shadow radius \u2192 `shadowRadius: blur`\n- Android elevation \u2192 `elevation: Math.round(blur / 2)` (minimum 1)\n\n## Typography\n- `fontSize` \u2192 `fontSize: N` (numeric, no units)\n- `fontWeight` \u2192 `fontWeight: 'N'` (string: '100' through '900')\n- `fontStyle: \"italic\"` \u2192 `fontStyle: 'italic'`\n- `fontFamily` \u2192 `fontFamily: 'Name'`\n- `letterSpacing` \u2192 `letterSpacing: N`\n- `lineHeight` \u2192 `lineHeight: Math.round(fontSize * lineHeight)` (computed absolute value)\n- `textAlign` \u2192 `textAlign: 'left'|'center'|'right'`\n- `underline` \u2192 `textDecorationLine: 'underline'`\n- `strikethrough` \u2192 `textDecorationLine: 'line-through'`\n- Both \u2192 `textDecorationLine: 'underline line-through'`\n\n## Dimensions\n- Fixed \u2192 `width: N, height: N` (numeric, no units \u2014 React Native uses density-independent pixels)\n- `fill_container` \u2192 `flex: 1` or `width: '100%'`\n\n## Image Handling\n- Network URL \u2192 `<Image source={{ uri: 'url' }} style={...} />`\n- Data URI \u2192 `<Image source={{ uri: 'data:image/...' }} style={...} />`\n- Local asset \u2192 `<Image source={require('./path')} style={...} />`\n- `objectFit: \"fit\"` \u2192 `resizeMode: 'contain'`\n- `objectFit: \"crop\"` \u2192 `resizeMode: 'cover'`\n- `objectFit: \"fill\"` \u2192 `resizeMode: 'stretch'`\n- Corner radius applied directly in style object\n\n## Opacity & Transform\n- `opacity: N` \u2192 `opacity: N` (numeric 0-1)\n- Variable ref \u2192 `/* var(--name) */ opacity: 1` placeholder\n- `rotation: N` \u2192 `transform: [{ rotate: 'Ndeg' }]` (string with deg suffix)\n\n## Positioning\n- Absolute children \u2192 `position: 'absolute', left: X, top: Y`\n- Container \u2192 `position: 'relative'` (default in RN, usually not needed)\n\n## SVG Elements (via react-native-svg)\n- Path nodes \u2192 `<Svg width={W} height={H} viewBox=\"0 0 W H\"><SvgPath d=\"...\" fill=\"color\" /></Svg>`\n- Polygon nodes \u2192 `<Svg><SvgPolygon points=\"x1,y1 x2,y2 ...\" fill=\"color\" /></Svg>`\n- Wrap in `<View style={positionStyles}>` if positioned\n\n## Icon Handling\n- Icon font nodes \u2192 `<IconName size={N} color=\"color\" />` (kebab-to-PascalCase)\n\n## Style Values \u2014 Key Differences from CSS\n- All numeric values are unitless (no `px`, `em`, `rem`)\n- String values must be quoted: `'center'`, `'row'`, `'absolute'`\n- `borderRadius` does not support shorthand \u2014 use per-corner properties for different values\n- No `box-shadow` \u2014 use `shadowColor` + `shadowOffset` + `shadowOpacity` + `shadowRadius`\n- No CSS gradients \u2014 use third-party libraries\n- `transform` takes an array of objects: `[{ rotate: '45deg' }, { scale: 2 }]`\n\n## Responsive Design\n- Use `Dimensions.get('window')` for screen width/height\n- Use `useWindowDimensions()` hook for reactive screen size\n- Use `flex: 1` and percentage widths for adaptive layouts\n- `ScrollView` for scrollable content"
|
|
1714
2287
|
},
|
|
1715
2288
|
{
|
|
1716
2289
|
"meta": {
|
|
1717
|
-
"name": "
|
|
1718
|
-
"description": "
|
|
2290
|
+
"name": "codegen-react",
|
|
2291
|
+
"description": "React + Tailwind CSS code generation rules \u2014 TSX output with Tailwind utility classes",
|
|
1719
2292
|
"phase": [
|
|
1720
2293
|
"generation"
|
|
1721
2294
|
],
|
|
1722
|
-
"trigger":
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
2295
|
+
"trigger": {
|
|
2296
|
+
"keywords": [
|
|
2297
|
+
"react",
|
|
2298
|
+
"tsx",
|
|
2299
|
+
"tailwind"
|
|
2300
|
+
]
|
|
2301
|
+
},
|
|
2302
|
+
"priority": 20,
|
|
2303
|
+
"budget": 2e3,
|
|
2304
|
+
"category": "knowledge"
|
|
1726
2305
|
},
|
|
1727
|
-
"content": '
|
|
2306
|
+
"content": '# React + Tailwind Code Generation\n\nGenerate React TSX components using Tailwind CSS utility classes.\n\n## Output Format\n- TypeScript TSX (`.tsx`)\n- Functional components with `export function ComponentName()`\n- Tailwind CSS for all styling (no inline styles, no CSS modules)\n\n## Layout Mapping\n- `layout: "vertical"` \u2192 `flex flex-col`\n- `layout: "horizontal"` \u2192 `flex flex-row`\n- `gap: N` \u2192 `gap-[Npx]`\n- `padding` \u2192 `p-[Npx]` or `pt-[N] pr-[N] pb-[N] pl-[N]` for per-side\n- `padding: [vertical, horizontal]` \u2192 `py-[Vpx] px-[Hpx]`\n- `justifyContent` \u2192 `justify-{start|center|end|between|around}`\n- `alignItems` \u2192 `items-{start|center|end|stretch}`\n- `clipContent: true` \u2192 `overflow-hidden`\n\n## Color & Fill Mapping\n- Solid fill `#hex` \u2192 `bg-[#hex]`\n- Variable ref `$name` \u2192 `bg-[var(--name)]`\n- Text fill \u2192 `text-[#hex]` or `text-[var(--name)]`\n- Gradient fills \u2192 `bg-gradient-to-{direction}` with `from-[color] to-[color]`\n\n## Border & Stroke Mapping\n- `stroke.thickness` \u2192 `border-[Npx]`\n- `stroke.color` \u2192 `border-[#hex]`\n- Variable ref \u2192 `border-[var(--name)]`\n\n## Corner Radius\n- Uniform \u2192 `rounded-[Npx]`\n- Per-corner `[tl, tr, br, bl]` \u2192 `rounded-[tl_tr_br_bl]` (Tailwind arbitrary values)\n- Ellipse \u2192 `rounded-full`\n\n## Effects\n- Drop shadow \u2192 `shadow-[offsetXpx_offsetYpx_blurpx_spreadpx_color]`\n- Inner shadow \u2192 use `shadow-inner` variant\n- Blur \u2192 `blur-[Npx]`\n\n## Typography\n- `fontSize` \u2192 `text-[Npx]`\n- `fontWeight` (numeric) \u2192 `font-[weight]`\n- `fontStyle: "italic"` \u2192 `italic`\n- `fontFamily` \u2192 `font-[\'Family_Name\']` (spaces replaced with underscores)\n- `lineHeight` \u2192 `leading-[value]`\n- `letterSpacing` \u2192 `tracking-[Npx]`\n- `textAlign` \u2192 `text-{left|center|right|justify}`\n- `textAlignVertical: "middle"` \u2192 `align-middle`\n- `textGrowth: "auto"` \u2192 `whitespace-nowrap`\n- `textGrowth: "fixed-width-height"` \u2192 `overflow-hidden`\n- `underline` \u2192 `underline`\n- `strikethrough` \u2192 `line-through`\n\n## Dimensions\n- Fixed \u2192 `w-[Npx] h-[Npx]`\n- `fill_container` width \u2192 `w-full`\n- `fill_container` height \u2192 `h-full`\n- Root component \u2192 `max-w-[Npx] w-full mx-auto` for responsive centering\n\n## Image Handling\n- `<img src={src} alt={name} className="w-[N] h-[N] object-{fit}" />`\n- `objectFit: "fit"` \u2192 `object-contain`\n- `objectFit: "crop"` \u2192 `object-cover`\n- `objectFit: "fill"` \u2192 `object-fill`\n- Corner radius on images \u2192 add `rounded-[Npx]`\n\n## Opacity & Transform\n- `opacity: N` \u2192 `opacity-[N%]` (multiply by 100)\n- Variable ref opacity \u2192 `opacity-[var(--name)]`\n- `rotation: N` \u2192 `rotate-[Ndeg]`\n\n## Positioning\n- Absolute children \u2192 `absolute left-[Xpx] top-[Ypx]`\n\n## Semantic HTML Tags\n- Font size >= 32 \u2192 `<h1>`\n- Font size >= 24 \u2192 `<h2>`\n- Font size >= 20 \u2192 `<h3>`\n- Other text \u2192 `<p>`\n- Lines \u2192 `<hr>`\n- Use `<nav>`, `<header>`, `<main>`, `<section>`, `<footer>`, `<article>` appropriately\n- Interactive elements: `<button>`, `<a>`, `<input>` where role suggests\n\n## Icon Handling\n- Icon font nodes \u2192 `<IconName size={N} color="color" />` (kebab-to-PascalCase)\n\n## Responsive Design\n- Mobile-first: base styles for mobile, `md:` for tablet, `lg:` for desktop\n- Convert fixed widths to `max-w-*` with `w-full`\n- Use `flex-wrap` for card grids on narrow viewports\n\n## Variable References\n- `$variable` refs are output as `var(--variable-name)` CSS custom properties\n- Background: `bg-[var(--name)]`\n- Text color: `text-[var(--name)]`\n- Border: `border-[var(--name)]`\n- Gap/padding with variable: `gap-[var(--name)]`, `p-[var(--name)]`'
|
|
1728
2307
|
},
|
|
1729
2308
|
{
|
|
1730
2309
|
"meta": {
|
|
1731
|
-
"name": "
|
|
1732
|
-
"description": "
|
|
2310
|
+
"name": "codegen-flutter",
|
|
2311
|
+
"description": "Flutter/Dart code generation rules \u2014 widget tree with BoxDecoration and EdgeInsets",
|
|
1733
2312
|
"phase": [
|
|
1734
2313
|
"generation"
|
|
1735
2314
|
],
|
|
1736
|
-
"trigger":
|
|
2315
|
+
"trigger": {
|
|
2316
|
+
"keywords": [
|
|
2317
|
+
"flutter",
|
|
2318
|
+
"dart",
|
|
2319
|
+
"mobile"
|
|
2320
|
+
]
|
|
2321
|
+
},
|
|
1737
2322
|
"priority": 20,
|
|
1738
|
-
"budget":
|
|
1739
|
-
"category": "
|
|
2323
|
+
"budget": 2e3,
|
|
2324
|
+
"category": "knowledge"
|
|
1740
2325
|
},
|
|
1741
|
-
"content": '
|
|
2326
|
+
"content": '# Flutter (Dart) Code Generation\n\nGenerate Flutter widget trees using Material Design widgets.\n\n## Output Format\n- Dart file (`.dart`)\n- `StatelessWidget` class with `build()` method returning widget tree\n- Import `package:flutter/material.dart`\n- Import `dart:math` for path/polygon rendering\n\n## Layout Mapping\n- `layout: "vertical"` \u2192 `Column(children: [...])`\n- `layout: "horizontal"` \u2192 `Row(children: [...])`\n- No layout / stacked children \u2192 `Stack(children: [...])` with `Positioned()` wrappers\n- `gap: N` \u2192 `SizedBox(height: N)` between children (Column) or `SizedBox(width: N)` between children (Row)\n- `justifyContent: "start"` \u2192 `mainAxisAlignment: MainAxisAlignment.start`\n- `justifyContent: "center"` \u2192 `mainAxisAlignment: MainAxisAlignment.center`\n- `justifyContent: "end"` \u2192 `mainAxisAlignment: MainAxisAlignment.end`\n- `justifyContent: "space_between"` \u2192 `mainAxisAlignment: MainAxisAlignment.spaceBetween`\n- `justifyContent: "space_around"` \u2192 `mainAxisAlignment: MainAxisAlignment.spaceAround`\n- `alignItems: "start"` \u2192 `crossAxisAlignment: CrossAxisAlignment.start`\n- `alignItems: "center"` \u2192 `crossAxisAlignment: CrossAxisAlignment.center`\n- `alignItems: "end"` \u2192 `crossAxisAlignment: CrossAxisAlignment.end`\n- Always include `mainAxisSize: MainAxisSize.min` on Column/Row\n\n## Container & Decoration\n- Container nodes \u2192 `Container()` widget with named parameters\n- `width: N` \u2192 `width: N`\n- `height: N` \u2192 `height: N`\n- `clipContent: true` \u2192 `clipBehavior: Clip.hardEdge`\n- Styling via `decoration: BoxDecoration(...)` parameter\n\n## Color & Fill Mapping\n- Solid fill `#RRGGBB` \u2192 `Color(0xFFRRGGBB)` (prefix FF for full alpha)\n- 8-digit hex `#RRGGBBAA` \u2192 `Color(0xAARRGGBB)` (alpha moved to front)\n- Variable ref `$name` \u2192 `Color(0x00000000) /* var(--name) */` (placeholder with comment)\n- Text fill \u2192 `color: Color(0xFFhex)` in `TextStyle`\n- Linear gradient \u2192 `gradient: LinearGradient(colors: [Color(...), Color(...)])`\n- Radial gradient \u2192 `gradient: RadialGradient(colors: [Color(...), Color(...)])`\n\n## Border & Stroke Mapping\n- `stroke.thickness + stroke.color` \u2192 `border: Border.all(color: Color(...), width: N)`\n- Variable ref thickness \u2192 `/* var(--name) */ 1` placeholder\n\n## Corner Radius\n- Uniform \u2192 `borderRadius: BorderRadius.circular(N)`\n- Per-corner \u2192 `borderRadius: BorderRadius.only(topLeft: Radius.circular(TL), topRight: Radius.circular(TR), bottomRight: Radius.circular(BR), bottomLeft: Radius.circular(BL))`\n\n## Effects\n- Drop shadow \u2192 `boxShadow: [BoxShadow(color: Color(...), blurRadius: N, offset: Offset(X, Y))]`\n- Blur \u2192 `BackdropFilter(filter: ImageFilter.blur(sigmaX: N, sigmaY: N), child: ...)`\n\n## Typography\n- Text nodes \u2192 `Text(\'content\', style: TextStyle(...))`\n- `fontSize` \u2192 `fontSize: N`\n- `fontWeight` \u2192 `fontWeight: FontWeight.wN00` (w100 through w900)\n- `fontStyle: "italic"` \u2192 `fontStyle: FontStyle.italic`\n- `fontFamily` \u2192 `fontFamily: \'Name\'`\n- `letterSpacing` \u2192 `letterSpacing: N`\n- `lineHeight` \u2192 `height: lineHeight` (multiplier in TextStyle)\n- `textAlign` \u2192 `textAlign: TextAlign.left|center|right|justify`\n- `underline` \u2192 `decoration: TextDecoration.underline`\n- `strikethrough` \u2192 `decoration: TextDecoration.lineThrough`\n- Combined \u2192 `decoration: TextDecoration.combine([TextDecoration.underline, TextDecoration.lineThrough])`\n- Fixed-size text \u2192 wrap in `SizedBox(width: N, height: N, child: Text(...))`\n\n## Padding\n- Uniform \u2192 `padding: EdgeInsets.all(N)`\n- Symmetric \u2192 `padding: EdgeInsets.symmetric(vertical: V, horizontal: H)`\n- Per-side `[top, right, bottom, left]` \u2192 `padding: EdgeInsets.fromLTRB(left, top, right, bottom)`\n- Variable ref \u2192 `EdgeInsets.all(/* var(--name) */ 0)` placeholder\n\n## Dimensions\n- Fixed \u2192 `width: N, height: N` on Container\n- Text sizing \u2192 wrap in `SizedBox`\n\n## Image Handling\n- Network URL \u2192 `Image.network(\'url\', width: N, height: N, fit: BoxFit.cover)`\n- Asset \u2192 `Image.asset(\'path\', width: N, height: N, fit: BoxFit.cover)`\n- Data URI \u2192 `Image.memory(base64Decode(\'...\'))`\n- `objectFit: "fit"` \u2192 `BoxFit.contain`\n- `objectFit: "crop"` \u2192 `BoxFit.cover`\n- Corner radius on images \u2192 `ClipRRect(borderRadius: BorderRadius.circular(N), child: Image(...))`\n\n## Opacity & Transform\n- Opacity \u2192 `Opacity(opacity: N, child: widget)` wrapper\n- Rotation \u2192 `Transform.rotate(angle: N * pi / 180, child: widget)` wrapper\n- Applied as wrapper widgets around the base widget\n\n## Positioning\n- Absolute children \u2192 `Positioned(left: X, top: Y, child: widget)` inside `Stack`\n\n## Ellipse\n- Circle/ellipse \u2192 `Container` with `BoxDecoration(shape: BoxShape.circle)`\n\n## Icon Handling\n- Icon font nodes \u2192 `Icon(LucideIcons.icon_name, size: N, color: Color(...))`\n- Icon name: kebab-case converted to snake_case\n\n## Path & Polygon\n- Path nodes \u2192 `CustomPaint(size: Size(W, H), painter: _PathPainter(pathData, color))`\n- Polygon nodes \u2192 `CustomPaint(size: Size(W, H), painter: _PolygonPainter(sides, color))`\n- Include helper `CustomPainter` classes at bottom of file\n\n## Responsive Design\n- Use `MediaQuery.of(context).size` for screen dimensions\n- `LayoutBuilder` for parent-relative sizing\n- `Flexible` and `Expanded` for proportional layouts'
|
|
1742
2327
|
},
|
|
1743
2328
|
{
|
|
1744
2329
|
"meta": {
|
|
1745
|
-
"name": "
|
|
1746
|
-
"description": "
|
|
2330
|
+
"name": "codegen-svelte",
|
|
2331
|
+
"description": "Svelte component code generation rules \u2014 markup with scoped styles",
|
|
1747
2332
|
"phase": [
|
|
1748
2333
|
"generation"
|
|
1749
2334
|
],
|
|
1750
|
-
"trigger":
|
|
2335
|
+
"trigger": {
|
|
2336
|
+
"keywords": [
|
|
2337
|
+
"svelte",
|
|
2338
|
+
"svelte5"
|
|
2339
|
+
]
|
|
2340
|
+
},
|
|
1751
2341
|
"priority": 20,
|
|
1752
|
-
"budget":
|
|
1753
|
-
"category": "
|
|
2342
|
+
"budget": 2e3,
|
|
2343
|
+
"category": "knowledge"
|
|
1754
2344
|
},
|
|
1755
|
-
"content": '
|
|
2345
|
+
"content": '# Svelte Component Code Generation\n\nGenerate Svelte components with markup and scoped `<style>` blocks.\n\n## Output Format\n- Svelte component (`.svelte`)\n- `<script lang="ts">` for component logic (optional, only when props/logic needed)\n- HTML template markup\n- `<style>` block with scoped CSS classes\n- Each node gets a unique, descriptive CSS class name derived from `node.name`\n\n## Layout Mapping\n- `layout: "vertical"` \u2192 `display: flex; flex-direction: column`\n- `layout: "horizontal"` \u2192 `display: flex; flex-direction: row`\n- `gap: N` \u2192 `gap: Npx`\n- `padding: N` \u2192 `padding: Npx`\n- `padding: [t, r, b, l]` \u2192 `padding: Tpx Rpx Bpx Lpx`\n- `justifyContent: "start"` \u2192 `justify-content: flex-start`\n- `justifyContent: "center"` \u2192 `justify-content: center`\n- `justifyContent: "end"` \u2192 `justify-content: flex-end`\n- `justifyContent: "space_between"` \u2192 `justify-content: space-between`\n- `justifyContent: "space_around"` \u2192 `justify-content: space-around`\n- `alignItems: "start"` \u2192 `align-items: flex-start`\n- `alignItems: "center"` \u2192 `align-items: center`\n- `alignItems: "end"` \u2192 `align-items: flex-end`\n- `clipContent: true` \u2192 `overflow: hidden`\n\n## Color & Fill Mapping\n- Solid fill `#hex` \u2192 `background: #hex`\n- Variable ref `$name` \u2192 `background: var(--name)`\n- Text fill \u2192 `color: #hex` or `color: var(--name)`\n- Linear gradient \u2192 `background: linear-gradient(Ndeg, color1 0%, color2 100%)`\n- Radial gradient \u2192 `background: radial-gradient(circle, color1 0%, color2 100%)`\n\n## Border & Stroke Mapping\n- `stroke.thickness` \u2192 `border-width: Npx; border-style: solid`\n- `stroke.color` \u2192 `border-color: #hex`\n- Variable ref \u2192 `border-width: var(--name)`, `border-color: var(--name)`\n\n## Corner Radius\n- Uniform \u2192 `border-radius: Npx`\n- Per-corner `[tl, tr, br, bl]` \u2192 `border-radius: TLpx TRpx BRpx BLpx`\n- Ellipse \u2192 `border-radius: 50%`\n\n## Effects\n- Drop shadow \u2192 `box-shadow: offsetXpx offsetYpx blurpx spreadpx color`\n- Inner shadow \u2192 `box-shadow: inset offsetXpx offsetYpx blurpx spreadpx color`\n- Multiple shadows comma-separated\n\n## Typography\n- `fontSize` \u2192 `font-size: Npx`\n- `fontWeight` \u2192 `font-weight: N`\n- `fontStyle: "italic"` \u2192 `font-style: italic`\n- `fontFamily` \u2192 `font-family: \'Name\', sans-serif`\n- `lineHeight` \u2192 `line-height: value`\n- `letterSpacing` \u2192 `letter-spacing: Npx`\n- `textAlign` \u2192 `text-align: left|center|right`\n- `underline` \u2192 `text-decoration: underline`\n- `strikethrough` \u2192 `text-decoration: line-through`\n\n## Dimensions\n- Fixed \u2192 `width: Npx; height: Npx`\n- `fill_container` \u2192 `width: 100%` or `height: 100%`\n\n## Image Handling\n- `<img class="className" src={src} alt={name} />`\n- `object-fit: contain|cover|fill` based on `objectFit` property\n- Corner radius applied via CSS class\n\n## Opacity & Transform\n- `opacity: N` \u2192 `opacity: N`\n- `rotation: N` \u2192 `transform: rotate(Ndeg)`\n\n## Positioning\n- Absolute children \u2192 `position: absolute; left: Xpx; top: Ypx`\n\n## Semantic HTML Tags\n- Font size >= 32 \u2192 `<h1>`\n- Font size >= 24 \u2192 `<h2>`\n- Font size >= 20 \u2192 `<h3>`\n- Other text \u2192 `<p>`\n- Lines \u2192 `<hr>`\n- Use semantic elements (`<nav>`, `<header>`, `<main>`, `<section>`, `<footer>`)\n\n## Icon Handling\n- Icon font nodes \u2192 `<i class="className" data-lucide="icon-name" />`\n- Set `width`, `height`, and `color` via CSS class\n\n## Svelte-Specific Patterns\n- Use `{#each items as item}` for lists\n- Use `{#if condition}` / `{:else}` for conditional rendering\n- Use `class:active={isActive}` directive for conditional classes\n- Props declared with `export let propName: Type`\n- Event forwarding with `on:click`\n- Slot content with `<slot />` and named slots `<slot name="header" />`\n- Reactive declarations with `$:` label\n- Styles are automatically scoped to the component\n\n## Variable References\n- `$variable` refs \u2192 `var(--variable-name)` in CSS\n- Background: `background: var(--name)`\n- Text color: `color: var(--name)`\n- Border: `border-color: var(--name)`\n- Define CSS custom properties in `:root` or `:global` style block'
|
|
1756
2346
|
},
|
|
1757
2347
|
{
|
|
1758
2348
|
"meta": {
|
|
1759
|
-
"name": "
|
|
1760
|
-
"description": "
|
|
2349
|
+
"name": "codegen-swiftui",
|
|
2350
|
+
"description": "SwiftUI code generation rules \u2014 declarative views with modifier chains",
|
|
1761
2351
|
"phase": [
|
|
1762
2352
|
"generation"
|
|
1763
2353
|
],
|
|
1764
|
-
"trigger":
|
|
2354
|
+
"trigger": {
|
|
2355
|
+
"keywords": [
|
|
2356
|
+
"swiftui",
|
|
2357
|
+
"swift",
|
|
2358
|
+
"ios",
|
|
2359
|
+
"macos",
|
|
2360
|
+
"apple"
|
|
2361
|
+
]
|
|
2362
|
+
},
|
|
1765
2363
|
"priority": 20,
|
|
1766
|
-
"budget":
|
|
1767
|
-
"category": "
|
|
2364
|
+
"budget": 2e3,
|
|
2365
|
+
"category": "knowledge"
|
|
1768
2366
|
},
|
|
1769
|
-
"content": `
|
|
1770
|
-
|
|
1771
|
-
OUTPUT RULES:
|
|
1772
|
-
- Output ONLY the complete HTML file, starting with <!DOCTYPE html>. No explanation.
|
|
1773
|
-
- ALL CSS must be inline in a <style> block. No external stylesheets except Google Fonts.
|
|
1774
|
-
- Use modern CSS: flexbox, gap, custom properties, clamp().
|
|
1775
|
-
- The page must render correctly at the specified viewport dimensions.
|
|
1776
|
-
- All images use colored placeholder rectangles with labels (no external images).
|
|
1777
|
-
- Icons use simple inline SVG shapes (geometric, not complex).
|
|
1778
|
-
- Include Google Fonts via <link> in the <head> if non-system fonts are specified.
|
|
1779
|
-
|
|
1780
|
-
DESIGN QUALITY:
|
|
1781
|
-
- This is a visual reference for a design tool \u2014 every pixel matters.
|
|
1782
|
-
- Create clear visual hierarchy: one dominant element per section, everything else subordinate.
|
|
1783
|
-
- Use whitespace generously \u2014 premium designs breathe.
|
|
1784
|
-
- Avoid template-ish layouts: don't put everything in the center, explore asymmetry.
|
|
1785
|
-
- Color should guide the eye: accent color on CTAs and key elements, neutral everywhere else.
|
|
1786
|
-
- Typography should create rhythm: vary size, weight, and color across the type scale.
|
|
1787
|
-
- Shadows should be subtle (0 2px 8px rgba(0,0,0,0.08)) \u2014 never heavy drop shadows.
|
|
1788
|
-
- Corner radius should be consistent across the design (8-12px for modern, 16px+ for friendly).
|
|
1789
|
-
- Sections should flow naturally: alternate background tints, use generous vertical padding (80-120px).
|
|
1790
|
-
|
|
1791
|
-
ANTI-PATTERNS TO AVOID:
|
|
1792
|
-
- Every card looking identical with blue icon + black title + gray text (the "AI template" look).
|
|
1793
|
-
- Centered everything \u2014 real designs use left-alignment and asymmetric layouts.
|
|
1794
|
-
- Too many things competing for attention \u2014 ruthlessly prioritize.
|
|
1795
|
-
- Decorative elements that serve no purpose \u2014 every element must earn its place.
|
|
1796
|
-
- Generic stock-photo-style image placeholders \u2014 use branded colored rectangles.
|
|
1797
|
-
- All buttons the same size and color \u2014 create a button hierarchy.
|
|
1798
|
-
|
|
1799
|
-
TEXT CONTENT:
|
|
1800
|
-
- Headlines: 2-6 words, punchy and specific to the product.
|
|
1801
|
-
- Subtitles: 1 sentence, max 15 words.
|
|
1802
|
-
- Feature descriptions: 1 sentence, max 20 words.
|
|
1803
|
-
- Button text: 1-3 words.
|
|
1804
|
-
- Never use lorem ipsum or generic "Your text here" placeholders.`
|
|
2367
|
+
"content": '# SwiftUI Code Generation\n\nGenerate SwiftUI views with modifier chains.\n\n## Output Format\n- Swift file (`.swift`)\n- `struct ViewName: View` with `var body: some View { ... }`\n- Import `SwiftUI`\n- Include `#Preview { ViewName() }` at bottom\n- Include helper shapes (SVGPath, PolygonShape) when path/polygon nodes exist\n\n## Layout Mapping\n- `layout: "vertical"` \u2192 `VStack(alignment:, spacing:) { ... }`\n- `layout: "horizontal"` \u2192 `HStack(alignment:, spacing:) { ... }`\n- No layout / stacked children \u2192 `ZStack(alignment: .topLeading) { ... }`\n- `gap: N` \u2192 `spacing: N` parameter on VStack/HStack\n- `alignItems` in vertical layout:\n - `"start"` \u2192 `alignment: .leading`\n - `"center"` \u2192 `alignment: .center`\n - `"end"` \u2192 `alignment: .trailing`\n- `alignItems` in horizontal layout:\n - `"start"` \u2192 `alignment: .top`\n - `"center"` \u2192 `alignment: .center`\n - `"end"` \u2192 `alignment: .bottom`\n\n## Modifier Chain Pattern\n- SwiftUI uses dot-chained modifiers on views\n- Order matters: `.padding()` before `.background()` adds padding inside background\n- Common chain: view \u2192 `.padding()` \u2192 `.frame()` \u2192 `.background()` \u2192 `.clipShape()` \u2192 `.shadow()` \u2192 `.opacity()` \u2192 `.offset()`\n\n## Container Styling\n- Empty container with fill + corner radius \u2192 `RoundedRectangle(cornerRadius: N).fill(color)`\n- Empty container with fill, no radius \u2192 `Rectangle().fill(color)`\n- Container with children \u2192 Stack + modifiers (`.padding()`, `.frame()`, `.background()`)\n- `clipContent: true` \u2192 `.clipped()`\n\n## Color & Fill Mapping\n- Solid fill `#RRGGBB` \u2192 `Color(red: R/255, green: G/255, blue: B/255)` (normalized 0-1 floats)\n- 8-digit hex `#RRGGBBAA` \u2192 `.opacity(A/255)` modifier chained on Color\n- Variable ref `$name` \u2192 `Color("var(--name)") /* variable */` placeholder\n- Background fill \u2192 `.background(Color(...))` modifier\n- Shape fill \u2192 `.fill(Color(...))` modifier\n- Linear gradient \u2192 `LinearGradient(stops: [.init(color:, location:)], startPoint:, endPoint:)`\n- Radial gradient \u2192 `RadialGradient(stops: [...], center: .center, startRadius: 0, endRadius: 100)`\n- Text color \u2192 `.foregroundColor(Color(...))`\n- Gradient direction angles map to UnitPoints: 0->bottom/top, 90->leading/trailing, 180->top/bottom, 270->trailing/leading\n\n## Border & Stroke Mapping\n- With corner radius \u2192 `.overlay(RoundedRectangle(cornerRadius: N).stroke(color, lineWidth: N))`\n- Without corner radius \u2192 `.overlay(Rectangle().stroke(color, lineWidth: N))`\n\n## Corner Radius\n- Uniform \u2192 `.clipShape(RoundedRectangle(cornerRadius: N))`\n- On shapes \u2192 `RoundedRectangle(cornerRadius: N)` as the shape itself\n\n## Effects\n- Drop shadow \u2192 `.shadow(color: Color(...), radius: N, x: X, y: Y)`\n- Blur \u2192 `.blur(radius: N)`\n\n## Typography\n- Text nodes \u2192 `Text("content")` with modifier chain\n- `fontSize + fontWeight` \u2192 `.font(.system(size: N, weight: .weightName))`\n- `fontSize` only \u2192 `.font(.system(size: N))`\n- Font weights: `.ultraLight`, `.thin`, `.light`, `.regular`, `.medium`, `.semibold`, `.bold`, `.heavy`, `.black`\n- `fontStyle: "italic"` \u2192 `.italic()`\n- Text color \u2192 `.foregroundColor(Color(...))`\n- `textAlign` \u2192 `.multilineTextAlignment(.leading|.center|.trailing)`\n- Fixed-size text \u2192 `.frame(width: N, height: N, alignment: .leading|.trailing)`\n- `letterSpacing` \u2192 `.kerning(N)`\n- `lineHeight` (with fontSize) \u2192 `.lineSpacing(lineHeight * fontSize - fontSize)`\n- `underline` \u2192 `.underline()`\n- `strikethrough` \u2192 `.strikethrough()`\n\n## Padding\n- Uniform \u2192 `.padding(N)`\n- Symmetric \u2192 `.padding(.vertical, V)` + `.padding(.horizontal, H)`\n- Per-side \u2192 `.padding(.top, T)` + `.padding(.trailing, R)` + `.padding(.bottom, B)` + `.padding(.leading, L)`\n- Variable ref \u2192 `.padding(/* var(--name) */ 0)` placeholder\n\n## Dimensions\n- Fixed \u2192 `.frame(width: N, height: N)`\n- Width only \u2192 `.frame(width: N)`\n- Height only \u2192 `.frame(height: N)`\n\n## Image Handling\n- Local asset \u2192 `Image("name")` with `.resizable()` + `.aspectRatio(contentMode:)` + `.frame()`\n- Network URL \u2192 `AsyncImage(url: URL(string: "url")) { image in image.modifiers } placeholder: { ProgressView() }`\n- Data URI \u2192 decode base64 at runtime with `UIImage(data:)` \u2192 `Image(uiImage:)`\n- `objectFit: "fit"` \u2192 `.aspectRatio(contentMode: .fit)`\n- `objectFit: "crop"` \u2192 `.aspectRatio(contentMode: .fill)`\n- Corner radius on images \u2192 `.clipShape(RoundedRectangle(cornerRadius: N))`\n\n## Opacity & Transform\n- Opacity \u2192 `.opacity(N)` modifier\n- Rotation \u2192 `.rotationEffect(.degrees(N))` modifier\n- Variable ref opacity \u2192 `.opacity(/* var(--name) */ 1.0)` placeholder\n\n## Positioning\n- Absolute children \u2192 `.offset(x: X, y: Y)` modifier\n\n## Ellipse\n- Ellipse node \u2192 `Ellipse()` with `.fill()`, `.frame()`, `.stroke()` modifiers\n\n## Icon Handling\n- Icon font nodes \u2192 `Image("icon.name")` with `.resizable()` + `.frame(width: N, height: N)`\n- Color \u2192 `.foregroundColor(Color(hex: "..."))`\n- Icon name: kebab-case converted to dot.notation\n\n## Path & Polygon\n- Path nodes \u2192 `SVGPath("path-data").fill(color)` (include SVGPath helper shape)\n- Polygon nodes \u2192 `PolygonShape(sides: N).fill(color)` (include PolygonShape helper)\n\n## Responsive Design\n- Use `GeometryReader { geometry in ... }` for parent-relative sizing\n- `geometry.size.width` and `geometry.size.height` for dynamic dimensions\n- Use `.frame(maxWidth: .infinity)` for full-width containers\n\n## Line Nodes\n- Line \u2192 `Rectangle()` with `.frame(width: N, height: 1)` + `.background(color)`'
|
|
1805
2368
|
},
|
|
1806
2369
|
{
|
|
1807
2370
|
"meta": {
|
|
1808
|
-
"name": "
|
|
1809
|
-
"description": "
|
|
2371
|
+
"name": "codegen-vue",
|
|
2372
|
+
"description": "Vue 3 SFC code generation rules \u2014 single file component with scoped CSS",
|
|
1810
2373
|
"phase": [
|
|
1811
|
-
"
|
|
2374
|
+
"generation"
|
|
1812
2375
|
],
|
|
1813
2376
|
"trigger": {
|
|
1814
2377
|
"keywords": [
|
|
1815
|
-
"
|
|
1816
|
-
"
|
|
1817
|
-
"
|
|
1818
|
-
"append"
|
|
2378
|
+
"vue",
|
|
2379
|
+
"vue3",
|
|
2380
|
+
"sfc"
|
|
1819
2381
|
]
|
|
1820
2382
|
},
|
|
1821
2383
|
"priority": 20,
|
|
1822
|
-
"budget":
|
|
1823
|
-
"category": "
|
|
2384
|
+
"budget": 2e3,
|
|
2385
|
+
"category": "knowledge"
|
|
1824
2386
|
},
|
|
1825
|
-
"content": `
|
|
1826
|
-
|
|
1827
|
-
When adding new elements to an existing design:
|
|
1828
|
-
|
|
1829
|
-
CONTEXT AWARENESS:
|
|
1830
|
-
- Analyze the existing design structure before adding new elements.
|
|
1831
|
-
- Match the visual style (colors, fonts, spacing, cornerRadius) of existing siblings.
|
|
1832
|
-
- Place new elements in logical positions within the hierarchy.
|
|
1833
|
-
|
|
1834
|
-
SIBLING CONSISTENCY:
|
|
1835
|
-
- New cards in a card row MUST match existing cards' width/height strategy (typically fill_container).
|
|
1836
|
-
- New inputs in a form MUST match existing inputs' width and height.
|
|
1837
|
-
- New sections MUST use the same padding and gap patterns as existing sections.
|
|
1838
|
-
|
|
1839
|
-
INSERTION RULES:
|
|
1840
|
-
- Use "_parent" to specify where the new node belongs in the tree.
|
|
1841
|
-
- New sections append after the last existing section by default.
|
|
1842
|
-
- New items within a list/grid append after the last existing item.
|
|
1843
|
-
- Preserve z-order: overlay elements (badges, indicators) come BEFORE content.
|
|
1844
|
-
|
|
1845
|
-
COMMON PATTERNS:
|
|
1846
|
-
- "Add a section" -> new frame with width="fill_container", height="fit_content", layout="vertical", matching section padding.
|
|
1847
|
-
- "Add a card" -> new frame matching sibling card structure (same children pattern, same styles).
|
|
1848
|
-
- "Add an input" -> new frame with role="input" or "form-input", width="fill_container", matching sibling inputs.
|
|
1849
|
-
- "Add a button" -> new frame with role="button", matching existing button style.
|
|
1850
|
-
- "Add a row" -> new frame with layout="horizontal", appropriate gap and alignment.
|
|
1851
|
-
|
|
1852
|
-
ID GENERATION:
|
|
1853
|
-
- Use unique descriptive IDs for new nodes (e.g. "new-feature-card", "contact-section").
|
|
1854
|
-
- Never reuse existing IDs.`
|
|
2387
|
+
"content": '# Vue 3 Single File Component Code Generation\n\nGenerate Vue 3 Single File Components with `<script setup>`, `<template>`, and `<style scoped>`.\n\n## Output Format\n- Vue 3 SFC (`.vue`)\n- `<script setup lang="ts">` for component logic\n- `<template>` with semantic HTML markup\n- `<style scoped>` with CSS classes (no Tailwind, no inline styles)\n- Each node gets a unique, descriptive CSS class name derived from `node.name`\n\n## Layout Mapping\n- `layout: "vertical"` \u2192 `display: flex; flex-direction: column`\n- `layout: "horizontal"` \u2192 `display: flex; flex-direction: row`\n- `gap: N` \u2192 `gap: Npx`\n- `padding: N` \u2192 `padding: Npx`\n- `padding: [t, r, b, l]` \u2192 `padding: Tpx Rpx Bpx Lpx`\n- `justifyContent: "start"` \u2192 `justify-content: flex-start`\n- `justifyContent: "center"` \u2192 `justify-content: center`\n- `justifyContent: "end"` \u2192 `justify-content: flex-end`\n- `justifyContent: "space_between"` \u2192 `justify-content: space-between`\n- `justifyContent: "space_around"` \u2192 `justify-content: space-around`\n- `alignItems: "start"` \u2192 `align-items: flex-start`\n- `alignItems: "center"` \u2192 `align-items: center`\n- `alignItems: "end"` \u2192 `align-items: flex-end`\n- `clipContent: true` \u2192 `overflow: hidden`\n\n## Color & Fill Mapping\n- Solid fill `#hex` \u2192 `background: #hex`\n- Variable ref `$name` \u2192 `background: var(--name)`\n- Text fill \u2192 `color: #hex` or `color: var(--name)`\n- Linear gradient \u2192 `background: linear-gradient(Ndeg, color1 0%, color2 100%)`\n- Radial gradient \u2192 `background: radial-gradient(circle, color1 0%, color2 100%)`\n\n## Border & Stroke Mapping\n- `stroke.thickness` \u2192 `border-width: Npx; border-style: solid`\n- `stroke.color` \u2192 `border-color: #hex`\n- Variable ref \u2192 `border-width: var(--name)`, `border-color: var(--name)`\n\n## Corner Radius\n- Uniform \u2192 `border-radius: Npx`\n- Per-corner `[tl, tr, br, bl]` \u2192 `border-radius: TLpx TRpx BRpx BLpx`\n- Ellipse \u2192 `border-radius: 50%`\n\n## Effects\n- Drop shadow \u2192 `box-shadow: offsetXpx offsetYpx blurpx spreadpx color`\n- Inner shadow \u2192 `box-shadow: inset offsetXpx offsetYpx blurpx spreadpx color`\n- Multiple shadows comma-separated\n\n## Typography\n- `fontSize` \u2192 `font-size: Npx`\n- `fontWeight` \u2192 `font-weight: N`\n- `fontStyle: "italic"` \u2192 `font-style: italic`\n- `fontFamily` \u2192 `font-family: \'Name\', sans-serif`\n- `lineHeight` \u2192 `line-height: value`\n- `letterSpacing` \u2192 `letter-spacing: Npx`\n- `textAlign` \u2192 `text-align: left|center|right`\n- `underline` \u2192 `text-decoration: underline`\n- `strikethrough` \u2192 `text-decoration: line-through`\n\n## Dimensions\n- Fixed \u2192 `width: Npx; height: Npx`\n- `fill_container` \u2192 `width: 100%` or `height: 100%`\n\n## Image Handling\n- `<img class="className" :src="src" :alt="name" />`\n- `object-fit: contain|cover|fill` based on `objectFit` property\n- Corner radius applied via CSS class\n\n## Opacity & Transform\n- `opacity: N` \u2192 `opacity: N`\n- `rotation: N` \u2192 `transform: rotate(Ndeg)`\n\n## Positioning\n- Absolute children \u2192 `position: absolute; left: Xpx; top: Ypx`\n\n## Semantic HTML Tags\n- Font size >= 32 \u2192 `<h1>`\n- Font size >= 24 \u2192 `<h2>`\n- Font size >= 20 \u2192 `<h3>`\n- Other text \u2192 `<p>`\n- Lines \u2192 `<hr>`\n- Use semantic elements (`<nav>`, `<header>`, `<main>`, `<section>`, `<footer>`)\n\n## Icon Handling\n- Icon font nodes \u2192 `<i class="className" data-lucide="icon-name" />`\n- Set `width`, `height`, and `color` via CSS class\n\n## Vue-Specific Patterns\n- Use `v-for` for repeated items: `<div v-for="item in items" :key="item.id">`\n- Use `v-if` / `v-else` for conditional rendering\n- Use `:class` binding for dynamic classes\n- Use `:style` binding sparingly (prefer CSS classes)\n- Props defined with `defineProps<{ ... }>()`\n- Emits defined with `defineEmits<{ ... }>()`\n\n## Variable References\n- `$variable` refs \u2192 `var(--variable-name)` in CSS\n- Background: `background: var(--name)`\n- Text color: `color: var(--name)`\n- Border: `border-color: var(--name)`\n- Define CSS custom properties in `:root` or scoped style block'
|
|
1855
2388
|
},
|
|
1856
2389
|
{
|
|
1857
2390
|
"meta": {
|
|
1858
|
-
"name": "
|
|
1859
|
-
"description": "
|
|
2391
|
+
"name": "icon-catalog",
|
|
2392
|
+
"description": "Icon usage rules and available icon names",
|
|
1860
2393
|
"phase": [
|
|
1861
2394
|
"generation"
|
|
1862
2395
|
],
|
|
1863
2396
|
"trigger": null,
|
|
1864
|
-
"priority":
|
|
2397
|
+
"priority": 20,
|
|
1865
2398
|
"budget": 1e3,
|
|
1866
|
-
"category": "
|
|
2399
|
+
"category": "base"
|
|
1867
2400
|
},
|
|
1868
|
-
"content":
|
|
2401
|
+
"content": 'ICONS:\n- Use "path" nodes, size 16-24px. ONLY use Feather icon names \u2014 PascalCase + "Icon" suffix (e.g. "SearchIcon").\n- System auto-resolves names to SVG paths. "d" is replaced automatically.\n- NEVER use emoji as icons. Use icon_font nodes for lucide icons.\n\nICON_FONT NODES:\n- Use icon_font type with iconFontName for lucide icons (e.g. iconFontName="search", "bell", "user").\n- Sizes: 14/20/24px. Fill can be a color string.\n- Icon-only buttons: frame(w=44, h=44, layout=none) > icon_font(x=12, y=12)\n\nCOMMON LUCIDE ICON NAMES:\nsearch, bell, user, heart, star, plus, x, check, chevron-right, chevron-left, chevron-down, chevron-up,\nsettings, home, mail, phone, calendar, clock, map-pin, link, external-link,\neye, eye-off, lock, unlock, key, shield,\narrow-right, arrow-left, arrow-up, arrow-down, arrow-up-right,\nmenu, more-horizontal, more-vertical, filter, sliders,\nimage, camera, video, file, folder, download, upload, share, copy, trash,\nedit, pen-tool, type, bold, italic, underline, align-left, align-center, align-right,\ngrid, list, layout, columns, maximize, minimize,\nsun, moon, cloud, zap, activity, trending-up, trending-down, bar-chart, pie-chart,\nusers, user-plus, user-check, message-circle, message-square, send,\nshopping-cart, shopping-bag, credit-card, dollar-sign, gift, tag, bookmark,\nplay, pause, skip-forward, skip-back, volume-2, mic,\ngithub, twitter, instagram, facebook, linkedin, youtube,\nglobe, wifi, bluetooth, monitor, smartphone, tablet, cpu, database, server, hard-drive,\ncode, terminal, git-branch, git-commit, git-pull-request,\nalert-circle, alert-triangle, info, help-circle, check-circle, x-circle'
|
|
1869
2402
|
},
|
|
1870
2403
|
{
|
|
1871
2404
|
"meta": {
|
|
@@ -1885,6 +2418,20 @@ ID GENERATION:
|
|
|
1885
2418
|
},
|
|
1886
2419
|
"content": 'CJK TYPOGRAPHY (Chinese/Japanese/Korean):\n- Headings: "Noto Sans SC" (Chinese) / "Noto Sans JP" / "Noto Sans KR". NEVER "Space Grotesk"/"Manrope" for CJK.\n- Body: "Inter" (system CJK fallback) or "Noto Sans SC".\n- CJK lineHeight: headings 1.3-1.4 (NOT 1.1), body 1.6-1.8. letterSpacing: 0, NEVER negative.\n- CJK buttons: each char is approximately fontSize wide. Container width >= (charCount x fontSize) + padding.\n- Detect CJK from user request language \u2014 use CJK fonts for ALL text nodes.'
|
|
1887
2420
|
},
|
|
2421
|
+
{
|
|
2422
|
+
"meta": {
|
|
2423
|
+
"name": "design-principles",
|
|
2424
|
+
"description": "Core design craft principles for high-quality output",
|
|
2425
|
+
"phase": [
|
|
2426
|
+
"generation"
|
|
2427
|
+
],
|
|
2428
|
+
"trigger": null,
|
|
2429
|
+
"priority": 25,
|
|
2430
|
+
"budget": 1e3,
|
|
2431
|
+
"category": "knowledge"
|
|
2432
|
+
},
|
|
2433
|
+
"content": "DESIGN CRAFT:\n- Type scale with real contrast: display 48-64, heading 28-36, body 16. Weight: 700 titles, 500 subtitles, 400 body.\n- Line height: tighter at large sizes (1.05-1.15 for 40px+), looser at small (1.5-1.6 for 16px).\n- Palette: 1 primary action color, 1 accent, neutral scale. Max 2 saturated colors. Page bg slightly tinted (#F8FAFC not #FFFFFF).\n- Contrast law: WCAG AA (4.5:1 body, 3:1 large text). Text: primary #0F172A for headings, muted #475569 for body.\n- 8px grid spacing: related items 8-16px, groups 24-32px, sections 48-80px. Section padding 80-120px vertical.\n- Hero: ONE headline, ONE subtitle, ONE CTA, optional visual. Every extra element dilutes focus.\n- Cards: consistent cornerRadius/padding/shadow. Content: image - title - description - action.\n- Nav: logo + 3-5 links + CTA. space_between distribution. Keep minimal.\n- Alternate section backgrounds (white/#F8FAFC) for natural separation."
|
|
2434
|
+
},
|
|
1888
2435
|
{
|
|
1889
2436
|
"meta": {
|
|
1890
2437
|
"name": "form-ui",
|
|
@@ -1932,8 +2479,8 @@ ID GENERATION:
|
|
|
1932
2479
|
},
|
|
1933
2480
|
{
|
|
1934
2481
|
"meta": {
|
|
1935
|
-
"name": "
|
|
1936
|
-
"description": "
|
|
2482
|
+
"name": "landing-page",
|
|
2483
|
+
"description": "Landing page and marketing site design patterns",
|
|
1937
2484
|
"phase": [
|
|
1938
2485
|
"generation"
|
|
1939
2486
|
],
|
|
@@ -1942,32 +2489,14 @@ ID GENERATION:
|
|
|
1942
2489
|
"landing",
|
|
1943
2490
|
"marketing",
|
|
1944
2491
|
"hero",
|
|
1945
|
-
"
|
|
1946
|
-
"\u5B98\u7F51",
|
|
1947
|
-
"\u9996\u9875",
|
|
1948
|
-
"\u4EA7\u54C1\u9875",
|
|
1949
|
-
"table",
|
|
1950
|
-
"grid",
|
|
1951
|
-
"\u8868\u683C",
|
|
1952
|
-
"\u8868\u5934",
|
|
1953
|
-
"dashboard",
|
|
1954
|
-
"\u6570\u636E",
|
|
1955
|
-
"admin",
|
|
1956
|
-
"testimonial",
|
|
1957
|
-
"pricing",
|
|
1958
|
-
"footer",
|
|
1959
|
-
"stats",
|
|
1960
|
-
"\u8BC4\u4EF7",
|
|
1961
|
-
"\u5B9A\u4EF7",
|
|
1962
|
-
"\u9875\u811A",
|
|
1963
|
-
"\u6570\u636E\u7EDF\u8BA1"
|
|
2492
|
+
"homepage"
|
|
1964
2493
|
]
|
|
1965
2494
|
},
|
|
1966
2495
|
"priority": 35,
|
|
1967
|
-
"budget":
|
|
1968
|
-
"category": "
|
|
2496
|
+
"budget": 1500,
|
|
2497
|
+
"category": "domain"
|
|
1969
2498
|
},
|
|
1970
|
-
"content": '
|
|
2499
|
+
"content": 'LANDING PAGE DESIGN PATTERNS:\n\nSTRUCTURE:\n- Navigation - Hero - Features - Social Proof - CTA - Footer\n- Each section: width="fill_container", height="fit_content", layout="vertical"\n- Root frame: width=1200, height=0 (auto-expands), gap=0\n\nNAVIGATION:\n- justifyContent="space_between", 3 groups: logo | nav-links | CTA button\n- padding=[0,80], alignItems="center", height 64-80px\n- Links evenly distributed in center group\n\nHERO SECTION:\n- padding=[80,80] or larger, generous whitespace\n- ONE headline (40-56px), ONE subtitle (16-18px), ONE CTA button\n- Optional visual: phone mockup or illustration on the right (two-column horizontal layout)\n- Every extra element dilutes focus \u2014 keep it minimal\n\nFEATURE SECTIONS:\n- Section title + 3-4 feature cards in horizontal layout\n- Cards: width="fill_container", height="fill_container" for even row alignment\n- Alternate section backgrounds (#FFFFFF / #F8FAFC) for natural separation\n- Section vertical padding: 80-120px\n\nSOCIAL PROOF:\n- Testimonials: card with quote + avatar + name/title\n- Stats: horizontal row with stat-cards (number + label)\n- Logos: horizontal row of company logos\n\nCTA SECTION:\n- Centered content, compelling headline, accent background or gradient\n- Single prominent button\n\nFOOTER:\n- Multi-column layout: brand + link groups + social\n- Muted colors, smaller text\n- padding=[48,80]\n\nGENERAL:\n- Centered content container ~1040-1160px across sections for alignment stability\n- Consistent cornerRadius (12-16px for cards)\n- clipContent: true on cards with images\n- Subtle shadows on cards'
|
|
1971
2500
|
},
|
|
1972
2501
|
{
|
|
1973
2502
|
"meta": {
|
|
@@ -1993,45 +2522,63 @@ ID GENERATION:
|
|
|
1993
2522
|
},
|
|
1994
2523
|
{
|
|
1995
2524
|
"meta": {
|
|
1996
|
-
"name": "
|
|
1997
|
-
"description": "
|
|
2525
|
+
"name": "dashboard",
|
|
2526
|
+
"description": "Dashboard and admin panel design patterns",
|
|
1998
2527
|
"phase": [
|
|
1999
2528
|
"generation"
|
|
2000
2529
|
],
|
|
2001
2530
|
"trigger": {
|
|
2002
2531
|
"keywords": [
|
|
2003
|
-
"
|
|
2004
|
-
"
|
|
2005
|
-
"
|
|
2006
|
-
"
|
|
2532
|
+
"dashboard",
|
|
2533
|
+
"admin",
|
|
2534
|
+
"analytics",
|
|
2535
|
+
"data"
|
|
2007
2536
|
]
|
|
2008
2537
|
},
|
|
2009
2538
|
"priority": 35,
|
|
2010
2539
|
"budget": 1500,
|
|
2011
2540
|
"category": "domain"
|
|
2012
2541
|
},
|
|
2013
|
-
"content": '
|
|
2542
|
+
"content": 'DASHBOARD DESIGN PATTERNS:\n\nSTRUCTURE:\n- Root frame: width=1200, height=0, layout="horizontal" (sidebar + main content)\n- Sidebar: width=240-280, height="fill_container", layout="vertical", dark or surface fill\n- Main content: width="fill_container", layout="vertical", gap=16-24\n\nSIDEBAR:\n- Logo/brand at top, padding=[24,16]\n- Navigation items: frame(layout="horizontal", gap=12, alignItems="center", padding=[10,16]) > icon_font + text\n- Active item: accent background or left border indicator\n- Section dividers between nav groups\n- User/settings at bottom\n\nTOP BAR:\n- height=56-64, padding=[0,24], layout="horizontal", justifyContent="space_between"\n- Left: page title or breadcrumbs\n- Right: search bar + notification icon + user avatar\n\nMETRICS ROW:\n- Horizontal layout with 3-4 stat-cards, each width="fill_container"\n- Each card: icon + metric value (28-36px, bold) + label (14px, muted) + optional trend indicator\n- padding=[20,24], gap=8, cornerRadius=12\n\nCHART SECTIONS:\n- Cards with header (title + filter/period selector) + chart area placeholder\n- Chart area: colored rectangle with rounded corners as placeholder\n- width="fill_container", cornerRadius=12\n\nDATA TABLES:\n- Table header: background fill, bold text, padding=[12,16]\n- Table rows: alternating subtle backgrounds, consistent column widths\n- Status badges: pill-shaped with semantic colors (green=active, amber=pending, red=error)\n- All cells use width="fill_container"\n\nSPACING:\n- Main content padding=[24,24], gap=16-24\n- Cards: padding=[20,24], gap=12-16\n- Consistent 12px cornerRadius across cards'
|
|
2014
2543
|
},
|
|
2015
2544
|
{
|
|
2016
2545
|
"meta": {
|
|
2017
|
-
"name": "
|
|
2018
|
-
"description": "
|
|
2546
|
+
"name": "role-definitions",
|
|
2547
|
+
"description": "Semantic role system with default property values",
|
|
2019
2548
|
"phase": [
|
|
2020
2549
|
"generation"
|
|
2021
2550
|
],
|
|
2022
2551
|
"trigger": {
|
|
2023
2552
|
"keywords": [
|
|
2553
|
+
"landing",
|
|
2554
|
+
"marketing",
|
|
2555
|
+
"hero",
|
|
2556
|
+
"website",
|
|
2557
|
+
"\u5B98\u7F51",
|
|
2558
|
+
"\u9996\u9875",
|
|
2559
|
+
"\u4EA7\u54C1\u9875",
|
|
2560
|
+
"table",
|
|
2561
|
+
"grid",
|
|
2562
|
+
"\u8868\u683C",
|
|
2563
|
+
"\u8868\u5934",
|
|
2024
2564
|
"dashboard",
|
|
2565
|
+
"\u6570\u636E",
|
|
2025
2566
|
"admin",
|
|
2026
|
-
"
|
|
2027
|
-
"
|
|
2567
|
+
"testimonial",
|
|
2568
|
+
"pricing",
|
|
2569
|
+
"footer",
|
|
2570
|
+
"stats",
|
|
2571
|
+
"\u8BC4\u4EF7",
|
|
2572
|
+
"\u5B9A\u4EF7",
|
|
2573
|
+
"\u9875\u811A",
|
|
2574
|
+
"\u6570\u636E\u7EDF\u8BA1"
|
|
2028
2575
|
]
|
|
2029
2576
|
},
|
|
2030
2577
|
"priority": 35,
|
|
2031
|
-
"budget":
|
|
2032
|
-
"category": "
|
|
2578
|
+
"budget": 2e3,
|
|
2579
|
+
"category": "knowledge"
|
|
2033
2580
|
},
|
|
2034
|
-
"content": '
|
|
2581
|
+
"content": 'SEMANTIC ROLES (add "role" to nodes \u2014 system fills unset props based on role):\n\nLayout roles:\n- section: layout=vertical, width=fill_container, height=fit_content, gap=24, padding=[60,80] (mobile: [40,16]), alignItems=center\n- row: layout=horizontal, width=fill_container, gap=16, alignItems=center\n- column: layout=vertical, width=fill_container, gap=16\n- centered-content: layout=vertical, width=1080 (mobile: fill_container), gap=24, alignItems=center\n- form-group: layout=vertical, width=fill_container, gap=16\n- divider: width=fill_container, height=1, layout=none (vertical divider: width=1, height=fill_container)\n- spacer: width=fill_container, height=40\n\nNavigation roles:\n- navbar: layout=horizontal, width=fill_container, height=72 (mobile: 56), padding=[0,80] (mobile: [0,16]), alignItems=center, justifyContent=space_between\n- nav-links: layout=horizontal, gap=24, alignItems=center\n- nav-link: textGrowth=auto, lineHeight=1.2\n\nInteractive roles:\n- button: padding=[12,24], height=44, layout=horizontal, gap=8, alignItems=center, justifyContent=center, cornerRadius=8. In navbar: padding=[8,16], height=36. In form-group: width=fill_container, height=48, cornerRadius=10\n- icon-button: width=44, height=44, layout=horizontal, justifyContent=center, alignItems=center, cornerRadius=8\n- badge: layout=horizontal, padding=[6,12], gap=4, alignItems=center, justifyContent=center, cornerRadius=999\n- tag: layout=horizontal, padding=[4,10], gap=4, alignItems=center, justifyContent=center, cornerRadius=6\n- pill: layout=horizontal, padding=[6,14], gap=6, alignItems=center, justifyContent=center, cornerRadius=999\n- input: height=48, layout=horizontal, padding=[12,16], alignItems=center, cornerRadius=8. In vertical layout: width=fill_container\n- form-input: width=fill_container, height=48, layout=horizontal, padding=[12,16], alignItems=center, cornerRadius=8\n- search-bar: layout=horizontal, height=44, padding=[10,16], gap=8, alignItems=center, cornerRadius=22\n\nDisplay roles:\n- card: layout=vertical, gap=12, cornerRadius=12, clipContent=true. In horizontal layout: width=fill_container, height=fill_container\n- stat-card: layout=vertical, gap=8, padding=[24,24], cornerRadius=12. In horizontal layout: width=fill_container, height=fill_container\n- pricing-card: layout=vertical, gap=16, padding=[32,24], cornerRadius=16, clipContent=true. In horizontal layout: width=fill_container, height=fill_container\n- image-card: layout=vertical, gap=0, cornerRadius=12, clipContent=true\n- feature-card: layout=vertical, gap=12, padding=[24,24], cornerRadius=12. In horizontal layout: width=fill_container, height=fill_container\n\nMedia roles:\n- phone-mockup: width=280, height=560, cornerRadius=32, layout=none\n- screenshot-frame: cornerRadius=12, clipContent=true\n- avatar: width/height=48, cornerRadius=24, clipContent=true (size adapts to explicit width)\n- icon: width=24, height=24\n\nTypography roles:\n- heading: lineHeight=1.2 (CJK: 1.35), letterSpacing=-0.5 (CJK: 0). In vertical layout: textGrowth=fixed-width, width=fill_container\n- subheading: lineHeight=1.3 (CJK: 1.4), textGrowth=fixed-width, width=fill_container\n- body-text: lineHeight=1.5 (CJK: 1.6), textGrowth=fixed-width, width=fill_container\n- caption: lineHeight=1.3 (CJK: 1.4), textGrowth=auto\n- label: lineHeight=1.2, textGrowth=auto, textAlignVertical=middle\n\nContent roles:\n- hero: layout=vertical, width=fill_container, height=fit_content, padding=[80,80] (mobile: [40,16]), gap=24, alignItems=center\n- feature-grid: layout=horizontal, width=fill_container, gap=24, alignItems=start\n- testimonial: layout=vertical, gap=16, padding=[24,24], cornerRadius=12\n- cta-section: layout=vertical, width=fill_container, height=fit_content, padding=[60,80] (mobile: [40,16]), gap=20, alignItems=center\n- footer: layout=vertical, width=fill_container, height=fit_content, padding=[48,80] (mobile: [32,16]), gap=24\n- stats-section: layout=horizontal, width=fill_container, height=fit_content, padding=[48,80] (mobile: [32,16]), gap=32, justifyContent=center, alignItems=center\n\nTable roles:\n- table: layout=vertical, width=fill_container, gap=0, clipContent=true\n- table-row: layout=horizontal, width=fill_container, alignItems=center, padding=[12,16]\n- table-header: layout=horizontal, width=fill_container, alignItems=center, padding=[12,16]\n- table-cell: width=fill_container\n\nYour explicit props ALWAYS override role defaults. Only unset properties get filled in.'
|
|
2035
2582
|
},
|
|
2036
2583
|
{
|
|
2037
2584
|
"meta": {
|
|
@@ -2197,6 +2744,7 @@ Add a 1-2 sentence description AFTER the JSON block, not before.
|
|
|
2197
2744
|
NEVER describe what you "would" create \u2014 ALWAYS output the actual JSON immediately.
|
|
2198
2745
|
NEVER output HTML, CSS, or React code \u2014 ONLY PenNode JSON.
|
|
2199
2746
|
NEVER say "I will create..." \u2014 START DIRECTLY WITH <step>.
|
|
2747
|
+
NEVER use "OpenPencil", "Pencil", or the tool name as brand/app name in designs. Use generic placeholders like "AppName", "Acme", or contextually relevant names.
|
|
2200
2748
|
|
|
2201
2749
|
You may include 1-2 brief <step> tags before the JSON (optional, keep them SHORT).
|
|
2202
2750
|
When a user asks non-design questions (explain, suggest colors, give advice), respond in text.`;
|
|
@@ -2466,7 +3014,18 @@ This approach produces higher-fidelity designs than generating everything at onc
|
|
|
2466
3014
|
copywriting: () => getSkillContent("copywriting"),
|
|
2467
3015
|
overflow: () => getSkillContent("overflow"),
|
|
2468
3016
|
cjk: () => getSkillContent("cjk"),
|
|
2469
|
-
variables: () => VARIABLE_RULES
|
|
3017
|
+
variables: () => VARIABLE_RULES,
|
|
3018
|
+
"codegen-planning": () => getSkillContent("codegen-planning"),
|
|
3019
|
+
"codegen-chunk": () => getSkillContent("codegen-chunk"),
|
|
3020
|
+
"codegen-assembly": () => getSkillContent("codegen-assembly"),
|
|
3021
|
+
"codegen-react": () => getSkillContent("codegen-react"),
|
|
3022
|
+
"codegen-vue": () => getSkillContent("codegen-vue"),
|
|
3023
|
+
"codegen-svelte": () => getSkillContent("codegen-svelte"),
|
|
3024
|
+
"codegen-html": () => getSkillContent("codegen-html"),
|
|
3025
|
+
"codegen-flutter": () => getSkillContent("codegen-flutter"),
|
|
3026
|
+
"codegen-swiftui": () => getSkillContent("codegen-swiftui"),
|
|
3027
|
+
"codegen-compose": () => getSkillContent("codegen-compose"),
|
|
3028
|
+
"codegen-react-native": () => getSkillContent("codegen-react-native")
|
|
2470
3029
|
};
|
|
2471
3030
|
_designMdContent = null;
|
|
2472
3031
|
}
|
|
@@ -2477,19 +3036,8 @@ async function handleOpenDocument(params) {
|
|
|
2477
3036
|
let filePath;
|
|
2478
3037
|
let doc;
|
|
2479
3038
|
if (!params.filePath || params.filePath === LIVE_CANVAS_PATH) {
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
filePath = LIVE_CANVAS_PATH;
|
|
2483
|
-
doc = await openDocument(LIVE_CANVAS_PATH);
|
|
2484
|
-
} else if (params.filePath === LIVE_CANVAS_PATH) {
|
|
2485
|
-
throw new Error(
|
|
2486
|
-
"No running OpenPencil instance found. Start the Electron app or dev server first."
|
|
2487
|
-
);
|
|
2488
|
-
} else {
|
|
2489
|
-
throw new Error(
|
|
2490
|
-
"filePath is required when no OpenPencil instance is running. Provide a path to an existing .op file or a new file to create."
|
|
2491
|
-
);
|
|
2492
|
-
}
|
|
3039
|
+
filePath = LIVE_CANVAS_PATH;
|
|
3040
|
+
doc = await openDocument(LIVE_CANVAS_PATH);
|
|
2493
3041
|
} else {
|
|
2494
3042
|
filePath = resolveDocPath(params.filePath);
|
|
2495
3043
|
const exists = await fileExists(filePath);
|
|
@@ -5136,6 +5684,15 @@ var init_esm = __esm({
|
|
|
5136
5684
|
}
|
|
5137
5685
|
});
|
|
5138
5686
|
|
|
5687
|
+
// src/utils/normalize-pen-file.ts
|
|
5688
|
+
var init_normalize_pen_file = __esm({
|
|
5689
|
+
"src/utils/normalize-pen-file.ts"() {
|
|
5690
|
+
"use strict";
|
|
5691
|
+
init_define_import_meta_env();
|
|
5692
|
+
init_src();
|
|
5693
|
+
}
|
|
5694
|
+
});
|
|
5695
|
+
|
|
5139
5696
|
// src/stores/history-store.ts
|
|
5140
5697
|
function areDocumentsEqual(a, b) {
|
|
5141
5698
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
@@ -5655,6 +6212,7 @@ var init_document_store = __esm({
|
|
|
5655
6212
|
init_define_import_meta_env();
|
|
5656
6213
|
init_esm();
|
|
5657
6214
|
init_nanoid();
|
|
6215
|
+
init_normalize_pen_file();
|
|
5658
6216
|
init_history_store();
|
|
5659
6217
|
init_canvas_store();
|
|
5660
6218
|
init_resolve_variables();
|
|
@@ -6145,7 +6703,8 @@ var init_document_store = __esm({
|
|
|
6145
6703
|
...createPageActions(set, get),
|
|
6146
6704
|
applyExternalDocument: (doc) => {
|
|
6147
6705
|
useHistoryStore.getState().pushState(get().document);
|
|
6148
|
-
const
|
|
6706
|
+
const normalized = normalizePenDocument(doc);
|
|
6707
|
+
const migrated = ensureDocumentNodeIds(migrateToPages(normalized));
|
|
6149
6708
|
const activePageId = useCanvasStore.getState().activePageId;
|
|
6150
6709
|
const pageExists = migrated.pages?.some((p) => p.id === activePageId);
|
|
6151
6710
|
const targetPageId = pageExists ? activePageId : migrated.pages?.[0]?.id;
|
|
@@ -18515,6 +19074,26 @@ var init_react_native_generator = __esm({
|
|
|
18515
19074
|
}
|
|
18516
19075
|
});
|
|
18517
19076
|
|
|
19077
|
+
// ../../packages/pen-codegen/src/utils.ts
|
|
19078
|
+
var init_utils = __esm({
|
|
19079
|
+
"../../packages/pen-codegen/src/utils.ts"() {
|
|
19080
|
+
"use strict";
|
|
19081
|
+
init_define_import_meta_env();
|
|
19082
|
+
init_src();
|
|
19083
|
+
init_css_variables_generator();
|
|
19084
|
+
init_css_variables_generator();
|
|
19085
|
+
init_src();
|
|
19086
|
+
}
|
|
19087
|
+
});
|
|
19088
|
+
|
|
19089
|
+
// ../../packages/pen-codegen/src/codegen-types.ts
|
|
19090
|
+
var init_codegen_types = __esm({
|
|
19091
|
+
"../../packages/pen-codegen/src/codegen-types.ts"() {
|
|
19092
|
+
"use strict";
|
|
19093
|
+
init_define_import_meta_env();
|
|
19094
|
+
}
|
|
19095
|
+
});
|
|
19096
|
+
|
|
18518
19097
|
// ../../packages/pen-codegen/src/index.ts
|
|
18519
19098
|
var init_src3 = __esm({
|
|
18520
19099
|
"../../packages/pen-codegen/src/index.ts"() {
|
|
@@ -18529,6 +19108,8 @@ var init_src3 = __esm({
|
|
|
18529
19108
|
init_swiftui_generator();
|
|
18530
19109
|
init_compose_generator();
|
|
18531
19110
|
init_react_native_generator();
|
|
19111
|
+
init_utils();
|
|
19112
|
+
init_codegen_types();
|
|
18532
19113
|
}
|
|
18533
19114
|
});
|
|
18534
19115
|
|
|
@@ -25379,7 +25960,7 @@ init_define_import_meta_env();
|
|
|
25379
25960
|
// ../cli/package.json
|
|
25380
25961
|
var package_default = {
|
|
25381
25962
|
name: "@zseven-w/openpencil",
|
|
25382
|
-
version: "0.
|
|
25963
|
+
version: "0.6.0",
|
|
25383
25964
|
description: "CLI for OpenPencil \u2014 control the design tool from your terminal",
|
|
25384
25965
|
author: {
|
|
25385
25966
|
name: "ZSeven-W",
|