@mindstudio-ai/remy 0.1.281 → 0.1.283
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/headless.js
CHANGED
|
@@ -281,8 +281,19 @@ async function* streamChat(params) {
|
|
|
281
281
|
}
|
|
282
282
|
var MAX_RETRIES = 5;
|
|
283
283
|
var INITIAL_BACKOFF_MS = 1e3;
|
|
284
|
-
|
|
285
|
-
|
|
284
|
+
var RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
285
|
+
"api_error",
|
|
286
|
+
// Anthropic's 500-equivalent ("The server had an error…")
|
|
287
|
+
"overloaded_error"
|
|
288
|
+
// Anthropic's 529-equivalent
|
|
289
|
+
]);
|
|
290
|
+
function isRetryableError(error, code) {
|
|
291
|
+
if (code && RETRYABLE_ERROR_CODES.has(code)) {
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
return /Network error/i.test(error) || /HTTP 5\d\d/i.test(error) || /Stream stalled/i.test(error) || /overloaded/i.test(error) || /terminated/i.test(error) || // The API's friendly mapping of a provider 500 — belt-and-suspenders for
|
|
295
|
+
// pods that don't send a machine-readable code.
|
|
296
|
+
/Internal API error/i.test(error) || // Anthropic fetches url-source image blocks server-side on every call;
|
|
286
297
|
// a transient failure anywhere in that chain (S3 signing edge, resize
|
|
287
298
|
// proxy, their fetcher) surfaces as this message. The images themselves
|
|
288
299
|
// are fine — the same URLs typically succeed on the next attempt.
|
|
@@ -297,7 +308,7 @@ async function* streamChatWithRetry(params, options) {
|
|
|
297
308
|
let retryableFailure = false;
|
|
298
309
|
for await (const event of streamChat(params)) {
|
|
299
310
|
if (event.type === "error") {
|
|
300
|
-
if (isRetryableError(event.error) && attempt < MAX_RETRIES - 1) {
|
|
311
|
+
if (isRetryableError(event.error, event.code) && attempt < MAX_RETRIES - 1) {
|
|
301
312
|
options?.onRetry?.(attempt, event.error);
|
|
302
313
|
retryableFailure = true;
|
|
303
314
|
break;
|
|
@@ -2698,8 +2709,8 @@ function formatSize(bytes) {
|
|
|
2698
2709
|
}
|
|
2699
2710
|
async function formatFile(dirPath, name, indent) {
|
|
2700
2711
|
try {
|
|
2701
|
-
const
|
|
2702
|
-
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(
|
|
2712
|
+
const stat3 = await fs16.stat(path8.join(dirPath, name));
|
|
2713
|
+
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat3.size)}`;
|
|
2703
2714
|
} catch {
|
|
2704
2715
|
return `${indent}${name}`;
|
|
2705
2716
|
}
|
|
@@ -5559,20 +5570,24 @@ __export(createWireframe_exports, {
|
|
|
5559
5570
|
definition: () => definition10,
|
|
5560
5571
|
execute: () => execute10
|
|
5561
5572
|
});
|
|
5562
|
-
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
5573
|
+
import { mkdir as mkdir2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
5563
5574
|
import { join as join2 } from "path";
|
|
5564
5575
|
var log9 = createLogger("createWireframe");
|
|
5565
5576
|
var WIREFRAMES_DIR = "src/.wireframes";
|
|
5566
5577
|
var UPLOAD_TIMEOUT_MS2 = 3e4;
|
|
5567
5578
|
var definition10 = {
|
|
5568
5579
|
name: "createWireframe",
|
|
5569
|
-
description: "Create a wireframe from self-contained HTML+CSS.
|
|
5580
|
+
description: "Create (or revise) a wireframe from self-contained HTML+CSS. The wireframe is saved to src/.wireframes/{slug}.html \u2014 reference it in your response and in specs as , with your notes in the surrounding prose. Calling again with the same slug overwrites the wireframe in place, so a revision keeps its path and existing references stay current.",
|
|
5570
5581
|
inputSchema: {
|
|
5571
5582
|
type: "object",
|
|
5572
5583
|
properties: {
|
|
5573
5584
|
name: {
|
|
5574
5585
|
type: "string",
|
|
5575
|
-
description: 'Short display name, e.g. "Feed Post Card". Becomes the caption
|
|
5586
|
+
description: 'Short display name, e.g. "Feed Post Card". Becomes the caption.'
|
|
5587
|
+
},
|
|
5588
|
+
slug: {
|
|
5589
|
+
type: "string",
|
|
5590
|
+
description: 'Filename stem, lowercase kebab-case (e.g. "feed-post-card"). The wireframe lives at src/.wireframes/{slug}.html. Re-use a slug to revise that wireframe in place.'
|
|
5576
5591
|
},
|
|
5577
5592
|
description: {
|
|
5578
5593
|
type: "string",
|
|
@@ -5583,20 +5598,24 @@ var definition10 = {
|
|
|
5583
5598
|
description: "The complete HTML document (<html>\u2026</html>), self-contained vanilla HTML/CSS/JS. No frontmatter \u2014 it is added for you."
|
|
5584
5599
|
}
|
|
5585
5600
|
},
|
|
5586
|
-
required: ["name", "description", "html"]
|
|
5601
|
+
required: ["name", "slug", "description", "html"]
|
|
5587
5602
|
}
|
|
5588
5603
|
};
|
|
5589
|
-
|
|
5590
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40).replace(/-+$/, "") || "wireframe";
|
|
5591
|
-
}
|
|
5604
|
+
var SLUG_RE = /^[a-z0-9][a-z0-9-]{0,79}$/;
|
|
5592
5605
|
function singleLine(value) {
|
|
5593
5606
|
return value.replace(/\s+/g, " ").trim();
|
|
5594
5607
|
}
|
|
5595
|
-
function validate(name, html) {
|
|
5608
|
+
function validate(name, slug, html) {
|
|
5596
5609
|
const warnings = [];
|
|
5597
5610
|
if (!name.trim()) {
|
|
5598
5611
|
return { error: "Error: name is required.", warnings };
|
|
5599
5612
|
}
|
|
5613
|
+
if (!SLUG_RE.test(slug)) {
|
|
5614
|
+
return {
|
|
5615
|
+
error: 'Error: slug must be lowercase kebab-case ([a-z0-9-], starting with a letter or digit, \u2264 80 chars), e.g. "feed-post-card".',
|
|
5616
|
+
warnings
|
|
5617
|
+
};
|
|
5618
|
+
}
|
|
5600
5619
|
if (!html.trim()) {
|
|
5601
5620
|
return { error: "Error: html is required.", warnings };
|
|
5602
5621
|
}
|
|
@@ -5655,13 +5674,13 @@ async function execute10(input, onLog, context) {
|
|
|
5655
5674
|
return "Error: createWireframe requires execution context";
|
|
5656
5675
|
}
|
|
5657
5676
|
const name = String(input.name ?? "");
|
|
5677
|
+
const slug = String(input.slug ?? "");
|
|
5658
5678
|
const description = String(input.description ?? "");
|
|
5659
5679
|
const html = String(input.html ?? "");
|
|
5660
|
-
const { error, warnings } = validate(name, html);
|
|
5680
|
+
const { error, warnings } = validate(name, slug, html);
|
|
5661
5681
|
if (error) {
|
|
5662
5682
|
return error;
|
|
5663
5683
|
}
|
|
5664
|
-
const slug = `${slugify(name)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
5665
5684
|
const relPath = `${WIREFRAMES_DIR}/${slug}.html`;
|
|
5666
5685
|
const content = [
|
|
5667
5686
|
"---",
|
|
@@ -5672,6 +5691,10 @@ async function execute10(input, onLog, context) {
|
|
|
5672
5691
|
""
|
|
5673
5692
|
].join("\n");
|
|
5674
5693
|
await mkdir2(join2(PROJECT_ROOT, WIREFRAMES_DIR), { recursive: true });
|
|
5694
|
+
const existed = await stat2(join2(PROJECT_ROOT, relPath)).then(
|
|
5695
|
+
() => true,
|
|
5696
|
+
() => false
|
|
5697
|
+
);
|
|
5675
5698
|
await writeFile2(join2(PROJECT_ROOT, relPath), content, "utf8");
|
|
5676
5699
|
onLog?.(`Wrote ${relPath}, mirroring for preview...`);
|
|
5677
5700
|
const mirror = await uploadMirror(context, slug, content);
|
|
@@ -5679,7 +5702,7 @@ async function execute10(input, onLog, context) {
|
|
|
5679
5702
|
log9.warn("Wireframe mirror upload failed", { slug, note: mirror.note });
|
|
5680
5703
|
}
|
|
5681
5704
|
const lines = [
|
|
5682
|
-
|
|
5705
|
+
`${existed ? "Revised" : "Created"} wireframe "${singleLine(name)}" at ${relPath}.${existed ? " Existing references to this path now show the new version." : ""}`,
|
|
5683
5706
|
`Reference it in your response and in specs with exactly: `
|
|
5684
5707
|
];
|
|
5685
5708
|
if (!mirror.ok) {
|
package/dist/index.js
CHANGED
|
@@ -262,8 +262,13 @@ async function* streamChat(params) {
|
|
|
262
262
|
};
|
|
263
263
|
}
|
|
264
264
|
}
|
|
265
|
-
function isRetryableError(error) {
|
|
266
|
-
|
|
265
|
+
function isRetryableError(error, code) {
|
|
266
|
+
if (code && RETRYABLE_ERROR_CODES.has(code)) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
return /Network error/i.test(error) || /HTTP 5\d\d/i.test(error) || /Stream stalled/i.test(error) || /overloaded/i.test(error) || /terminated/i.test(error) || // The API's friendly mapping of a provider 500 — belt-and-suspenders for
|
|
270
|
+
// pods that don't send a machine-readable code.
|
|
271
|
+
/Internal API error/i.test(error) || // Anthropic fetches url-source image blocks server-side on every call;
|
|
267
272
|
// a transient failure anywhere in that chain (S3 signing edge, resize
|
|
268
273
|
// proxy, their fetcher) surfaces as this message. The images themselves
|
|
269
274
|
// are fine — the same URLs typically succeed on the next attempt.
|
|
@@ -278,7 +283,7 @@ async function* streamChatWithRetry(params, options) {
|
|
|
278
283
|
let retryableFailure = false;
|
|
279
284
|
for await (const event of streamChat(params)) {
|
|
280
285
|
if (event.type === "error") {
|
|
281
|
-
if (isRetryableError(event.error) && attempt < MAX_RETRIES - 1) {
|
|
286
|
+
if (isRetryableError(event.error, event.code) && attempt < MAX_RETRIES - 1) {
|
|
282
287
|
options?.onRetry?.(attempt, event.error);
|
|
283
288
|
retryableFailure = true;
|
|
284
289
|
break;
|
|
@@ -360,7 +365,7 @@ async function fetchRemyContext(config) {
|
|
|
360
365
|
return null;
|
|
361
366
|
}
|
|
362
367
|
}
|
|
363
|
-
var log, MAX_RETRIES, INITIAL_BACKOFF_MS, FALLBACK_ACK;
|
|
368
|
+
var log, MAX_RETRIES, INITIAL_BACKOFF_MS, RETRYABLE_ERROR_CODES, FALLBACK_ACK;
|
|
364
369
|
var init_api = __esm({
|
|
365
370
|
"src/api.ts"() {
|
|
366
371
|
"use strict";
|
|
@@ -368,6 +373,12 @@ var init_api = __esm({
|
|
|
368
373
|
log = createLogger("api");
|
|
369
374
|
MAX_RETRIES = 5;
|
|
370
375
|
INITIAL_BACKOFF_MS = 1e3;
|
|
376
|
+
RETRYABLE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
377
|
+
"api_error",
|
|
378
|
+
// Anthropic's 500-equivalent ("The server had an error…")
|
|
379
|
+
"overloaded_error"
|
|
380
|
+
// Anthropic's 529-equivalent
|
|
381
|
+
]);
|
|
371
382
|
FALLBACK_ACK = "[Message sent to agent. Agent is working in the background and will report back with its results when finished.]";
|
|
372
383
|
}
|
|
373
384
|
});
|
|
@@ -3805,8 +3816,8 @@ function formatSize(bytes) {
|
|
|
3805
3816
|
}
|
|
3806
3817
|
async function formatFile(dirPath, name, indent) {
|
|
3807
3818
|
try {
|
|
3808
|
-
const
|
|
3809
|
-
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(
|
|
3819
|
+
const stat3 = await fs15.stat(path8.join(dirPath, name));
|
|
3820
|
+
return `${indent}${name}${" ".repeat(Math.max(1, 30 - indent.length - name.length))}${formatSize(stat3.size)}`;
|
|
3810
3821
|
} catch {
|
|
3811
3822
|
return `${indent}${name}`;
|
|
3812
3823
|
}
|
|
@@ -6741,19 +6752,22 @@ __export(createWireframe_exports, {
|
|
|
6741
6752
|
definition: () => definition10,
|
|
6742
6753
|
execute: () => execute10
|
|
6743
6754
|
});
|
|
6744
|
-
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
6755
|
+
import { mkdir as mkdir2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
6745
6756
|
import { join as join2 } from "path";
|
|
6746
|
-
function slugify(name) {
|
|
6747
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40).replace(/-+$/, "") || "wireframe";
|
|
6748
|
-
}
|
|
6749
6757
|
function singleLine(value) {
|
|
6750
6758
|
return value.replace(/\s+/g, " ").trim();
|
|
6751
6759
|
}
|
|
6752
|
-
function validate(name, html) {
|
|
6760
|
+
function validate(name, slug, html) {
|
|
6753
6761
|
const warnings = [];
|
|
6754
6762
|
if (!name.trim()) {
|
|
6755
6763
|
return { error: "Error: name is required.", warnings };
|
|
6756
6764
|
}
|
|
6765
|
+
if (!SLUG_RE.test(slug)) {
|
|
6766
|
+
return {
|
|
6767
|
+
error: 'Error: slug must be lowercase kebab-case ([a-z0-9-], starting with a letter or digit, \u2264 80 chars), e.g. "feed-post-card".',
|
|
6768
|
+
warnings
|
|
6769
|
+
};
|
|
6770
|
+
}
|
|
6757
6771
|
if (!html.trim()) {
|
|
6758
6772
|
return { error: "Error: html is required.", warnings };
|
|
6759
6773
|
}
|
|
@@ -6812,13 +6826,13 @@ async function execute10(input, onLog, context) {
|
|
|
6812
6826
|
return "Error: createWireframe requires execution context";
|
|
6813
6827
|
}
|
|
6814
6828
|
const name = String(input.name ?? "");
|
|
6829
|
+
const slug = String(input.slug ?? "");
|
|
6815
6830
|
const description = String(input.description ?? "");
|
|
6816
6831
|
const html = String(input.html ?? "");
|
|
6817
|
-
const { error, warnings } = validate(name, html);
|
|
6832
|
+
const { error, warnings } = validate(name, slug, html);
|
|
6818
6833
|
if (error) {
|
|
6819
6834
|
return error;
|
|
6820
6835
|
}
|
|
6821
|
-
const slug = `${slugify(name)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
6822
6836
|
const relPath = `${WIREFRAMES_DIR}/${slug}.html`;
|
|
6823
6837
|
const content = [
|
|
6824
6838
|
"---",
|
|
@@ -6829,6 +6843,10 @@ async function execute10(input, onLog, context) {
|
|
|
6829
6843
|
""
|
|
6830
6844
|
].join("\n");
|
|
6831
6845
|
await mkdir2(join2(PROJECT_ROOT, WIREFRAMES_DIR), { recursive: true });
|
|
6846
|
+
const existed = await stat2(join2(PROJECT_ROOT, relPath)).then(
|
|
6847
|
+
() => true,
|
|
6848
|
+
() => false
|
|
6849
|
+
);
|
|
6832
6850
|
await writeFile2(join2(PROJECT_ROOT, relPath), content, "utf8");
|
|
6833
6851
|
onLog?.(`Wrote ${relPath}, mirroring for preview...`);
|
|
6834
6852
|
const mirror = await uploadMirror(context, slug, content);
|
|
@@ -6836,7 +6854,7 @@ async function execute10(input, onLog, context) {
|
|
|
6836
6854
|
log10.warn("Wireframe mirror upload failed", { slug, note: mirror.note });
|
|
6837
6855
|
}
|
|
6838
6856
|
const lines = [
|
|
6839
|
-
|
|
6857
|
+
`${existed ? "Revised" : "Created"} wireframe "${singleLine(name)}" at ${relPath}.${existed ? " Existing references to this path now show the new version." : ""}`,
|
|
6840
6858
|
`Reference it in your response and in specs with exactly: `
|
|
6841
6859
|
];
|
|
6842
6860
|
if (!mirror.ok) {
|
|
@@ -6849,7 +6867,7 @@ async function execute10(input, onLog, context) {
|
|
|
6849
6867
|
}
|
|
6850
6868
|
return lines.join("\n");
|
|
6851
6869
|
}
|
|
6852
|
-
var log10, WIREFRAMES_DIR, UPLOAD_TIMEOUT_MS2, definition10;
|
|
6870
|
+
var log10, WIREFRAMES_DIR, UPLOAD_TIMEOUT_MS2, definition10, SLUG_RE;
|
|
6853
6871
|
var init_createWireframe = __esm({
|
|
6854
6872
|
"src/subagents/designExpert/tools/createWireframe.ts"() {
|
|
6855
6873
|
"use strict";
|
|
@@ -6860,13 +6878,17 @@ var init_createWireframe = __esm({
|
|
|
6860
6878
|
UPLOAD_TIMEOUT_MS2 = 3e4;
|
|
6861
6879
|
definition10 = {
|
|
6862
6880
|
name: "createWireframe",
|
|
6863
|
-
description: "Create a wireframe from self-contained HTML+CSS.
|
|
6881
|
+
description: "Create (or revise) a wireframe from self-contained HTML+CSS. The wireframe is saved to src/.wireframes/{slug}.html \u2014 reference it in your response and in specs as , with your notes in the surrounding prose. Calling again with the same slug overwrites the wireframe in place, so a revision keeps its path and existing references stay current.",
|
|
6864
6882
|
inputSchema: {
|
|
6865
6883
|
type: "object",
|
|
6866
6884
|
properties: {
|
|
6867
6885
|
name: {
|
|
6868
6886
|
type: "string",
|
|
6869
|
-
description: 'Short display name, e.g. "Feed Post Card". Becomes the caption
|
|
6887
|
+
description: 'Short display name, e.g. "Feed Post Card". Becomes the caption.'
|
|
6888
|
+
},
|
|
6889
|
+
slug: {
|
|
6890
|
+
type: "string",
|
|
6891
|
+
description: 'Filename stem, lowercase kebab-case (e.g. "feed-post-card"). The wireframe lives at src/.wireframes/{slug}.html. Re-use a slug to revise that wireframe in place.'
|
|
6870
6892
|
},
|
|
6871
6893
|
description: {
|
|
6872
6894
|
type: "string",
|
|
@@ -6877,9 +6899,10 @@ var init_createWireframe = __esm({
|
|
|
6877
6899
|
description: "The complete HTML document (<html>\u2026</html>), self-contained vanilla HTML/CSS/JS. No frontmatter \u2014 it is added for you."
|
|
6878
6900
|
}
|
|
6879
6901
|
},
|
|
6880
|
-
required: ["name", "description", "html"]
|
|
6902
|
+
required: ["name", "slug", "description", "html"]
|
|
6881
6903
|
}
|
|
6882
6904
|
};
|
|
6905
|
+
SLUG_RE = /^[a-z0-9][a-z0-9-]{0,79}$/;
|
|
6883
6906
|
}
|
|
6884
6907
|
});
|
|
6885
6908
|
|
|
@@ -12,7 +12,7 @@ Your designer. Consult for any visual decision — choosing a color, picking fon
|
|
|
12
12
|
|
|
13
13
|
The design expert cannot see your conversation with the user, so include relevant context and requirements in your task. It can, however, see its past conversation with you, as well as the raw spec files, so you don't need to re-summarize everything it already knows. Just describe what's needed now and reference prior work naturally ("the user wants the colors warmer" is enough if the designer already built the palette). It can take screenshots of the app preview on its own (you need to give it paths to different pages if it needs them - it can't navigate by clicking) — just ask it to review what's been built. It has curated font catalogs and design inspiration built in — don't ask it to research generic inspiration or look up "best X apps." Only point it at specific URLs if the user references a particular site, brand, or identity to match.
|
|
14
14
|
|
|
15
|
-
The designer will return concrete resources: hex values, font names with CSS URLs, image URLs, layout descriptions, as well as specific techniques, CSS properties, animation timings, code snippets, and other values. Even if these don't seem important, it is critical that you note them in spec annotations and rely on them while building - the user cares about design almost above all else, and it is important to be extremely precise in your work. The designer can also return code-fenced typography and color schemes (self-contained HTML and CSS) - write these directly into specs for future reference. Wireframes arrive as file references like ` - write these directly into specs for future reference. Wireframes arrive as file references like ``: copy the reference line into specs verbatim (it renders as a visual preview in chat and in the spec), and read the file at that path while building to get the exact markup and CSS the designer specified. When the designer revises a wireframe it overwrites the same path, so existing references stay current.
|
|
16
16
|
|
|
17
17
|
When delegating, describe the design problem — where the asset will be used, what it needs to communicate, what the brand feels like. Do not specify technical details like image formats, pixel dimensions, generation techniques, or workarounds. The design expert makes those decisions.
|
|
18
18
|
|
|
@@ -16,7 +16,7 @@ Think about the ways you can truly elevate the design. Use image generation to c
|
|
|
16
16
|
- After you've taken a screenshot, use analyze image to ask different questions about it - don't re-screenshot the page unnecessarily.
|
|
17
17
|
- Match the image engine to the job: `renderImage` (a browser rendering HTML you author) for token-exact graphics — share cards, wordmarks, flat icon tiles; `generateImages` (an image model) for organic, photographic, and illustrated work. Don't ask the image model to hit exact hex codes or typography, and don't hand-write SVG path data — compose HTML/CSS and render it.
|
|
18
18
|
- When you write user-facing copy (headlines, captions, labels, body text), hand it to `polishCopy` before finalizing. It tightens prose so it reads like a person wrote it rather than a machine, without changing what it says. Cheap and fast — use it on any copy that will ship.
|
|
19
|
-
- Deliver wireframes with `createWireframe`. Include the
|
|
19
|
+
- Deliver wireframes with `createWireframe`. Include the `` line in your response where the wireframe belongs — it renders as a live preview, and the developer reads the file for the exact markup. Same slug = revise in place; new slug = new wireframe.
|
|
20
20
|
|
|
21
21
|
## Voice
|
|
22
22
|
- No emoji, no filler.
|
|
@@ -25,7 +25,7 @@ Some surfaces are deep enough to carry their own craft reference in <available_s
|
|
|
25
25
|
|
|
26
26
|
### Wireframes
|
|
27
27
|
|
|
28
|
-
When you need to show a layout, component, interaction, or animation, create a wireframe with the `createWireframe` tool: pass a `name`, a one-line `description`, and self-contained HTML+CSS. The
|
|
28
|
+
When you need to show a layout, component, interaction, or animation, create a wireframe with the `createWireframe` tool: pass a `name`, a kebab-case `slug`, a one-line `description`, and self-contained HTML+CSS. The wireframe is saved to `src/.wireframes/{slug}.html`, and you reference it as `` — include that line in your response wherever the wireframe belongs, with your notes in the surrounding prose. The reference line renders as a live visual preview, and the developer reads the file itself for the exact markup and CSS. A reference only renders once its wireframe exists, so make the `createWireframe` call for every reference you write — in the same response is fine.
|
|
29
29
|
|
|
30
30
|
Never use ASCII art, box-drawing characters, or code-block diagrams to describe layouts. Always use a wireframe instead, even if it's just grey rectangles with labels. A 20-line wireframe with placeholder boxes communicates proportions, spacing, and hierarchy better than any text diagram. For abstract layouts, use skeleton-style placeholders (grey boxes, rounded rects) rather than mocking up real content.
|
|
31
31
|
|
|
@@ -35,9 +35,9 @@ Wireframes render in a small transparent iframe. Set a background color and shad
|
|
|
35
35
|
|
|
36
36
|
Wireframes are vanilla HTML/CSS/JS (no React). For animations beyond CSS, use GSAP via CDN: `<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>`
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
To revise a wireframe, call `createWireframe` again with the same slug — the file is overwritten in place and every existing reference to that path (in chat and in specs) shows the new version. Read the current file first if you're iterating on it. Use a new slug when it's genuinely a different wireframe, not a revision.
|
|
39
39
|
|
|
40
|
-
Quick skeleton wireframe (grey boxes, just showing layout and hierarchy) — `createWireframe` with name "Content Card Layout", description "Card with image area, title, metadata row, rating, and actions. Skeleton placeholders showing proportions and hierarchy.", and this html:
|
|
40
|
+
Quick skeleton wireframe (grey boxes, just showing layout and hierarchy) — `createWireframe` with name "Content Card Layout", slug "content-card-layout", description "Card with image area, title, metadata row, rating, and actions. Skeleton placeholders showing proportions and hierarchy.", and this html:
|
|
41
41
|
|
|
42
42
|
```html
|
|
43
43
|
<html lang="en"><head>
|
|
@@ -72,7 +72,7 @@ Quick skeleton wireframe (grey boxes, just showing layout and hierarchy) — `cr
|
|
|
72
72
|
</html>
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
Detailed component wireframe (showing specific design decisions) — `createWireframe` with name "Feed Post Card", description "Photo post card with header, image frame, action row (like/comment/share/bookmark), like count, and caption. Shows spacing, typography hierarchy, and icon placement.", and this html:
|
|
75
|
+
Detailed component wireframe (showing specific design decisions) — `createWireframe` with name "Feed Post Card", slug "feed-post-card", description "Photo post card with header, image frame, action row (like/comment/share/bookmark), like count, and caption. Shows spacing, typography hierarchy, and icon placement.", and this html:
|
|
76
76
|
|
|
77
77
|
```html
|
|
78
78
|
<html lang="en"><head>
|