lavish-axi 0.1.58 → 0.1.59
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 -3
- package/dist/chrome-client.js +63 -29
- package/dist/chrome.css +3 -0
- package/dist/cli.mjs +34 -12
- package/package.json +1 -1
- package/plugin.json +1 -1
package/README.md
CHANGED
|
@@ -166,9 +166,9 @@ pnpm link
|
|
|
166
166
|
- **Self-paint warning** - `lavish-axi <html-file>`, `export`, and `share` run a render-free check for artifacts missing an explicit page background and return a one-line `self_paint_warning`.
|
|
167
167
|
The check fails open - any stylesheet link, `@import`, Tailwind runtime script, `color-scheme`, or `html`/`body`/`:root` background signal suppresses it - and it never blocks the open.
|
|
168
168
|
- **Open-time layout gate** - The browser chrome masks an artifact only while the real in-iframe audit waits for fonts and final geometry.
|
|
169
|
-
The first completed check reveals the artifact, whatever it found
|
|
170
|
-
The user can click **Show anyway**, and a bounded safety timeout fails open
|
|
171
|
-
If the review cannot load at all - the chrome's own script never runs, or the server does not answer the artifact's load request after several retries - the mask
|
|
169
|
+
The first completed client-side check reveals the artifact, whatever it found and even if reporting that check to the server fails; the gate never holds the review hostage waiting for a repair or a network round-trip.
|
|
170
|
+
The user can click **Show anyway**, and a bounded safety timeout fails open from every gate state.
|
|
171
|
+
If the review cannot load at all - the chrome's own script never runs, or the server does not answer the artifact's load request after several retries - the mask names the problem and offers **Check and reload** without removing the independent **Show anyway** escape.
|
|
172
172
|
A review already loaded in another browser tab is named the same way, with a **Take over here** button that moves it into the current tab, because Lavish loads an artifact in one tab at a time.
|
|
173
173
|
- **Layout issues inbox** - Detection is passive. After fonts and finite animations settle, the injected SDK confirms severe failures from direct rendered evidence such as materially escaped meaningful content or required controls, clipped text fragments, viewport reachability, or near-total semantic occlusion.
|
|
174
174
|
Explicit ellipsis and line clamp, standard visually hidden accessibility text, intentional scrollers or masks, parent overhang, generic element scroll geometry, decorative overlap, and uncertain motion do not produce findings by themselves.
|
package/dist/chrome-client.js
CHANGED
|
@@ -151,6 +151,8 @@ const layoutGateOverlay = /** @type {HTMLDivElement} */ (document.getElementById
|
|
|
151
151
|
const layoutGateTitle = /** @type {HTMLDivElement} */ (document.getElementById("layoutGateTitle"));
|
|
152
152
|
const layoutGateCopy = /** @type {HTMLParagraphElement} */ (document.getElementById("layoutGateCopy"));
|
|
153
153
|
const layoutGateAction = /** @type {HTMLButtonElement} */ (document.getElementById("layoutGateAction"));
|
|
154
|
+
const layoutGateBypass = /** @type {HTMLButtonElement} */ (document.getElementById("layoutGateBypass"));
|
|
155
|
+
const layoutGateEscape = /** @type {any} */ (window).__lavishLayoutGateEscape;
|
|
154
156
|
const warningsWrap = /** @type {HTMLDivElement} */ (document.getElementById("warningsWrap"));
|
|
155
157
|
const warningsButton = /** @type {HTMLButtonElement} */ (document.getElementById("warningsButton"));
|
|
156
158
|
const warningsCount = /** @type {HTMLSpanElement} */ (document.getElementById("warningsCount"));
|
|
@@ -185,7 +187,6 @@ let outdatedReloadInFlight = false;
|
|
|
185
187
|
let unrestorableDraftMiss = null;
|
|
186
188
|
let retiredDrafts = loadRetiredDrafts();
|
|
187
189
|
let layoutGateVisible = false;
|
|
188
|
-
let layoutGateArmed = false;
|
|
189
190
|
let layoutGateManuallyBypassed = !layoutGateEnabled;
|
|
190
191
|
let layoutGateFailureActive = false;
|
|
191
192
|
// A failure only the user can retire. The artifact-load card clears itself once a load succeeds;
|
|
@@ -1261,6 +1262,7 @@ function normalizeLayoutFindings(value) {
|
|
|
1261
1262
|
}
|
|
1262
1263
|
|
|
1263
1264
|
function clearLayoutGateTimer() {
|
|
1265
|
+
layoutGateEscape?.cancel?.();
|
|
1264
1266
|
if (layoutGateTimer) clearTimeout(layoutGateTimer);
|
|
1265
1267
|
layoutGateTimer = undefined;
|
|
1266
1268
|
}
|
|
@@ -1289,7 +1291,8 @@ function setLayoutGateActive(active) {
|
|
|
1289
1291
|
// its own: the artifact never loaded and retrying stopped helping. The overlay is reused because
|
|
1290
1292
|
// it already covers the empty artifact area; without this the user is left looking at either a
|
|
1291
1293
|
// spinner that never resolves or a blank frame, with nothing explaining it and nothing to click.
|
|
1292
|
-
// Bumping the cycle
|
|
1294
|
+
// Bumping the cycle invalidates the previous timer; setLayoutGateFailure immediately replaces it
|
|
1295
|
+
// with a fresh hold timer so the card cannot strand the visual gate.
|
|
1293
1296
|
function setLayoutGateFailure(title, copy, actionLabel = "Reload", onAction, { sticky = false } = {}) {
|
|
1294
1297
|
if (ended) return;
|
|
1295
1298
|
// A sticky card is the user's to retire, and that has to hold against being overwritten as
|
|
@@ -1299,8 +1302,9 @@ function setLayoutGateFailure(title, copy, actionLabel = "Reload", onAction, { s
|
|
|
1299
1302
|
layoutGateFailureActive = true;
|
|
1300
1303
|
layoutGateFailureSticky = sticky;
|
|
1301
1304
|
layoutGateCycle += 1;
|
|
1302
|
-
|
|
1303
|
-
|
|
1305
|
+
// Failure copy must not disable the visual gate's own recovery paths. Keep a fresh hold timer
|
|
1306
|
+
// over the card so a server replacement or any other failure cannot strand the artifact behind
|
|
1307
|
+
// a sticky message forever.
|
|
1304
1308
|
if (layoutGateTitle) layoutGateTitle.textContent = title;
|
|
1305
1309
|
if (layoutGateCopy) layoutGateCopy.textContent = copy;
|
|
1306
1310
|
if (layoutGateAction) {
|
|
@@ -1308,7 +1312,12 @@ function setLayoutGateFailure(title, copy, actionLabel = "Reload", onAction, { s
|
|
|
1308
1312
|
layoutGateAction.textContent = actionLabel;
|
|
1309
1313
|
layoutGateAction.onclick = onAction || (() => location.reload());
|
|
1310
1314
|
}
|
|
1315
|
+
if (layoutGateBypass) {
|
|
1316
|
+
layoutGateBypass.hidden = false;
|
|
1317
|
+
layoutGateBypass.onclick = () => forceRevealLayoutGate("manual");
|
|
1318
|
+
}
|
|
1311
1319
|
setLayoutGateActive(true);
|
|
1320
|
+
armLayoutGateTimer();
|
|
1312
1321
|
}
|
|
1313
1322
|
|
|
1314
1323
|
// Every failure card in this feature is raised in a state where the server may not be listening,
|
|
@@ -1362,35 +1371,31 @@ function clearLayoutGateFailure() {
|
|
|
1362
1371
|
layoutGateAction.textContent = "Show anyway";
|
|
1363
1372
|
layoutGateAction.onclick = () => forceRevealLayoutGate("manual");
|
|
1364
1373
|
}
|
|
1374
|
+
if (layoutGateBypass) layoutGateBypass.hidden = true;
|
|
1365
1375
|
revealLayoutGate();
|
|
1366
1376
|
}
|
|
1367
1377
|
|
|
1368
1378
|
function revealLayoutGate() {
|
|
1369
|
-
if (layoutGateFailureSticky) return;
|
|
1370
1379
|
clearLayoutGateTimer();
|
|
1371
|
-
|
|
1380
|
+
layoutGateEscape?.reveal?.();
|
|
1372
1381
|
setLayoutGateActive(false);
|
|
1373
1382
|
}
|
|
1374
1383
|
|
|
1375
1384
|
function forceRevealLayoutGate(reason) {
|
|
1376
|
-
if (
|
|
1377
|
-
if (reason === "manual")
|
|
1385
|
+
if (ended) return;
|
|
1386
|
+
if (reason === "manual") {
|
|
1387
|
+
layoutGateManuallyBypassed = true;
|
|
1388
|
+
layoutGateEscape?.manualReveal?.();
|
|
1389
|
+
}
|
|
1378
1390
|
revealLayoutGate();
|
|
1379
1391
|
}
|
|
1380
1392
|
|
|
1381
|
-
function
|
|
1382
|
-
clearLayoutGateFailure();
|
|
1383
|
-
// A sticky failure owns the overlay until the user acts on it, so a later load must not repaint
|
|
1384
|
-
// the checking card over the message it left there.
|
|
1385
|
-
if (layoutGateFailureSticky) return;
|
|
1386
|
-
if (!layoutGateEnabled || layoutGateManuallyBypassed || ended) return;
|
|
1387
|
-
|
|
1388
|
-
layoutGateCycle += 1;
|
|
1389
|
-
layoutGateArmed = true;
|
|
1390
|
-
setLayoutGateCard("checking");
|
|
1391
|
-
setLayoutGateActive(true);
|
|
1393
|
+
function armLayoutGateTimer() {
|
|
1392
1394
|
clearLayoutGateTimer();
|
|
1393
|
-
|
|
1395
|
+
if (layoutGateEscape?.arm) {
|
|
1396
|
+
layoutGateEscape.arm(layoutGateMaxHoldMs, () => forceRevealLayoutGate("timeout"));
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1394
1399
|
const cycle = layoutGateCycle;
|
|
1395
1400
|
layoutGateTimer = setTimeout(() => {
|
|
1396
1401
|
if (cycle !== layoutGateCycle || !layoutGateVisible || ended) return;
|
|
@@ -1399,22 +1404,35 @@ function startLayoutGateCycle() {
|
|
|
1399
1404
|
layoutGateTimer?.unref?.();
|
|
1400
1405
|
}
|
|
1401
1406
|
|
|
1407
|
+
function startLayoutGateCycle() {
|
|
1408
|
+
clearLayoutGateFailure();
|
|
1409
|
+
if (!layoutGateEnabled || layoutGateManuallyBypassed || ended) return;
|
|
1410
|
+
|
|
1411
|
+
layoutGateCycle += 1;
|
|
1412
|
+
setLayoutGateActive(true);
|
|
1413
|
+
// A sticky failure owns the card copy, but never the reveal. Do not repaint it as a checking
|
|
1414
|
+
// card, and do arm a fresh timer for reloads that happen while the sticky card is present.
|
|
1415
|
+
if (!layoutGateFailureSticky) setLayoutGateCard("checking");
|
|
1416
|
+
armLayoutGateTimer();
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1402
1419
|
// The gate only waits for fonts and final geometry now. It never holds the artifact hostage
|
|
1403
1420
|
// pending an agent repair: findings are the user's to triage, so a completed pass always reveals
|
|
1404
1421
|
// and hands the result to the passive inbox.
|
|
1405
1422
|
function handleLayoutGatePass() {
|
|
1406
|
-
if (
|
|
1407
|
-
if (!layoutGateArmed && !layoutGateVisible) return;
|
|
1423
|
+
if (ended || !layoutGateVisible) return;
|
|
1408
1424
|
revealLayoutGate();
|
|
1409
1425
|
}
|
|
1410
1426
|
|
|
1411
1427
|
function initializeLayoutGate() {
|
|
1428
|
+
if (layoutGateEscape?.isManuallyBypassed?.()) layoutGateManuallyBypassed = true;
|
|
1412
1429
|
if (!layoutGateEnabled) {
|
|
1413
1430
|
setLayoutGateActive(false);
|
|
1414
1431
|
return;
|
|
1415
1432
|
}
|
|
1416
1433
|
|
|
1417
1434
|
if (layoutGateAction) layoutGateAction.onclick = () => forceRevealLayoutGate("manual");
|
|
1435
|
+
if (layoutGateBypass) layoutGateBypass.onclick = () => forceRevealLayoutGate("manual");
|
|
1418
1436
|
startLayoutGateCycle();
|
|
1419
1437
|
}
|
|
1420
1438
|
|
|
@@ -1801,6 +1819,7 @@ function markSessionEnded() {
|
|
|
1801
1819
|
layoutGateManuallyBypassed = true;
|
|
1802
1820
|
layoutGateFailureSticky = false;
|
|
1803
1821
|
revealLayoutGate();
|
|
1822
|
+
layoutGateEscape?.end?.();
|
|
1804
1823
|
postToFrame({ type: "lavish:setAnnotationMode", enabled: false });
|
|
1805
1824
|
endedOverlay.hidden = false;
|
|
1806
1825
|
}
|
|
@@ -2737,14 +2756,24 @@ window.addEventListener("message", (event) => {
|
|
|
2737
2756
|
|
|
2738
2757
|
const msg = event.data || {};
|
|
2739
2758
|
const messageToken = String(msg.artifact_load_token || "");
|
|
2740
|
-
if (messageToken !== artifactLoadToken)
|
|
2759
|
+
if (messageToken !== artifactLoadToken) {
|
|
2760
|
+
// A pass can be stamped by the load that just lost a token race. Ask the current artifact
|
|
2761
|
+
// document to run the audit again instead of consuming the only pass for this cycle.
|
|
2762
|
+
if (msg.type === "lavish:layoutDiagnostics") postToFrame({ type: "lavish:requestLayoutDiagnostics" });
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2741
2765
|
const messageSequence = ++artifactMessageSequence;
|
|
2742
2766
|
artifactSpokeToken = messageToken;
|
|
2743
2767
|
clearTimeout(artifactSilenceTimer);
|
|
2744
2768
|
if (msg.type === "lavish:layoutDiagnostics") {
|
|
2745
2769
|
const diagnosticSequence = ++layoutDiagnosticSequence;
|
|
2770
|
+
const complete = msg.complete !== false;
|
|
2771
|
+
// The gate is visual, so the client-side settled pass is the release signal. Reporting the
|
|
2772
|
+
// pass is deliberately fire-and-forget: a server restart or a diagnostics 4xx/5xx must not
|
|
2773
|
+
// hold a rendered artifact hostage to a network round-trip.
|
|
2774
|
+
if (complete) handleLayoutGatePass();
|
|
2746
2775
|
submitLayoutDiagnostics({
|
|
2747
|
-
complete
|
|
2776
|
+
complete,
|
|
2748
2777
|
targetPresenceComplete: msg.target_presence_complete === true,
|
|
2749
2778
|
artifactRevision: msg.artifact_revision,
|
|
2750
2779
|
artifactLoadToken: msg.artifact_load_token,
|
|
@@ -2759,9 +2788,14 @@ window.addEventListener("message", (event) => {
|
|
|
2759
2788
|
if (messageSequence === artifactMessageSequence) armArtifactAvailabilityProbe(messageToken);
|
|
2760
2789
|
return;
|
|
2761
2790
|
}
|
|
2762
|
-
if (msg.complete !== false) handleLayoutGatePass();
|
|
2763
2791
|
})
|
|
2764
|
-
.catch(() => {
|
|
2792
|
+
.catch(() => {
|
|
2793
|
+
// A failed report is still a completed client-side pass. Keep this fallback explicit so a
|
|
2794
|
+
// future change cannot accidentally make the network request the gate's release path.
|
|
2795
|
+
if (complete && messageToken === artifactLoadToken && diagnosticSequence === layoutDiagnosticSequence) {
|
|
2796
|
+
handleLayoutGatePass();
|
|
2797
|
+
}
|
|
2798
|
+
});
|
|
2765
2799
|
return;
|
|
2766
2800
|
}
|
|
2767
2801
|
// The artifact spoke, so it rendered and ran its SDK - there is nothing fatal to probe for.
|
|
@@ -3148,9 +3182,9 @@ setAgentPresence("waiting");
|
|
|
3148
3182
|
// to wait for - start read-only instead of looking live until a Send gets silently refused.
|
|
3149
3183
|
if (sessionData.initialEnded) markSessionEnded();
|
|
3150
3184
|
|
|
3151
|
-
// Reaching this line is the only proof that this file parsed and ran to completion. The
|
|
3152
|
-
//
|
|
3153
|
-
//
|
|
3185
|
+
// Reaching this line is the only proof that this file parsed and ran to completion. The inline
|
|
3186
|
+
// bootstrap already owns the gate's bounded escape if this script fails; retire only its separate
|
|
3187
|
+
// boot-failure timer now that the full client has taken over.
|
|
3154
3188
|
const chromeBootWindow = /** @type {Record<string, any>} */ (/** @type {unknown} */ (window));
|
|
3155
3189
|
chromeBootWindow.__lavishChromeReady = true;
|
|
3156
3190
|
chromeBootWindow.__lavishCancelChromeBootFailsafe?.();
|
package/dist/chrome.css
CHANGED
package/dist/cli.mjs
CHANGED
|
@@ -5373,6 +5373,7 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
|
|
|
5373
5373
|
const target = whiteboardEntryByIndex(msg.diagramIndex);
|
|
5374
5374
|
if (target) target.iframe.src = whiteboardFrameSrc(target);
|
|
5375
5375
|
}
|
|
5376
|
+
if (msg.type === "lavish:requestLayoutDiagnostics") scheduleLayoutAudit(true);
|
|
5376
5377
|
});
|
|
5377
5378
|
function enhanceMermaid() {
|
|
5378
5379
|
for (const svg of findMermaidSvgs()) {
|
|
@@ -5546,6 +5547,7 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
|
|
|
5546
5547
|
let layoutAuditTimer = 0;
|
|
5547
5548
|
let layoutAuditRun = 0;
|
|
5548
5549
|
let lastLayoutAuditSignature = null;
|
|
5550
|
+
let layoutAuditPublishRequested = false;
|
|
5549
5551
|
let layoutAuditPassSequence = 0;
|
|
5550
5552
|
function toPixelNumber(value) {
|
|
5551
5553
|
const parsed = Number.parseFloat(String(value || "0"));
|
|
@@ -6143,7 +6145,12 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
|
|
|
6143
6145
|
new Promise((resolve) => window.setTimeout(resolve, layoutAuditAnimationMaxWaitMs))
|
|
6144
6146
|
]);
|
|
6145
6147
|
if (!settled) {
|
|
6146
|
-
for (const animation of finite)
|
|
6148
|
+
for (const animation of finite) {
|
|
6149
|
+
animation.finished.then(
|
|
6150
|
+
() => scheduleLayoutAudit(),
|
|
6151
|
+
() => scheduleLayoutAudit()
|
|
6152
|
+
);
|
|
6153
|
+
}
|
|
6147
6154
|
}
|
|
6148
6155
|
return settled;
|
|
6149
6156
|
}
|
|
@@ -6151,7 +6158,8 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
|
|
|
6151
6158
|
const severe = findings.filter((finding) => finding?.severity === "error");
|
|
6152
6159
|
const viewportWidth = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
6153
6160
|
const signature = JSON.stringify({ complete, targetPresenceComplete, viewportWidth, severe });
|
|
6154
|
-
if (signature === lastLayoutAuditSignature) return;
|
|
6161
|
+
if (!layoutAuditPublishRequested && signature === lastLayoutAuditSignature) return;
|
|
6162
|
+
layoutAuditPublishRequested = false;
|
|
6155
6163
|
lastLayoutAuditSignature = signature;
|
|
6156
6164
|
postArtifactMessage("lavish:layoutDiagnostics", {
|
|
6157
6165
|
complete,
|
|
@@ -6183,7 +6191,8 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
|
|
|
6183
6191
|
targetPresenceComplete
|
|
6184
6192
|
);
|
|
6185
6193
|
}
|
|
6186
|
-
function scheduleLayoutAudit() {
|
|
6194
|
+
function scheduleLayoutAudit(publishRequested = false) {
|
|
6195
|
+
if (publishRequested) layoutAuditPublishRequested = true;
|
|
6187
6196
|
if (layoutAuditTimer) window.clearTimeout(layoutAuditTimer);
|
|
6188
6197
|
const runId = ++layoutAuditRun;
|
|
6189
6198
|
layoutAuditTimer = window.setTimeout(() => {
|
|
@@ -6194,10 +6203,10 @@ function createArtifactSdk(deriveQueueKey, isNativeInteractive = isNativeInterac
|
|
|
6194
6203
|
}
|
|
6195
6204
|
function startLayoutAudit() {
|
|
6196
6205
|
scheduleLayoutAudit();
|
|
6197
|
-
window.addEventListener("load", scheduleLayoutAudit, { once: true });
|
|
6198
|
-
window.addEventListener("resize", scheduleLayoutAudit, { passive: true });
|
|
6199
|
-
window.addEventListener("animationend", scheduleLayoutAudit, { passive: true });
|
|
6200
|
-
window.addEventListener("transitionend", scheduleLayoutAudit, { passive: true });
|
|
6206
|
+
window.addEventListener("load", () => scheduleLayoutAudit(), { once: true });
|
|
6207
|
+
window.addEventListener("resize", () => scheduleLayoutAudit(), { passive: true });
|
|
6208
|
+
window.addEventListener("animationend", () => scheduleLayoutAudit(), { passive: true });
|
|
6209
|
+
window.addEventListener("transitionend", () => scheduleLayoutAudit(), { passive: true });
|
|
6201
6210
|
}
|
|
6202
6211
|
function reportLocalAssetFailure(event) {
|
|
6203
6212
|
const el = event.target;
|
|
@@ -10022,23 +10031,36 @@ function extractArtifactHead(html) {
|
|
|
10022
10031
|
return { faviconTag, title };
|
|
10023
10032
|
}
|
|
10024
10033
|
var CHROME_BOOT_FAILSAFE_MS = 15e3;
|
|
10034
|
+
var CHROME_LAYOUT_GATE_MAX_HOLD_MS = 12e3;
|
|
10025
10035
|
var CHROME_BOOT_FAILSAFE_PROBE_TIMEOUT_MS = 4e3;
|
|
10026
10036
|
var CHROME_BOOT_FAILSAFE_JS = `(function(){
|
|
10027
10037
|
var t=setTimeout(fail,${CHROME_BOOT_FAILSAFE_MS});
|
|
10028
|
-
var o,h,c,a;
|
|
10038
|
+
var o=document.getElementById("layoutGateOverlay"),h,c,a,b,gt=0,manual=false,ended=false;
|
|
10039
|
+
try{ended=JSON.parse(document.getElementById("lavish-session").textContent).initialEnded===true;}catch(e){}
|
|
10040
|
+
function cancelGate(){if(gt)clearTimeout(gt);gt=0;}
|
|
10041
|
+
function reveal(){cancelGate();if(o)o.hidden=true;if(document.body)document.body.classList.remove("layout-gate-active");}
|
|
10042
|
+
function manualReveal(){if(ended)return false;manual=true;reveal();return true;}
|
|
10043
|
+
function armGate(ms,onTimeout){cancelGate();if(!ended)gt=setTimeout(function(){if(ended)return;if(onTimeout)onTimeout();else reveal();},ms);}
|
|
10044
|
+
function showBypass(){b=document.getElementById("layoutGateBypass");if(b){b.hidden=false;b.onclick=manualReveal;}}
|
|
10045
|
+
window.__lavishLayoutGateEscape={arm:armGate,cancel:cancelGate,reveal:reveal,manualReveal:manualReveal,showBypass:showBypass,end:function(){ended=true;cancelGate();},isEnded:function(){return ended;},isManuallyBypassed:function(){return manual;}};
|
|
10046
|
+
a=document.getElementById("layoutGateAction");
|
|
10047
|
+
if(ended){reveal();b=document.getElementById("endedOverlay");if(b)b.hidden=false;}
|
|
10048
|
+
if(a&&!ended)a.onclick=manualReveal;
|
|
10049
|
+
if(o&&!o.hidden&&!ended)armGate(${CHROME_LAYOUT_GATE_MAX_HOLD_MS});
|
|
10029
10050
|
window.__lavishCancelChromeBootFailsafe=function(){clearTimeout(t);};
|
|
10030
10051
|
window.__lavishChromeBootFailed=function(){clearTimeout(t);fail();};
|
|
10031
10052
|
function fail(){
|
|
10032
|
-
if(window.__lavishChromeReady)return;
|
|
10033
|
-
o=document.getElementById("layoutGateOverlay");
|
|
10053
|
+
if(window.__lavishChromeReady||ended)return;
|
|
10034
10054
|
h=document.getElementById("layoutGateTitle");
|
|
10035
10055
|
c=document.getElementById("layoutGateCopy");
|
|
10036
10056
|
a=document.getElementById("layoutGateAction");
|
|
10037
10057
|
if(h)h.textContent="Lavish could not finish loading.";
|
|
10038
10058
|
if(c)c.textContent="The Lavish editor script did not load. The server usually restarted while this page was opening. Check and reload to reconnect.";
|
|
10039
10059
|
if(a){a.textContent="Check and reload";a.disabled=false;a.onclick=check;}
|
|
10060
|
+
showBypass();
|
|
10040
10061
|
if(o)o.hidden=false;
|
|
10041
10062
|
if(document.body)document.body.classList.add("layout-gate-active");
|
|
10063
|
+
armGate(${CHROME_LAYOUT_GATE_MAX_HOLD_MS});
|
|
10042
10064
|
}
|
|
10043
10065
|
function check(){
|
|
10044
10066
|
if(a)a.disabled=true;
|
|
@@ -10105,7 +10127,7 @@ ${faviconTag}
|
|
|
10105
10127
|
<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><div class="warnings-wrap" id="warningsWrap" hidden><button class="warnings-button" id="warningsButton" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="warningsDrawer">${chromeIcons.warning}<span class="warnings-count" id="warningsCount">0</span></button><div class="menu warnings-drawer" id="warningsDrawer" role="dialog" aria-labelledby="warningsTitle" aria-describedby="warningsSummary" hidden><div class="warnings-head"><h2 class="warnings-title" id="warningsTitle">Layout issues</h2><p class="warnings-summary" id="warningsSummary"></p></div><div class="warnings-toolbar"><label class="warnings-selectall"><input type="checkbox" id="warningsSelectAll"><span>Select all</span></label><span class="warnings-selected" id="warningsSelected" role="status" aria-live="polite"></span></div><div class="warnings-list" id="warningsList"></div><div class="warnings-foot"><p class="warnings-note">Queueing sends a repair request with your next feedback. An issue is marked resolved only after a newer artifact load and a complete check at the same viewport no longer finds it.</p><button class="button" id="warningsQueueButton" type="button" disabled>Queue selected fixes</button></div></div></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>
|
|
10106
10128
|
<div class="layout"><div class="frame"><iframe id="artifact" sandbox="allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads" data-artifact-src="/artifact/${session.key}/index.html"></iframe></div><div class="panel-scrim" id="panelScrim"></div><aside class="panel" id="panel"><div class="panel-head" id="panelHead"><span class="panel-handle" aria-hidden="true"></span><div class="panel-head-row"><h2>Conversation</h2><span class="panel-summary" id="panelSummary" role="status" aria-live="polite"></span><button class="panel-toggle" id="panelToggle" type="button" aria-expanded="false" aria-controls="panel" aria-label="Show conversation">${chromeIcons.chevronUp}</button></div></div><div class="panel-scroll" id="panelScroll"><div class="chat" id="chatLog"></div><div class="annotation-pills" id="annotationPills"></div></div><div class="composer" id="chatComposer"><div class="presence-banner handoff-banner" id="handoffBanner" hidden><span>This review is open in another Lavish tab.</span><button class="handoff-takeover" id="handoffTakeover" type="button">Take over here</button></div><div class="presence-banner handoff-banner" id="outdatedBanner" hidden><span id="outdatedText">The Lavish server this page was connected to is no longer running. Reloading will work once it is running again.</span><span class="outdated-actions"><button class="handoff-takeover" id="outdatedReload" type="button">Check and reload</button><button class="handoff-takeover" id="outdatedDismiss" type="button">Dismiss</button></span></div><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="chat-attachments" id="chatAttachments"></div><div class="chat-attachment-toolbar"><button class="chat-attach" id="chatAttach" type="button">Attach images</button><input id="chatAttachInput" type="file" accept="${escapeHtml(acceptedMime.join(","))}" multiple hidden><span class="chat-attachment-notice" id="chatAttachmentNotice" role="status"></span></div><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>
|
|
10107
10129
|
<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>
|
|
10108
|
-
<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>
|
|
10130
|
+
<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><button class="button ended-action layout-gate-bypass" id="layoutGateBypass" type="button" hidden>Show anyway</button></div></div>
|
|
10109
10131
|
<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>
|
|
10110
10132
|
<div class="whiteboard-overlay" id="whiteboardOverlay" hidden><div class="whiteboard-shell"><div class="whiteboard-error" id="whiteboardError" hidden></div><button class="whiteboard-close" id="whiteboardClose" type="button" aria-label="Close whiteboard"><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><iframe id="whiteboardFrame" title="Excalidraw whiteboard" sandbox="allow-scripts allow-popups"></iframe></div></div>
|
|
10111
10133
|
<script id="lavish-session" type="application/json">${sessionJson}</script>
|
|
@@ -10372,7 +10394,7 @@ var POLL_WAKE_PATH_RULES = Object.freeze([
|
|
|
10372
10394
|
]);
|
|
10373
10395
|
var POLL_SEND_AND_END_RULE = "`Send & End` ends the session. Its final feedback is still delivered once. After that response, polling stops, and the agent must not reopen the session uninvited.";
|
|
10374
10396
|
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.";
|
|
10375
|
-
var VERSION = "0.1.
|
|
10397
|
+
var VERSION = "0.1.59";
|
|
10376
10398
|
function detectInvokingAgent(env = process.env) {
|
|
10377
10399
|
return ["CODEX_SANDBOX", "CODEX_THREAD_ID"].some((key) => Object.hasOwn(env, key)) ? "codex" : "generic";
|
|
10378
10400
|
}
|
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.59",
|
|
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",
|