lavish-axi 0.1.37 → 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 CHANGED
@@ -158,12 +158,12 @@ pnpm link
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) and can submit queued prompts with **Send & end session**, which sends the prompts and user-ended attribution together.
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.
162
162
  - **Keyboard shortcuts** - In the chrome composer, Enter sends queued prompts and Shift+Enter inserts a newline.
163
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.
164
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.
165
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.
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.
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.
167
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.
168
168
  Agent-initiated ends keep reopening normally, same as before.
169
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.
@@ -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;
@@ -852,10 +845,10 @@ body.lavish {
852
845
  align-items: center;
853
846
  gap: 8px;
854
847
  justify-content: flex-end;
848
+ min-width: 0;
855
849
  position: relative;
856
850
  }
857
851
  .send-hint {
858
- margin-right: auto;
859
852
  color: var(--fg-faint);
860
853
  font-size: 11px;
861
854
  line-height: 1.35;
@@ -863,6 +856,12 @@ body.lavish {
863
856
  .send-hint[hidden] {
864
857
  display: none;
865
858
  }
859
+ @media (max-width: 320px) {
860
+ .composer {
861
+ padding-left: 12px;
862
+ padding-right: 12px;
863
+ }
864
+ }
866
865
  .ended-overlay {
867
866
  position: fixed;
868
867
  inset: var(--bar-h) 0 0 0;
package/dist/cli.mjs CHANGED
@@ -6108,9 +6108,7 @@ var chromeIcons = {
6108
6108
  exit: chromeIcon(
6109
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"/>',
6110
6110
  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)
6111
+ )
6114
6112
  };
6115
6113
  function displayPathParts(file, home = homedir()) {
6116
6114
  const normalizedFile = file.replaceAll("\\", "/");
@@ -6200,7 +6198,7 @@ ${faviconTag}
6200
6198
  </head>
6201
6199
  <body class="${bodyClass}">
6202
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>
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>
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 &amp; End</span></button><button class="button" id="send">Send to Agent</button></div></div></aside></div>
6204
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>
6205
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>
6206
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>
@@ -6412,7 +6410,7 @@ function normalizePagePath(path6) {
6412
6410
  var COMMANDS = /* @__PURE__ */ new Set(["open", "poll", "end", "stop", "server", "playbook", "design", "setup", "export", "share"]);
6413
6411
  var RESERVED = new Set(RESERVED_COMMANDS);
6414
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`.";
6415
- var VERSION = "0.1.37";
6413
+ var VERSION = "0.1.38";
6416
6414
  async function run(argv) {
6417
6415
  await ensureStateDir();
6418
6416
  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.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",