lavish-axi 0.1.36 → 0.1.38
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 +3 -2
- package/dist/chrome-client.js +25 -22
- package/dist/chrome.css +44 -24
- package/dist/cli.mjs +42 -8
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -157,12 +157,13 @@ pnpm link
|
|
|
157
157
|
- **Feedback controls** - Native controls (radios, checkboxes, inputs, selects, buttons, labels, disclosure summaries, contenteditable) are interactive automatically, so they do not need `data-lavish-action`.
|
|
158
158
|
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
159
|
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
|
-
|
|
160
|
+
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), while the composer exposes **Send & End** beside **Send to Agent** to submit queued prompts and user-ended attribution together.
|
|
161
162
|
- **Keyboard shortcuts** - In the chrome composer, Enter sends queued prompts and Shift+Enter inserts a newline.
|
|
162
163
|
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.
|
|
163
164
|
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.
|
|
164
165
|
- **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.
|
|
165
|
-
- **Session end etiquette** - Lavish tracks who ended a session: a human clicking **End session** (or **Send &
|
|
166
|
+
- **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.
|
|
166
167
|
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.
|
|
167
168
|
Agent-initiated ends keep reopening normally, same as before.
|
|
168
169
|
`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.
|
package/dist/chrome-client.js
CHANGED
|
@@ -15,14 +15,11 @@ function isModeToggleHotkeyEvent(event) {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const frame = /** @type {HTMLIFrameElement} */ (document.getElementById("artifact"));
|
|
18
|
+
const panelScroll = /** @type {HTMLDivElement} */ (document.getElementById("panelScroll"));
|
|
18
19
|
const annotationPills = /** @type {HTMLDivElement} */ (document.getElementById("annotationPills"));
|
|
19
20
|
const chatLog = /** @type {HTMLDivElement} */ (document.getElementById("chatLog"));
|
|
20
21
|
const chatInput = /** @type {HTMLTextAreaElement} */ (document.getElementById("chatInput"));
|
|
21
22
|
const sendButton = /** @type {HTMLButtonElement} */ (document.getElementById("send"));
|
|
22
|
-
const sendCaret = /** @type {HTMLButtonElement} */ (document.getElementById("sendCaret"));
|
|
23
|
-
const sendActions = /** @type {HTMLDivElement} */ (document.getElementById("sendActions"));
|
|
24
|
-
const sendMenu = /** @type {HTMLDivElement} */ (document.getElementById("sendMenu"));
|
|
25
|
-
const sendFromMenuButton = /** @type {HTMLButtonElement} */ (document.getElementById("sendFromMenu"));
|
|
26
23
|
const sendAndEndButton = /** @type {HTMLButtonElement} */ (document.getElementById("sendAndEnd"));
|
|
27
24
|
const annotationSwitch = /** @type {HTMLButtonElement} */ (document.getElementById("annotation"));
|
|
28
25
|
const moreWrap = /** @type {HTMLDivElement} */ (document.getElementById("moreWrap"));
|
|
@@ -55,7 +52,7 @@ const layoutGateTitle = /** @type {HTMLDivElement} */ (document.getElementById("
|
|
|
55
52
|
const layoutGateCopy = /** @type {HTMLParagraphElement} */ (document.getElementById("layoutGateCopy"));
|
|
56
53
|
const layoutGateAction = /** @type {HTMLButtonElement} */ (document.getElementById("layoutGateAction"));
|
|
57
54
|
const layoutIssueBanner = /** @type {HTMLDivElement} */ (document.getElementById("layoutIssueBanner"));
|
|
58
|
-
const sendHint = /** @type {
|
|
55
|
+
const sendHint = /** @type {HTMLDivElement} */ (document.getElementById("sendHint"));
|
|
59
56
|
const artifactSrc = frame.dataset.artifactSrc || frame.getAttribute?.("data-artifact-src") || frame.src || "";
|
|
60
57
|
|
|
61
58
|
const queued = loadQueuedPrompts();
|
|
@@ -146,12 +143,12 @@ function render() {
|
|
|
146
143
|
closeButton.addEventListener("click", (event) => removeQueuedPrompt(Number(closeButton.dataset.index), event));
|
|
147
144
|
}
|
|
148
145
|
updateSendState();
|
|
146
|
+
scrollPanelToBottom();
|
|
149
147
|
}
|
|
150
148
|
|
|
151
149
|
function updateSendState() {
|
|
152
150
|
sendButton.disabled = ended || agentPresence === "working";
|
|
153
|
-
|
|
154
|
-
sendFromMenuButton.disabled = sendButton.disabled;
|
|
151
|
+
sendAndEndButton.disabled = sendButton.disabled;
|
|
155
152
|
}
|
|
156
153
|
|
|
157
154
|
function showSendHint() {
|
|
@@ -175,7 +172,6 @@ function setMenuOpen(button, menu, open) {
|
|
|
175
172
|
|
|
176
173
|
function closeMenus() {
|
|
177
174
|
setMenuOpen(moreButton, moreMenu, false);
|
|
178
|
-
setMenuOpen(sendCaret, sendMenu, false);
|
|
179
175
|
}
|
|
180
176
|
|
|
181
177
|
function toggleMenu(button, menu) {
|
|
@@ -204,14 +200,15 @@ async function copyText(text) {
|
|
|
204
200
|
return true;
|
|
205
201
|
}
|
|
206
202
|
|
|
207
|
-
function addChat(role, text) {
|
|
203
|
+
function addChat(role, text, shouldScroll = true) {
|
|
208
204
|
if (!text) return;
|
|
209
205
|
|
|
210
206
|
const el = document.createElement("div");
|
|
211
207
|
el.className = "bubble " + role;
|
|
212
208
|
el.innerHTML = "<small>" + (role === "agent" ? "Agent" : "You") + "</small><div>" + escapeHtml(text) + "</div>";
|
|
213
209
|
chatLog.appendChild(el);
|
|
214
|
-
|
|
210
|
+
if (shouldScroll) scrollElementIntoView(el);
|
|
211
|
+
return el;
|
|
215
212
|
}
|
|
216
213
|
|
|
217
214
|
function syncChat(chat) {
|
|
@@ -219,9 +216,14 @@ function syncChat(chat) {
|
|
|
219
216
|
el.remove();
|
|
220
217
|
}
|
|
221
218
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
219
|
+
let lastChatBubble = null;
|
|
220
|
+
for (const item of chat) lastChatBubble = addChat(item.role, item.text, false) || lastChatBubble;
|
|
221
|
+
if (workingBubble) {
|
|
222
|
+
chatLog.appendChild(workingBubble);
|
|
223
|
+
scrollElementIntoView(workingBubble);
|
|
224
|
+
} else if (lastChatBubble) {
|
|
225
|
+
scrollElementIntoView(lastChatBubble);
|
|
226
|
+
}
|
|
225
227
|
}
|
|
226
228
|
|
|
227
229
|
function setAgentPresence(state) {
|
|
@@ -241,7 +243,15 @@ function setAgentPresence(state) {
|
|
|
241
243
|
workingBubble.innerHTML = '<span class="spinner"></span><span>Working...</span>';
|
|
242
244
|
chatLog.appendChild(workingBubble);
|
|
243
245
|
}
|
|
244
|
-
|
|
246
|
+
scrollElementIntoView(workingBubble);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function scrollPanelToBottom() {
|
|
250
|
+
panelScroll.scrollTop = panelScroll.scrollHeight;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function scrollElementIntoView(el) {
|
|
254
|
+
el.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
245
255
|
}
|
|
246
256
|
|
|
247
257
|
function removeQueuedPrompt(index, event) {
|
|
@@ -303,11 +313,7 @@ function sendQueued(endAfter) {
|
|
|
303
313
|
render();
|
|
304
314
|
}
|
|
305
315
|
if (!queued.length) {
|
|
306
|
-
|
|
307
|
-
endSession();
|
|
308
|
-
} else {
|
|
309
|
-
showSendHint();
|
|
310
|
-
}
|
|
316
|
+
showSendHint();
|
|
311
317
|
return;
|
|
312
318
|
}
|
|
313
319
|
hideSendHint();
|
|
@@ -722,9 +728,7 @@ function toggleAnnotationMode() {
|
|
|
722
728
|
annotationSwitch.onclick = toggleAnnotationMode;
|
|
723
729
|
|
|
724
730
|
sendButton.onclick = () => sendQueued(false);
|
|
725
|
-
sendFromMenuButton.onclick = () => sendQueued(false);
|
|
726
731
|
sendAndEndButton.onclick = () => sendQueued(true);
|
|
727
|
-
sendCaret.onclick = () => toggleMenu(sendCaret, sendMenu);
|
|
728
732
|
moreButton.onclick = () => toggleMenu(moreButton, moreMenu);
|
|
729
733
|
chatInput.addEventListener("keydown", (event) => {
|
|
730
734
|
if (event.key === "Enter" && !event.shiftKey && !event.isComposing) {
|
|
@@ -753,7 +757,6 @@ endButton.onclick = () => {
|
|
|
753
757
|
document.addEventListener("mousedown", (event) => {
|
|
754
758
|
const target = /** @type {Node} */ (event.target);
|
|
755
759
|
if (!moreMenu.hidden && !moreWrap.contains(target)) setMenuOpen(moreButton, moreMenu, false);
|
|
756
|
-
if (!sendMenu.hidden && !sendActions.contains(target)) setMenuOpen(sendCaret, sendMenu, false);
|
|
757
760
|
});
|
|
758
761
|
document.addEventListener("keydown", (event) => {
|
|
759
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;
|
|
@@ -639,11 +632,21 @@ body.lavish {
|
|
|
639
632
|
margin: 16px 16px 8px;
|
|
640
633
|
font-weight: var(--w-semi);
|
|
641
634
|
letter-spacing: 0;
|
|
635
|
+
flex: 0 0 auto;
|
|
636
|
+
}
|
|
637
|
+
.panel-scroll {
|
|
638
|
+
flex: 1 1 auto;
|
|
639
|
+
min-width: 0;
|
|
640
|
+
min-height: 0;
|
|
641
|
+
overflow-y: auto;
|
|
642
|
+
overflow-x: hidden;
|
|
643
|
+
display: flex;
|
|
644
|
+
flex-direction: column;
|
|
642
645
|
}
|
|
643
646
|
.chat {
|
|
644
|
-
flex:
|
|
647
|
+
flex: 0 0 auto;
|
|
645
648
|
min-height: 0;
|
|
646
|
-
overflow:
|
|
649
|
+
overflow: visible;
|
|
647
650
|
padding: 0 16px 12px;
|
|
648
651
|
display: flex;
|
|
649
652
|
flex-direction: column;
|
|
@@ -704,9 +707,13 @@ body.lavish {
|
|
|
704
707
|
gap: 8px;
|
|
705
708
|
padding: 12px 16px;
|
|
706
709
|
border-top: var(--hairline-subtle);
|
|
710
|
+
background: var(--bg-panel);
|
|
707
711
|
min-width: 0;
|
|
708
712
|
flex-shrink: 0;
|
|
709
713
|
box-sizing: border-box;
|
|
714
|
+
position: sticky;
|
|
715
|
+
bottom: 0;
|
|
716
|
+
z-index: 4;
|
|
710
717
|
}
|
|
711
718
|
.presence-banner {
|
|
712
719
|
border: 1px solid var(--border);
|
|
@@ -726,9 +733,17 @@ body.lavish {
|
|
|
726
733
|
flex-wrap: wrap;
|
|
727
734
|
gap: 6px;
|
|
728
735
|
min-width: 0;
|
|
736
|
+
padding: 0 16px 12px;
|
|
737
|
+
flex: 0 0 auto;
|
|
738
|
+
}
|
|
739
|
+
.annotation-pills:empty {
|
|
740
|
+
display: none;
|
|
729
741
|
}
|
|
730
742
|
.pill-wrap {
|
|
731
|
-
|
|
743
|
+
display: flex;
|
|
744
|
+
flex-direction: column;
|
|
745
|
+
align-items: flex-start;
|
|
746
|
+
width: min(320px, 100%);
|
|
732
747
|
max-width: 100%;
|
|
733
748
|
}
|
|
734
749
|
.pill {
|
|
@@ -769,11 +784,10 @@ body.lavish {
|
|
|
769
784
|
}
|
|
770
785
|
.pill-tooltip {
|
|
771
786
|
display: none;
|
|
772
|
-
position:
|
|
787
|
+
position: static;
|
|
773
788
|
z-index: 5;
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
width: min(320px, 80vw);
|
|
789
|
+
width: 100%;
|
|
790
|
+
margin-top: 8px;
|
|
777
791
|
border: 1px solid var(--border-strong);
|
|
778
792
|
border-radius: var(--radius-lg);
|
|
779
793
|
background: var(--bg-bar);
|
|
@@ -831,10 +845,10 @@ body.lavish {
|
|
|
831
845
|
align-items: center;
|
|
832
846
|
gap: 8px;
|
|
833
847
|
justify-content: flex-end;
|
|
848
|
+
min-width: 0;
|
|
834
849
|
position: relative;
|
|
835
850
|
}
|
|
836
851
|
.send-hint {
|
|
837
|
-
margin-right: auto;
|
|
838
852
|
color: var(--fg-faint);
|
|
839
853
|
font-size: 11px;
|
|
840
854
|
line-height: 1.35;
|
|
@@ -842,6 +856,12 @@ body.lavish {
|
|
|
842
856
|
.send-hint[hidden] {
|
|
843
857
|
display: none;
|
|
844
858
|
}
|
|
859
|
+
@media (max-width: 320px) {
|
|
860
|
+
.composer {
|
|
861
|
+
padding-left: 12px;
|
|
862
|
+
padding-right: 12px;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
845
865
|
.ended-overlay {
|
|
846
866
|
position: fixed;
|
|
847
867
|
inset: var(--bar-h) 0 0 0;
|
package/dist/cli.mjs
CHANGED
|
@@ -5717,7 +5717,15 @@ async function serve({
|
|
|
5717
5717
|
return;
|
|
5718
5718
|
}
|
|
5719
5719
|
await watchSession(session, watchers, events, logEvent);
|
|
5720
|
-
|
|
5720
|
+
const artifactHtml = await readFile3(session.file, "utf8").catch(() => "");
|
|
5721
|
+
const { faviconTag, title } = extractArtifactHead(artifactHtml);
|
|
5722
|
+
res.type("html").send(
|
|
5723
|
+
createChromeHtml(session, {
|
|
5724
|
+
layoutGateEnabled: shouldEnableLayoutGate(req.query || {}),
|
|
5725
|
+
faviconTag,
|
|
5726
|
+
title: title ? `${title} \xB7 Lavish` : "Lavish Editor"
|
|
5727
|
+
})
|
|
5728
|
+
);
|
|
5721
5729
|
} catch (error) {
|
|
5722
5730
|
next(error);
|
|
5723
5731
|
}
|
|
@@ -6100,9 +6108,7 @@ var chromeIcons = {
|
|
|
6100
6108
|
exit: chromeIcon(
|
|
6101
6109
|
'<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"/>',
|
|
6102
6110
|
15
|
|
6103
|
-
)
|
|
6104
|
-
send: chromeIcon('<path d="M22 2 11 13"/><path d="M22 2 15 22l-4-9-9-4Z"/>', 14),
|
|
6105
|
-
caret: chromeIcon('<path d="m6 9 6 6 6-6"/>', 13, 2)
|
|
6111
|
+
)
|
|
6106
6112
|
};
|
|
6107
6113
|
function displayPathParts(file, home = homedir()) {
|
|
6108
6114
|
const normalizedFile = file.replaceAll("\\", "/");
|
|
@@ -6141,7 +6147,34 @@ function normalizeFlagValue(value) {
|
|
|
6141
6147
|
if (Array.isArray(value)) return normalizeFlagValue(value[0]);
|
|
6142
6148
|
return value === void 0 || value === null ? "" : String(value).trim().toLowerCase();
|
|
6143
6149
|
}
|
|
6144
|
-
|
|
6150
|
+
var LAVISH_DEFAULT_FAVICON = `<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>\u{1F48E}</text></svg>">`;
|
|
6151
|
+
function readTagAttr(tag, name) {
|
|
6152
|
+
const attrRe = /([a-z][\w:-]*)\s*=\s*("([^"]*)"|'([^']*)'|([^\s>]+))/gi;
|
|
6153
|
+
const target = name.toLowerCase();
|
|
6154
|
+
let match;
|
|
6155
|
+
while ((match = attrRe.exec(tag)) !== null) {
|
|
6156
|
+
if (match[1].toLowerCase() === target) {
|
|
6157
|
+
return (match[3] ?? match[4] ?? match[5] ?? "").trim();
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
return "";
|
|
6161
|
+
}
|
|
6162
|
+
function extractArtifactHead(html) {
|
|
6163
|
+
const head = String(html || "").slice(0, 1e4);
|
|
6164
|
+
let faviconTag = LAVISH_DEFAULT_FAVICON;
|
|
6165
|
+
const linkTags = head.match(/<link\b(?:"[^"]*"|'[^']*'|[^"'>])*>/gi) || [];
|
|
6166
|
+
const iconTag = linkTags.find((tag) => /(^|\s)icon(\s|$)/i.test(readTagAttr(tag, "rel")));
|
|
6167
|
+
const iconHref = iconTag ? readTagAttr(iconTag, "href") : "";
|
|
6168
|
+
if (iconHref && /^(data:|https?:|\/\/)/i.test(iconHref)) {
|
|
6169
|
+
const safeHref = iconHref.replace(/&/g, "&").replace(/"/g, """);
|
|
6170
|
+
faviconTag = `<link rel="icon" href="${safeHref}">`;
|
|
6171
|
+
}
|
|
6172
|
+
let title = "";
|
|
6173
|
+
const titleMatch = head.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
6174
|
+
if (titleMatch) title = titleMatch[1].replace(/\s+/g, " ").trim();
|
|
6175
|
+
return { faviconTag, title };
|
|
6176
|
+
}
|
|
6177
|
+
function createChromeHtml(session, { layoutGateEnabled = true, faviconTag = LAVISH_DEFAULT_FAVICON, title = "Lavish Editor" } = {}) {
|
|
6145
6178
|
const sessionJson = jsonScript({
|
|
6146
6179
|
key: session.key,
|
|
6147
6180
|
file: session.file,
|
|
@@ -6159,12 +6192,13 @@ function createChromeHtml(session, { layoutGateEnabled = true } = {}) {
|
|
|
6159
6192
|
<head>
|
|
6160
6193
|
<meta charset="utf-8">
|
|
6161
6194
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6162
|
-
<title
|
|
6195
|
+
<title>${escapeHtml(title)}</title>
|
|
6196
|
+
${faviconTag}
|
|
6163
6197
|
<link rel="stylesheet" href="/chrome.css">
|
|
6164
6198
|
</head>
|
|
6165
6199
|
<body class="${bodyClass}">
|
|
6166
6200
|
<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>
|
|
6167
|
-
<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="chat" id="chatLog"></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><
|
|
6201
|
+
<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>
|
|
6168
6202
|
<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>
|
|
6169
6203
|
<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>
|
|
6170
6204
|
<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>
|
|
@@ -6376,7 +6410,7 @@ function normalizePagePath(path6) {
|
|
|
6376
6410
|
var COMMANDS = /* @__PURE__ */ new Set(["open", "poll", "end", "stop", "server", "playbook", "design", "setup", "export", "share"]);
|
|
6377
6411
|
var RESERVED = new Set(RESERVED_COMMANDS);
|
|
6378
6412
|
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`.";
|
|
6379
|
-
var VERSION = "0.1.
|
|
6413
|
+
var VERSION = "0.1.38";
|
|
6380
6414
|
async function run(argv) {
|
|
6381
6415
|
await ensureStateDir();
|
|
6382
6416
|
const normalizedArgv = normalizeArgv(argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lavish-axi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
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",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"format": "prettier --write .",
|
|
24
24
|
"format:check": "prettier --check .",
|
|
25
25
|
"prepack": "npm run build",
|
|
26
|
+
"prepare": "npm run build",
|
|
26
27
|
"test": "node --test",
|
|
27
28
|
"typecheck": "tsc --noEmit"
|
|
28
29
|
},
|