lavish-axi 0.1.84 → 0.1.86
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 +6 -0
- package/dist/chrome-client.js +44 -3
- package/dist/cli.mjs +401 -108
- package/dist/whiteboard/whiteboard.js +504 -504
- package/package.json +1 -1
- package/plugin.json +1 -1
package/README.md
CHANGED
|
@@ -199,6 +199,7 @@ pnpm link
|
|
|
199
199
|
- **Live reload** - Lavish watches the HTML artifact file by default and preserves review context across reloads: the artifact iframe scroll position, an open annotation card's unsent text, and answers to `data-lavish-question` controls (application-owned form state is left alone). Unsent annotation text also survives a full reload of the review page itself. While a queued layout-issue batch is outstanding, closely spaced saves coalesce so one batch of fixes costs one refresh. To also reload on sibling asset changes, add `data-lavish-live-reload-root` to the root element or `<meta name="lavish-live-reload" content="root">`.
|
|
200
200
|
If the element an unsent annotation was attached to is gone from the artifact for good, Lavish cannot reopen that card, and if the artifact replaces a queued note while you are editing it, the replacement is kept as the artifact sent it. Either way, Lavish writes your edited text into the conversation panel under **Unsent annotation** - selectable, never written over anything you have typed, and kept there across reloads; no note is ever dropped to make room for a newer one, and a note the browser refuses to store says so where it is shown.
|
|
201
201
|
- **Feedback controls** - Native controls (radios, checkboxes, inputs, selects, buttons, labels, disclosure summaries, contenteditable) and custom widgets with an interactive ARIA role (`button`, `checkbox`, `combobox`, `menuitem`, `menuitemcheckbox`, `menuitemradio`, `option`, `radio`, `switch`, `tab`, `treeitem`) are interactive automatically, so they do not need `data-lavish-action`. A link (`<a href>`) stays annotatable when it carries one of these roles or sits inside such a widget.
|
|
202
|
+
Artifacts can start in explore mode with `data-lavish-annotate="off"` on the root `<html>` element so stray clicks do not open annotation cards. Annotation stays on by default (or with `"on"`); unknown values default to on. The toolbar reflects the page default and can override it; the reviewer's choice persists across artifact reloads within that review page. See `lavish-axi playbook input` for author guidance.
|
|
202
203
|
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()`.
|
|
203
204
|
If someone may open a decision artifact as a standalone HTML file, `lavish-axi playbook input` provides an optional Copy all answers control they can use to paste their form answers back into chat.
|
|
204
205
|
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.
|
|
@@ -243,6 +244,10 @@ pnpm link
|
|
|
243
244
|
The agent updates the artifact's Mermaid source, which remains authoritative.
|
|
244
245
|
Flowchart, sequence, class, ER, and state diagrams convert to editable shapes; other diagram types are images that reviewers can draw and annotate.
|
|
245
246
|
Lavish changes only the browser view, so saved, standalone, and exported artifacts still render plain Mermaid.
|
|
247
|
+
- **Whiteboard libraries** - Copy Excalidraw library files (`.excalidrawlib`, for example from [libraries.excalidraw.com](https://libraries.excalidraw.com)) into `~/.lavish-axi/excalidraw-libraries/`, or `LAVISH_AXI_STATE_DIR/excalidraw-libraries/` when set. Every whiteboard's library panel lists their items, and an item you drop on the canvas reaches the agent by name in the edit summary.
|
|
248
|
+
Agents run `lavish-axi libraries` to find item refs and can show a flowchart node as one of those icons by adding a `%% lavish-icon <node-id> <library-id>/<item name>` comment to the Mermaid source. Mermaid ignores the comment, so saved, exported, and shared copies, and whiteboards missing that library, show the plain node.
|
|
249
|
+
Lavish only reads that directory and never downloads libraries. Files over 10 MB, more than 25 MB or 5,000 items in total, and unreadable files are skipped; `lavish-axi libraries` lists each skip with its reason.
|
|
250
|
+
Each library carries its author's terms (AWS Architecture Icons, for example, are covered by AWS's own usage terms), so check them before using one.
|
|
246
251
|
- **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.
|
|
247
252
|
Set `LAVISH_AXI_IDLE_TIMEOUT_MS=0` or `off` to disable idle self-shutdown.
|
|
248
253
|
- **Server upgrades** - One background server serves every session, so upgrading `lavish-axi` while reviews are open makes the next `lavish-axi <html-file>` replace that server. Only the review page for the artifact being opened reloads itself once the replacement answers - and not even that one while you have unsent annotation text open, which gets the same banner instead so the reload is yours to make. Every other open review page keeps working and shows a banner reading "Lavish was updated. This page is running the previous version.", with a Check and reload button and a Dismiss button, so no page you are reading reloads on its own. After `lavish-axi stop` those pages say Lavish was stopped and to reload after you start it again, and a restart that only picks up a local build says that rather than claiming an update. An open review page whose server simply goes away - idle self-shutdown, a crash, a machine that slept - shows that same banner once reconnecting has failed for a few seconds, rather than retrying a dead server silently while looking fine; it clears itself if the server comes back, and dismissing it keeps it dismissed until then.
|
|
@@ -272,6 +277,7 @@ pnpm link
|
|
|
272
277
|
| `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, `--private` locks one behind a generated password, and the same command republishes or unpublishes an existing page with `--site`/`--update-key`. |
|
|
273
278
|
| `lavish-axi stop` | Shut down the background server. |
|
|
274
279
|
| `lavish-axi playbook [id]` | List focused artifact guidance or show one playbook; agents must open each matching playbook before writing HTML. |
|
|
280
|
+
| `lavish-axi libraries` | List the Excalidraw libraries in the whiteboard library directory; `--search <text>` or `--library <id>` lists item refs for `%% lavish-icon` nodes. |
|
|
275
281
|
| `lavish-axi design` | Show agent-facing design guidance, including optional CDN snippets and the whiteboard (Mermaid) opt-in snippet. |
|
|
276
282
|
| `lavish-axi setup hooks` | Install or repair optional SessionStart hooks for Claude Code, Codex, OpenCode, and GitHub Copilot CLI; restart the agent session afterward. |
|
|
277
283
|
| `lavish-axi setup plugin` | Register the installed package as an [Agent Plugin](https://agent-plugins.org) in VS Code, Cursor, and GitHub Copilot CLI; opt-in, idempotent, no marketplace involved. Reload each client afterward. |
|
package/dist/chrome-client.js
CHANGED
|
@@ -193,6 +193,9 @@ const artifactSrc = frame.dataset.artifactSrc || frame.getAttribute?.("data-arti
|
|
|
193
193
|
|
|
194
194
|
const queued = loadQueuedPrompts();
|
|
195
195
|
let annotation = true;
|
|
196
|
+
// Page defaults follow artifact loads until the reviewer explicitly chooses a mode.
|
|
197
|
+
let annotationOverridden = false;
|
|
198
|
+
let annotationDefaultReceived = false;
|
|
196
199
|
let ended = false;
|
|
197
200
|
let agentPresence = "waiting";
|
|
198
201
|
const layoutGateEnabled = sessionData.layoutGateEnabled !== false;
|
|
@@ -3501,6 +3504,7 @@ async function replaceArtifactFrame({ recoveryRetry = false } = {}) {
|
|
|
3501
3504
|
artifactLoadRevision = revision;
|
|
3502
3505
|
artifactLoadToken = token;
|
|
3503
3506
|
artifactSpokeToken = "";
|
|
3507
|
+
annotationDefaultReceived = false;
|
|
3504
3508
|
inlineWhiteboardChannels.clear();
|
|
3505
3509
|
setHandoffSuperseded(false);
|
|
3506
3510
|
startLayoutGateCycle();
|
|
@@ -3574,11 +3578,32 @@ function postToWhiteboard(index, placement, message) {
|
|
|
3574
3578
|
else postToInlineWhiteboard(index, message);
|
|
3575
3579
|
}
|
|
3576
3580
|
|
|
3577
|
-
async function
|
|
3581
|
+
async function fetchMermaidSourcesData() {
|
|
3578
3582
|
const response = await fetch("/api/" + key + "/mermaid-sources");
|
|
3579
3583
|
if (!response.ok) throw new Error("could not read the artifact's Mermaid sources");
|
|
3580
3584
|
const data = await response.json();
|
|
3581
|
-
return
|
|
3585
|
+
return {
|
|
3586
|
+
sources: Array.isArray(data.sources) ? data.sources : [],
|
|
3587
|
+
librariesVersion: typeof data.libraries_version === "string" ? data.libraries_version : "",
|
|
3588
|
+
};
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
async function fetchMermaidSources() {
|
|
3592
|
+
return (await fetchMermaidSourcesData()).sources;
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3595
|
+
// Fetched per init so a frame converts with the libraries its source hash was
|
|
3596
|
+
// computed from; a failed fetch resolves null and the frame converts without
|
|
3597
|
+
// libraries.
|
|
3598
|
+
function fetchWhiteboardLibraries() {
|
|
3599
|
+
return fetch("/api/whiteboard-libraries")
|
|
3600
|
+
.then((response) => (response.ok ? response.json() : null))
|
|
3601
|
+
.then((data) =>
|
|
3602
|
+
Array.isArray(data?.libraries)
|
|
3603
|
+
? { libraries: data.libraries, version: typeof data.version === "string" ? data.version : "" }
|
|
3604
|
+
: null,
|
|
3605
|
+
)
|
|
3606
|
+
.catch(() => null);
|
|
3582
3607
|
}
|
|
3583
3608
|
|
|
3584
3609
|
async function authenticateWhiteboardChannel(token) {
|
|
@@ -3607,11 +3632,17 @@ function whiteboardRecord(index) {
|
|
|
3607
3632
|
|
|
3608
3633
|
async function handleWhiteboardReady(index, mode, isCurrent) {
|
|
3609
3634
|
try {
|
|
3610
|
-
const
|
|
3635
|
+
const librariesRequest = fetchWhiteboardLibraries();
|
|
3636
|
+
const { sources, librariesVersion } = await fetchMermaidSourcesData();
|
|
3611
3637
|
const source = sources.find((item) => item.index === index);
|
|
3612
3638
|
if (!source) throw new Error("this diagram's Mermaid source was not found in the artifact file");
|
|
3613
3639
|
const savedResponse = await fetch("/api/" + key + "/whiteboard/" + index);
|
|
3614
3640
|
const saved = savedResponse.ok ? (await savedResponse.json()).whiteboard : null;
|
|
3641
|
+
const librariesData = await librariesRequest;
|
|
3642
|
+
// A library file that changed between the two requests leaves the frame
|
|
3643
|
+
// with libraries that do not match this source hash: treat that like a
|
|
3644
|
+
// failed fetch, so the conversion's autosave is held.
|
|
3645
|
+
const librariesMatch = Boolean(librariesData) && (!librariesVersion || librariesData.version === librariesVersion);
|
|
3615
3646
|
const record = whiteboardRecord(index);
|
|
3616
3647
|
record.source = String(source.source || "");
|
|
3617
3648
|
record.sourceHash = String(source.hash || "");
|
|
@@ -3624,6 +3655,8 @@ async function handleWhiteboardReady(index, mode, isCurrent) {
|
|
|
3624
3655
|
source: record.source,
|
|
3625
3656
|
sourceHash: record.sourceHash,
|
|
3626
3657
|
saved,
|
|
3658
|
+
libraries: librariesData ? librariesData.libraries : [],
|
|
3659
|
+
librariesUnavailable: !librariesMatch,
|
|
3627
3660
|
theme: whiteboardTheme(),
|
|
3628
3661
|
});
|
|
3629
3662
|
return true;
|
|
@@ -4213,6 +4246,13 @@ window.addEventListener("message", (event) => {
|
|
|
4213
4246
|
return;
|
|
4214
4247
|
}
|
|
4215
4248
|
// The artifact spoke, so it rendered and ran its SDK - there is nothing fatal to probe for.
|
|
4249
|
+
if (msg.type === "lavish:annotationMode" && typeof msg.enabled === "boolean" && !annotationDefaultReceived) {
|
|
4250
|
+
annotationDefaultReceived = true;
|
|
4251
|
+
if (!annotationOverridden && !ended && !terminalSubmission) annotation = msg.enabled;
|
|
4252
|
+
annotationSwitch.setAttribute("aria-pressed", String(annotation && !ended));
|
|
4253
|
+
// Reconcile even if frame load replayed the old mode before this report arrived.
|
|
4254
|
+
postToFrame({ type: "lavish:setAnnotationMode", enabled: annotation && !ended });
|
|
4255
|
+
}
|
|
4216
4256
|
if (msg.type === "lavish:queuePrompt") {
|
|
4217
4257
|
enqueuePrompt(msg.prompt);
|
|
4218
4258
|
// Queued from inside the artifact, where the closed dock is the only sign it landed.
|
|
@@ -4376,6 +4416,7 @@ loadFrame();
|
|
|
4376
4416
|
|
|
4377
4417
|
function toggleAnnotationMode() {
|
|
4378
4418
|
if (ended || terminalSubmission) return;
|
|
4419
|
+
annotationOverridden = true;
|
|
4379
4420
|
annotation = !annotation;
|
|
4380
4421
|
annotationSwitch.setAttribute("aria-pressed", String(annotation));
|
|
4381
4422
|
postToFrame({ type: "lavish:setAnnotationMode", enabled: annotation });
|