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 +7 -6
- package/dist/chrome-client.js +3 -16
- package/dist/chrome.css +24 -17
- package/dist/cli.mjs +107 -11
- package/package.json +1 -1
- package/skills/lavish/SKILL.md +2 -1
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
|
|
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)
|
|
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 &
|
|
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
|
|
package/dist/chrome-client.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
407
|
+
.button-danger {
|
|
408
408
|
display: inline-flex;
|
|
409
|
-
align-items:
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
409
|
+
align-items: center;
|
|
410
|
+
gap: 8px;
|
|
411
|
+
background: transparent;
|
|
412
|
+
color: var(--danger);
|
|
413
413
|
}
|
|
414
|
-
.
|
|
415
|
-
|
|
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
|
-
.
|
|
422
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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="
|
|
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 & 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.
|
|
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
package/skills/lavish/SKILL.md
CHANGED
|
@@ -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
|