lavish-axi 0.1.37 → 0.1.39

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
@@ -137,7 +137,8 @@ pnpm link
137
137
  Before writing HTML, choose a design system in strict priority order: follow a user-requested look first; otherwise 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 Tailwind or theme config, CSS variables or design tokens, component library, brand assets, or existing styled pages.
138
138
  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.
139
139
  Only when both come up empty, run `lavish-axi design` for a copy-pasteable Tailwind CSS v4 + DaisyUI v5 CDN fallback, a content-to-playbook router, and Mermaid diagram tooling.
140
- That fallback guidance recommends DaisyUI's `luxury` theme by default, warns not to `@apply` DaisyUI classes inside Tailwind browser-runtime style blocks, includes an optional layout safety CSS snippet for dense nested grid/flex layouts, and provides a pinned Mermaid CDN snippet with initialization for flows, architecture, state, and sequence diagrams.
140
+ That fallback guidance recommends DaisyUI's `luxury` theme by default, warns not to `@apply` DaisyUI classes inside Tailwind browser-runtime style blocks, includes an optional layout safety CSS snippet for dense nested grid/flex layouts, and provides a pinned, theme-aware Mermaid CDN snippet for flows, architecture, state, and sequence diagrams.
141
+ The Mermaid snippet waits for page styles, chooses its light or dark rendering from the effective page background, and keeps diagrams in sync with page-theme and OS appearance changes.
141
142
  - **Open-time layout gate** - The browser chrome masks each artifact until the real in-iframe layout audit reports no error-severity findings.
142
143
  Warning-only artifacts reveal normally; error findings notify the agent through the same `layout_warnings` poll path and keep the curtain up until a clean reload.
143
144
  The user can click **Show anyway**, and a bounded safety timeout reveals with a persistent layout-issues banner so review is never blocked indefinitely.
@@ -158,17 +159,17 @@ pnpm link
158
159
  For reversible choices, let option clicks update local state, then queue exactly one final answer from a per-question submit or Queue answer button with `window.lavish.queuePrompt()`.
159
160
  Mark only custom (non-native) clickable elements with `data-lavish-action` so Lavish does not annotate them, and use `data-lavish-question` or `queueKey` when pre-send updates for the same question should replace each other.
160
161
  Queued annotation preview pills and chat history share a scrollable Conversation panel above a sticky composer, so long feedback queues do not push the text box or send controls off screen.
161
- The browser chrome keeps editing actions in the overflow menu (copy path, reload artifact, copy DOM snapshot, export standalone HTML, publish link, end session) and can submit queued prompts with **Send & end session**, which sends the prompts and user-ended attribution together.
162
+ The browser chrome keeps editing actions in the overflow menu (copy path, reload artifact, copy DOM snapshot, export standalone HTML, publish link, end session), while the composer exposes **Send & End** beside **Send to Agent** to submit queued prompts and user-ended attribution together.
162
163
  - **Keyboard shortcuts** - In the chrome composer, Enter sends queued prompts and Shift+Enter inserts a newline.
163
164
  In the annotation card, Enter queues the annotation, Shift+Enter inserts a newline, and Ctrl+Enter (Cmd+Enter on macOS) queues it and sends all queued prompts immediately.
164
165
  Cmd+I or Ctrl+I toggles between annotate and explore mode from either the browser chrome or the artifact iframe, including while focus is in a textarea or control.
165
166
  - **Agent presence** - The browser shows when no agent is listening, keeps queued feedback and fresh layout warnings for the next successful `lavish-axi poll` send even across reloads, and only blocks human sends while the agent is working on delivered feedback. The no-timeout poll writes an immediate stderr banner and periodic stderr heartbeats while stdout stays reserved for the final response; if the poll is interrupted or times out, re-run it because queued feedback is never lost.
166
- - **Session end etiquette** - Lavish tracks who ended a session: a human clicking **End session** (or **Send & end session**) in the browser is a user-initiated end, while `lavish-axi end <html-file>` is agent-initiated.
167
+ - **Session end etiquette** - Lavish tracks who ended a session: a human clicking **End session** (or **Send & End**) in the browser is a user-initiated end, while `lavish-axi end <html-file>` is agent-initiated.
167
168
  A plain `lavish-axi <html-file>` after a user-initiated end refuses to reopen the browser and returns guidance instead; pass `--reopen` only when the user asks for further review or something important needs their visual attention.
168
169
  Agent-initiated ends keep reopening normally, same as before.
