lavish-axi 0.1.57 → 0.1.59

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 CHANGED
@@ -31,10 +31,10 @@ HTML is the new markdown. Lavish is the new editor for your HTML artifacts.
31
31
  Agents are good at producing rich HTML artifacts, but the human-agent collaboration loop on such artifacts is lacking and falls back into screenshots and long responses for “tell me what to change.”
32
32
  That loses the thing HTML is best at: interactivity.
33
33
 
34
- Lavish Editor opens agent-generated HTML files in a local browser, lets you pinpoint elements and selected text, edit rendered Mermaid diagrams as whiteboards, and send feedback to the agent to address.
34
+ Lavish Editor opens agent-generated HTML files in a local browser, lets you pinpoint elements and selected text, edit diagrams your agent authored as Mermaid whiteboards, and send feedback to the agent to address.
35
35
 
36
36
  - **Local-first** - Review local HTML artifacts with a local CLI and no cloud dependency in the core feedback loop; hosted sharing through third-party ht-ml.app is explicit and opt-in.
37
- - **Human-AI collaboration** - Annotate elements and selected text ranges, edit Mermaid diagrams as whiteboards, and send messages to the agent without leaving Lavish Editor.
37
+ - **Human-AI collaboration** - Annotate elements and selected text ranges, edit Mermaid whiteboard diagrams, and send messages to the agent without leaving Lavish Editor.
38
38
  - **Battery included** - Lavish Editor teaches your agent good visualization for common use cases such as product or technical plans, design explorations and more out of the box.
39
39
 
