lavish-axi 0.1.85 → 0.1.87
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 +2 -1
- package/dist/chrome-client.js +12 -0
- package/dist/cli.mjs +25 -7
- 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.
|
|
@@ -249,7 +250,7 @@ pnpm link
|
|
|
249
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.
|
|
250
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.
|
|
251
252
|
Set `LAVISH_AXI_IDLE_TIMEOUT_MS=0` or `off` to disable idle self-shutdown.
|
|
252
|
-
- **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.
|
|
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 a newer CLI replaces an older server: a CLI older than the running server uses it as is, so two installed versions on one machine do not restart each other's server. Releases up to and including 0.1.86 still replace a server of a different version, so this turn-by-turn restart stops only when both installed versions contain this fix. 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.
|
|
253
254
|
A page still using the legacy event stream reloads once when it reconnects to the replacement server, moving itself onto the current transport without retaining an HTTP connection. If unsent annotation text is open, it shows the neutral server-restarted banner instead and waits for you to reload.
|
|
254
255
|
Every **Check and reload** control asks the server whether it is running before it navigates; while nothing answers, the page stays where it is and says so, and a check that gets no answer at all says that instead of guessing.
|
|
255
256
|
In-flight `lavish-axi poll` commands end with an interrupted-poll error and are safe to re-run; queued feedback is never lost, and annotation text you have typed but not queued yet survives the reload as described under **Live reload**.
|
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();
|
|
@@ -4242,6 +4246,13 @@ window.addEventListener("message", (event) => {
|
|
|
4242
4246
|
return;
|
|
4243
4247
|
}
|
|
4244
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
|
+
}
|
|
4245
4256
|
if (msg.type === "lavish:queuePrompt") {
|
|
4246
4257
|
enqueuePrompt(msg.prompt);
|
|
4247
4258
|
// Queued from inside the artifact, where the closed dock is the only sign it landed.
|
|
@@ -4405,6 +4416,7 @@ loadFrame();
|
|
|
4405
4416
|
|
|
4406
4417
|
function toggleAnnotationMode() {
|
|
4407
4418
|
if (ended || terminalSubmission) return;
|
|
4419
|
+
annotationOverridden = true;
|
|
4408
4420
|
annotation = !annotation;
|
|
4409
4421
|
annotationSwitch.setAttribute("aria-pressed", String(annotation));
|
|
4410
4422
|
postToFrame({ type: "lavish:setAnnotationMode", enabled: annotation });
|
package/dist/cli.mjs
CHANGED
|
@@ -224,6 +224,7 @@ var PLAYBOOKS = [
|
|
|
224
224
|
"Show selected state separately from queued state so the user trusts what will be sent back."
|
|
225
225
|
],
|
|
226
226
|
design_rules: [
|
|
227
|
+
`Set data-lavish-annotate="off" on <html> for decision, choice, and form-shaped pages; leave annotation on (the default, or "on") when reviewing a frontend or layout. Unknown values default to on. The toolbar can override the page default and keeps the reviewer's choice across artifact reloads.`,
|
|
227
228
|
"Native controls - radios, checkboxes, text inputs, selects, textareas, buttons, options, labels, disclosure summaries, and contenteditable regions - and custom widgets with an interactive ARIA role (button, checkbox, combobox, menuitem, option, radio, switch, tab, treeitem, and the menuitem variants) are interactive automatically: clicks toggle, focus, and type instead of annotating, so they do not need data-lavish-action. An <a href> stays annotatable when it carries one of these roles or sits inside such a widget. Build choice and option UIs from these whenever you can.",
|
|
228
229
|
"For reversible choices, do not call window.lavish.queuePrompt() from radio change handlers or option click handlers. Those handlers should only update local selected state.",
|
|
229
230
|
"Use a per-question form submit or explicit Queue answer button to read the current values and call window.lavish.queuePrompt() exactly once for the final answer.",
|
|
@@ -5498,7 +5499,8 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
|
|
|
5498
5499
|
function postArtifactMessage(type, payload = {}) {
|
|
5499
5500
|
parent.postMessage({ type, ...payload, artifact_load_token: String(artifactLoadToken || "") }, "*");
|
|
5500
5501
|
}
|
|
5501
|
-
|
|
5502
|
+
const initialAnnotationMode = document.documentElement.getAttribute("data-lavish-annotate") !== "off";
|
|
5503
|
+
let annotationMode = initialAnnotationMode;
|
|
5502
5504
|
let hovered = null;
|
|
5503
5505
|
let selected = null;
|
|
5504
5506
|
let ignoreNextClick = false;
|
|
@@ -6872,6 +6874,7 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
|
|
|
6872
6874
|
return shadow;
|
|
6873
6875
|
}
|
|
6874
6876
|
function closeCard() {
|
|
6877
|
+
const hadCard = activeCardContext !== null;
|
|
6875
6878
|
activeCardContext = null;
|
|
6876
6879
|
if (activeAttachments) {
|
|
6877
6880
|
activeAttachments.destroy();
|
|
@@ -6885,7 +6888,7 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
|
|
|
6885
6888
|
hovered = null;
|
|
6886
6889
|
clearTextHighlight();
|
|
6887
6890
|
selected = null;
|
|
6888
|
-
scheduleReviewStateReport();
|
|
6891
|
+
if (hadCard) scheduleReviewStateReport();
|
|
6889
6892
|
}
|
|
6890
6893
|
function showAnnotationCard(target, options2 = {}) {
|
|
6891
6894
|
cancelPendingDraftRestore();
|
|
@@ -7176,6 +7179,7 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
|
|
|
7176
7179
|
true
|
|
7177
7180
|
);
|
|
7178
7181
|
setAnnotationMode(annotationMode);
|
|
7182
|
+
postArtifactMessage("lavish:annotationMode", { enabled: initialAnnotationMode });
|
|
7179
7183
|
if (document.readyState === "loading") {
|
|
7180
7184
|
document.addEventListener("DOMContentLoaded", startLayoutAudit, { once: true });
|
|
7181
7185
|
} else {
|
|
@@ -12770,7 +12774,7 @@ var AGENT_REPLY_INPUT_LIMIT_BYTES = AGENT_REPLY_JSON_LIMIT_BYTES - AGENT_REPLY_J
|
|
|
12770
12774
|
var AGENT_REPLY_LIMIT_LABEL = "2 MB JSON request limit";
|
|
12771
12775
|
var POLL_STATE_HEADER = "lavish-poll-state";
|
|
12772
12776
|
var CODEX_POLL_WAKE_PATH_GUIDANCE = "Codex detected: completed background tasks may not resume Codex automatically, so keep the poll attached to the active turn.";
|
|
12773
|
-
var VERSION = "0.1.
|
|
12777
|
+
var VERSION = "0.1.87";
|
|
12774
12778
|
function detectInvokingAgent(env = process.env) {
|
|
12775
12779
|
return ["CODEX_SANDBOX", "CODEX_THREAD_ID"].some((key) => Object.hasOwn(env, key)) ? "codex" : "generic";
|
|
12776
12780
|
}
|
|
@@ -14185,7 +14189,7 @@ async function ensureServer({ forceRestart = false, reloadKey = "" } = {}) {
|
|
|
14185
14189
|
}
|
|
14186
14190
|
}
|
|
14187
14191
|
await startServer(port, { alsoListen });
|
|
14188
|
-
const replacedForNetwork = Boolean(existing) && existing.app === "lavish-axi" && existing.network_stale === true && !forceRestart && typeof existing.version === "string" && existing.version
|
|
14192
|
+
const replacedForNetwork = Boolean(existing) && existing.app === "lavish-axi" && existing.network_stale === true && !forceRestart && typeof existing.version === "string" && !isServerOlderThanCli(VERSION, existing.version);
|
|
14189
14193
|
let networkRestarted = replacedForNetwork;
|
|
14190
14194
|
let raceRestarted = false;
|
|
14191
14195
|
let deadline = Date.now() + 5e3;
|
|
@@ -14248,12 +14252,26 @@ function shouldRestartServer(currentVersion, healthBody, forceRestart = false) {
|
|
|
14248
14252
|
if (forceRestart && healthBody.app === "lavish-axi") return true;
|
|
14249
14253
|
if (healthBody.network_stale === true && healthBody.app === "lavish-axi") return true;
|
|
14250
14254
|
if (typeof healthBody.version !== "string" || healthBody.version === "") return true;
|
|
14251
|
-
return healthBody.version
|
|
14255
|
+
return isServerOlderThanCli(currentVersion, healthBody.version);
|
|
14256
|
+
}
|
|
14257
|
+
function isServerOlderThanCli(currentVersion, runningVersion) {
|
|
14258
|
+
if (runningVersion === currentVersion) return false;
|
|
14259
|
+
const current = parseReleaseVersion(currentVersion);
|
|
14260
|
+
const running = parseReleaseVersion(runningVersion);
|
|
14261
|
+
if (!current || !running) return true;
|
|
14262
|
+
for (let index = 0; index < 3; index += 1) {
|
|
14263
|
+
if (running[index] !== current[index]) return running[index] < current[index];
|
|
14264
|
+
}
|
|
14265
|
+
return false;
|
|
14266
|
+
}
|
|
14267
|
+
function parseReleaseVersion(version) {
|
|
14268
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(String(version));
|
|
14269
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
|
|
14252
14270
|
}
|
|
14253
14271
|
function serverReplacementReason(currentVersion, healthBody, forceRestart = false) {
|
|
14254
14272
|
if (!shouldRestartServer(currentVersion, healthBody, forceRestart)) return "";
|
|
14255
14273
|
const runningVersion = healthBody.version;
|
|
14256
|
-
if (typeof runningVersion !== "string" || runningVersion === "" || runningVersion
|
|
14274
|
+
if (typeof runningVersion !== "string" || runningVersion === "" || isServerOlderThanCli(currentVersion, runningVersion)) {
|
|
14257
14275
|
return "upgrade";
|
|
14258
14276
|
}
|
|
14259
14277
|
return forceRestart ? "local-build" : "";
|
|
@@ -14269,7 +14287,7 @@ function shouldKillProcessOnPort(currentVersion, healthBody) {
|
|
|
14269
14287
|
if (!healthBody || typeof healthBody !== "object") return false;
|
|
14270
14288
|
if (typeof healthBody.version !== "string" || healthBody.version === "") return true;
|
|
14271
14289
|
if (healthBody.app !== "lavish-axi") return false;
|
|
14272
|
-
return healthBody.version
|
|
14290
|
+
return isServerOlderThanCli(currentVersion, healthBody.version);
|
|
14273
14291
|
}
|
|
14274
14292
|
async function canControlServerOnPort(baseUrl, healthBody, processMatchesLavish) {
|
|
14275
14293
|
if (!healthBody || typeof healthBody !== "object") return false;
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "lavish-axi",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.87",
|
|
5
5
|
"description": "HTML is the new markdown. Lavish is the new editor for your HTML artifacts.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Kun Chen",
|