@songsid/agend 2.3.3-beta.5 → 2.3.3-beta.6
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/dist/backend/claude-code.js +4 -2
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/grok.js +57 -11
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/types.d.ts +18 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/daemon.d.ts +6 -0
- package/dist/daemon.js +17 -0
- package/dist/daemon.js.map +1 -1
- package/dist/fleet-manager.d.ts +5 -0
- package/dist/fleet-manager.js +24 -1
- package/dist/fleet-manager.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +6 -0
- package/dist/instance-lifecycle.js +19 -3
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +12 -4
- package/dist/locale.js.map +1 -1
- package/dist/outbound-schemas.js +1 -1
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/settings-confirmation.d.ts +17 -3
- package/dist/settings-confirmation.js +8 -5
- package/dist/settings-confirmation.js.map +1 -1
- package/dist/settings-http-confirmation.js +31 -1
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/topic-commands.d.ts +21 -5
- package/dist/topic-commands.js +92 -36
- package/dist/topic-commands.js.map +1 -1
- package/dist/ui/chat-strings.js +6 -4
- package/dist/ui/chat-thread.js +45 -15
- package/dist/ui/fleet-office-skin.js +37 -14
- package/dist/ui/fleet-office.js +31 -7
- package/dist/ui/html-attachment.js +22 -8
- package/dist/ui/preview-panel.js +9 -6
- package/dist/ui/preview.js +10 -3
- package/dist/ui/settings-confirm.js +18 -0
- package/dist/ui/shared/app-i18n.js +13 -2
- package/dist/ui/shared/app-shell.js +35 -1
- package/dist/ui/shared/app.css +15 -0
- package/dist/web-preview.d.ts +5 -2
- package/dist/web-preview.js +6 -3
- package/dist/web-preview.js.map +1 -1
- package/package.json +1 -1
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
// @property {(ctx, item, active, ms, env) => void} [drawFurniture] zones: one furniture item (active: activeWhen holds)
|
|
28
28
|
// @property {(ctx, L, env) => void} [drawForeground] zones: a pack's foreground over the whole 1280×720 scene, above
|
|
29
29
|
// the people, furniture and items in the air, below the crowd counts, name tags and bubbles
|
|
30
|
-
// @property {(ctx, x, y, look, env) => void} [drawTag] zones: a name tag under feet at (x, y)
|
|
30
|
+
// @property {(ctx, x, y, look, env, compact?) => void} [drawTag] zones: a name tag under feet at (x, y) (compact: #2860)
|
|
31
31
|
// @property {(ctx, area, n, env) => void} [drawCrowd] zones: an area's "+n" (who found no spot in it)
|
|
32
32
|
// @property {(ctx, desk, look, ms, env) => void} drawCharacter the occupant in its pose at time `ms` (0: still); in
|
|
33
33
|
// zones `desk` is the character's box at its current position (its feet at desk.seat); walking, `look.heading` is
|
|
@@ -293,21 +293,44 @@ function drawBubble(ctx, x, y, text, env, lift = 0) {
|
|
|
293
293
|
/** A zones name tag's widest text (logical units): longer names are cut with an ellipsis. */
|
|
294
294
|
export const TAG_TEXT_W = 84;
|
|
295
295
|
const TAG_FONT = "600 10px system-ui, sans-serif";
|
|
296
|
+
/**
|
|
297
|
+
* #2860: the compact tag a crowded area keeps on (it used to show tags on hover only, #2513): a smaller font, a smaller
|
|
298
|
+
* plate, a shorter cut, the same level badge.
|
|
299
|
+
*/
|
|
300
|
+
const COMPACT = { font: "500 9px system-ui, sans-serif", textW: 48, pad: 6, top: 2, h: 11 };
|
|
301
|
+
const NORMAL = { font: TAG_FONT, textW: TAG_TEXT_W, pad: 10, top: 3, h: 14 };
|
|
296
302
|
/** The box a name tag under feet at (x, y) takes (what drawTag draws; bubbles keep clear of it, #2513). */
|
|
297
|
-
export function tagBox(ctx, x, y, label) {
|
|
303
|
+
export function tagBox(ctx, x, y, label, compact = false) {
|
|
304
|
+
const k = compact ? COMPACT : NORMAL;
|
|
305
|
+
ctx.save();
|
|
306
|
+
ctx.font = k.font;
|
|
307
|
+
const t = fitText(ctx, label, k.textW), w = Math.ceil((ctx.measureText ? ctx.measureText(t).width : t.length * 6) + k.pad);
|
|
308
|
+
ctx.restore();
|
|
309
|
+
return { x: Math.round(x - w / 2), y: y + k.top, w, h: k.h, text: t };
|
|
310
|
+
}
|
|
311
|
+
/** #2860: what a tag covers with its level badge: on a full tag's top-left corner (half outside); left of a compact one. */
|
|
312
|
+
export function tagFootprint(ctx, box, badge, compact = false) {
|
|
313
|
+
if (!badge) return box;
|
|
314
|
+
if (compact) { const w = badgeWidth(ctx, badge) + 1; return { x: box.x - w, y: box.y, w: box.w + w, h: box.h }; }
|
|
315
|
+
return { x: box.x - 10, y: box.y - 7, w: box.w + 10, h: box.h + 7 };
|
|
316
|
+
}
|
|
317
|
+
function badgeWidth(ctx, badge) {
|
|
318
|
+
if (badge.mark) return 8;
|
|
298
319
|
ctx.save();
|
|
299
|
-
ctx.font =
|
|
300
|
-
const
|
|
320
|
+
ctx.font = BADGE_FONT;
|
|
321
|
+
const w = Math.max(12, Math.ceil((ctx.measureText ? ctx.measureText(badge.text).width : badge.text.length * 5) + 6));
|
|
301
322
|
ctx.restore();
|
|
302
|
-
return
|
|
323
|
+
return w;
|
|
303
324
|
}
|
|
304
325
|
/**
|
|
305
326
|
* #2661: a level badge on a name tag's top-left corner (x, y: the tag's corner), overlapping its edge like an app's
|
|
306
327
|
* badge — it takes none of the tag's width. `badge`: { text, tier } (fleet-office-levels.js badgeOf), or { mark } for a
|
|
307
328
|
* pinned title that hides the number: a small diamond.
|
|
308
329
|
*/
|
|
309
|
-
export function drawBadge(ctx, x, y, badge, pal) {
|
|
330
|
+
export function drawBadge(ctx, x, y, badge, pal, inline = false) {
|
|
310
331
|
if (!badge) return;
|
|
332
|
+
// #2860 inline (a compact tag): (x, y) is the tag's left edge and middle; the badge sits just left of it, whole.
|
|
333
|
+
if (inline) { const w = badgeWidth(ctx, badge); x = x - 1 - w + (badge.mark ? w / 2 : w / 2 - 3); y = y + (badge.mark ? 0 : 1); }
|
|
311
334
|
const c = tierColours(badge.tier, pal);
|
|
312
335
|
ctx.save();
|
|
313
336
|
ctx.globalAlpha = 1;
|
|
@@ -331,18 +354,18 @@ export function drawBadge(ctx, x, y, badge, pal) {
|
|
|
331
354
|
}
|
|
332
355
|
const BADGE_FONT = "700 8px system-ui, sans-serif";
|
|
333
356
|
|
|
334
|
-
/** Zones: a name tag under someone's feet at (x, y). */
|
|
335
|
-
function drawTag(ctx, x, y, look, env) {
|
|
357
|
+
/** Zones: a name tag under someone's feet at (x, y); `compact` (#2860): the smaller one a crowded area keeps on. */
|
|
358
|
+
function drawTag(ctx, x, y, look, env, compact = false) {
|
|
336
359
|
const { pal } = env;
|
|
360
|
+
const b = tagBox(ctx, x, y, look.label, compact);
|
|
337
361
|
ctx.save();
|
|
338
362
|
if (look.dim) ctx.globalAlpha = 0.35;
|
|
339
|
-
ctx.font = TAG_FONT;
|
|
340
|
-
|
|
341
|
-
ctx.
|
|
342
|
-
ctx.
|
|
343
|
-
ctx.fillStyle = pal.text; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText(t, x, y + 10.5);
|
|
363
|
+
ctx.font = compact ? COMPACT.font : TAG_FONT;
|
|
364
|
+
ctx.fillStyle = pal.surface; ctx.fillRect(b.x, b.y, b.w, b.h);
|
|
365
|
+
ctx.strokeStyle = look.match ? pal.focus : pal.border; ctx.lineWidth = look.match ? 2 : 1; ctx.strokeRect(b.x + 0.5, b.y + 0.5, b.w - 1, b.h - 1);
|
|
366
|
+
ctx.fillStyle = pal.text; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText(b.text, x, b.y + b.h / 2 + 0.5);
|
|
344
367
|
ctx.restore();
|
|
345
|
-
if (look.badge) drawBadge(ctx,
|
|
368
|
+
if (look.badge) compact ? drawBadge(ctx, b.x, b.y + b.h / 2, look.badge, pal, true) : drawBadge(ctx, b.x, b.y, look.badge, pal);
|
|
346
369
|
}
|
|
347
370
|
|
|
348
371
|
/**
|
package/dist/ui/fleet-office.js
CHANGED
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
// any part — an office names it as its style; one this page cannot load is drawn as Desks, and says so.
|
|
34
34
|
// - Crowds (#2513): an area with more people than seats packs the rest into a tighter grid, then counts who is left
|
|
35
35
|
// ("+N", in its corner, never over its name); there idle and asleep people say nothing, at most a few speak (calls
|
|
36
|
-
// first, then work), no bubble covers a name tag, and the tags
|
|
37
|
-
// (per browser) leaves the sleepers out.
|
|
36
|
+
// first, then work), no bubble covers a name tag, and the tags are compact (#2860; one that would touch another shows
|
|
37
|
+
// on hover or focus only). "Hide paused and stopped" (per browser) leaves the sleepers out.
|
|
38
38
|
// - Share… (#2636; an admin, on the dashboard reached directly or over the public link): a link anyone can open to watch an office live, read-only,
|
|
39
39
|
// until it ends (office-share-dialog.js; the page it opens is office-share-page.js).
|
|
40
40
|
// - Edit layout (an admin, a stored office on a zones skin): fleet-office-layout.js, saved as the office's overrides.
|
|
@@ -64,7 +64,7 @@ import { nodeText, stateWords } from "./fleet-org.js";
|
|
|
64
64
|
import { hits, needle } from "./fleet-canvas-layout.js";
|
|
65
65
|
import { AUTO_ID, LOBBY, autoLayout, backingScale, coatOf, createFlights, customLayout, deskAt, desksOf, filterOk, lampOf, layoutOk, lightOf, membersOf,
|
|
66
66
|
officePose, officeRooms, signOf, workOn } from "./fleet-office-model.js";
|
|
67
|
-
import { BUILT_IN_SKINS, CHAR_DEPTH, DEFAULT_PAL, bubbleBox, loadCozySkin, loadPackSkin, modeOf, skinFor, tagBox } from "./fleet-office-skin.js";
|
|
67
|
+
import { BUILT_IN_SKINS, CHAR_DEPTH, DEFAULT_PAL, bubbleBox, loadCozySkin, loadPackSkin, modeOf, skinFor, tagBox, tagFootprint } from "./fleet-office-skin.js";
|
|
68
68
|
import { CALLING, activeFurniture, bubbleAt, hiddenBy, bubbleRank, NIGHT, WANDER, createMail, createWalkers, isNight, linesFor, npcKey, personAt, placeBubbles, thinBubbles, walkTargets, zonesLayout } from "./fleet-office-zones.js";
|
|
69
69
|
import { actsOk, createOfficeStore } from "./fleet-office-store.js";
|
|
70
70
|
import { badgeOf, fixedOf } from "./fleet-office-levels.js";
|
|
@@ -131,6 +131,8 @@ const deviceRatio = () => Math.max(0.5, Math.min(4, Number(globalThis.devicePixe
|
|
|
131
131
|
const SIZE0 = { w: 960, h: 600 };
|
|
132
132
|
/** #2554: All offices' Fit keeps this many CSS px clear round the tiles (the zoom controls' column and a margin). */
|
|
133
133
|
const FIT_PAD = 60;
|
|
134
|
+
/** #2860: how far below its place a crowded area's compact tag drops when a neighbour's is in the way (a tag's height + 1). */
|
|
135
|
+
const COMPACT_ROW = 12;
|
|
134
136
|
/** #2554: in All offices a tile's name tags show (always) from this many CSS px a unit up; below it on hover only. */
|
|
135
137
|
export const TAG_MIN_K = 0.75;
|
|
136
138
|
const PAN_KEYS = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] };
|
|
@@ -418,13 +420,19 @@ export function drawPart(ctx, p, a, ox, oy, ms, still, hot, env, tagsOff = false
|
|
|
418
420
|
if (sk.drawForeground) sk.drawForeground(ctx, p.L, env);
|
|
419
421
|
// Who found no spot in an area: a count in its corner (never over its name).
|
|
420
422
|
for (const [ar, n] of p.L.crowd || []) if (p.L.areas[ar] && sk.drawCrowd) sk.drawCrowd(ctx, p.L.areas[ar], n, env);
|
|
421
|
-
// In an area over capacity
|
|
423
|
+
// In an area over capacity (#2513: a crowd's full tags covered each other) everyone keeps a compact tag (#2860:
|
|
424
|
+
// in Cozy, the default, most of a fleet is in a crowd, and hover-only hid names and levels); one that would touch
|
|
425
|
+
// a tag already drawn, its badge included, is left to hover or focus. The full tags go first.
|
|
422
426
|
const over = p.L.over || new Set();
|
|
427
|
+
const crowded = (q) => over.has(q.d.area) && q.d.key !== hot && !q.look.match;
|
|
423
428
|
const tagged = (q) => !((tagsOff || over.has(q.d.area)) && q.d.key !== hot && !q.look.match);
|
|
429
|
+
const taken = [];
|
|
430
|
+
const touches = (b) => taken.some((o) => b.x < o.x + o.w && o.x < b.x + b.w && b.y < o.y + o.h && o.y < b.y + b.h);
|
|
424
431
|
for (const q of people) {
|
|
425
432
|
if (live.hidden.has(q.look.name) || !tagged(q)) continue;
|
|
426
433
|
sk.drawTag(ctx, q.at.x, q.at.y, q.look, env);
|
|
427
|
-
|
|
434
|
+
const box = tagBox(ctx, q.at.x, q.at.y, q.look.label);
|
|
435
|
+
tags.push(box); taken.push(tagFootprint(ctx, box, q.look.badge));
|
|
428
436
|
}
|
|
429
437
|
// A furniture item's own name (#2574: an NPC drawn as furniture), tagged like a person; none on a tile too small to read.
|
|
430
438
|
// #2698: an item hiding its people carries their tags too, in one row at its tag point (its own first; each keeps
|
|
@@ -434,7 +442,10 @@ export function drawPart(ctx, p, a, ox, oy, ms, still, hot, env, tagsOff = false
|
|
|
434
442
|
for (const f of furniture) {
|
|
435
443
|
const on = people.filter((q) => live.hidden.get(q.look.name) === f.id);
|
|
436
444
|
if (!on.length) {
|
|
437
|
-
if (!tagsOff && f.label) {
|
|
445
|
+
if (!tagsOff && f.label) {
|
|
446
|
+
const nl = npcLook(p, f), box = tagBox(ctx, f.x, f.y, nl.label);
|
|
447
|
+
sk.drawTag(ctx, f.x, f.y, nl, env); tags.push(box); taken.push(tagFootprint(ctx, box, nl.badge));
|
|
448
|
+
}
|
|
438
449
|
continue;
|
|
439
450
|
}
|
|
440
451
|
const order = new Map((f.activeFor || []).map((n, i) => [n, i]));
|
|
@@ -447,11 +458,24 @@ export function drawPart(ctx, p, a, ox, oy, ms, still, hot, env, tagsOff = false
|
|
|
447
458
|
if (q ? !tagged(q) : tagsOff) continue;
|
|
448
459
|
sk.drawTag(ctx, r.x, f.y, r.look, env);
|
|
449
460
|
const box = tagBox(ctx, r.x, f.y, r.look.label);
|
|
450
|
-
tags.push(box);
|
|
461
|
+
tags.push(box); taken.push(tagFootprint(ctx, box, r.look.badge));
|
|
451
462
|
if (q) held.push({ name: q.look.name, id: f.id, box });
|
|
452
463
|
}
|
|
453
464
|
}
|
|
454
465
|
p.heldTags = held;
|
|
466
|
+
// The crowd's compact tags go last, clear of every full tag above: people's, an item's own name and the row it
|
|
467
|
+
// carries (#2862 review). A crowd's grid is tighter than a compact tag is wide (OVERFLOW.STEPS down to 32): a
|
|
468
|
+
// neighbour's tag goes a row lower (COMPACT_ROW) rather than out, so a row of people reads as two staggered rows.
|
|
469
|
+
if (!tagsOff) for (const q of people) {
|
|
470
|
+
if (live.hidden.has(q.look.name) || !crowded(q)) continue;
|
|
471
|
+
for (const dy of [0, COMPACT_ROW]) {
|
|
472
|
+
const box = tagBox(ctx, q.at.x, q.at.y + dy, q.look.label, true), foot = tagFootprint(ctx, box, q.look.badge, true);
|
|
473
|
+
if (touches(foot)) continue;
|
|
474
|
+
sk.drawTag(ctx, q.at.x, q.at.y + dy, q.look, env, true);
|
|
475
|
+
tags.push(box); taken.push(foot);
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
455
479
|
} else {
|
|
456
480
|
for (const d of p.desks) { const look = p.looks.get(d.key); sk.drawCharacter(ctx, d, actPosed(p, look), ms, env); speakers.push({ look, x: d.x + 58, y: d.y + 12, area: p.roomOf.get(d.key) ?? null }); }
|
|
457
481
|
blit(p.layers.front);
|
|
@@ -3,24 +3,38 @@
|
|
|
3
3
|
// card. What runs is then handed to the preview frame exactly like a block's (AgendPreview.start): the file URL itself
|
|
4
4
|
// is never opened or framed on the dashboard's origin.
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* #2863: the sizes a preview takes (UTF-8 bytes of the HTML), the same as a block's (preview.js LIMITS): up to ASK it
|
|
8
|
+
* runs at once; above it the person is asked first ("Preview anyway?"); above MAX there is no preview, only the
|
|
9
|
+
* download. MAX is the frame's own ceiling (web-preview.ts PREVIEW_HTML_MAX_BYTES).
|
|
10
|
+
*/
|
|
11
|
+
export const HTML_PREVIEW_ASK = 8 * 1024 * 1024;
|
|
12
|
+
export const HTML_PREVIEW_MAX = 50 * 1024 * 1024;
|
|
8
13
|
|
|
9
14
|
/**
|
|
10
|
-
* Read an attachment's HTML: { ok: true, code } or { ok: false, reason } with reason "
|
|
11
|
-
*
|
|
12
|
-
*
|
|
15
|
+
* Read an attachment's HTML: { ok: true, code } or { ok: false, reason } with reason "large" (above `ask` and not
|
|
16
|
+
* `anyway`: nothing is fetched when its listed size says so; `size` is what it is), "over" (above `max`: its listed size
|
|
17
|
+
* is checked before anything is fetched, the bytes again after), "gone" (the fleet no longer serves that id — the file
|
|
18
|
+
* changed, or the fleet restarted), or "failed". The text card reads with its own, single cap (ask = max).
|
|
13
19
|
*/
|
|
14
|
-
export async function loadHtmlAttachment(att, fetchImpl = globalThis.fetch) {
|
|
20
|
+
export async function loadHtmlAttachment(att, fetchImpl = globalThis.fetch, { ask = HTML_PREVIEW_ASK, max = HTML_PREVIEW_MAX, anyway = false } = {}) {
|
|
15
21
|
if (!att || typeof att.id !== "string" || !/^[0-9a-f]{32}$/.test(att.id)) return { ok: false, reason: "failed" };
|
|
16
|
-
if (typeof att.size === "number" && att.size >
|
|
22
|
+
if (typeof att.size === "number" && att.size > max) return { ok: false, reason: "over" };
|
|
23
|
+
if (typeof att.size === "number" && att.size > ask && !anyway) return { ok: false, reason: "large", size: att.size };
|
|
17
24
|
let res;
|
|
18
25
|
try { res = await fetchImpl(`/ui/file/${att.id}`, { credentials: "same-origin", cache: "no-store" }); }
|
|
19
26
|
catch { return { ok: false, reason: "failed" }; }
|
|
20
27
|
if (!res || !res.ok) return { ok: false, reason: res && res.status === 404 ? "gone" : "failed" };
|
|
21
28
|
let bytes;
|
|
22
29
|
try { bytes = await res.arrayBuffer(); } catch { return { ok: false, reason: "failed" }; }
|
|
23
|
-
if (bytes.byteLength >
|
|
30
|
+
if (bytes.byteLength > max) return { ok: false, reason: "over" };
|
|
31
|
+
if (bytes.byteLength > ask && !anyway) return { ok: false, reason: "large", size: bytes.byteLength };
|
|
24
32
|
// UTF-8, as a block's text is; a BOM is dropped.
|
|
25
33
|
return { ok: true, code: new TextDecoder("utf-8").decode(bytes) };
|
|
26
34
|
}
|
|
35
|
+
|
|
36
|
+
/** #2863: a size as the person reads it: "12.3 MB" (1 MB = 1024 × 1024 bytes, as the caps are). */
|
|
37
|
+
export function sizeLabel(bytes) {
|
|
38
|
+
const mb = bytes / (1024 * 1024);
|
|
39
|
+
return `${mb >= 10 ? Math.round(mb) : Math.round(mb * 10) / 10} MB`;
|
|
40
|
+
}
|
package/dist/ui/preview-panel.js
CHANGED
|
@@ -152,11 +152,12 @@ export function PreviewPanel({ name, split, msgs, subscribe, keyOf, reveal, down
|
|
|
152
152
|
const [, dayTick] = useState(0);
|
|
153
153
|
useEffect(() => (open ? onDayChange(() => dayTick(n => n + 1)) : undefined), [!!open]);
|
|
154
154
|
|
|
155
|
-
|
|
156
|
-
const
|
|
155
|
+
// #2863: "large" — over 8 MB, nothing runs until "Preview anyway" (or the card's, which opened it with `anyway`).
|
|
156
|
+
const ui = { state: (s, reason) => setRun({ state: s === "starting" || s === "running" || s === "large" ? s : "idle", reason: reason || "" }) };
|
|
157
|
+
const start = (anyway) => {
|
|
157
158
|
if (!open || !holder.current) return;
|
|
158
159
|
setView("preview");
|
|
159
|
-
P().start(frameKey(open.key), holder.current, open.code, ui, { fill: true });
|
|
160
|
+
P().start(frameKey(open.key), holder.current, open.code, ui, anyway === true || open.anyway ? { fill: true, anyway: true } : { fill: true });
|
|
160
161
|
};
|
|
161
162
|
const stop = () => { if (open) P().stop(frameKey(open.key), "stopped", ""); };
|
|
162
163
|
// A new block in the panel: what ran before is gone (openPanel stopped it); the card's click starts it here — or,
|
|
@@ -224,7 +225,8 @@ export function PreviewPanel({ name, split, msgs, subscribe, keyOf, reveal, down
|
|
|
224
225
|
if (newer.code != null) { openPanel(newer); return; }
|
|
225
226
|
const was = open && open.key, mine = reading.current = newer;
|
|
226
227
|
setNewerNote(t("chat.pvLoading"));
|
|
227
|
-
|
|
228
|
+
// A click on the newer version reads it whatever its size (up to the 50 MB ceiling); over 8 MB the panel then asks.
|
|
229
|
+
const r = await loadHtmlAttachment(newer.att, undefined, { anyway: true });
|
|
228
230
|
if (reading.current !== mine) return;
|
|
229
231
|
reading.current = null;
|
|
230
232
|
const now = panelStore.get().open;
|
|
@@ -270,9 +272,10 @@ export function PreviewPanel({ name, split, msgs, subscribe, keyOf, reveal, down
|
|
|
270
272
|
<button type="button" class=${`btn btn-sm${view === "code" ? " on" : ""}`} aria-pressed=${view === "code" ? "true" : "false"} onClick=${() => setView("code")}><${Icon} name="code" size=${14} />${t("chat.pvCode")}</button>
|
|
271
273
|
</div>
|
|
272
274
|
<span class="grow"></span>
|
|
273
|
-
${going ? html`<button type="button" class="btn btn-sm pv-panel-reload" title=${t("chat.pvReload")} onClick=${() => { stop(); start(); }}><${Icon} name="restart" size=${14} /><span class="lbl">${t("chat.pvReload")}</span></button>
|
|
275
|
+
${going ? html`<button type="button" class="btn btn-sm pv-panel-reload" title=${t("chat.pvReload")} onClick=${() => { stop(); start(true); }}><${Icon} name="restart" size=${14} /><span class="lbl">${t("chat.pvReload")}</span></button>
|
|
274
276
|
<button type="button" class="btn btn-sm pv-panel-stop" title=${t("chat.pvStop")} onClick=${stop}><${Icon} name="stop" size=${14} /><span class="lbl">${t("chat.pvStop")}</span></button>`
|
|
275
|
-
: html`<button type="button" class="btn btn-sm btn-primary pv-panel-
|
|
277
|
+
: run.state === "large" ? html`<button type="button" class="btn btn-sm btn-primary pv-panel-anyway" title=${t("chat.pvAnyway")} disabled=${!a.ok} onClick=${() => start(true)}><${Icon} name="play" size=${14} /><span class="lbl">${t("chat.pvAnyway")}</span></button>`
|
|
278
|
+
: html`<button type="button" class="btn btn-sm btn-primary pv-panel-run" title=${t("chat.pvPreview")} disabled=${!a.ok} onClick=${() => start()}><${Icon} name="play" size=${14} /><span class="lbl">${t("chat.pvPreview")}</span></button>`}
|
|
276
279
|
<button type="button" class="btn btn-sm pv-panel-dl" title=${t("chat.pvDownload")} onClick=${() => download(open.code, fileName)}><${Icon} name="download" size=${14} /><span class="lbl">${t("chat.pvDownload")}</span></button>
|
|
277
280
|
</div>
|
|
278
281
|
${newer && newer.key !== open.key ? html`<div class="pv-newer" role="status"><span>${t("chat.pvNewer", timeOf(newer.ts))}</span>
|
package/dist/ui/preview.js
CHANGED
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
})(typeof globalThis !== "undefined" ? globalThis : this, function (root) {
|
|
21
21
|
"use strict";
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
// #2863: askBytes — above it the person is asked first ("Preview anyway?"); maxBytes — above it only a download (the
|
|
24
|
+
// frame's own ceiling, web-preview.ts PREVIEW_HTML_MAX_BYTES; html-attachment.js reads with the same two).
|
|
25
|
+
var LIMITS = { askBytes: 8 * 1024 * 1024, maxBytes: 50 * 1024 * 1024, minHeight: 40, maxHeight: 4000, readyMs: 3000, watchdogMs: 10000, resizePerSec: 10, minDelta: 2, growthFreeze: 5, growthWindowMs: 2000 };
|
|
24
26
|
var OPT_IN_KEY = "agend_html_preview"; // localStorage: "on" — this device allows previews
|
|
25
27
|
var NEVER_KEY = "agend_html_preview_never"; // sessionStorage: "1" — never preview on this device (this session)
|
|
26
28
|
// #1568: the banner was acknowledged ("I understand"). localStorage: the sign-in's session handle — hidden while the
|
|
@@ -54,6 +56,8 @@
|
|
|
54
56
|
function now() { return root.performance && typeof root.performance.now === "function" ? root.performance.now() : Date.now(); }
|
|
55
57
|
function store(kind) { try { return root[kind] || null; } catch (e) { return null; } }
|
|
56
58
|
function utf8Bytes(s) { try { return new root.TextEncoder().encode(s).length; } catch (e) { return String(s).length * 3; } }
|
|
59
|
+
/** #2863: "12 MB" / "8.4 MB" (1 MB = 1024 × 1024 bytes, as the caps are; html-attachment.js sizeLabel). */
|
|
60
|
+
function sizeLabel(bytes) { var mb = bytes / (1024 * 1024); return (mb >= 10 ? Math.round(mb) : Math.round(mb * 10) / 10) + " MB"; }
|
|
57
61
|
function randomCh() {
|
|
58
62
|
var b = new Uint8Array(16);
|
|
59
63
|
root.crypto.getRandomValues(b);
|
|
@@ -229,7 +233,8 @@
|
|
|
229
233
|
|
|
230
234
|
/**
|
|
231
235
|
* Run `html` in `holder` (a node of the card) for `key`. `ui` is told what happened: ui.state(name, reason) with
|
|
232
|
-
* "starting" | "running" | "stopped" | "unavailable"
|
|
236
|
+
* "starting" | "running" | "stopped" | "unavailable" | "large" (#2863: over LIMITS.askBytes — nothing runs until it is
|
|
237
|
+
* asked again with opts.anyway, the person's "Preview anyway"). One preview at a time: any other running one stops first.
|
|
233
238
|
* opts.fill (#1481, the side panel): the frame fills its holder — the same frame from mountPreview, only sized by the
|
|
234
239
|
* panel instead of by the frame's height messages, which it then ignores.
|
|
235
240
|
*/
|
|
@@ -239,7 +244,9 @@
|
|
|
239
244
|
function start(key, holder, html, ui, opts) {
|
|
240
245
|
var a = availability();
|
|
241
246
|
if (!a.ok) { ui.state("unavailable", a.reason); return false; }
|
|
242
|
-
|
|
247
|
+
var bytes = typeof html === "string" ? utf8Bytes(html) : Infinity;
|
|
248
|
+
if (bytes > LIMITS.maxBytes) { ui.state("unavailable", say("pvHtmlOver", "This HTML is over 50 MB; it is not previewed. Download it to open it.")); return false; }
|
|
249
|
+
if (bytes > LIMITS.askBytes && !(opts && opts.anyway)) { ui.state("large", say("pvLarge", "This file is large ({0}). Preview anyway?", sizeLabel(bytes))); return false; }
|
|
243
250
|
stopAll("another");
|
|
244
251
|
starts++;
|
|
245
252
|
var doc = holder.ownerDocument;
|
|
@@ -65,6 +65,8 @@ function publish() {
|
|
|
65
65
|
// Monotonic: when the server's countdown reaches zero on this page's clock.
|
|
66
66
|
deadline: at + Math.max(0, Number(view.remaining_ms) || 0),
|
|
67
67
|
withdraw: view.can_withdraw && view.state === "pending" ? () => withdraw(view.id) : null,
|
|
68
|
+
// #2868: this browser may confirm it itself now (the server's read says so; it checks again on the click).
|
|
69
|
+
applyNow: view.can_self_confirm && view.state === "pending" ? () => applyNow(view.id) : null,
|
|
68
70
|
dismiss: TERMINAL.has(view.state) ? () => forget(view.id) : null,
|
|
69
71
|
})),
|
|
70
72
|
});
|
|
@@ -150,6 +152,22 @@ export async function withdraw(id) {
|
|
|
150
152
|
return false;
|
|
151
153
|
}
|
|
152
154
|
|
|
155
|
+
/**
|
|
156
|
+
* #2868: "Apply now" — confirm this session's own pending request from this browser (#2170's rule, asked again by the
|
|
157
|
+
* server). Its answer is followed like any other; a refusal (no longer allowed here) leaves it waiting and is { ok: false }.
|
|
158
|
+
*/
|
|
159
|
+
export async function applyNow(id) {
|
|
160
|
+
const e = tracked.get(id);
|
|
161
|
+
if (!e) return { ok: false };
|
|
162
|
+
const token = ticket(e);
|
|
163
|
+
let res;
|
|
164
|
+
try { res = await api(`/api/settings/pending/${encodeURIComponent(id)}/confirm`, { method: "POST", body: "{}" }); } catch { return { ok: false }; }
|
|
165
|
+
if (isPendingResponse(res) && res.body.pending_change.id === id) { update(res.body.pending_change, token); schedule(); return { ok: true }; }
|
|
166
|
+
// Refused (403: not allowed here any more): read it again, so "Apply now" goes until the server offers it again.
|
|
167
|
+
if (e.view.can_self_confirm) update({ ...e.view, can_self_confirm: false }, token);
|
|
168
|
+
return { ok: false, status: res.status };
|
|
169
|
+
}
|
|
170
|
+
|
|
153
171
|
/** This session's requests still open on the server (after a reload): followed like new ones. */
|
|
154
172
|
export async function attach() {
|
|
155
173
|
let res;
|
|
@@ -89,7 +89,13 @@ register("app", {
|
|
|
89
89
|
pendingWeb: "Confirmed by your account (this browser is web-verified). General is told.",
|
|
90
90
|
pending_applied: "Confirmed and applied", pending_rejected: "Not applied", pending_expired: "Expired — nothing was changed",
|
|
91
91
|
pending_stale: "Not applied — settings changed meanwhile", pending_failed: "Confirmed, but applying it failed",
|
|
92
|
-
|
|
92
|
+
pendingLeft: "{0} left", withdraw: "Withdraw",
|
|
93
|
+
// #2868: where to confirm it, said plainly; the host's command folded away unless it is the only way.
|
|
94
|
+
pendingPosting: "Sending the confirm card to General…", pendingChatWhere: "To apply it, press Confirm on the card in General on {0}.",
|
|
95
|
+
pendingHostOnly: "No General can take the confirm card. Confirm it on the host: {0}", pendingAdvanced: "Advanced: confirm on the host",
|
|
96
|
+
pendingSelf: "This browser is verified, so you can also apply it here.", pendingApplyNow: "Apply now",
|
|
97
|
+
pendingApplyRefused: "This browser can't apply it here now — confirm it in General.",
|
|
98
|
+
pendingAuthorize: "Authorize this browser to apply changes like this here next time, without General.",
|
|
93
99
|
opWriting: "Applying settings… {0}/{1}", opWaiting: "Settings: waiting for confirmation in chat", opApplying: "Applying settings…",
|
|
94
100
|
opDone: "Settings applied", opRestart: "Settings saved — AgEnD needs a restart to use them", opPartial: "Some settings were not applied",
|
|
95
101
|
opFailed: "Settings were not applied", openSettings: "Open Settings",
|
|
@@ -131,7 +137,12 @@ register("app", {
|
|
|
131
137
|
pendingWeb: "由你的帳號確認(這個瀏覽器已通過網頁驗證),也會通知 General。",
|
|
132
138
|
pending_applied: "已確認並套用", pending_rejected: "未套用", pending_expired: "已逾時——沒有任何變更",
|
|
133
139
|
pending_stale: "未套用——設定在這期間已變動", pending_failed: "已確認,但套用失敗",
|
|
134
|
-
|
|
140
|
+
pendingLeft: "剩 {0}", withdraw: "撤回",
|
|
141
|
+
pendingPosting: "正在把確認卡送到 General…", pendingChatWhere: "要套用,請到 {0} 的 General,在確認卡上按「確認」。",
|
|
142
|
+
pendingHostOnly: "沒有可以接收確認卡的 General。請在主機上確認:{0}", pendingAdvanced: "進階:在主機上確認",
|
|
143
|
+
pendingSelf: "這個瀏覽器已通過驗證,也可以直接在這裡套用。", pendingApplyNow: "直接套用",
|
|
144
|
+
pendingApplyRefused: "這個瀏覽器現在無法直接套用,請到 General 確認。",
|
|
145
|
+
pendingAuthorize: "授權這個瀏覽器,之後這類變更可以直接套用,不必到 General。",
|
|
135
146
|
opWriting: "正在套用設定… {0}/{1}", opWaiting: "設定:等待在聊天中確認", opApplying: "正在套用設定…",
|
|
136
147
|
opDone: "設定已套用", opRestart: "設定已儲存——需要重新啟動 AgEnD 才會生效", opPartial: "部分設定沒有套用",
|
|
137
148
|
opFailed: "設定沒有套用", openSettings: "開啟設定",
|
|
@@ -17,6 +17,7 @@ import { applyPageTheme, pageNow, resolvedMode, themesStore } from "./app-themes
|
|
|
17
17
|
import { ErrorState, Skeleton } from "./ui-states.js";
|
|
18
18
|
import { InstanceNav, RosterNav, namesStore, setNamesPrimary } from "./instance-nav.js";
|
|
19
19
|
import { usageRailStore } from "./header-tools.js";
|
|
20
|
+
import { useWebAuthorization } from "./web-authorize.js";
|
|
20
21
|
import { RightRail, sidePanelStore } from "./side-panel.js";
|
|
21
22
|
import { OwnerCssKeepBar } from "./owner-css.js";
|
|
22
23
|
import { AgentWidget, fleetBinds, sidebarWidget, sidebarWidgetStore } from "./agent-widget.js";
|
|
@@ -575,13 +576,46 @@ export function PendingChanges({ inline = false } = {}) {
|
|
|
575
576
|
${p.dismiss ? html`<button type="button" class="icon-btn" aria-label=${t("app.close")} title=${t("app.close")} onClick=${p.dismiss}><${Icon} name="close" size=${14} /></button>` : null}</div>
|
|
576
577
|
${p.label ? html`<div class="pending-label">${p.label}</div>` : null}
|
|
577
578
|
${Array.isArray(p.summary) && p.summary.length ? html`<ul class="pending-summary">${p.summary.map((line, i) => html`<li key=${i}>${line}</li>`)}</ul>` : null}
|
|
578
|
-
${p.state === "pending" ? html
|
|
579
|
+
${p.state === "pending" ? html`<${PendingHow} p=${p} />` : null}
|
|
579
580
|
${(p.state === "applying" || p.state === "applied") && p.confirmation && p.confirmation.kind === "web" ? html`<p class="note">${t("app.pendingWeb")}</p>` : null}
|
|
580
581
|
${p.state !== "pending" && p.state !== "applying" && p.state !== "applied" && p.outcome && p.outcome.message ? html`<p class="note">${p.outcome.message}</p>` : null}
|
|
581
582
|
${p.withdraw ? html`<div class="pending-actions"><button type="button" class="btn btn-sm" onClick=${p.withdraw}>${t("app.withdraw")}</button></div>` : null}
|
|
582
583
|
</section>`)}</div>`;
|
|
583
584
|
}
|
|
584
585
|
|
|
586
|
+
/** #2868: where the confirm card was posted: "Discord", or "Discord (team-b)" for a connection with an id of its own. */
|
|
587
|
+
function whereText(where) {
|
|
588
|
+
const names = (Array.isArray(where) ? where : []).map((w) => `${PLATFORM_NAMES[w.platform] || w.platform}${w.connection && w.connection !== w.platform ? ` (${w.connection})` : ""}`);
|
|
589
|
+
return names.length ? [...new Set(names)].join(" / ") : "Discord / Telegram";
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* #2868: how a pending change is confirmed, in the order a person can act on it. First where: the General card (naming
|
|
593
|
+
* the platform it was posted on), or the host's command when no General could take it. Then, when this browser could
|
|
594
|
+
* confirm it itself (the server's read says so: #2170's rule), "Apply now"; when it isn't verified but could be,
|
|
595
|
+
* "Authorize this browser" (the same DM card as the composer's). The host's command otherwise sits folded under
|
|
596
|
+
* "Advanced".
|
|
597
|
+
*/
|
|
598
|
+
function PendingHow({ p }) {
|
|
599
|
+
const a = useWebAuthorization();
|
|
600
|
+
const [refused, setRefused] = useState(false);
|
|
601
|
+
const c = p.confirmation || {}, cmd = `agend settings confirm ${p.id}`;
|
|
602
|
+
const lead = c.kind === "posting" ? t("app.pendingPosting") : c.kind === "host_cli" ? t("app.pendingHostOnly", cmd) : t("app.pendingChatWhere", whereText(c.where));
|
|
603
|
+
// A browser that isn't verified but could be: authorizing it makes this one (and later ones) "Apply now".
|
|
604
|
+
const route = c.kind !== "host_cli" && !p.applyNow && a.st;
|
|
605
|
+
const canAsk = route && a.st.state === "none" && (a.st.reason === "not_authorized" || a.st.reason === "authorization_expired");
|
|
606
|
+
const asking = route && a.st.state === "pending";
|
|
607
|
+
const apply = async () => { setRefused(false); const r = await p.applyNow(); if (!r || !r.ok) setRefused(true); };
|
|
608
|
+
return html`<p class="note">${lead}</p>
|
|
609
|
+
${p.applyNow ? html`<div class="pending-self"><span class="note">${t("app.pendingSelf")}</span>
|
|
610
|
+
<button type="button" class="btn btn-sm btn-primary" onClick=${apply}>${t("app.pendingApplyNow")}</button></div>` : null}
|
|
611
|
+
${refused && !p.applyNow ? html`<p class="note" role="status">${t("app.pendingApplyRefused")}</p>` : null}
|
|
612
|
+
${canAsk ? html`<div class="pending-self"><span class="note">${t("app.pendingAuthorize")}</span>
|
|
613
|
+
<button type="button" class="btn btn-sm" onClick=${a.ask}>${t("webauth.authorize")}</button></div>` : null}
|
|
614
|
+
${asking ? html`<p class="note">${t("webauth.waiting", mmss(a.left))}</p>` : null}
|
|
615
|
+
${a.notice && (canAsk || asking) ? html`<p class="note" role="status">${a.notice}</p>` : null}
|
|
616
|
+
${c.kind !== "host_cli" ? html`<details class="pending-host"><summary>${t("app.pendingAdvanced")}</summary><code>${cmd}</code></details>` : null}`;
|
|
617
|
+
}
|
|
618
|
+
|
|
585
619
|
/** Settings' Apply, followed from another panel: one line, and the way back to it (§5: progress shows in any panel). */
|
|
586
620
|
function OperationLine({ route }) {
|
|
587
621
|
const { settingsOp: op } = useStore(appStore);
|
package/dist/ui/shared/app.css
CHANGED
|
@@ -1214,6 +1214,18 @@ body.side-resizing, body.side-resizing * { cursor: col-resize !important; user-s
|
|
|
1214
1214
|
.v-filter-box input::placeholder { color: var(--text-3); }
|
|
1215
1215
|
.v-filter-clear { width: 28px; height: 28px; }
|
|
1216
1216
|
.v-filter .note { padding: var(--s-1) var(--s-1) 0; font-variant-numeric: tabular-nums; }
|
|
1217
|
+
/* A short viewport (a phone in landscape: about 300–480 px tall): the head, nav, filter and footer pinned around the
|
|
1218
|
+
list left it no room, and what did not fit was drawn over the footer (user report 2026-10-30). There the sidebar
|
|
1219
|
+
scrolls as one column: nothing shrinks, the list grows to its rows, the nav rows sit two to a row, and the filter
|
|
1220
|
+
stays in view at the bottom while the list scrolls past. Portrait phones and desktops are taller than this. */
|
|
1221
|
+
@media (max-height: 500px) {
|
|
1222
|
+
.sidebar { overflow-y: auto; overscroll-behavior: contain; }
|
|
1223
|
+
.sidebar > *, .view-roster > * { flex-shrink: 0; }
|
|
1224
|
+
.view-roster { flex: none; }
|
|
1225
|
+
.view-roster > .side-section { flex: none; overflow: visible; }
|
|
1226
|
+
.side-nav { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
1227
|
+
.v-filter { position: sticky; bottom: 0; z-index: 2; background: var(--bg-sidebar); }
|
|
1228
|
+
}
|
|
1217
1229
|
/* alpha.2 (N1): status and CLI chips under the filter box — each a group of checkboxes under its header (#1700). */
|
|
1218
1230
|
/* #1776: the "N / M" count is on the headers' row, at its right end — not a row of its own. The headers (and an open
|
|
1219
1231
|
group's list, the reset) wrap in their own box beside it, so a filter's summary or a narrow sidebar moves a header
|
|
@@ -1516,6 +1528,9 @@ textarea.s-yaml { white-space: pre; overflow-x: auto; }
|
|
|
1516
1528
|
.pending-label { color: var(--text-2); }
|
|
1517
1529
|
.pending-summary { list-style: disc; padding-left: var(--s-5); color: var(--text-2); font-size: var(--fs-xs); }
|
|
1518
1530
|
.pending-actions { display: flex; justify-content: flex-end; }
|
|
1531
|
+
.pending-self { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-2); }
|
|
1532
|
+
.pending-host summary { cursor: pointer; color: var(--text-2); font-size: var(--fs-xs); }
|
|
1533
|
+
.pending-host code { display: block; margin-top: var(--s-1); font-size: var(--fs-xs); overflow-wrap: anywhere; user-select: all; }
|
|
1519
1534
|
/* Settings' pending bar sits at the bottom of the page: toasts and request cards stand above it, never on its buttons. */
|
|
1520
1535
|
.shell:has(.s-pending) .toasts { bottom: calc(var(--s-6) + 64px); }
|
|
1521
1536
|
/* In a chat the composer owns the bottom (Stop reply, Send): toasts come in under the header instead (alpha.2 sweep). */
|
package/dist/web-preview.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { type Server } from "node:http";
|
|
2
2
|
import { type HostGuardConfig } from "./web-host-guard.js";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* The most HTML a preview takes (UTF-8 bytes): the parent refuses more before sending (a download only), the shim refuses
|
|
5
|
+
* it too. #2863: 50 MiB, the default upload limit; the page asks first ("Preview anyway?") above 8 MiB.
|
|
6
|
+
*/
|
|
4
7
|
export declare const PREVIEW_HTML_MAX_BYTES: number;
|
|
5
8
|
export interface PreviewWebConfig {
|
|
6
9
|
preview?: unknown;
|
|
@@ -64,7 +67,7 @@ export declare function dashboardOrigins(settings: PreviewSettings, healthPort:
|
|
|
64
67
|
export declare function previewFrameCsp(ancestors: readonly string[]): string;
|
|
65
68
|
/**
|
|
66
69
|
* The shim (design §4): posts `ready` with the listener's boot id; takes ONE `render` — from its parent window, from
|
|
67
|
-
* an exact listed dashboard origin (never "null"), with exactly {v, type, ch, html}, html ≤
|
|
70
|
+
* an exact listed dashboard origin (never "null"), with exactly {v, type, ch, html}, html ≤ PREVIEW_HTML_MAX_BYTES — removes the WebRTC
|
|
68
71
|
* entry points from its realm (defence in depth only), and writes a heartbeat/height prologue and the HTML into its
|
|
69
72
|
* own document. Every later message is ignored.
|
|
70
73
|
*/
|
package/dist/web-preview.js
CHANGED
|
@@ -14,8 +14,11 @@
|
|
|
14
14
|
import { randomBytes } from "node:crypto";
|
|
15
15
|
import { createServer } from "node:http";
|
|
16
16
|
import { allowedHostNames, hostnameOf, LOOPBACK_HOST_NAMES } from "./web-host-guard.js";
|
|
17
|
-
/**
|
|
18
|
-
|
|
17
|
+
/**
|
|
18
|
+
* The most HTML a preview takes (UTF-8 bytes): the parent refuses more before sending (a download only), the shim refuses
|
|
19
|
+
* it too. #2863: 50 MiB, the default upload limit; the page asks first ("Preview anyway?") above 8 MiB.
|
|
20
|
+
*/
|
|
21
|
+
export const PREVIEW_HTML_MAX_BYTES = 50 * 1024 * 1024;
|
|
19
22
|
/** The settings a fleet runs with: `web.preview`, `web.preview_port` (default health_port + 1), `web.preview_origin`. */
|
|
20
23
|
export function previewSettings(web, healthPort) {
|
|
21
24
|
// An ephemeral web listener (port 0: tests, harnesses) gets an ephemeral preview listener; its real port is filled
|
|
@@ -163,7 +166,7 @@ export function previewFrameCsp(ancestors) {
|
|
|
163
166
|
}
|
|
164
167
|
/**
|
|
165
168
|
* The shim (design §4): posts `ready` with the listener's boot id; takes ONE `render` — from its parent window, from
|
|
166
|
-
* an exact listed dashboard origin (never "null"), with exactly {v, type, ch, html}, html ≤
|
|
169
|
+
* an exact listed dashboard origin (never "null"), with exactly {v, type, ch, html}, html ≤ PREVIEW_HTML_MAX_BYTES — removes the WebRTC
|
|
167
170
|
* entry points from its realm (defence in depth only), and writes a heartbeat/height prologue and the HTML into its
|
|
168
171
|
* own document. Every later message is ignored.
|
|
169
172
|
*/
|
package/dist/web-preview.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-preview.js","sourceRoot":"","sources":["../src/web-preview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAA0D,MAAM,WAAW,CAAC;AACjG,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,mBAAmB,EAAwB,MAAM,qBAAqB,CAAC;AAE9G
|
|
1
|
+
{"version":3,"file":"web-preview.js","sourceRoot":"","sources":["../src/web-preview.ts"],"names":[],"mappings":"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"}
|