40
40
  Lavish Editor is an [AXI](https://axi.md), which means -
@@ -162,13 +162,13 @@ pnpm link
162
162
  - **Portable artifacts** - The artifact runs in a sandboxed iframe while Lavish injects a small SDK for annotations, snapshots, feedback controls, and render-time layout checks.
163
163
  Author-defined links and popups can open in top-level tabs, while artifact documents remain sandboxed without same-origin access.
164
164
  Lavish does not inject any design system, so the saved HTML file renders identically whether you open it through `lavish-axi` or directly in a browser.
165
- Run `lavish-axi design` for the single source of agent-facing design guidance and optional CDN or Mermaid snippets.
165
+ Run `lavish-axi design` for the single source of agent-facing design guidance, including optional CDN snippets and the whiteboard (Mermaid) opt-in snippet.
166
166
  - **Self-paint warning** - `lavish-axi <html-file>`, `export`, and `share` run a render-free check for artifacts missing an explicit page background and return a one-line `self_paint_warning`.
167
167
  The check fails open - any stylesheet link, `@import`, Tailwind runtime script, `color-scheme`, or `html`/`body`/`:root` background signal suppresses it - and it never blocks the open.
168
168
  - **Open-time layout gate** - The browser chrome masks an artifact only while the real in-iframe audit waits for fonts and final geometry.
169
- The first completed check reveals the artifact, whatever it found, unless the mask is naming one of the failures below; the gate never holds the review hostage waiting for a repair.
170
- The user can click **Show anyway**, and a bounded safety timeout fails open when no check has completed.
171
- If the review cannot load at all - the chrome's own script never runs, or the server does not answer the artifact's load request after several retries - the mask is replaced by a message naming the problem and a **Check and reload** button, instead of holding the artifact behind a check that will never complete.
169
+ The first completed client-side check reveals the artifact, whatever it found and even if reporting that check to the server fails; the gate never holds the review hostage waiting for a repair or a network round-trip.
170
+ The user can click **Show anyway**, and a bounded safety timeout fails open from every gate state.
171
+ If the review cannot load at all - the chrome's own script never runs, or the server does not answer the artifact's load request after several retries - the mask names the problem and offers **Check and reload** without removing the independent **Show anyway** escape.
172
172
  A review already loaded in another browser tab is named the same way, with a **Take over here** button that moves it into the current tab, because Lavish loads an artifact in one tab at a time.
173
173
  - **Layout issues inbox** - Detection is passive. After fonts and finite animations settle, the injected SDK confirms severe failures from direct rendered evidence such as materially escaped meaningful content or required controls, clipped text fragments, viewport reachability, or near-total semantic occlusion.
174
174
  Explicit ellipsis and line clamp, standard visually hidden accessibility text, intentional scrollers or masks, parent overhang, generic element scroll geometry, decorative overlap, and uncertain motion do not produce findings by themselves.
@@ -219,7 +219,8 @@ pnpm link
219
219
  As a browser-side abuse guard, each chrome page allows 30 upload attempts per rolling minute, 4 uploads in flight at once, and 256 MiB of attempted image bytes over its lifetime; rejected uploads stay visible for retry or removal.
220
220
  Attachments are cleaned up by `LAVISH_AXI_ATTACHMENT_TTL_MS` (default 7 days; `0`/`off` disables) but only once no pending prompt still references them; `LAVISH_AXI_MAX_ATTACHMENT_DISK_MB` (default 512 MiB; `0`/`off` disables) caps total attachment disk.
221
221
  The cap is enforced when an image is uploaded, not just periodically: the upload first reclaims unreferenced files (oldest first, and never one added within the last hour), and if it still would not fit, that upload is refused with a storage-full error on its chip instead of discarding an image the user is about to send.
222
- - **Mermaid diagrams** - In the Lavish browser, every rendered Mermaid diagram in a `.mermaid` container becomes an embedded editable Excalidraw whiteboard.
222
+ - **Mermaid diagrams** - Whiteboards are an opt-in: agents author a diagram as Mermaid only when you ask for an editable whiteboard, and hand-authored inline SVG illustrations are the default figure medium otherwise.
223
+ In the Lavish browser, every rendered Mermaid diagram in a `.mermaid` container becomes an embedded editable Excalidraw whiteboard.
223
224
  Click a diagram to unlock editing, and use its Fullscreen action to edit it over the whole viewport.
224
225
  Whiteboard scenes autosave locally.
225
226
  If a live reload changes the Mermaid source, an unmodified whiteboard silently re-converts to the new diagram. If the reviewer had edited the scene, reopening it lets them re-convert and discard the saved edits or keep editing the saved scene.
@@ -253,7 +254,7 @@ pnpm link
253
254
  | `lavish-axi share <html-file>` | Publish the artifact (local assets inlined) to [ht-ml.app](https://ht-ml.app), a third-party host not part of Lavish, and print a visitable URL plus a secret update key; shares are public by default, and `--password` makes viewers enter the password before viewing. |
254
255
  | `lavish-axi stop` | Shut down the background server. |
255
256
  | `lavish-axi playbook [id]` | List focused artifact guidance or show one playbook; agents must open each matching playbook before writing HTML. |
256
- | `lavish-axi design` | Show agent-facing design guidance, including optional CDN and Mermaid snippets. |
257
+ | `lavish-axi design` | Show agent-facing design guidance, including optional CDN snippets and the whiteboard (Mermaid) opt-in snippet. |
257
258
  | `lavish-axi setup hooks` | Install or repair optional SessionStart hooks for Claude Code, Codex, OpenCode, and GitHub Copilot CLI; restart the agent session afterward. |
258
259
  | `lavish-axi setup plugin` | Register the installed package as an [Agent Plugin](https://agent-plugins.org) in VS Code, Cursor, and GitHub Copilot CLI; opt-in, idempotent, no marketplace involved. Reload each client afterward. |
259
260
  | `lavish-axi server` | Run the local Lavish Editor server. |
@@ -151,6 +151,8 @@ const layoutGateOverlay = /** @type {HTMLDivElement} */ (document.getElementById
151
151
  const layoutGateTitle = /** @type {HTMLDivElement} */ (document.getElementById("layoutGateTitle"));
152
152
  const layoutGateCopy = /** @type {HTMLParagraphElement} */ (document.getElementById("layoutGateCopy"));
153
153
  const layoutGateAction = /** @type {HTMLButtonElement} */ (document.getElementById("layoutGateAction"));
154
+ const layoutGateBypass = /** @type {HTMLButtonElement} */ (document.getElementById("layoutGateBypass"));
155
+ const layoutGateEscape = /** @type {any} */ (window).__lavishLayoutGateEscape;
154
156
  const warningsWrap = /** @type {HTMLDivElement} */ (document.getElementById("warningsWrap"));
155
157
  const warningsButton = /** @type {HTMLButtonElement} */ (document.getElementById("warningsButton"));
156
158
  const warningsCount = /** @type {HTMLSpanElement} */ (document.getElementById("warningsCount"));
@@ -185,7 +187,6 @@ let outdatedReloadInFlight = false;
185
187
  let unrestorableDraftMiss = null;
186
188
  let retiredDrafts = loadRetiredDrafts();
187
189
  let layoutGateVisible = false;
188
- let layoutGateArmed = false;
189
190
  let layoutGateManuallyBypassed = !layoutGateEnabled;
190
191
  let layoutGateFailureActive = false;
191
192
  // A failure only the user can retire. The artifact-load card clears itself once a load succeeds;
@@ -1261,6 +1262,7 @@ function normalizeLayoutFindings(value) {
1261
1262
  }
1262
1263
 
1263
1264
  function clearLayoutGateTimer() {
1265
+ layoutGateEscape?.cancel?.();
1264
1266
  if (layoutGateTimer) clearTimeout(layoutGateTimer);
1265
1267
  layoutGateTimer = undefined;
1266
1268
  }
@@ -1289,7 +1291,8 @@ function setLayoutGateActive(active) {
1289
1291
  // its own: the artifact never loaded and retrying stopped helping. The overlay is reused because
1290
1292
  // it already covers the empty artifact area; without this the user is left looking at either a
1291
1293
  // spinner that never resolves or a blank frame, with nothing explaining it and nothing to click.
1292
- // Bumping the cycle retires any pending reveal timer so a stale one cannot hide this card.
1294
+ // Bumping the cycle invalidates the previous timer; setLayoutGateFailure immediately replaces it
1295
+ // with a fresh hold timer so the card cannot strand the visual gate.
1293
1296
  function setLayoutGateFailure(title, copy, actionLabel = "Reload", onAction, { sticky = false } = {}) {
1294
1297
  if (ended) return;
1295
1298
  // A sticky card is the user's to retire, and that has to hold against being overwritten as
@@ -1299,8 +1302,9 @@ function setLayoutGateFailure(title, copy, actionLabel = "Reload", onAction, { s
1299
1302
  layoutGateFailureActive = true;
1300
1303
  layoutGateFailureSticky = sticky;
1301
1304
  layoutGateCycle += 1;
1302
- clearLayoutGateTimer();
1303
- layoutGateArmed = false;
1305
+ // Failure copy must not disable the visual gate's own recovery paths. Keep a fresh hold timer
1306
+ // over the card so a server replacement or any other failure cannot strand the artifact behind
1307
+ // a sticky message forever.
1304
1308
  if (layoutGateTitle) layoutGateTitle.textContent = title;
1305
1309
  if (layoutGateCopy) layoutGateCopy.textContent = copy;
1306
1310
  if (layoutGateAction) {
@@ -1308,7 +1312,12 @@ function setLayoutGateFailure(title, copy, actionLabel = "Reload", onAction, { s
1308
1312
  layoutGateAction.textContent = actionLabel;
1309
1313
  layoutGateAction.onclick = onAction || (() => location.reload());
1310
1314
  }
1315
+ if (layoutGateBypass) {
1316
+ layoutGateBypass.hidden = false;
1317
+ layoutGateBypass.onclick = () => forceRevealLayoutGate("manual");
1318
+ }
1311
1319
  setLayoutGateActive(true);
1320
+ armLayoutGateTimer();
1312
1321
  }
1313
1322
 
1314
1323
  // Every failure card in this feature is raised in a state where the server may not be listening,
@@ -1362,35 +1371,31 @@ function clearLayoutGateFailure() {
1362
1371
  layoutGateAction.textContent = "Show anyway";
1363
1372
  layoutGateAction.onclick = () => forceRevealLayoutGate("manual");
1364
1373
  }
1374
+ if (layoutGateBypass) layoutGateBypass.hidden = true;
1365
1375
  revealLayoutGate();
1366
1376
  }
1367
1377
 
1368
1378
  function revealLayoutGate() {
1369
- if (layoutGateFailureSticky) return;
1370
1379
  clearLayoutGateTimer();
1371
- layoutGateArmed = false;
1380
+ layoutGateEscape?.reveal?.();
1372
1381
  setLayoutGateActive(false);
1373
1382
  }
1374
1383
 
1375
1384
  function forceRevealLayoutGate(reason) {
1376
- if (!layoutGateEnabled || ended) return;
1377
- if (reason === "manual") layoutGateManuallyBypassed = true;
1385
+ if (ended) return;
1386
+ if (reason === "manual") {
1387
+ layoutGateManuallyBypassed = true;
1388
+ layoutGateEscape?.manualReveal?.();
1389
+ }
1378
1390
  revealLayoutGate();
1379
1391
  }
1380
1392
 
1381
- function startLayoutGateCycle() {
1382
- clearLayoutGateFailure();
1383
- // A sticky failure owns the overlay until the user acts on it, so a later load must not repaint
1384
- // the checking card over the message it left there.
1385
- if (layoutGateFailureSticky) return;
1386
- if (!layoutGateEnabled || layoutGateManuallyBypassed || ended) return;
1387
-
1388
- layoutGateCycle += 1;
1389
- layoutGateArmed = true;
1390
- setLayoutGateCard("checking");
1391
- setLayoutGateActive(true);
1393
+ function armLayoutGateTimer() {
1392
1394
  clearLayoutGateTimer();
1393
-
1395
+ if (layoutGateEscape?.arm) {
1396
+ layoutGateEscape.arm(layoutGateMaxHoldMs, () => forceRevealLayoutGate("timeout"));
1397
+ return;
1398
+ }
1394
1399
  const cycle = layoutGateCycle;
1395
1400
  layoutGateTimer = setTimeout(() => {
1396
1401
  if (cycle !== layoutGateCycle || !layoutGateVisible || ended) return;
@@ -1399,22 +1404,35 @@ function startLayoutGateCycle() {
1399
1404
  layoutGateTimer?.unref?.();
1400
1405
  }
1401
1406
 
1407
+ function startLayoutGateCycle() {
1408
+ clearLayoutGateFailure();
1409
+ if (!layoutGateEnabled || layoutGateManuallyBypassed || ended) return;
1410
+
1411
+ layoutGateCycle += 1;
1412
+ setLayoutGateActive(true);
1413
+ // A sticky failure owns the card copy, but never the reveal. Do not repaint it as a checking
1414
+ // card, and do arm a fresh timer for reloads that happen while the sticky card is present.
1415
+ if (!layoutGateFailureSticky) setLayoutGateCard("checking");
1416
+ armLayoutGateTimer();
1417
+ }
1418
+
1402
1419
  // The gate only waits for fonts and final geometry now. It never holds the artifact hostage
1403
1420
  // pending an agent repair: findings are the user's to triage, so a completed pass always reveals
1404
1421
  // and hands the result to the passive inbox.
1405
1422
  function handleLayoutGatePass() {
1406
- if (!layoutGateEnabled || layoutGateManuallyBypassed) return;
1407
- if (!layoutGateArmed && !layoutGateVisible) return;
1423
+ if (ended || !layoutGateVisible) return;
1408
1424
  revealLayoutGate();
1409
1425
  }
1410
1426
 
1411
1427
  function initializeLayoutGate() {
1428
+ if (layoutGateEscape?.isManuallyBypassed?.()) layoutGateManuallyBypassed = true;
1412
1429
  if (!layoutGateEnabled) {
1413
1430
  setLayoutGateActive(false);
1414
1431
  return;
1415
1432
  }
1416
1433
 
1417
1434
  if (layoutGateAction) layoutGateAction.onclick = () => forceRevealLayoutGate("manual");
1435
+ if (layoutGateBypass) layoutGateBypass.onclick = () => forceRevealLayoutGate("manual");
1418
1436
  startLayoutGateCycle();
1419
1437
  }
1420
1438
 
@@ -1801,6 +1819,7 @@ function markSessionEnded() {
1801
1819
  layoutGateManuallyBypassed = true;
1802
1820
  layoutGateFailureSticky = false;
1803
1821
  revealLayoutGate();
1822
+ layoutGateEscape?.end?.();
1804
1823
  postToFrame({ type: "lavish:setAnnotationMode", enabled: false });
1805
1824
  endedOverlay.hidden = false;
1806
1825
  }
@@ -2737,14 +2756,24 @@ window.addEventListener("message", (event) => {
2737
2756
 
2738
2757
  const msg = event.data || {};
2739
2758
  const messageToken = String(msg.artifact_load_token || "");
2740
- if (messageToken !== artifactLoadToken) return;
2759
+ if (messageToken !== artifactLoadToken) {
2760
+ // A pass can be stamped by the load that just lost a token race. Ask the current artifact
2761
+ // document to run the audit again instead of consuming the only pass for this cycle.
2762
+ if (msg.type === "lavish:layoutDiagnostics") postToFrame({ type: "lavish:requestLayoutDiagnostics" });
2763
+ return;
2764
+ }
2741
2765
  const messageSequence = ++artifactMessageSequence;
2742
2766
  artifactSpokeToken = messageToken;
2743
2767
  clearTimeout(artifactSilenceTimer);
2744
2768
  if (msg.type === "lavish:layoutDiagnostics") {
2745
2769
  const diagnosticSequence = ++layoutDiagnosticSequence;
2770
+ const complete = msg.complete !== false;
2771
+ // The gate is visual, so the client-side settled pass is the release signal. Reporting the
2772
+ // pass is deliberately fire-and-forget: a server restart or a diagnostics 4xx/5xx must not
2773
+ // hold a rendered artifact hostage to a network round-trip.
2774
+ if (complete) handleLayoutGatePass();
2746
2775
  submitLayoutDiagnostics({
2747
- complete: msg.complete !== false,
2776
+ complete,
2748
2777
  targetPresenceComplete: msg.target_presence_complete === true,
2749
2778
  artifactRevision: msg.artifact_revision,
2750
2779
  artifactLoadToken: msg.artifact_load_token,
@@ -2759,9 +2788,14 @@ window.addEventListener("message", (event) => {
2759
2788
  if (messageSequence === artifactMessageSequence) armArtifactAvailabilityProbe(messageToken);
2760
2789
  return;
2761
2790
  }
2762
- if (msg.complete !== false) handleLayoutGatePass();
2763
2791
  })
2764
- .catch(() => {});
2792
+ .catch(() => {
2793
+ // A failed report is still a completed client-side pass. Keep this fallback explicit so a
2794
+ // future change cannot accidentally make the network request the gate's release path.
2795
+ if (complete && messageToken === artifactLoadToken && diagnosticSequence === layoutDiagnosticSequence) {
2796
+ handleLayoutGatePass();
2797
+ }
2798
+ });
2765
2799
  return;
2766
2800
  }
2767
2801
  // The artifact spoke, so it rendered and ran its SDK - there is nothing fatal to probe for.
@@ -3148,9 +3182,9 @@ setAgentPresence("waiting");
3148
3182
  // to wait for - start read-only instead of looking live until a Send gets silently refused.
3149
3183
  if (sessionData.initialEnded) markSessionEnded();
3150
3184
 
3151
- // Reaching this line is the only proof that this file parsed and ran to completion. The page it
3152
- // bootstraps ships with the layout-gate overlay already covering the artifact, and only this
3153
- // script ever takes it down - so the inline failsafe in the page holds it up until here.
3185
+ // Reaching this line is the only proof that this file parsed and ran to completion. The inline
3186
+ // bootstrap already owns the gate's bounded escape if this script fails; retire only its separate
3187
+ // boot-failure timer now that the full client has taken over.
3154
3188
  const chromeBootWindow = /** @type {Record<string, any>} */ (/** @type {unknown} */ (window));
3155
3189
  chromeBootWindow.__lavishChromeReady = true;
3156
3190
  chromeBootWindow.__lavishCancelChromeBootFailsafe?.();
package/dist/chrome.css CHANGED
@@ -1328,6 +1328,9 @@ body.lavish {
1328
1328
  .ended-action {
1329
1329
  margin-top: var(--space-8);
1330
1330
  }
1331
+ .layout-gate-bypass {
1332
+ margin-left: var(--space-4);
1333
+ }
1331
1334
  iframe {
1332
1335
  width: 100%;
1333
1336
  height: 100%;
package/dist/cli.mjs CHANGED
@@ -20,11 +20,11 @@ var PLAYBOOK_ROUTER_HELP = "One artifact often combines several playbooks (for e
20
20
  var PLAYBOOKS = [
21
21
  {
22
22
  id: "diagram",
23
- use_when: "Map relationships, flows, state, and architecture",
23
+ use_when: "Explain relationships, flows, state, architecture, and concepts with illustrations",
24
24
  choose: [
25
- "Use Mermaid when automatic node placement and edge routing matter more than rich card content.",
26
- "Use CSS grid, SVG, or positioned HTML when each item needs prose, code, controls, or detailed annotations.",
27
- "Use a hybrid shape for large systems: a small overview diagram followed by detailed module cards."
25
+ "Default to hand-authored inline SVG: it gives proportion, emphasis, spatial metaphor, and annotation-ready structure that generated layouts cannot.",
26
+ "Use Mermaid only when the user asks for an editable whiteboard: rendered Mermaid in a `.mermaid` container becomes an Excalidraw whiteboard in the Lavish browser.",
27
+ "For large systems, draw a small overview illustration and put detail in module cards below it, instead of one dense auto-laid graph."
28
28
  ],
29
29
  structure: [
30
30
  "Lead with the question the diagram answers, not with the implementation detail that produced it.",
@@ -32,15 +32,18 @@ var PLAYBOOKS = [
32
32
  "For complex systems, separate topology from detail so the overview stays readable."
33
33
  ],
34
34
  design_rules: [
35
- "Use page-scoped class names and avoid generic names like .node that can collide with diagram libraries.",
36
- "Prefer top-down flow for multi-step diagrams unless the flow is genuinely linear and short.",
37
- "Quote labels that contain punctuation or code-like names, and use explicit line breaks where the renderer supports them.",
38
- "Initialize Mermaid to match the page theme and re-render when the theme changes: pick the Mermaid theme from the effective page appearance (light or dark) at render time, and use the theme-aware `lavish-axi design` Mermaid snippet rather than hardcoding a single theme, since Mermaid does not restyle an already-rendered SVG when the viewer toggles the page theme."
35
+ "Size with viewBox plus width:100%; never fixed pixel dimensions, and keep every element inside the viewBox.",
36
+ "Color through currentColor and the page's CSS custom properties so figures follow the artifact's light and dark themes.",
37
+ "Give every meaningful node, edge, and region a stable id and a <title> so reviewers can annotate precisely.",
38
+ "Keep labels to a few words and put prose beside the figure in HTML - SVG text does not wrap, so short labels are also the overflow discipline.",
39
+ "Keep figures self-contained: no external images, fonts, or scripts, so exports render offline.",
40
+ "Render-verify before serving: screenshot the artifact in light, dark, and a narrow viewport - the layout audit deliberately skips SVG interiors.",
41
+ "When the user asked for a whiteboard, initialize Mermaid theme-aware with the `lavish-axi design` snippet rather than hardcoding one theme."
39
42
  ],
40
43
  pitfalls: [
41
- "Do not cram every file or function into one diagram when a layered explanation would be clearer.",
42
- "Do not hand-build boxes-and-arrows from div/flexbox for a flow: it does not auto-route edges and reads worse than Mermaid; reach for Mermaid or SVG for richly annotated nodes.",
43
- "Do not let default diagram colors clash with the page palette or dark mode.",
44
+ "Do not cram every file or function into one figure when a layered explanation would be clearer.",
45
+ "Do not hand-build boxes-and-arrows from div/flexbox: inline SVG owns figures, HTML owns the prose around them.",
46
+ "Do not reach for Mermaid to save authoring effort - it surrenders position, size, and emphasis to the engine.",
44
47
  "Do not present unverified architecture claims as facts. Cite the files or commands that support them."
45
48
  ],
46
49
  lavish_notes: [
@@ -402,7 +405,7 @@ var LAYOUT_SAFETY_CSS_SNIPPET = `<style>
402
405
  }
403
406
  </style>`;
404
407
  var DESIGN_PRIORITY_RULE = "Decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and match that project's design system: Tailwind or theme config, shared CSS variables or design tokens, component library, brand assets, or existing styled pages. If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN, and prefer that CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user.";
405
- var DESIGN_SYSTEM_HINT = "Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML: " + DESIGN_PRIORITY_RULE + " Run `lavish-axi design` for a content-to-playbook router, a copy-pasteable CDN snippet, a Mermaid CDN snippet/init for diagrams, and the DaisyUI component reference. When you deliver the artifact, state which of the three design sources you used and why.";
408
+ var DESIGN_SYSTEM_HINT = "Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML: " + DESIGN_PRIORITY_RULE + " Run `lavish-axi design` for a content-to-playbook router, a copy-pasteable CDN snippet, the whiteboard (Mermaid) opt-in snippet, and the DaisyUI component reference. When you deliver the artifact, state which of the three design sources you used and why.";
406
409
  var DAISYUI_THEMES = [
407
410
  "light",
408
411
  "dark",
@@ -457,8 +460,8 @@ function createDesignOutput() {
457
460
  layout_safety_note: "Optional copy-paste CSS for artifacts with dense nested grid/flex layouts, badges, wide monospace or pixel fonts, or local media. Paste it into the artifact yourself when useful. Lavish never auto-injects it, so direct-open portability stays intact.",
458
461
  other_design_systems: "If the user asks for a different design system (Bootstrap, custom CSS, plain HTML, etc.), use that instead - Lavish does not require DaisyUI."
459
462
  },
460
- diagram_tooling: {
461
- use_when: "Use this for flows / architecture / state / sequence diagrams after opening the diagram playbook; Mermaid handles layout and edge routing better than hand-built div/flexbox boxes.",
463
+ whiteboard_tooling: {
464
+ use_when: "Opt-in only: author a diagram as Mermaid in a `.mermaid` container solely when the user asks for an editable whiteboard - Lavish turns it into an Excalidraw whiteboard whose edits come back as feedback. Every other figure is hand-authored inline SVG per the diagram playbook.",
462
465
  mermaid_cdn_snippet: MERMAID_CDN_SNIPPET,
463
466
  cdn_urls: { mermaid: MERMAID_CDN_URL },
464
467
  versions: { mermaid: MERMAID_VERSION }
@@ -5370,6 +5373,7 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
5370
5373
  const target = whiteboardEntryByIndex(msg.diagramIndex);
5371
5374
  if (target) target.iframe.src = whiteboardFrameSrc(target);
5372
5375
  }
5376
+ if (msg.type === "lavish:requestLayoutDiagnostics") scheduleLayoutAudit(true);
5373
5377
  });
5374
5378
  function enhanceMermaid() {
5375
5379
  for (const svg of findMermaidSvgs()) {
@@ -5543,6 +5547,7 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
5543
5547
  let layoutAuditTimer = 0;
5544
5548
  let layoutAuditRun = 0;
5545
5549
  let lastLayoutAuditSignature = null;
5550
+ let layoutAuditPublishRequested = false;
5546
5551
  let layoutAuditPassSequence = 0;
5547
5552
  function toPixelNumber(value) {
5548
5553
  const parsed = Number.parseFloat(String(value || "0"));
@@ -6140,7 +6145,12 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
6140
6145
  new Promise((resolve) => window.setTimeout(resolve, layoutAuditAnimationMaxWaitMs))
6141
6146
  ]);
6142
6147
  if (!settled) {
6143
- for (const animation of finite) animation.finished.then(scheduleLayoutAudit, scheduleLayoutAudit);
6148
+ for (const animation of finite) {
6149
+ animation.finished.then(
6150
+ () => scheduleLayoutAudit(),
6151
+ () => scheduleLayoutAudit()
6152
+ );
6153
+ }
6144
6154
  }
6145
6155
  return settled;
6146
6156
  }
@@ -6148,7 +6158,8 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
6148
6158
  const severe = findings.filter((finding) => finding?.severity === "error");
6149
6159
  const viewportWidth = window.innerWidth || document.documentElement.clientWidth || 0;
6150
6160
  const signature = JSON.stringify({ complete, targetPresenceComplete, viewportWidth, severe });
6151
- if (signature === lastLayoutAuditSignature) return;
6161
+ if (!layoutAuditPublishRequested && signature === lastLayoutAuditSignature) return;
6162
+ layoutAuditPublishRequested = false;
6152
6163
  lastLayoutAuditSignature = signature;
6153
6164
  postArtifactMessage("lavish:layoutDiagnostics", {
6154
6165
  complete,
@@ -6180,7 +6191,8 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
6180
6191
  targetPresenceComplete
6181
6192
  );
6182
6193
  }
6183
- function scheduleLayoutAudit() {
6194
+ function scheduleLayoutAudit(publishRequested = false) {
6195
+ if (publishRequested) layoutAuditPublishRequested = true;
6184
6196
  if (layoutAuditTimer) window.clearTimeout(layoutAuditTimer);
6185
6197
  const runId = ++layoutAuditRun;
6186
6198
  layoutAuditTimer = window.setTimeout(() => {
@@ -6191,10 +6203,10 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
6191
6203
  }
6192
6204
  function startLayoutAudit() {
6193
6205
  scheduleLayoutAudit();
6194
- window.addEventListener("load", scheduleLayoutAudit, { once: true });
6195
- window.addEventListener("resize", scheduleLayoutAudit, { passive: true });
6196
- window.addEventListener("animationend", scheduleLayoutAudit, { passive: true });
6197
- window.addEventListener("transitionend", scheduleLayoutAudit, { passive: true });
6206
+ window.addEventListener("load", () => scheduleLayoutAudit(), { once: true });
6207
+ window.addEventListener("resize", () => scheduleLayoutAudit(), { passive: true });
6208
+ window.addEventListener("animationend", () => scheduleLayoutAudit(), { passive: true });
6209
+ window.addEventListener("transitionend", () => scheduleLayoutAudit(), { passive: true });
6198
6210
  }
6199
6211
  function reportLocalAssetFailure(event) {
6200
6212
  const el = event.target;
@@ -10019,23 +10031,36 @@ function extractArtifactHead(html) {
10019
10031
  return { faviconTag, title };
10020
10032
  }
10021
10033
  var CHROME_BOOT_FAILSAFE_MS = 15e3;
10034
+ var CHROME_LAYOUT_GATE_MAX_HOLD_MS = 12e3;
10022
10035
  var CHROME_BOOT_FAILSAFE_PROBE_TIMEOUT_MS = 4e3;
10023
10036
  var CHROME_BOOT_FAILSAFE_JS = `(function(){
10024
10037
  var t=setTimeout(fail,${CHROME_BOOT_FAILSAFE_MS});
10025
- var o,h,c,a;
10038
+ var o=document.getElementById("layoutGateOverlay"),h,c,a,b,gt=0,manual=false,ended=false;
10039
+ try{ended=JSON.parse(document.getElementById("lavish-session").textContent).initialEnded===true;}catch(e){}
10040
+ function cancelGate(){if(gt)clearTimeout(gt);gt=0;}
10041
+ function reveal(){cancelGate();if(o)o.hidden=true;if(document.body)document.body.classList.remove("layout-gate-active");}
10042
+ function manualReveal(){if(ended)return false;manual=true;reveal();return true;}
10043
+ function armGate(ms,onTimeout){cancelGate();if(!ended)gt=setTimeout(function(){if(ended)return;if(onTimeout)onTimeout();else reveal();},ms);}
10044
+ function showBypass(){b=document.getElementById("layoutGateBypass");if(b){b.hidden=false;b.onclick=manualReveal;}}
10045
+ window.__lavishLayoutGateEscape={arm:armGate,cancel:cancelGate,reveal:reveal,manualReveal:manualReveal,showBypass:showBypass,end:function(){ended=true;cancelGate();},isEnded:function(){return ended;},isManuallyBypassed:function(){return manual;}};
10046
+ a=document.getElementById("layoutGateAction");
10047
+ if(ended){reveal();b=document.getElementById("endedOverlay");if(b)b.hidden=false;}
10048
+ if(a&&!ended)a.onclick=manualReveal;
10049
+ if(o&&!o.hidden&&!ended)armGate(${CHROME_LAYOUT_GATE_MAX_HOLD_MS});
10026
10050
  window.__lavishCancelChromeBootFailsafe=function(){clearTimeout(t);};
10027
10051
  window.__lavishChromeBootFailed=function(){clearTimeout(t);fail();};
10028
10052
  function fail(){
10029
- if(window.__lavishChromeReady)return;
10030
- o=document.getElementById("layoutGateOverlay");
10053
+ if(window.__lavishChromeReady||ended)return;
10031
10054
  h=document.getElementById("layoutGateTitle");
10032
10055
  c=document.getElementById("layoutGateCopy");
10033
10056
  a=document.getElementById("layoutGateAction");
10034
10057
  if(h)h.textContent="Lavish could not finish loading.";
10035
10058
  if(c)c.textContent="The Lavish editor script did not load. The server usually restarted while this page was opening. Check and reload to reconnect.";
10036
10059
  if(a){a.textContent="Check and reload";a.disabled=false;a.onclick=check;}
10060
+ showBypass();
10037
10061
  if(o)o.hidden=false;
10038
10062
  if(document.body)document.body.classList.add("layout-gate-active");
10063
+ armGate(${CHROME_LAYOUT_GATE_MAX_HOLD_MS});
10039
10064
  }
10040
10065
  function check(){
10041
10066
  if(a)a.disabled=true;
@@ -10102,7 +10127,7 @@ ${faviconTag}
10102
10127
  <div class="bar"><div class="brand"><span class="brand-mark">Lavish</span><span class="brand-support">Editor</span></div><div class="spacer" aria-hidden="true"></div><div class="warnings-wrap" id="warningsWrap" hidden><button class="warnings-button" id="warningsButton" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="warningsDrawer">${chromeIcons.warning}<span class="warnings-count" id="warningsCount">0</span></button><div class="menu warnings-drawer" id="warningsDrawer" role="dialog" aria-labelledby="warningsTitle" aria-describedby="warningsSummary" hidden><div class="warnings-head"><h2 class="warnings-title" id="warningsTitle">Layout issues</h2><p class="warnings-summary" id="warningsSummary"></p></div><div class="warnings-toolbar"><label class="warnings-selectall"><input type="checkbox" id="warningsSelectAll"><span>Select all</span></label><span class="warnings-selected" id="warningsSelected" role="status" aria-live="polite"></span></div><div class="warnings-list" id="warningsList"></div><div class="warnings-foot"><p class="warnings-note">Queueing sends a repair request with your next feedback. An issue is marked resolved only after a newer artifact load and a complete check at the same viewport no longer finds it.</p><button class="button" id="warningsQueueButton" type="button" disabled>Queue selected fixes</button></div></div></div><button class="annotate-switch" id="annotation" type="button" aria-pressed="true" title="${escapeHtml(modeToggleHint)}"><span class="switch-track" aria-hidden="true"><span class="switch-knob"></span></span><span>Annotate</span></button><div class="more-wrap" id="moreWrap"><button class="more-button" id="moreButton" type="button" title="More" aria-haspopup="menu" aria-expanded="false">${chromeIcons.more}</button><div class="menu more-menu" id="moreMenu" hidden><div class="menu-head"><div class="menu-label">Editing</div><button class="menu-file" id="copyPath" type="button" title="Copy path \xB7 ${escapeHtml(session.file)}">${chromeIcons.file}<span class="menu-file-text"><span class="path-head">${escapeHtml(pathHead)}</span><span class="path-tail">${escapeHtml(pathTail)}</span></span><span class="copy-hint" id="copyHint"><span class="icon-copy">${chromeIcons.copy}</span><span class="icon-check">${chromeIcons.check}</span><span id="copyHintText">Copy</span></span></button></div><div class="menu-rule"></div><button class="menu-item" id="reloadArtifact" type="button">${chromeIcons.refresh}<span>Reload artifact</span></button><button class="menu-item" id="copySnapshot" type="button">${chromeIcons.camera}<span>Copy DOM snapshot</span></button><button class="menu-item" id="exportArtifact" type="button">${chromeIcons.download}<span>Export standalone HTML</span></button><button class="menu-item" id="shareArtifact" type="button">${chromeIcons.globe}<span>Publish link</span></button><div class="menu-rule"></div><button class="menu-item danger" id="end" type="button">${chromeIcons.exit}<span>End session</span></button></div></div></div>
10103
10128
  <div class="layout"><div class="frame"><iframe id="artifact" sandbox="allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads" data-artifact-src="/artifact/${session.key}/index.html"></iframe></div><div class="panel-scrim" id="panelScrim"></div><aside class="panel" id="panel"><div class="panel-head" id="panelHead"><span class="panel-handle" aria-hidden="true"></span><div class="panel-head-row"><h2>Conversation</h2><span class="panel-summary" id="panelSummary" role="status" aria-live="polite"></span><button class="panel-toggle" id="panelToggle" type="button" aria-expanded="false" aria-controls="panel" aria-label="Show conversation">${chromeIcons.chevronUp}</button></div></div><div class="panel-scroll" id="panelScroll"><div class="chat" id="chatLog"></div><div class="annotation-pills" id="annotationPills"></div></div><div class="composer" id="chatComposer"><div class="presence-banner handoff-banner" id="handoffBanner" hidden><span>This review is open in another Lavish tab.</span><button class="handoff-takeover" id="handoffTakeover" type="button">Take over here</button></div><div class="presence-banner handoff-banner" id="outdatedBanner" hidden><span id="outdatedText">The Lavish server this page was connected to is no longer running. Reloading will work once it is running again.</span><span class="outdated-actions"><button class="handoff-takeover" id="outdatedReload" type="button">Check and reload</button><button class="handoff-takeover" id="outdatedDismiss" type="button">Dismiss</button></span></div><div class="presence-banner" id="presenceBanner" hidden>Your agent is not listening. If this persists, ask your agent to poll for updates from Lavish.</div><textarea id="chatInput" placeholder="Write a message for the agent..."></textarea><div class="chat-attachments" id="chatAttachments"></div><div class="chat-attachment-toolbar"><button class="chat-attach" id="chatAttach" type="button">Attach images</button><input id="chatAttachInput" type="file" accept="${escapeHtml(acceptedMime.join(","))}" multiple hidden><span class="chat-attachment-notice" id="chatAttachmentNotice" role="status"></span></div><div class="send-hint" id="sendHint" hidden>Write a message or annotate an element first.</div><div class="actions" id="sendActions"><button class="button button-danger" id="sendAndEnd" type="button">${chromeIcons.exit}<span>Send &amp; End</span></button><button class="button" id="send">Send to Agent</button></div></div></aside></div>
10104
10129
  <div class="share-overlay" id="shareDialog" role="dialog" aria-modal="true" aria-labelledby="shareTitleText" hidden><form class="share-card" id="shareForm"><div class="share-head"><div><div class="share-kicker">Publish to <a class="share-link" href="https://ht-ml.app" target="_blank" rel="noopener noreferrer">ht-ml.app</a></div><h2 id="shareTitleText">Publish artifact</h2></div><button class="share-close" id="shareClose" type="button" aria-label="Close publish dialog"><svg width="14" height="14" viewBox="0 0 10 10" fill="none" aria-hidden="true" focusable="false"><path d="M1 1L9 9M9 1L1 9" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button></div><p class="share-note">ht-ml.app is a separate, third-party hosting service, not part of Lavish. Publishing sends this artifact to its servers.</p><p class="share-copy">This uploads this artifact to ht-ml.app with local assets inlined. Without a password, the page is PUBLIC and anyone with the link can open it. With a password, the page is PRIVATE and viewers must supply the password to view.</p><p class="share-note">Do not publish secrets. The Lavish annotation SDK is not included.</p><div class="share-grid"><label>Password (optional)<input id="sharePassword" name="password" type="password" autocomplete="new-password" placeholder="Leave blank for a public page"></label></div><div class="share-status" id="shareStatus" role="status"></div><div class="share-result" id="shareResult" hidden><label>Share URL<div class="share-copy-row"><input id="shareUrl" readonly><button class="share-copy-btn" id="copyShareUrl" type="button">Copy URL</button></div></label><label>Update key (secret)<div class="share-copy-row"><input id="shareUpdateKey" readonly><button class="share-copy-btn" id="copyUpdateKey" type="button">Copy key</button></div></label><p class="share-note">Keep the update key private. ht-ml.app returns it once and it is the only way to update or delete this page later.</p></div><div class="share-actions"><button class="share-cancel" id="shareCancel" type="button">Cancel</button><button class="button" id="sharePublish" type="submit">Publish</button></div></form></div>
10105
- <div class="ended-overlay layout-gate-overlay" id="layoutGateOverlay"${layoutGateHidden}><div class="ended-card"><div class="ended-title" id="layoutGateTitle">Checking layout.<br>One moment.</div><p class="ended-copy" id="layoutGateCopy">Lavish is waiting for fonts and final geometry before revealing this artifact.</p><button class="button ended-action" id="layoutGateAction" type="button">Show anyway</button></div></div>
10130
+ <div class="ended-overlay layout-gate-overlay" id="layoutGateOverlay"${layoutGateHidden}><div class="ended-card"><div class="ended-title" id="layoutGateTitle">Checking layout.<br>One moment.</div><p class="ended-copy" id="layoutGateCopy">Lavish is waiting for fonts and final geometry before revealing this artifact.</p><button class="button ended-action" id="layoutGateAction" type="button">Show anyway</button><button class="button ended-action layout-gate-bypass" id="layoutGateBypass" type="button" hidden>Show anyway</button></div></div>
10106
10131
  <div class="ended-overlay" id="endedOverlay" hidden><div class="ended-card"><div class="ended-title">Session ended.<br>Return to your agent to continue.</div><p class="ended-copy">${escapeHtml(session.file)}</p></div></div>
10107
10132
  <div class="whiteboard-overlay" id="whiteboardOverlay" hidden><div class="whiteboard-shell"><div class="whiteboard-error" id="whiteboardError" hidden></div><button class="whiteboard-close" id="whiteboardClose" type="button" aria-label="Close whiteboard"><svg width="14" height="14" viewBox="0 0 10 10" fill="none" aria-hidden="true" focusable="false"><path d="M1 1L9 9M9 1L1 9" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button><iframe id="whiteboardFrame" title="Excalidraw whiteboard" sandbox="allow-scripts allow-popups"></iframe></div></div>
10108
10133
  <script id="lavish-session" type="application/json">${sessionJson}</script>
@@ -10369,7 +10394,7 @@ var POLL_WAKE_PATH_RULES = Object.freeze([
10369
10394
  ]);
10370
10395
  var POLL_SEND_AND_END_RULE = "`Send & End` ends the session. Its final feedback is still delivered once. After that response, polling stops, and the agent must not reopen the session uninvited.";
10371
10396
  var CODEX_POLL_WAKE_PATH_GUIDANCE = "Codex detected: completed background tasks may not resume Codex automatically, so keep the poll attached to the active turn.";
10372
- var VERSION = "0.1.57";
10397
+ var VERSION = "0.1.59";
10373
10398
  function detectInvokingAgent(env = process.env) {
10374
10399
  return ["CODEX_SANDBOX", "CODEX_THREAD_ID"].some((key) => Object.hasOwn(env, key)) ? "codex" : "generic";
10375
10400
  }
@@ -10475,10 +10500,10 @@ function createHomeOutput({ bin, sessions, includeSessions = true, agent = "gene
10475
10500
  } : {},
10476
10501
  visual_guidance: [
10477
10502
  "Use visual hierarchy to make the most important decisions, risks, tradeoffs, and next actions obvious at a glance",
10478
- "Use visual structure such as sections, cards, tables, diagrams, annotated snippets, and side-by-side comparisons instead of long prose",
10503
+ "Show, don't tell: explain concepts, flows, relationships, and comparisons with labeled illustrations - hand-authored inline SVG, per the diagram playbook - and show existing UI or state with screenshots of the real pages (run the app read-only if needed); reserve prose for what cannot be shown, such as rationale, trade-offs, and open questions",
10504
+ "Structure the prose that remains with sections, cards, tables, annotated snippets, and side-by-side comparisons instead of long paragraphs",
10479
10505
  "Choose typography, spacing, color, and layout deliberately so the artifact has a clear point of view",
10480
- "Prevent horizontal overflow at every nesting level: nested grid/flex children also need minmax(0, 1fr) tracks and min-width: 0, especially when badges, labels, or status text use wide pixel or monospace fonts; wrap, truncate, or contain long unbreakable text deliberately",
10481
- "When the artifact would describe existing or current UI or state, show it instead: capture screenshots of the real pages (run the app read-only if needed) and embed them, rather than explaining the current look in prose; reserve prose for what cannot be shown such as rationale, trade-offs, and open questions"
10506
+ "Prevent horizontal overflow at every nesting level: nested grid/flex children also need minmax(0, 1fr) tracks and min-width: 0, especially when badges, labels, or status text use wide pixel or monospace fonts; wrap, truncate, or contain long unbreakable text deliberately"
10482
10507
  ],
10483
10508
  playbooks: listPlaybooks(),
10484
10509
  help: [
@@ -10486,7 +10511,7 @@ function createHomeOutput({ bin, sessions, includeSessions = true, agent = "gene
10486
10511
  "Unless the user specifies another location, create HTML artifacts in the current working directory under `.lavish/`",
10487
10512
  "Lavish serves the html file through a local express.js server. If your html needs to reference other filesystem assets such as images, CSS, fonts, and local scripts, copy them into the same directory as the HTML file, then reference them with relative paths from that directory. Never prepend `/` to those asset paths - root paths won't work",
10488
10513
  `Run \`lavish-axi poll <html-file>\` to wait for user feedback. It long-polls and stays silent until the user sends feedback or ends the session, so leave it running - never kill it. Detected layout issues never return this poll: the browser files them in the user's Layout issues inbox in the Lavish top bar, and they arrive as an ordinary tag "layout-warnings" prompt only when the user selects them and queues the fixes. Never edit the artifact to chase a layout issue the user has not queued. The only exception is a fatal artifact_failures response, which means the review surface itself could not be used. ${pollExecutionGuidance({ agent })} ${POLL_SEND_AND_END_RULE}`,
10489
- 'Rendered Mermaid diagrams in `.mermaid` containers become embedded, editable Excalidraw whiteboards in the browser (click a diagram to unlock editing; a Fullscreen action opens it over the whole viewport) - flowchart, sequence, class, ER, and state diagrams convert to editable shapes; other types embed as an image to draw on. Scenes autosave locally; an unmodified autosave silently re-converts when a reload changes the Mermaid source. If the reviewer edited the scene, they choose to re-convert and discard saved edits or keep editing the saved scene. Standalone and exported copies still render plain Mermaid. Queue feedback adds a prompt to the Conversation panel; when the user sends it, poll returns a tag "whiteboard" prompt carrying a bounded edit summary plus local scenePath (.excalidraw JSON) and previewPath (PNG) files - read the summary first, open the files only when needed, then apply the edits by updating the Mermaid source in the artifact (never try to write the scene back)',
10514
+ 'Mermaid is the whiteboard opt-in, not the diagram default: only when the user asks for an editable whiteboard, author that diagram as Mermaid in a `.mermaid` container. Rendered Mermaid diagrams there become embedded, editable Excalidraw whiteboards in the browser (click a diagram to unlock editing; a Fullscreen action opens it over the whole viewport) - flowchart, sequence, class, ER, and state diagrams convert to editable shapes; other types embed as an image to draw on. Scenes autosave locally; an unmodified autosave silently re-converts when a reload changes the Mermaid source. If the reviewer edited the scene, they choose to re-convert and discard saved edits or keep editing the saved scene. Standalone and exported copies still render plain Mermaid. Queue feedback adds a prompt to the Conversation panel; when the user sends it, poll returns a tag "whiteboard" prompt carrying a bounded edit summary plus local scenePath (.excalidraw JSON) and previewPath (PNG) files - read the summary first, open the files only when needed, then apply the edits by updating the Mermaid source in the artifact (never try to write the scene back)',
10490
10515
  "Run `lavish-axi end <html-file>` to end a session as the agent - ending it this way still allows a plain reopen later. When the user ends it from the browser instead, a later `lavish-axi <html-file>` refuses to reopen it without `--reopen`",
10491
10516
  "Run `lavish-axi export <html-file> [--out <path>]` to write a portable copy of the artifact - one HTML file with its LOCAL assets inlined - so it opens with no Lavish server and no sibling files. Remote CDN/font references are left as links, so it needs network to render those. Users can also export from the browser chrome's overflow menu",
10492
10517
  "Run `lavish-axi share <html-file> [--password <pw>] [--token <t>]` to publish the artifact on ht-ml.app (https://ht-ml.app), a third-party hosting service not part of Lavish, and get back a visitable URL. Shares are PUBLIC by default, so anyone with the link can open them. Pass --password to publish a PRIVATE password-protected page; viewers must supply the password to view. Local assets are inlined; remote refs load over the network. It returns the url plus a secret update_key for managing the page later. Use --token or LAVISH_AXI_HTML_APP_TOKEN only when you have an optional bearer token; it is never required. Users can also publish from the browser chrome's overflow menu",
@@ -11494,7 +11519,7 @@ Examples:
11494
11519
  `,
11495
11520
  design: `Usage: lavish-axi design
11496
11521
 
11497
- Show a copy-pasteable CDN snippet for Tailwind CSS browser runtime v4 + DaisyUI v5 + themes, Mermaid diagram tooling, a content-to-playbook router, an optional layout safety CSS snippet, plus technical reference for DaisyUI components. ${PLAYBOOK_ROUTER_HELP} Lavish artifacts stay portable HTML. This CDN snippet is the design fallback, not the default: inspect the subject project before falling back, and paste the layout safety CSS only when useful for dense nested grid/flex layouts, badges, wide fonts, or local media. ${DESIGN_PRIORITY_RULE}
11522
+ Show a copy-pasteable CDN snippet for Tailwind CSS browser runtime v4 + DaisyUI v5 + themes, the whiteboard (Mermaid) opt-in snippet, a content-to-playbook router, an optional layout safety CSS snippet, plus technical reference for DaisyUI components. ${PLAYBOOK_ROUTER_HELP} Lavish artifacts stay portable HTML. This CDN snippet is the design fallback, not the default: inspect the subject project before falling back, and paste the layout safety CSS only when useful for dense nested grid/flex layouts, badges, wide fonts, or local media. ${DESIGN_PRIORITY_RULE}
11498
11523
  `,
11499
11524
  setup: `Usage: lavish-axi setup hooks
11500
11525
  lavish-axi setup plugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavish-axi",
3
- "version": "0.1.57",
3
+ "version": "0.1.59",
4
4
  "packageManager": "pnpm@11.1.1",
5
5
  "description": "HTML is the new markdown. Lavish is the new editor for your HTML artifacts.",
6
6
  "type": "module",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "lavish-axi",
4
- "version": "0.1.57",
4
+ "version": "0.1.59",
5
5
  "description": "HTML is the new markdown. Lavish is the new editor for your HTML artifacts.",
6
6
  "author": {
7
7
  "name": "Kun Chen",
@@ -52,18 +52,18 @@ Use lavish-axi when the user asks for a visual artifact, HTML explainer, interac
52
52
  ## Visual guidance
53
53
 
54
54
  - Use visual hierarchy to make the most important decisions, risks, tradeoffs, and next actions obvious at a glance
55
- - Use visual structure such as sections, cards, tables, diagrams, annotated snippets, and side-by-side comparisons instead of long prose
55
+ - Show, don't tell: explain concepts, flows, relationships, and comparisons with labeled illustrations - hand-authored inline SVG, per the diagram playbook - and show existing UI or state with screenshots of the real pages (run the app read-only if needed); reserve prose for what cannot be shown, such as rationale, trade-offs, and open questions
56
+ - Structure the prose that remains with sections, cards, tables, annotated snippets, and side-by-side comparisons instead of long paragraphs
56
57
  - Choose typography, spacing, color, and layout deliberately so the artifact has a clear point of view
57
58
  - Prevent horizontal overflow at every nesting level: nested grid/flex children also need minmax(0, 1fr) tracks and min-width: 0, especially when badges, labels, or status text use wide pixel or monospace fonts; wrap, truncate, or contain long unbreakable text deliberately
58
- - When the artifact would describe existing or current UI or state, show it instead: capture screenshots of the real pages (run the app read-only if needed) and embed them, rather than explaining the current look in prose; reserve prose for what cannot be shown such as rationale, trade-offs, and open questions
59
59
 
60
60
  ## Playbooks
61
61
 
62
62
  Run `npx -y lavish-axi playbook <id>` for focused, detailed guidance on any of these.
63
63
  One artifact often combines several playbooks (for example a plan that includes a comparison and a diagram), so MUST open each matching playbook before writing HTML.
64
- For flows, architecture, state, or sequence diagrams, do not hand-build boxes-and-arrows from div/flexbox; open the diagram playbook and use the theme-aware Mermaid snippet from `npx -y lavish-axi design` unless SVG is needed for richly annotated nodes.
64
+ Figures are hand-authored inline SVG by default - open the diagram playbook before drawing, and never build boxes-and-arrows from div/flexbox. Use the Mermaid whiteboard snippet from `npx -y lavish-axi design` only when the user asks for an editable whiteboard.
65
65
 
66
- - `diagram` - Map relationships, flows, state, and architecture
66
+ - `diagram` - Explain relationships, flows, state, architecture, and concepts with illustrations
67
67
  - `table` - Turn dense records into scan-friendly review surfaces
68
68
  - `comparison` - Show options, tradeoffs, and current vs target behavior
69
69
  - `plan` - Explain a product or technical plan before implementation
@@ -77,11 +77,11 @@ For flows, architecture, state, or sequence diagrams, do not hand-build boxes-an
77
77
  - Unless the user specifies another location, create HTML artifacts in the current working directory under `.lavish/`
78
78
  - Lavish serves the html file through a local express.js server. If your html needs to reference other filesystem assets such as images, CSS, fonts, and local scripts, copy them into the same directory as the HTML file, then reference them with relative paths from that directory. Never prepend `/` to those asset paths - root paths won't work
79
79
  - Run `npx -y lavish-axi poll <html-file>` to wait for user feedback. It long-polls and stays silent until the user sends feedback or ends the session, so leave it running - never kill it. Detected layout issues never return this poll: the browser files them in the user's Layout issues inbox in the Lavish top bar, and they arrive as an ordinary tag "layout-warnings" prompt only when the user selects them and queues the fixes. Never edit the artifact to chase a layout issue the user has not queued. The only exception is a fatal artifact_failures response, which means the review surface itself could not be used. Keep the poll in the foreground by default and let it return the feedback directly to the agent. A background poll is allowed only through a harness-native tracked background-job facility whose completion result is guaranteed to resume or notify the same agent. Never use `nohup`, shell `&`, `disown`, redirected fire-and-forget processes, or a detached terminal without an explicit verified callback merely to keep polling alive. If the harness has no completion-aware background facility, use the foreground poll or first wire a verified wake callback into the surrounding supervisor. Do not tell the user the artifact is being monitored until that wake path is live. If the poll gets killed or times out before feedback arrives, re-run it - feedback remains queued until delivery. Poll delivery consumes the response, so read it completely. `Send & End` ends the session. Its final feedback is still delivered once. After that response, polling stops, and the agent must not reopen the session uninvited.
80
- - Rendered Mermaid diagrams in `.mermaid` containers become embedded, editable Excalidraw whiteboards in the browser (click a diagram to unlock editing; a Fullscreen action opens it over the whole viewport) - flowchart, sequence, class, ER, and state diagrams convert to editable shapes; other types embed as an image to draw on. Scenes autosave locally; an unmodified autosave silently re-converts when a reload changes the Mermaid source. If the reviewer edited the scene, they choose to re-convert and discard saved edits or keep editing the saved scene. Standalone and exported copies still render plain Mermaid. Queue feedback adds a prompt to the Conversation panel; when the user sends it, poll returns a tag "whiteboard" prompt carrying a bounded edit summary plus local scenePath (.excalidraw JSON) and previewPath (PNG) files - read the summary first, open the files only when needed, then apply the edits by updating the Mermaid source in the artifact (never try to write the scene back)
80
+ - Mermaid is the whiteboard opt-in, not the diagram default: only when the user asks for an editable whiteboard, author that diagram as Mermaid in a `.mermaid` container. Rendered Mermaid diagrams there become embedded, editable Excalidraw whiteboards in the browser (click a diagram to unlock editing; a Fullscreen action opens it over the whole viewport) - flowchart, sequence, class, ER, and state diagrams convert to editable shapes; other types embed as an image to draw on. Scenes autosave locally; an unmodified autosave silently re-converts when a reload changes the Mermaid source. If the reviewer edited the scene, they choose to re-convert and discard saved edits or keep editing the saved scene. Standalone and exported copies still render plain Mermaid. Queue feedback adds a prompt to the Conversation panel; when the user sends it, poll returns a tag "whiteboard" prompt carrying a bounded edit summary plus local scenePath (.excalidraw JSON) and previewPath (PNG) files - read the summary first, open the files only when needed, then apply the edits by updating the Mermaid source in the artifact (never try to write the scene back)
81
81
  - Run `npx -y lavish-axi end <html-file>` to end a session as the agent - ending it this way still allows a plain reopen later. When the user ends it from the browser instead, a later `npx -y lavish-axi <html-file>` refuses to reopen it without `--reopen`
82
82
  - Run `npx -y lavish-axi export <html-file> [--out <path>]` to write a portable copy of the artifact - one HTML file with its LOCAL assets inlined - so it opens with no Lavish server and no sibling files. Remote CDN/font references are left as links, so it needs network to render those. Users can also export from the browser chrome's overflow menu
83
83
  - Run `npx -y lavish-axi share <html-file> [--password <pw>] [--token <t>]` to publish the artifact on ht-ml.app (https://ht-ml.app), a third-party hosting service not part of Lavish, and get back a visitable URL. Shares are PUBLIC by default, so anyone with the link can open them. Pass --password to publish a PRIVATE password-protected page; viewers must supply the password to view. Local assets are inlined; remote refs load over the network. It returns the url plus a secret update_key for managing the page later. Use --token or LAVISH_AXI_HTML_APP_TOKEN only when you have an optional bearer token; it is never required. Users can also publish from the browser chrome's overflow menu
84
84
  - Run `npx -y lavish-axi stop` to shut down the background server (it also self-stops when idle or after the last session ends with nothing connected)
85
85
  - Run `npx -y lavish-axi playbook <playbook_id>` for focused artifact guidance. One artifact often combines several playbooks (for example a plan that includes a comparison and a diagram), so MUST open each matching playbook before writing HTML.
86
- - Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML: Decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and match that project's design system: Tailwind or theme config, shared CSS variables or design tokens, component library, brand assets, or existing styled pages. If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN, and prefer that CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user. Run `npx -y lavish-axi design` for a content-to-playbook router, a copy-pasteable CDN snippet, a Mermaid CDN snippet/init for diagrams, and the DaisyUI component reference. When you deliver the artifact, state which of the three design sources you used and why.
86
+ - Lavish does not auto-inject any design system - artifacts stay portable so they render identically when opened directly without lavish-axi running. Before writing any HTML: Decide the design direction in this strict priority order, and only move to the next step when the current one truly yields nothing: (1) if the user asked for a specific look or named design system, use that; (2) otherwise you must first inspect the project the artifact is about - the subject or product whose content or UI it represents, which may differ from your current working directory - and match that project's design system: Tailwind or theme config, shared CSS variables or design tokens, component library, brand assets, or existing styled pages. If the artifact previews, proposes, or mocks a specific app's UI, render it in that app's own design system so it faithfully shows the product, even when you are running in a different repo; (3) only when both steps come up empty, use the Lavish-recommended Tailwind CSS browser runtime v4 + DaisyUI v5, available via CDN, and prefer that CDN snippet over hand-writing styles unless explicitly instructed otherwise by the user. Run `npx -y lavish-axi design` for a content-to-playbook router, a copy-pasteable CDN snippet, the whiteboard (Mermaid) opt-in snippet, and the DaisyUI component reference. When you deliver the artifact, state which of the three design sources you used and why.
87
87
  - Use lavish-axi when the user asks for a visual artifact, HTML explainer, interactive prototype, review surface, product or technical plan, comparison, report, or browser-based feedback loop