169
170
  `lavish-axi poll`'s `ended` response and the `feedback` response for the final batch before an end both carry `next_step` guidance telling the agent to stop polling and deliver remaining updates in chat instead of reopening.
170
171
  - **Precise targets** - Text annotations include selected text plus range anchors, so agents are not limited to whole-element selectors.
171
- - **Mermaid diagrams** - Rendered Mermaid diagrams become pannable and zoomable while you explore (drag to pan, scroll to zoom) and freeze when you turn on annotation so a click lands on a single node. Clicking a node annotates the whole node and sends the agent its diagram id, node id, and rendered label instead of just a CSS selector. Lavish only enhances the live render, so the saved HTML still opens identically anywhere.
172
+ - **Mermaid diagrams** - The `lavish-axi design` Mermaid snippet matches diagram rendering to the effective artifact page background and re-renders when a page-theme or OS appearance change alters that appearance. Rendered Mermaid diagrams become pannable and zoomable while you explore (drag to pan, scroll to zoom) and freeze when you turn on annotation so a click lands on a single node. Clicking a node annotates the whole node and sends the agent its diagram id, node id, and rendered label instead of just a CSS selector. Lavish only enhances the live render, so the saved HTML still opens identically anywhere.
172
173
  - **Server cleanup** - The detached server stops after the last session ends when nothing is connected, or after `LAVISH_AXI_IDLE_TIMEOUT_MS` (default 30 minutes) with no browser or poll connections.
173
174
  Set `LAVISH_AXI_IDLE_TIMEOUT_MS=0` or `off` to disable idle self-shutdown.
174
175
  - **Local-first state** - Session state stays under `~/.lavish-axi/` by default, or `LAVISH_AXI_STATE_DIR` when set.
