@diffui.mcp/install 0.1.0 → 0.2.1
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/README.md +25 -8
- package/launcher.mjs +61 -2
- package/package.json +4 -3
- package/skill/SKILL.md +226 -0
package/README.md
CHANGED
|
@@ -9,15 +9,26 @@ Thin Node launcher for the diffui MCP server. Cursor (and other MCP hosts) run:
|
|
|
9
9
|
"args": ["-y", "@diffui.mcp/install@latest"],
|
|
10
10
|
"env": {
|
|
11
11
|
"DIFFUI_API_BASE": "https://diffui.ai",
|
|
12
|
-
"DIFFUI_API_KEY": "dui_..."
|
|
12
|
+
"DIFFUI_API_KEY": "dui_...",
|
|
13
|
+
"npm_config_prefix": "",
|
|
14
|
+
"NPM_CONFIG_PREFIX": "",
|
|
15
|
+
"PATH": "/opt/homebrew/bin:/opt/homebrew/opt/node@22/bin:/opt/homebrew/opt/node@20/bin:/usr/local/bin:/usr/bin:/bin"
|
|
13
16
|
}
|
|
14
17
|
}
|
|
15
18
|
```
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
`
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
Cursor injects `npm_config_prefix` inside its app bundle (`…/Cursor.app/…/resources/lib`).
|
|
21
|
+
That path does not exist, so `npx` fails with `ENOENT` unless those vars are cleared.
|
|
22
|
+
The diffui install deeplink sets empty prefix values and prepends common system paths to `PATH`.
|
|
23
|
+
|
|
24
|
+
On first start, the launcher:
|
|
25
|
+
|
|
26
|
+
1. Copies the bundled design-first skill to `~/.cursor/skills/diffui/SKILL.md` (updates when
|
|
27
|
+
the launcher npm version changes; set `DIFFUI_SKIP_SKILL_INSTALL=1` to skip).
|
|
28
|
+
2. Downloads the platform `diffui-mcp` binary from `{DIFFUI_API_BASE}/mcp/` into
|
|
29
|
+
`~/.diffui/bin/diffui-mcp`, then execs it with inherited stdio.
|
|
30
|
+
|
|
31
|
+
Credentials can also come from the MCP `authenticate` tool (`~/.diffui/credentials`).
|
|
21
32
|
|
|
22
33
|
## Publish to npm
|
|
23
34
|
|
|
@@ -28,8 +39,14 @@ Prerequisites:
|
|
|
28
39
|
2. npm account in the [@diffui.mcp](https://www.npmjs.com/org/diffui.mcp) org.
|
|
29
40
|
|
|
30
41
|
```bash
|
|
42
|
+
./scripts/publish-mcp-install.sh
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This syncs `skills/diffui/SKILL.md` into the package, then publishes. Or manually:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
./scripts/sync-mcp-skill.sh
|
|
31
49
|
cd packages/mcp-install
|
|
32
|
-
npm login
|
|
33
50
|
npm publish --access public
|
|
34
51
|
```
|
|
35
52
|
|
|
@@ -59,5 +76,5 @@ npm link
|
|
|
59
76
|
|
|
60
77
|
## Cursor deeplink
|
|
61
78
|
|
|
62
|
-
The diffui app builds install links with `command: npx
|
|
63
|
-
`args: ["-y", "@diffui.mcp/install@latest"]
|
|
79
|
+
The diffui app builds install links with `command: npx`,
|
|
80
|
+
`args: ["-y", "@diffui.mcp/install@latest"]`, npm prefix overrides, and env for API base and key.
|
package/launcher.mjs
CHANGED
|
@@ -1,20 +1,41 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* diffui MCP launcher for Cursor / Claude / npx.
|
|
4
|
-
* Ensures ~/.diffui/bin/diffui-mcp exists,
|
|
4
|
+
* Ensures ~/.diffui/bin/diffui-mcp exists, installs the Cursor skill to
|
|
5
|
+
* ~/.cursor/skills/diffui on first start (or when the launcher version changes),
|
|
6
|
+
* then execs the MCP binary with inherited stdio.
|
|
5
7
|
*
|
|
6
8
|
* Env (from Cursor mcp.json or shell):
|
|
7
9
|
* DIFFUI_API_BASE — API origin (default https://diffui.ai)
|
|
8
10
|
* DIFFUI_API_KEY — optional; authenticate tool can store ~/.diffui/credentials
|
|
11
|
+
* DIFFUI_SKIP_SKILL_INSTALL — set to 1 to skip copying SKILL.md
|
|
9
12
|
*/
|
|
10
13
|
import { spawn, spawnSync } from "node:child_process";
|
|
11
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
chmodSync,
|
|
16
|
+
copyFileSync,
|
|
17
|
+
createWriteStream,
|
|
18
|
+
existsSync,
|
|
19
|
+
mkdirSync,
|
|
20
|
+
readFileSync,
|
|
21
|
+
writeFileSync,
|
|
22
|
+
} from "node:fs";
|
|
12
23
|
import { arch, homedir, platform } from "node:os";
|
|
13
24
|
import { dirname, join } from "node:path";
|
|
25
|
+
import { fileURLToPath } from "node:url";
|
|
14
26
|
import { pipeline } from "node:stream/promises";
|
|
15
27
|
import { get as httpsGet } from "node:https";
|
|
16
28
|
import { get as httpGet } from "node:http";
|
|
17
29
|
|
|
30
|
+
// Cursor MCP may inherit npm prefix inside the app bundle; drop it before any child spawns.
|
|
31
|
+
for (const key of ["npm_config_prefix", "NPM_CONFIG_PREFIX"]) {
|
|
32
|
+
const value = process.env[key];
|
|
33
|
+
if (value && /Cursor\.app/i.test(value)) {
|
|
34
|
+
delete process.env[key];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const launcherDir = dirname(fileURLToPath(import.meta.url));
|
|
18
39
|
const base = String(process.env.DIFFUI_API_BASE || "https://diffui.ai").replace(/\/+$/, "");
|
|
19
40
|
const dest = join(homedir(), ".diffui", "bin", "diffui-mcp");
|
|
20
41
|
|
|
@@ -31,6 +52,43 @@ function binaryName() {
|
|
|
31
52
|
throw new Error(`diffui-mcp: unsupported platform ${platform()}-${arch()}`);
|
|
32
53
|
}
|
|
33
54
|
|
|
55
|
+
function launcherVersion() {
|
|
56
|
+
try {
|
|
57
|
+
const pkg = JSON.parse(readFileSync(join(launcherDir, "package.json"), "utf8"));
|
|
58
|
+
return String(pkg.version || "0.0.0");
|
|
59
|
+
} catch {
|
|
60
|
+
return "0.0.0";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function ensureCursorSkill() {
|
|
65
|
+
if (process.env.DIFFUI_SKIP_SKILL_INSTALL === "1") return;
|
|
66
|
+
|
|
67
|
+
const skillSrc = join(launcherDir, "skill", "SKILL.md");
|
|
68
|
+
if (!existsSync(skillSrc)) return;
|
|
69
|
+
|
|
70
|
+
const skillDir = join(homedir(), ".cursor", "skills", "diffui");
|
|
71
|
+
const skillDest = join(skillDir, "SKILL.md");
|
|
72
|
+
const versionStamp = join(skillDir, ".diffui-install-version");
|
|
73
|
+
const version = launcherVersion();
|
|
74
|
+
|
|
75
|
+
let installed = "";
|
|
76
|
+
if (existsSync(versionStamp)) {
|
|
77
|
+
try {
|
|
78
|
+
installed = readFileSync(versionStamp, "utf8").trim();
|
|
79
|
+
} catch {
|
|
80
|
+
installed = "";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (installed === version && existsSync(skillDest)) return;
|
|
85
|
+
|
|
86
|
+
mkdirSync(skillDir, { recursive: true });
|
|
87
|
+
copyFileSync(skillSrc, skillDest);
|
|
88
|
+
writeFileSync(versionStamp, `${version}\n`, "utf8");
|
|
89
|
+
process.stderr.write(`diffui-mcp: installed Cursor skill at ${skillDest}\n`);
|
|
90
|
+
}
|
|
91
|
+
|
|
34
92
|
async function download(url, path) {
|
|
35
93
|
const getter = url.startsWith("https:") ? httpsGet : httpGet;
|
|
36
94
|
await new Promise((resolve, reject) => {
|
|
@@ -72,6 +130,7 @@ async function ensureBinary() {
|
|
|
72
130
|
return dest;
|
|
73
131
|
}
|
|
74
132
|
|
|
133
|
+
ensureCursorSkill();
|
|
75
134
|
const bin = await ensureBinary();
|
|
76
135
|
const child = spawn(bin, [], {
|
|
77
136
|
stdio: "inherit",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diffui.mcp/install",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Launcher for the diffui MCP server (stdio).
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Launcher for the diffui MCP server (stdio). Installs the Cursor skill on first start.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"launcher.mjs",
|
|
12
|
-
"README.md"
|
|
12
|
+
"README.md",
|
|
13
|
+
"skill/SKILL.md"
|
|
13
14
|
],
|
|
14
15
|
"engines": {
|
|
15
16
|
"node": ">=18"
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: diffui-designer
|
|
3
|
+
description: >-
|
|
4
|
+
Runs the diffui MCP workflow before any HTML/CSS/JS is written: create_project,
|
|
5
|
+
generate_options (4 variants), generate_page, create_build_link, then implement
|
|
6
|
+
code from the build markdown. Auth (whoami/authenticate) only on tool errors —
|
|
7
|
+
installs usually include DIFFUI_API_KEY already. REQUIRED when the user asks to
|
|
8
|
+
create, build, or design a webpage, website, landing page, app screen, dashboard,
|
|
9
|
+
or UI — including "create a webpage for …", "design first then build/create in …",
|
|
10
|
+
themed sites (jungle, arctic, etc.), and output paths like demoBuild/. Do NOT
|
|
11
|
+
skip diffui and design manually in prose or jump straight to coding when the
|
|
12
|
+
diffui MCP server is available. Also use when the user mentions diffui.
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Diffui designer
|
|
16
|
+
|
|
17
|
+
You orchestrate diffui (an AI design-generation canvas) through its **MCP tools**.
|
|
18
|
+
The user sees all option images inline in the tool result. Follow this choreography.
|
|
19
|
+
|
|
20
|
+
## When this skill applies (required)
|
|
21
|
+
|
|
22
|
+
If the **diffui MCP server** is enabled and the user wants a new webpage or UI
|
|
23
|
+
look — even when they also name an output folder (`demoBuild/bongo`,
|
|
24
|
+
`demoBuild/svalbardLan`, etc.) — you **must** use diffui for the design phase.
|
|
25
|
+
Treat these as the same class of request:
|
|
26
|
+
|
|
27
|
+
- "Create a webpage for …"
|
|
28
|
+
- "Design a landing page …"
|
|
29
|
+
- "Design first, then create/build it in …"
|
|
30
|
+
- "Build a website for …" (when no existing diffui designs are referenced)
|
|
31
|
+
|
|
32
|
+
**Two phases — never merge them:**
|
|
33
|
+
|
|
34
|
+
1. **Design (diffui MCP)** — `create_project` →
|
|
35
|
+
`generate_options` → pick/iterate → (optional `generate_page` for multi-page) →
|
|
36
|
+
`create_build_link`.
|
|
37
|
+
2. **Implementation (code)** — write HTML/CSS/JS only **after** phase 1, following
|
|
38
|
+
the build markdown from `create_build_link` into the user's target path.
|
|
39
|
+
|
|
40
|
+
**Minimal path (single landing page):** `generate_options` → user picks →
|
|
41
|
+
`create_build_link` → code. Do not require brand creation or extra `generate_page`
|
|
42
|
+
calls for a one-page request.
|
|
43
|
+
|
|
44
|
+
**Forbidden before `generate_options`:**
|
|
45
|
+
|
|
46
|
+
- Creating `index.html`, stylesheets, or app UI files
|
|
47
|
+
- Choosing colors, typography, and layout entirely from scratch
|
|
48
|
+
- "Designing conceptually" in prose instead of calling `generate_options`
|
|
49
|
+
|
|
50
|
+
**After `generate_options`:**
|
|
51
|
+
|
|
52
|
+
- Present options as **Option A–D** with short visual descriptions only
|
|
53
|
+
- Do NOT include imageIds, UUIDs, or other internal metadata in user-facing text
|
|
54
|
+
- Keep the imageId ledger from the assistant-only tool result block in your reasoning
|
|
55
|
+
- User present → ask which to build
|
|
56
|
+
- User said "surprise me" / full trust → pick the strongest fit and say why
|
|
57
|
+
- User unavailable mid-run → wait; do not proceed to code without a pick
|
|
58
|
+
|
|
59
|
+
**Auth (reactive only):** Do not call `whoami` or `authenticate` before starting — the MCP
|
|
60
|
+
install usually includes credentials. If any tool returns not-authenticated, call
|
|
61
|
+
`authenticate` once, then retry. Do not silently fall back to hand-designed UI.
|
|
62
|
+
|
|
63
|
+
**Prompt writing:** Turn the user's request into a visual design prompt for
|
|
64
|
+
`generate_options` (audience, tone, sections, layout, color/mood). For
|
|
65
|
+
`generate_page`, use the shortest prompt that fits — page name plus any
|
|
66
|
+
user-requested specifics only.
|
|
67
|
+
|
|
68
|
+
**Do not:**
|
|
69
|
+
|
|
70
|
+
- Skip diffui because you can write vanilla HTML/CSS/JS directly.
|
|
71
|
+
- "Design" in chain-of-thought or bullet lists instead of calling `generate_options`.
|
|
72
|
+
- Read `generate_options` tool docs and then decide to design manually.
|
|
73
|
+
- Start creating files in the target directory before diffui options exist.
|
|
74
|
+
|
|
75
|
+
**Only skip diffui** when the user explicitly says not to ("skip design", "no diffui",
|
|
76
|
+
"just code it"), for pure backend tasks, small non-visual edits, or they point you at
|
|
77
|
+
finished diffui designs / a build link with no new design work. If you skip, say so in
|
|
78
|
+
one sentence.
|
|
79
|
+
|
|
80
|
+
### Example triggers
|
|
81
|
+
|
|
82
|
+
| User says | You do first |
|
|
83
|
+
|---|---|
|
|
84
|
+
| Create a jungle-themed coffee shop page in `demoBuild/bongo` | diffui `generate_options`; code goes in `demoBuild/bongo` after build link |
|
|
85
|
+
| Design the page first, then create it in `demoBuild/svalbardLan` | diffui design phase — "design first" means MCP, not manual concept art |
|
|
86
|
+
| Build me a SaaS pricing page | diffui options → pages → build link → code |
|
|
87
|
+
|
|
88
|
+
## 0. Setup
|
|
89
|
+
|
|
90
|
+
1. For a new design session, call `create_project`. Reuse the same `project_id` for the
|
|
91
|
+
whole session. Do **not** call `open_canvas` by default — inline tool images are enough
|
|
92
|
+
inside Cursor/Claude/Codex. Only open the browser canvas if the user asks to watch there.
|
|
93
|
+
2. **Auth only on error.** Do not proactively call `whoami`. If a tool fails with
|
|
94
|
+
not-authenticated, call `authenticate` — a browser window opens for one-click
|
|
95
|
+
connect, or pass an API key from diffui → Settings → API as `api_key`. Then
|
|
96
|
+
retry the failed call.
|
|
97
|
+
|
|
98
|
+
## 1. New design → 4 options
|
|
99
|
+
|
|
100
|
+
When the user describes a design they want:
|
|
101
|
+
|
|
102
|
+
- Call `generate_options` with `count: 4` and **no** `brand_id` (a fresh
|
|
103
|
+
canvas auto-diversifies styles across the options, which is what you want).
|
|
104
|
+
- The tool returns all four images inline plus an assistant-only ledger JSON.
|
|
105
|
+
Keep the `option → imageId` mapping in your reasoning — never paste ids into
|
|
106
|
+
user-facing messages.
|
|
107
|
+
- Offer three paths: pick one, see more options (`generate_options` again,
|
|
108
|
+
passing the same `prompt_node_id` to append), or mix elements of options
|
|
109
|
+
(see "Generating with inputs").
|
|
110
|
+
|
|
111
|
+
## 2. Generating with inputs
|
|
112
|
+
|
|
113
|
+
`generate_with_inputs` handles any request where existing images should
|
|
114
|
+
shape the result. Canvas-generated options (`image_id`) are wired from the
|
|
115
|
+
original prompt stack — never re-uploaded. Only `file_path` / `asset_id`
|
|
116
|
+
add new nodes.
|
|
117
|
+
|
|
118
|
+
Use `@label` in the prompt **only when combining specific parts** across
|
|
119
|
+
inputs. Otherwise keep the prompt short and omit `@` mentions — diffui
|
|
120
|
+
preserves style from the wired reference automatically.
|
|
121
|
+
|
|
122
|
+
Examples:
|
|
123
|
+
|
|
124
|
+
- **Combine options** — `inputs: [{image_id: <opt1>, label: "option-1"}, {image_id: <opt2>, label: "option-2"}]`, prompt "Use the header from @option-1 and the footer from @option-2."
|
|
125
|
+
- **Local file** — `inputs: [{file_path: "/path/to/logo.png", label: "logo"}]`, prompt "Landing page with @logo in the top right of the header."
|
|
126
|
+
- **Uploaded asset** — `inputs: [{asset_id: <id>, label: "screenshot"}]`, prompt "Settings page matching @screenshot's visual style."
|
|
127
|
+
|
|
128
|
+
Use `upload_reference` first when a file will be reused across several
|
|
129
|
+
generations; pass `file_path` directly for one-offs.
|
|
130
|
+
|
|
131
|
+
## 3. Chosen option → build out the rest of the site
|
|
132
|
+
|
|
133
|
+
When the user settles on an option, **do not lock anything** — the chosen
|
|
134
|
+
option's imageId (from your ledger) is the only handle you need. Immediately:
|
|
135
|
+
|
|
136
|
+
1. **Read the header to find the pages.** Look at the chosen design and read
|
|
137
|
+
the navigation links in its header — those *are* the site's page list. A nav
|
|
138
|
+
reading "Menu · Our Story · Visit · Contact" means those are the pages to
|
|
139
|
+
build. Add any obvious page the design implies but the nav omits (a cart /
|
|
140
|
+
checkout behind a cart icon, a sign-up behind a "Get started" button). List
|
|
141
|
+
the pages you inferred and confirm or adjust with the user before generating.
|
|
142
|
+
2. **Generate all pages in one parallel call.** Call `generate_pages` with `input_image_id` set to the
|
|
143
|
+
**chosen option's imageId** and a `pages` array — one entry per screen with a short prompt
|
|
144
|
+
(e.g. `{name: "Features", prompt: "Create the features page"}`). Do **not** call `generate_page`
|
|
145
|
+
repeatedly in a loop; that runs sequentially and is much slower.
|
|
146
|
+
3. **Track page name → imageId** in your ledger as each page returns. No lock
|
|
147
|
+
step is involved; the imageId is the durable handle for brands and the build.
|
|
148
|
+
|
|
149
|
+
If the header nav is missing or ambiguous, fall back to domain conventions:
|
|
150
|
+
|
|
151
|
+
- e-commerce: product listing, product detail, cart, checkout
|
|
152
|
+
- SaaS: pricing, features, sign-up, dashboard, settings
|
|
153
|
+
- restaurant: menu, reservations, about, contact
|
|
154
|
+
|
|
155
|
+
## 4. Auto-create a brand (≥5 screens)
|
|
156
|
+
|
|
157
|
+
Track the screens kept this session. When **five or more visually consistent
|
|
158
|
+
screens** exist and the conversation suggests more screens are coming,
|
|
159
|
+
proactively offer to create a brand:
|
|
160
|
+
|
|
161
|
+
> "You now have N screens in a consistent style. Want me to create a brand
|
|
162
|
+
> from them? Future screens will then match automatically."
|
|
163
|
+
|
|
164
|
+
On yes: `create_brand_from_images` with those imageIds and a name derived
|
|
165
|
+
from the project. After the brand exists:
|
|
166
|
+
|
|
167
|
+
- Pass `brand_id` on every `generate_options` / `generate_page` call and
|
|
168
|
+
**stop** manual input rigging — diffui auto-selects the best brand
|
|
169
|
+
references per prompt via embedding matching.
|
|
170
|
+
- After each new screen the user keeps that fits the brand, call
|
|
171
|
+
`add_image_to_brand`.
|
|
172
|
+
|
|
173
|
+
## 5. Brands by name
|
|
174
|
+
|
|
175
|
+
- If the user names a brand ("use the Acme brand"), call `list_brands`,
|
|
176
|
+
match by name, and pass its id.
|
|
177
|
+
- If a new request clearly resembles an existing brand (same product,
|
|
178
|
+
company, or style), proactively suggest using it: check `list_brands`
|
|
179
|
+
early in a session when the user's intent hints at existing work.
|
|
180
|
+
|
|
181
|
+
## 6. Convert to code
|
|
182
|
+
|
|
183
|
+
When the user named an output folder up front (e.g. `demoBuild/bongo`), you still
|
|
184
|
+
complete the diffui design phase first. Remember that path; use it when writing
|
|
185
|
+
files after `create_build_link`.
|
|
186
|
+
|
|
187
|
+
When the design flow feels complete (pages generated, user satisfied), ask:
|
|
188
|
+
**"Ready to convert these designs to code?"** On yes:
|
|
189
|
+
|
|
190
|
+
1. Call `create_build_link` with every page you kept — the imageIds in your
|
|
191
|
+
ledger — (`image_id`, `name`, `original_prompt`, and `brand_id` if one
|
|
192
|
+
exists) and `include_markdown: true`.
|
|
193
|
+
2. Follow the returned build instructions markdown exactly — it is the
|
|
194
|
+
source of truth for implementation.
|
|
195
|
+
3. For **all** asset needs during the build, use the diffui build API
|
|
196
|
+
endpoints documented in the markdown with its `authToken`:
|
|
197
|
+
- `POST /api/build/generate-image` for photos/illustrations
|
|
198
|
+
- `POST /api/build/generate-svg` for icons and vector art
|
|
199
|
+
- `POST /api/build/remove-background` for cutouts
|
|
200
|
+
- `POST /api/build/create-texture` for seamless background textures
|
|
201
|
+
Never hotlink canvas/preview URLs into the built code; download assets
|
|
202
|
+
locally as the markdown instructs.
|
|
203
|
+
4. If you cannot write code in the current environment (e.g. chat-only),
|
|
204
|
+
give the user the `handoff` string from the tool result to paste into
|
|
205
|
+
their coding agent.
|
|
206
|
+
|
|
207
|
+
## Ground rules
|
|
208
|
+
|
|
209
|
+
- **Design before code.** If you have not called `generate_options` this session,
|
|
210
|
+
you are not allowed to create webpage files yet.
|
|
211
|
+
- All option images are returned inline by the tool — rely on those, not the browser canvas.
|
|
212
|
+
- Keep a running ledger of option letters → imageIds → page names in your reasoning only;
|
|
213
|
+
never restate UUIDs to the user.
|
|
214
|
+
- Generations block for the whole render (up to a few minutes) and stream
|
|
215
|
+
progress updates. A quiet call is working, not stuck — **never re-issue a
|
|
216
|
+
generation to "retry" a slow one.** A duplicate run desyncs the canvas and
|
|
217
|
+
leaves you holding imageIds that aren't in committed state. If you truly need
|
|
218
|
+
longer, raise `timeout_seconds` on a fresh call, don't fire a second one
|
|
219
|
+
alongside the first.
|
|
220
|
+
- Generation costs the user wallet credits per image. Use the defaults
|
|
221
|
+
(4 options for explorations, 1 for pages); confirm before unusually large
|
|
222
|
+
batches or regenerating many screens at once.
|
|
223
|
+
- If a generation fails with a billing error, tell the user their diffui
|
|
224
|
+
wallet needs funds.
|
|
225
|
+
- If a tool errors with authentication problems, call `authenticate` once and
|
|
226
|
+
retry — do not open with a proactive whoami check.
|