@@ -187,13 +188,13 @@ pnpm link
187
188
  | `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. |
188
189
  | `lavish-axi stop` | Shut down the background server. |
189
190
  | `lavish-axi playbook [id]` | List focused artifact guidance or show one playbook; agents must open each matching playbook before writing HTML. |
190
- | `lavish-axi design` | Show the Tailwind + DaisyUI CDN fallback, content-to-playbook router, Mermaid diagram tooling, `luxury` default theme, DaisyUI `@apply` warning, and layout safety snippet. |
191
+ | `lavish-axi design` | Show the Tailwind + DaisyUI CDN fallback, content-to-playbook router, theme-aware Mermaid diagram tooling, `luxury` default theme, DaisyUI `@apply` warning, and layout safety snippet. |
191
192
  | `lavish-axi setup hooks` | Install or repair optional SessionStart hooks for Claude Code, Codex, OpenCode, and GitHub Copilot CLI; restart the agent session afterward. |
192
193
  | `lavish-axi server` | Run the local Lavish Editor server. |
193
194
 
194
195
  Known playbook IDs: `diagram`, `table`, `comparison`, `plan`, `code`, `input`, `slides`.
195
196
  One artifact often combines several playbooks, such as a plan that includes a comparison and a diagram, so agents must match against each `use_when` trigger and open every matching playbook before writing HTML.
196
- For flows, architecture, state, or sequence diagrams, open the diagram playbook and use the Mermaid tooling from `lavish-axi design` unless SVG is needed for richly annotated nodes; avoid hand-built div/flexbox boxes-and-arrows.
197
+ For flows, architecture, state, or sequence diagrams, open the diagram playbook and use the theme-aware Mermaid tooling from `lavish-axi design` unless SVG is needed for richly annotated nodes; avoid hand-built div/flexbox boxes-and-arrows.
197
198
 
198
199
  ### Flags
199
200
 
@@ -20,10 +20,6 @@ const annotationPills = /** @type {HTMLDivElement} */ (document.getElementById("
20
20
  const chatLog = /** @type {HTMLDivElement} */ (document.getElementById("chatLog"));
21
21
  const chatInput = /** @type {HTMLTextAreaElement} */ (document.getElementById("chatInput"));
22
22
  const sendButton = /** @type {HTMLButtonElement} */ (document.getElementById("send"));
23
- const sendCaret = /** @type {HTMLButtonElement} */ (document.getElementById("sendCaret"));
24
- const sendActions = /** @type {HTMLDivElement} */ (document.getElementById("sendActions"));
25
- const sendMenu = /** @type {HTMLDivElement} */ (document.getElementById("sendMenu"));
26
- const sendFromMenuButton = /** @type {HTMLButtonElement} */ (document.getElementById("sendFromMenu"));
27
23
  const sendAndEndButton = /** @type {HTMLButtonElement} */ (document.getElementById("sendAndEnd"));
28
24
  const annotationSwitch = /** @type {HTMLButtonElement} */ (document.getElementById("annotation"));
29
25
  const moreWrap = /** @type {HTMLDivElement} */ (document.getElementById("moreWrap"));
@@ -56,7 +52,7 @@ const layoutGateTitle = /** @type {HTMLDivElement} */ (document.getElementById("
56
52
  const layoutGateCopy = /** @type {HTMLParagraphElement} */ (document.getElementById("layoutGateCopy"));
57
53
  const layoutGateAction = /** @type {HTMLButtonElement} */ (document.getElementById("layoutGateAction"));
58
54
  const layoutIssueBanner = /** @type {HTMLDivElement} */ (document.getElementById("layoutIssueBanner"));
59
- const sendHint = /** @type {HTMLSpanElement} */ (document.getElementById("sendHint"));
55
+ const sendHint = /** @type {HTMLDivElement} */ (document.getElementById("sendHint"));
60
56
  const artifactSrc = frame.dataset.artifactSrc || frame.getAttribute?.("data-artifact-src") || frame.src || "";
61
57
 
62
58
  const queued = loadQueuedPrompts();
@@ -152,8 +148,7 @@ function render() {
152
148
 
153
149
  function updateSendState() {
154
150
  sendButton.disabled = ended || agentPresence === "working";
155
- sendCaret.disabled = ended || agentPresence === "working";
156
- sendFromMenuButton.disabled = sendButton.disabled;
151
+ sendAndEndButton.disabled = sendButton.disabled;
157
152
  }
158
153
 
159
154
  function showSendHint() {
@@ -177,7 +172,6 @@ function setMenuOpen(button, menu, open) {
177
172
 
178
173
  function closeMenus() {
179
174
  setMenuOpen(moreButton, moreMenu, false);
180
- setMenuOpen(sendCaret, sendMenu, false);
181
175
  }
182
176
 
183
177
  function toggleMenu(button, menu) {
@@ -319,11 +313,7 @@ function sendQueued(endAfter) {
319
313
  render();
320
314
  }
321
315
  if (!queued.length) {
322
- if (endAfter) {
323
- endSession();
324
- } else {
325
- showSendHint();
326
- }
316
+ showSendHint();
327
317
  return;
328
318
  }
329
319
  hideSendHint();
@@ -738,9 +728,7 @@ function toggleAnnotationMode() {
738
728
  annotationSwitch.onclick = toggleAnnotationMode;
739
729
 
740
730
  sendButton.onclick = () => sendQueued(false);
741
- sendFromMenuButton.onclick = () => sendQueued(false);
742
731
  sendAndEndButton.onclick = () => sendQueued(true);
743
- sendCaret.onclick = () => toggleMenu(sendCaret, sendMenu);
744
732
  moreButton.onclick = () => toggleMenu(moreButton, moreMenu);
745
733
  chatInput.addEventListener("keydown", (event) => {
746
734
  if (event.key === "Enter" && !event.shiftKey && !event.isComposing) {
@@ -769,7 +757,6 @@ endButton.onclick = () => {
769
757
  document.addEventListener("mousedown", (event) => {
770
758
  const target = /** @type {Node} */ (event.target);
771
759
  if (!moreMenu.hidden && !moreWrap.contains(target)) setMenuOpen(moreButton, moreMenu, false);
772
- if (!sendMenu.hidden && !sendActions.contains(target)) setMenuOpen(sendCaret, sendMenu, false);
773
760
  });
774
761
  document.addEventListener("keydown", (event) => {
775
762
  if (event.key === "Escape") {
package/dist/chrome.css CHANGED
@@ -404,25 +404,18 @@ body.lavish {
404
404
  opacity: 0.55;
405
405
  cursor: not-allowed;
406
406
  }
407
- .split {
407
+ .button-danger {
408
408
  display: inline-flex;
409
- align-items: stretch;
410
- }
411
- .send-main {
412
- border-radius: var(--radius-md) 0 0 var(--radius-md);
409
+ align-items: center;
410
+ gap: 8px;
411
+ background: transparent;
412
+ color: var(--danger);
413
413
  }
414
- .send-caret {
415
- border-radius: 0 var(--radius-md) var(--radius-md) 0;
416
- padding: 9px;
417
- border-left: 1px solid rgba(23, 19, 10, 0.22);
418
- display: grid;
419
- place-items: center;
414
+ .button-danger:hover:not(:disabled) {
415
+ background: rgba(240, 100, 100, 0.1);
420
416
  }
421
- .send-menu {
422
- right: 0;
423
- bottom: 44px;
424
- width: 208px;
425
- border-radius: 11px;
417
+ .button-danger > svg {
418
+ flex-shrink: 0;
426
419
  }
427
420
  .share-overlay {
428
421
  position: fixed;
@@ -659,6 +652,14 @@ body.lavish {
659
652
  flex-direction: column;
660
653
  gap: 10px;
661
654
  }
655
+
656
+ .chat:empty::before {
657
+ content: "Agent hasn't sent a message yet. Click an element in the artifact to annotate, or type below to start.";
658
+ margin-top: 2px;
659
+ color: var(--fg-muted);
660
+ font-size: 13px;
661
+ line-height: 1.45;
662
+ }
662
663
  .bubble {
663
664
  max-width: 85%;
664
665
  border-radius: var(--radius-xl);
@@ -852,10 +853,10 @@ body.lavish {
852
853
  align-items: center;
853
854
  gap: 8px;
854
855
  justify-content: flex-end;
856
+ min-width: 0;
855
857
  position: relative;
856
858
  }
857
859
  .send-hint {
858
- margin-right: auto;
859
860
  color: var(--fg-faint);
860
861
  font-size: 11px;
861
862
  line-height: 1.35;
@@ -863,6 +864,12 @@ body.lavish {
863
864
  .send-hint[hidden] {
864
865
  display: none;
865
866
  }
867
+ @media (max-width: 320px) {
868
+ .composer {
869
+ padding-left: 12px;
870
+ padding-right: 12px;
871
+ }
872
+ }
866
873
  .ended-overlay {
867
874
  position: fixed;
868
875
  inset: var(--bar-h) 0 0 0;
package/dist/cli.mjs CHANGED
@@ -34,7 +34,8 @@ var PLAYBOOKS = [
34
34
  design_rules: [
35
35
  "Use page-scoped class names and avoid generic names like .node that can collide with diagram libraries.",
36
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."
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."
38
39
  ],
39
40
  pitfalls: [
40
41
  "Do not cram every file or function into one diagram when a layered explanation would be clearer.",
@@ -283,11 +284,108 @@ var DESIGN_CDN_SNIPPET = `<link rel="stylesheet" href="${DESIGN_CDN_URLS.daisyui
283
284
  var MERMAID_CDN_SNIPPET = `<script type="module">
284
285
  import mermaid from "${MERMAID_CDN_URL}";
285
286
 
286
- mermaid.initialize({
287
- startOnLoad: true,
288
- theme: "base",
289
- securityLevel: "strict",
290
- });
287
+ // Render Mermaid in a theme that matches the artifact page, and re-render when
288
+ // the viewer flips the page theme - Mermaid never restyles an already-rendered
289
+ // SVG on its own, so a fixed theme clashes in either light or dark mode.
290
+ const darkQuery = window.matchMedia("(prefers-color-scheme: dark)");
291
+
292
+ // Normalize any CSS color the browser produces (rgb, oklch, hsl, named, ...)
293
+ // to [r, g, b, a] bytes via a 1x1 canvas, so parsing never breaks on modern
294
+ // color syntaxes like DaisyUI's oklch() values.
295
+ const paint = document.createElement("canvas").getContext("2d");
296
+ function toRgba(color) {
297
+ paint.clearRect(0, 0, 1, 1);
298
+ paint.fillStyle = "#000";
299
+ paint.fillStyle = color;
300
+ paint.fillRect(0, 0, 1, 1);
301
+ return paint.getImageData(0, 0, 1, 1).data;
302
+ }
303
+
304
+ function compositeRgba(foreground, background) {
305
+ const foregroundAlpha = foreground[3] / 255;
306
+ const backgroundAlpha = background[3] / 255;
307
+ const alpha = foregroundAlpha + backgroundAlpha * (1 - foregroundAlpha);
308
+ if (alpha === 0) return [0, 0, 0, 0];
309
+ return [
310
+ (foreground[0] * foregroundAlpha + background[0] * backgroundAlpha * (1 - foregroundAlpha)) / alpha,
311
+ (foreground[1] * foregroundAlpha + background[1] * backgroundAlpha * (1 - foregroundAlpha)) / alpha,
312
+ (foreground[2] * foregroundAlpha + background[2] * backgroundAlpha * (1 - foregroundAlpha)) / alpha,
313
+ alpha * 255,
314
+ ];
315
+ }
316
+
317
+ function pageIsDark() {
318
+ // Trust the actually-rendered page background so this works with any theming
319
+ // mechanism: prefers-color-scheme, a data-theme attribute, or plain CSS.
320
+ const root = document.documentElement;
321
+ const rootBackground = toRgba(getComputedStyle(root).backgroundColor);
322
+ const bodyBackground = document.body ? toRgba(getComputedStyle(document.body).backgroundColor) : [0, 0, 0, 0];
323
+ const [r, g, b, a] = compositeRgba(bodyBackground, rootBackground);
324
+ if (a > 0) {
325
+ return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 < 0.5;
326
+ }
327
+ const colorScheme = getComputedStyle(root).colorScheme;
328
+ if (colorScheme.includes("dark") && !colorScheme.includes("light")) return true;
329
+ if (colorScheme.includes("light") && !colorScheme.includes("dark")) return false;
330
+ return darkQuery.matches;
331
+ }
332
+
333
+ const diagrams = [...document.querySelectorAll(".mermaid")].map((el) => ({ el, src: el.textContent }));
334
+ let applied;
335
+ let rendering = false;
336
+ let queued = false;
337
+ function queueRender() {
338
+ queued = true;
339
+ if (rendering) return;
340
+ void render();
341
+ }
342
+ async function render() {
343
+ rendering = true;
344
+ try {
345
+ while (queued) {
346
+ queued = false;
347
+ const theme = pageIsDark() ? "dark" : "default";
348
+ if (theme === applied) continue;
349
+ mermaid.initialize({ startOnLoad: false, theme, securityLevel: "strict" });
350
+ for (const { el, src } of diagrams) {
351
+ el.removeAttribute("data-processed");
352
+ el.textContent = src;
353
+ }
354
+ try {
355
+ await mermaid.run({ nodes: diagrams.map((d) => d.el) });
356
+ } catch (error) {
357
+ console.error("Mermaid diagram render failed:", error);
358
+ return;
359
+ }
360
+ applied = theme;
361
+ }
362
+ } finally {
363
+ rendering = false;
364
+ if (queued) queueRender();
365
+ }
366
+ }
367
+
368
+ // First render once stylesheets are applied (no wrong-theme flash), then keep
369
+ // the diagrams in sync with page-theme toggles and OS light/dark changes.
370
+ if (document.readyState === "complete") queueRender();
371
+ else window.addEventListener("load", queueRender, { once: true });
372
+ const themeObserver = new MutationObserver(queueRender);
373
+ for (const el of [document.documentElement, document.body]) {
374
+ if (!el) continue;
375
+ themeObserver.observe(el, {
376
+ attributes: true,
377
+ attributeFilter: ["data-theme", "class", "style"],
378
+ });
379
+ }
380
+ document.addEventListener("change", queueRender, true);
381
+ document.addEventListener(
382
+ "transitionend",
383
+ ({ propertyName }) => {
384
+ if (propertyName === "background-color") queueRender();
385
+ },
386
+ true,
387
+ );
388
+ darkQuery.addEventListener("change", queueRender);
291
389
  </script>`;
292
390
  var LAYOUT_SAFETY_CSS_SNIPPET = `<style>
293
391
  *, *::before, *::after { box-sizing: border-box; }
@@ -6108,9 +6206,7 @@ var chromeIcons = {
6108
6206
  exit: chromeIcon(
6109
6207
  '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/>',
6110
6208
  15
6111
- ),
6112
- send: chromeIcon('<path d="M22 2 11 13"/><path d="M22 2 15 22l-4-9-9-4Z"/>', 14),
6113
- caret: chromeIcon('<path d="m6 9 6 6 6-6"/>', 13, 2)
6209
+ )
6114
6210
  };
6115
6211
  function displayPathParts(file, home = homedir()) {
6116
6212
  const normalizedFile = file.replaceAll("\\", "/");
@@ -6200,7 +6296,7 @@ ${faviconTag}
6200
6296
  </head>
6201
6297
  <body class="${bodyClass}">
6202
6298
  <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><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>
6203
- <div class="layout"><div class="frame"><iframe id="artifact" sandbox="allow-scripts allow-forms allow-popups allow-downloads" data-artifact-src="/artifact/${session.key}/index.html"></iframe><div class="layout-issue-banner" id="layoutIssueBanner" hidden>This surface may have layout issues. Your agent has been notified.</div></div><aside class="panel"><h2>Conversation</h2><div class="panel-scroll" id="panelScroll"><div class="chat" id="chatLog"></div><div class="annotation-pills" id="annotationPills"></div></div><div class="composer"><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="actions" id="sendActions"><span class="send-hint" id="sendHint" hidden>Write a message or annotate an element first.</span><div class="split"><button class="button send-main" id="send">Send to Agent</button><button class="button send-caret" id="sendCaret" type="button" title="Send options" aria-haspopup="menu" aria-expanded="false">${chromeIcons.caret}</button></div><div class="menu send-menu" id="sendMenu" hidden><button class="menu-item" id="sendFromMenu" type="button">${chromeIcons.send}<span>Send to Agent</span></button><button class="menu-item danger" id="sendAndEnd" type="button">${chromeIcons.exit}<span>Send &amp; end session</span></button></div></div></div></aside></div>
6299
+ <div class="layout"><div class="frame"><iframe id="artifact" sandbox="allow-scripts allow-forms allow-popups allow-downloads" data-artifact-src="/artifact/${session.key}/index.html"></iframe><div class="layout-issue-banner" id="layoutIssueBanner" hidden>This surface may have layout issues. Your agent has been notified.</div></div><aside class="panel"><h2>Conversation</h2><div class="panel-scroll" id="panelScroll"><div class="chat" id="chatLog"></div><div class="annotation-pills" id="annotationPills"></div></div><div class="composer"><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="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>
6204
6300
  <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>
6205
6301
  <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>
6206
6302
  <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>
@@ -6412,7 +6508,7 @@ function normalizePagePath(path6) {
6412
6508
  var COMMANDS = /* @__PURE__ */ new Set(["open", "poll", "end", "stop", "server", "playbook", "design", "setup", "export", "share"]);
6413
6509
  var RESERVED = new Set(RESERVED_COMMANDS);
6414
6510
  var DESCRIPTION = "Lavish Editor helps agents turn rich HTML artifacts into collaborative human review surfaces. Whenever you are about to give user a complex response that will be easier to understand via a rich / interactive page, consider using Lavish Editor. First generate an interactive HTML artifact according to user request, then run `lavish-axi <html-file>` so the user can visually review it, annotate elements or selected text, queue prompts, and send feedback back through `lavish-axi poll`.";
6415
- var VERSION = "0.1.37";
6511
+ var VERSION = "0.1.39";
6416
6512
  async function run(argv) {
6417
6513
  await ensureStateDir();
6418
6514
  const normalizedArgv = normalizeArgv(argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lavish-axi",
3
- "version": "0.1.37",
3
+ "version": "0.1.39",
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",
@@ -32,6 +32,7 @@ Use lavish-axi when the user asks for a visual artifact, HTML explainer, interac
32
32
  1. Create the HTML artifact (default location `.lavish/<name>.html` in the working directory).
33
33
  2. Run `npx -y lavish-axi <html-file>` to open or resume a review session in the browser.
34
34
  3. Run `npx -y lavish-axi poll <html-file>` to long-poll for the user's annotations, queued prompts, and browser-reported `layout_warnings`.
35
+ On the first poll, prefer `--agent-reply "<one-line summary of what you built and what to review first>"` so the conversation panel opens with context.
35
36
  The poll stays silent until the user acts or the real browser reports fresh layout warnings - leave it running, never kill it.
36
37
  If your harness limits how long a foreground command may run, run the poll as a background task; if it gets killed or times out anyway, just re-run it - queued feedback is never lost.
37
38
  4. If poll returns `layout_warnings`, follow the returned `next_step`: fix and re-check fresh error-severity findings, but proceed with a note instead of looping when every current warning is persistent or low-severity.
@@ -51,7 +52,7 @@ Use lavish-axi when the user asks for a visual artifact, HTML explainer, interac
51
52
 
52
53
  Run `npx -y lavish-axi playbook <id>` for focused, detailed guidance on any of these.
53
54
  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.
54
- For flows, architecture, state, or sequence diagrams, do not hand-build boxes-and-arrows from div/flexbox; open the diagram playbook and use Mermaid unless SVG is needed for richly annotated nodes.
55
+ 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.
55
56
 
56
57
  - `diagram` - Map relationships, flows, state, and architecture
57
58
  - `table` - Turn dense records into scan-friendly review surfaces