@songsid/agend 2.3.3-beta.4 → 2.3.3-beta.5
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/agent-cli.js +13 -112
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.d.ts +0 -1
- package/dist/agent-endpoint.js +1 -9
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.d.ts +3 -6
- package/dist/agent-whats-new.js +7 -18
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.d.ts +0 -7
- package/dist/auth-api.js +1 -15
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/codex.d.ts +1 -0
- package/dist/backend/codex.js +106 -15
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/types.d.ts +17 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/bundled-skills.d.ts +4 -6
- package/dist/bundled-skills.js +10 -14
- package/dist/bundled-skills.js.map +1 -1
- package/dist/channel/mcp-server.js +5 -5
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.d.ts +1 -3
- package/dist/channel/mcp-tools.js +4 -58
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +6 -3
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +11 -3
- package/dist/daemon.js +59 -107
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard-live.d.ts +1 -1
- package/dist/dashboard-live.js +3 -3
- package/dist/dashboard-live.js.map +1 -1
- package/dist/dashboard-permissions.d.ts +1 -1
- package/dist/dashboard-permissions.js +1 -3
- package/dist/dashboard-permissions.js.map +1 -1
- package/dist/fleet-manager.d.ts +26 -44
- package/dist/fleet-manager.js +70 -226
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/credential-profiles/SKILL.md +11 -1
- package/dist/general-knowledge/skills/session-management/SKILL.md +6 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +13 -204
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +26 -63
- package/dist/instance-lifecycle.js +1 -1
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +2 -0
- package/dist/locale.js.map +1 -1
- package/dist/outbound-handlers.js +19 -7
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +0 -62
- package/dist/outbound-schemas.js +2 -41
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +0 -9
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +2 -0
- package/dist/public-web-link.js +5 -0
- package/dist/public-web-link.js.map +1 -1
- package/dist/search/search-docs.d.ts +1 -1
- package/dist/search/search-docs.js +2 -2
- package/dist/search/search-docs.js.map +1 -1
- package/dist/settings-api.d.ts +6 -0
- package/dist/settings-api.js +14 -2
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +3 -1
- package/dist/settings-change.js.map +1 -1
- package/dist/tool-permissions.js +3 -10
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.d.ts +6 -2
- package/dist/topic-commands.js +2 -0
- package/dist/topic-commands.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/ui/chat-ambient.js +4 -4
- package/dist/ui/chat-commands.js +3 -2
- package/dist/ui/chat-store.js +4 -1
- package/dist/ui/chat-strings.js +6 -42
- package/dist/ui/chat-thread.js +6 -44
- package/dist/ui/panel-chat.js +21 -50
- package/dist/ui/preview-panel.js +3 -15
- package/dist/ui/search-dialog.js +30 -14
- package/dist/ui/settings-appearance.js +1 -3
- package/dist/ui/settings-guests.js +13 -4
- package/dist/ui/settings-learn.js +1 -1
- package/dist/ui/settings-mods.js +5 -12
- package/dist/ui/settings-registry.js +0 -1
- package/dist/ui/settings-strings.js +6 -6
- package/dist/ui/shared/app-prefs.js +17 -17
- package/dist/ui/shared/app-shell.js +12 -8
- package/dist/ui/shared/app-store.js +6 -2
- package/dist/ui/shared/app.css +4 -36
- package/dist/ui/shared/app.js +5 -4
- package/dist/ui/shared/instance-nav.js +9 -9
- package/dist/ui/shared/instance-switch.js +8 -2
- package/dist/ui/shared/panel-view.js +22 -8
- package/dist/ui/shared/view-strings.js +6 -0
- package/dist/usage/usage-api.d.ts +3 -0
- package/dist/usage/usage-api.js +5 -1
- package/dist/usage/usage-api.js.map +1 -1
- package/dist/view-api.d.ts +2 -0
- package/dist/view-api.js +64 -0
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.d.ts +9 -17
- package/dist/web-api.js +255 -371
- package/dist/web-api.js.map +1 -1
- package/dist/web-appearance-tools.d.ts +0 -2
- package/dist/web-appearance-tools.js +2 -5
- package/dist/web-appearance-tools.js.map +1 -1
- package/dist/web-appearance-view.d.ts +0 -6
- package/dist/web-appearance-view.js +0 -1
- package/dist/web-appearance-view.js.map +1 -1
- package/dist/web-appearance.d.ts +14 -7
- package/dist/web-appearance.js +5 -6
- package/dist/web-appearance.js.map +1 -1
- package/dist/web-asset-parts.d.ts +1 -17
- package/dist/web-asset-parts.js +1 -15
- package/dist/web-asset-parts.js.map +1 -1
- package/dist/web-auth.js +1 -7
- package/dist/web-auth.js.map +1 -1
- package/dist/web-chat-history.d.ts +3 -7
- package/dist/web-chat-history.js +10 -17
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-commands.d.ts +9 -1
- package/dist/web-commands.js +13 -0
- package/dist/web-commands.js.map +1 -1
- package/dist/web-experimental.d.ts +2 -11
- package/dist/web-experimental.js +8 -36
- package/dist/web-experimental.js.map +1 -1
- package/dist/web-guest-access.d.ts +40 -2
- package/dist/web-guest-access.js +50 -14
- package/dist/web-guest-access.js.map +1 -1
- package/dist/web-mod-bundle.d.ts +2 -45
- package/dist/web-mod-bundle.js +15 -63
- package/dist/web-mod-bundle.js.map +1 -1
- package/dist/web-mod-store.d.ts +3 -48
- package/dist/web-mod-store.js +30 -97
- package/dist/web-mod-store.js.map +1 -1
- package/dist/web-prefs.d.ts +13 -0
- package/dist/web-prefs.js +38 -0
- package/dist/web-prefs.js.map +1 -1
- package/dist/web-preview.d.ts +1 -11
- package/dist/web-preview.js +6 -20
- package/dist/web-preview.js.map +1 -1
- package/dist/web-session.d.ts +11 -3
- package/dist/web-session.js +22 -11
- package/dist/web-session.js.map +1 -1
- package/dist/web-settings-patch.d.ts +1 -0
- package/dist/web-settings-patch.js +6 -3
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-settings-tools.d.ts +0 -13
- package/dist/web-settings-tools.js +12 -84
- package/dist/web-settings-tools.js.map +1 -1
- package/package.json +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +0 -376
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +0 -672
- package/dist/ui/app-effects.js +0 -251
- package/dist/ui/chat-effect-posters.js +0 -42
- package/dist/ui/chat-effects.js +0 -167
- package/dist/ui/effect-trial-controller.js +0 -124
- package/dist/ui/effect-trial-offer.js +0 -2
- package/dist/ui/effect-trial-panel.js +0 -40
- package/dist/ui/effects-waker.js +0 -2
- package/dist/ui/settings-effects.js +0 -202
- package/dist/ui/settings-owner-effects.js +0 -82
- package/dist/ui/shared/effect-app-runtime.js +0 -299
- package/dist/ui/shared/effect-baseline.js +0 -64
- package/dist/ui/shared/effect-chat-runtime.js +0 -162
- package/dist/ui/shared/effect-cursor.js +0 -126
- package/dist/ui/shared/effect-display-metadata.js +0 -39
- package/dist/ui/shared/effect-frame.js +0 -146
- package/dist/ui/shared/effect-geometry.js +0 -137
- package/dist/ui/shared/effect-host-style.js +0 -10
- package/dist/ui/shared/effect-owner-regions.js +0 -74
- package/dist/ui/shared/effect-owner-source.js +0 -122
- package/dist/ui/shared/effect-policy.js +0 -151
- package/dist/ui/shared/effect-poster-reader.js +0 -98
- package/dist/ui/shared/effect-posters.js +0 -117
- package/dist/ui/shared/effect-profiler.js +0 -184
- package/dist/ui/shared/effect-profiles.js +0 -65
- package/dist/ui/shared/effect-registry.js +0 -545
- package/dist/ui/shared/effect-runtime-loader.js +0 -236
- package/dist/ui/shared/effect-safety.js +0 -153
- package/dist/ui/shared/effect-stock-snapshot.js +0 -43
- package/dist/ui/shared/effect-stock-trust.js +0 -217
- package/dist/ui/shared/effect-trial-shadow.js +0 -62
- package/dist/ui/shared/effect-trial-source.js +0 -103
- package/dist/ui/shared/effect-trial-stage.js +0 -170
- package/dist/ui/shared/effect-trial.css +0 -40
- package/dist/ui/shared/feature-flags.js +0 -127
- package/dist/ui/shared/owner-effect-prefs.js +0 -117
- package/dist/web-effect-bootstrap.d.ts +0 -12
- package/dist/web-effect-bootstrap.js +0 -49
- package/dist/web-effect-bootstrap.js.map +0 -1
- package/dist/web-effect-bundle.d.ts +0 -28
- package/dist/web-effect-bundle.js +0 -171
- package/dist/web-effect-bundle.js.map +0 -1
- package/dist/web-effect-canonical.d.ts +0 -8
- package/dist/web-effect-canonical.js +0 -27
- package/dist/web-effect-canonical.js.map +0 -1
- package/dist/web-effect-library.d.ts +0 -114
- package/dist/web-effect-library.js +0 -530
- package/dist/web-effect-library.js.map +0 -1
- package/dist/web-effect-playback.d.ts +0 -30
- package/dist/web-effect-playback.js +0 -71
- package/dist/web-effect-playback.js.map +0 -1
- package/dist/web-effect-poster.d.ts +0 -8
- package/dist/web-effect-poster.js +0 -52
- package/dist/web-effect-poster.js.map +0 -1
- package/dist/web-effect-profiles.d.ts +0 -118
- package/dist/web-effect-profiles.js +0 -181
- package/dist/web-effect-profiles.js.map +0 -1
- package/dist/web-effect-service.d.ts +0 -93
- package/dist/web-effect-service.js +0 -335
- package/dist/web-effect-service.js.map +0 -1
- package/dist/web-effect-tools.d.ts +0 -48
- package/dist/web-effect-tools.js +0 -129
- package/dist/web-effect-tools.js.map +0 -1
- package/dist/web-effect-trials.d.ts +0 -86
- package/dist/web-effect-trials.js +0 -204
- package/dist/web-effect-trials.js.map +0 -1
- package/dist/web-effect-upload.d.ts +0 -27
- package/dist/web-effect-upload.js +0 -185
- package/dist/web-effect-upload.js.map +0 -1
- package/dist/web-effect-worker.d.ts +0 -8
- package/dist/web-effect-worker.js +0 -170
- package/dist/web-effect-worker.js.map +0 -1
- package/dist/web-effect.d.ts +0 -103
- package/dist/web-effect.js +0 -153
- package/dist/web-effect.js.map +0 -1
- package/dist/web-experimental-http.d.ts +0 -2
- package/dist/web-experimental-http.js +0 -12
- package/dist/web-experimental-http.js.map +0 -1
- package/dist/web-mod-agent.d.ts +0 -50
- package/dist/web-mod-agent.js +0 -207
- package/dist/web-mod-agent.js.map +0 -1
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/** #1629: host-owned static cursor controller; no product startup, metadata reader, Worker or frame. */
|
|
2
|
-
import { effectHostStyle } from "./effect-host-style.js";
|
|
3
|
-
import { checkStaticEffectCursor } from "./effect-runtime-loader.js";
|
|
4
|
-
const owners = new WeakMap(), ATTRIBUTE = "data-agend-effect-cursor";
|
|
5
|
-
let serial = 0;
|
|
6
|
-
const finite = n => typeof n === "number" && Number.isFinite(n) && n >= 0;
|
|
7
|
-
function selection(value) {
|
|
8
|
-
try {
|
|
9
|
-
if (!value || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return null;
|
|
10
|
-
const fields = Object.getOwnPropertyDescriptors(value);
|
|
11
|
-
if (Reflect.ownKeys(fields).length !== 3 || !["id", "generation", "hotspot"].every(k => fields[k] && Object.hasOwn(fields[k], "value"))) return null;
|
|
12
|
-
const { id, generation, hotspot } = value;
|
|
13
|
-
if (typeof id !== "string" || !/^[a-f0-9]{32}$/.test(id) || !Number.isSafeInteger(generation) || generation < 0 || !Array.isArray(hotspot) || Object.getPrototypeOf(hotspot) !== Array.prototype) return null;
|
|
14
|
-
const entries = Object.getOwnPropertyDescriptors(hotspot);
|
|
15
|
-
if (Reflect.ownKeys(entries).length !== 3 || entries.length?.value !== 2 || ![0, 1].every(k => entries[k] && Object.hasOwn(entries[k], "value") && Number.isInteger(entries[k].value) && entries[k].value >= 0 && entries[k].value <= 63)) return null;
|
|
16
|
-
return Object.freeze({ id, generation, hotspot: Object.freeze([entries[0].value, entries[1].value]) });
|
|
17
|
-
} catch { return null; }
|
|
18
|
-
}
|
|
19
|
-
function cursorMatches(value, url, hotspot) {
|
|
20
|
-
const m = typeof value === "string" && /^url\((["']?)(blob:[^"'()\s]+)\1\)\s+(\d+)\s+(\d+),\s*auto$/.exec(value);
|
|
21
|
-
return !!m && m[2] === url && Number(m[3]) === hotspot[0] && Number(m[4]) === hotspot[1];
|
|
22
|
-
}
|
|
23
|
-
/** readImage must be the existing shared loader's settled cursor operation. enabled is a host gate, never effect data. */
|
|
24
|
-
export function createEffectCursor({ window: host, current, readImage, onChange = () => {} } = {}) {
|
|
25
|
-
const unavailable = { ok: false, update() {}, snapshot: () => ({ status: "unavailable", id: null }), dispose() {} };
|
|
26
|
-
if (!host?.document || owners.has(host) || ![current, readImage, onChange, host.getComputedStyle, host.Blob, host.URL?.createObjectURL, host.URL?.revokeObjectURL, host.AbortController, host.setTimeout, host.clearTimeout, host.performance?.now].every(x => typeof x === "function")) return unavailable;
|
|
27
|
-
const token = {}; owners.set(host, token);
|
|
28
|
-
let finishDisposed; const disposed = new Promise(resolve => { finishDisposed = resolve; });
|
|
29
|
-
const doc = host.document; let dead = false, previous = null, wanted = null, pending = null, active = null, blocked = null, generation = 0, checking = false, notifying = false, status = "off";
|
|
30
|
-
const safe = fn => { try { return fn(); } catch { return null; } };
|
|
31
|
-
const release = () => { if (dead && !pending) { if (owners.get(host) === token) owners.delete(host); finishDisposed(); } };
|
|
32
|
-
function alive() {
|
|
33
|
-
if (dead || checking) return false;
|
|
34
|
-
checking = true; const value = safe(() => current() === true && host.document === doc); checking = false;
|
|
35
|
-
return !dead && value === true;
|
|
36
|
-
}
|
|
37
|
-
function now() {
|
|
38
|
-
const value = safe(() => host.performance.now());
|
|
39
|
-
if (!finite(value) || previous !== null && value < previous) { dispose(); return null; }
|
|
40
|
-
previous = value; return value;
|
|
41
|
-
}
|
|
42
|
-
function target() {
|
|
43
|
-
const all = doc.querySelectorAll("#app > .shell"), el = all.length === 1 ? all[0] : null;
|
|
44
|
-
return el?.ownerDocument === doc && el.isConnected === true ? el : null;
|
|
45
|
-
}
|
|
46
|
-
function remove(row) {
|
|
47
|
-
if (!row) return;
|
|
48
|
-
// Removing our exact attribute disarms even a stylesheet whose CSSOM has become unreadable.
|
|
49
|
-
safe(() => { if (row.element.getAttribute(ATTRIBUTE) === row.token) row.element.removeAttribute(ATTRIBUTE); });
|
|
50
|
-
safe(() => row.rule?.style.removeProperty("cursor"));
|
|
51
|
-
safe(() => { const at = [...row.sheet.cssRules].indexOf(row.rule); if (at >= 0) row.sheet.deleteRule(at); });
|
|
52
|
-
if (row.url) { safe(() => host.URL.revokeObjectURL(row.url)); row.url = null; }
|
|
53
|
-
}
|
|
54
|
-
function clear() { const old = active; active = null; remove(old); }
|
|
55
|
-
function tell() { if (dead || notifying) return; notifying = true; try { safe(() => onChange(snapshot())); } finally { notifying = false; } }
|
|
56
|
-
function snapshot() { return Object.freeze({ status: dead ? "unavailable" : status, id: wanted?.selection.id ?? null }); }
|
|
57
|
-
function dispose() {
|
|
58
|
-
if (dead) return; dead = true; generation++; wanted = null; clear();
|
|
59
|
-
if (pending) { safe(() => pending.controller.abort()); remove(pending); }
|
|
60
|
-
safe(() => host.removeEventListener?.("pagehide", dispose)); release();
|
|
61
|
-
}
|
|
62
|
-
if (!alive() || now() === null || !effectHostStyle(host)) { dispose(); return unavailable; }
|
|
63
|
-
safe(() => host.addEventListener?.("pagehide", dispose));
|
|
64
|
-
function valid(op) {
|
|
65
|
-
if (!alive() || pending !== op || op.generation !== generation || op.controller.signal.aborted || wanted !== op.wanted) return false;
|
|
66
|
-
const t = now();
|
|
67
|
-
return t !== null && t >= op.began && t - op.began < 10000 && target() === op.element && effectHostStyle(host) === op.sheet;
|
|
68
|
-
}
|
|
69
|
-
function fail(op) {
|
|
70
|
-
if (!dead && pending === op && generation === op.generation && wanted === op.wanted) { blocked = wanted; status = "unavailable"; tell(); }
|
|
71
|
-
}
|
|
72
|
-
async function load(op) {
|
|
73
|
-
try {
|
|
74
|
-
if (!valid(op)) return;
|
|
75
|
-
const bytes = await readImage(op.wanted.selection, op.controller.signal);
|
|
76
|
-
if (!valid(op)) return;
|
|
77
|
-
const checked = checkStaticEffectCursor(bytes); if (!checked) { fail(op); return; }
|
|
78
|
-
const view = new DataView(checked.buffer, checked.byteOffset, checked.byteLength), width = view.getUint32(16), height = view.getUint32(20);
|
|
79
|
-
if (op.wanted.selection.hotspot.some((v, n) => v >= (n === 0 ? width : height))) { fail(op); return; }
|
|
80
|
-
op.url = host.URL.createObjectURL(new host.Blob([checked], { type: "image/png" }));
|
|
81
|
-
const url = new URL(op.url); if (url.protocol !== "blob:" || url.origin !== host.location.origin || !/^blob:[^"'()\s\\]+$/.test(op.url)) { fail(op); return; }
|
|
82
|
-
const image = doc.createElement("img"); image.alt = ""; image.src = op.url; op.image = image;
|
|
83
|
-
if (typeof image.decode !== "function") { fail(op); return; }
|
|
84
|
-
await image.decode();
|
|
85
|
-
if (!valid(op)) return;
|
|
86
|
-
if (image.naturalWidth !== width || image.naturalHeight !== height) { fail(op); return; }
|
|
87
|
-
if (op.element.hasAttribute(ATTRIBUTE)) { fail(op); return; }
|
|
88
|
-
op.token = `agend-effect-cursor-${++serial}`;
|
|
89
|
-
const index = op.sheet.insertRule(`#app > .shell[${ATTRIBUTE}="${op.token}"]{}`, op.sheet.cssRules.length); op.rule = op.sheet.cssRules[index];
|
|
90
|
-
const hotspot = op.wanted.selection.hotspot, value = `url("${op.url}") ${hotspot[0]} ${hotspot[1]}, auto`;
|
|
91
|
-
op.rule.style.setProperty("cursor", value); op.element.setAttribute(ATTRIBUTE, op.token);
|
|
92
|
-
if (!valid(op) || !cursorMatches(op.rule.style.getPropertyValue("cursor"), op.url, hotspot) || !cursorMatches(host.getComputedStyle(op.element).cursor, op.url, hotspot)) { fail(op); return; }
|
|
93
|
-
active = op; status = "running"; tell();
|
|
94
|
-
} catch { fail(op); }
|
|
95
|
-
finally {
|
|
96
|
-
safe(() => host.clearTimeout(op.timer));
|
|
97
|
-
if (active !== op) remove(op);
|
|
98
|
-
if (pending === op) { pending = null; if (!dead && wanted === op.wanted && !active && blocked !== wanted) { blocked = wanted; status = "unavailable"; tell(); } }
|
|
99
|
-
release(); if (!dead) start();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
function start() {
|
|
103
|
-
if (!alive() || pending || active || !wanted || blocked === wanted) return;
|
|
104
|
-
const began = now(), element = safe(target), sheet = effectHostStyle(host);
|
|
105
|
-
if (began === null || !element || !sheet || element.hasAttribute(ATTRIBUTE)) { blocked = wanted; status = "unavailable"; tell(); return; }
|
|
106
|
-
const op = { wanted, generation, began, element, sheet, controller: new host.AbortController(), url: null, rule: null, token: null, timer: null }; pending = op; status = "loading";
|
|
107
|
-
op.timer = host.setTimeout(() => { if (pending !== op) return; op.controller.abort(); remove(op); fail(op); }, 10000);
|
|
108
|
-
tell(); void load(op);
|
|
109
|
-
}
|
|
110
|
-
function update(value, enabled) {
|
|
111
|
-
if (!alive() || now() === null) { dispose(); return; }
|
|
112
|
-
const next = enabled === true ? selection(value) : null;
|
|
113
|
-
const same = !!next && wanted && next.id === wanted.selection.id && next.generation === wanted.selection.generation && next.hotspot.every((n, i) => n === wanted.selection.hotspot[i]);
|
|
114
|
-
if (!same) {
|
|
115
|
-
generation++; wanted = next ? { selection: next } : null; blocked = null; clear();
|
|
116
|
-
if (pending) { safe(() => pending.controller.abort()); remove(pending); }
|
|
117
|
-
status = next ? "loading" : "off";
|
|
118
|
-
}
|
|
119
|
-
if (active) {
|
|
120
|
-
const row = active;
|
|
121
|
-
if (safe(target) !== row.element || effectHostStyle(host) !== row.sheet || row.element.getAttribute(ATTRIBUTE) !== row.token || !cursorMatches(safe(() => host.getComputedStyle(row.element).cursor), row.url, row.wanted.selection.hotspot)) { clear(); blocked = wanted; status = "unavailable"; }
|
|
122
|
-
}
|
|
123
|
-
start(); tell();
|
|
124
|
-
}
|
|
125
|
-
return { ok: true, update, snapshot, dispose, whenDisposed: () => disposed };
|
|
126
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/** #1629: closed host display metadata. Neither this object nor its token grants a trial read. */
|
|
2
|
-
const KEYS = ["v", "offer", "id", "title", "expiresAt"], ID = /^[0-9a-f]{32}$/;
|
|
3
|
-
const FORBIDDEN = /(?!\u200d)[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
|
|
4
|
-
const VISIBLE = /[^\p{M}\p{C}\p{Z}\u115f\u1160\u3164\uffa0\u2800]/u;
|
|
5
|
-
let graphemes = null;
|
|
6
|
-
export function checkEffectTrialOffer(value) {
|
|
7
|
-
try {
|
|
8
|
-
if (!value || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) return null;
|
|
9
|
-
const keys = Reflect.ownKeys(value);
|
|
10
|
-
if (keys.length !== KEYS.length || keys.some(k => typeof k !== "string" || !KEYS.includes(k))) return null;
|
|
11
|
-
const x = Object.create(null);
|
|
12
|
-
for (const key of KEYS) {
|
|
13
|
-
const d = Object.getOwnPropertyDescriptor(value, key);
|
|
14
|
-
if (!d || !("value" in d) || !d.enumerable) return null;
|
|
15
|
-
x[key] = d.value;
|
|
16
|
-
}
|
|
17
|
-
if (x.v !== 1 || typeof x.offer !== "string" || !ID.test(x.offer) || typeof x.id !== "string" || !ID.test(x.id)
|
|
18
|
-
|| typeof x.title !== "string" || x.title.length > 400 || x.title.normalize("NFC") !== x.title || x.title.trim() !== x.title
|
|
19
|
-
|| FORBIDDEN.test(x.title) || !VISIBLE.test(x.title) || !Number.isSafeInteger(x.expiresAt) || x.expiresAt < 0) return null;
|
|
20
|
-
graphemes ??= new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
21
|
-
let count = 0; for (const _part of graphemes.segment(x.title)) if (++count > 40) return null;
|
|
22
|
-
if (count < 1) return null;
|
|
23
|
-
return Object.freeze({ v: 1, offer: x.offer, id: x.id, title: x.title, expiresAt: x.expiresAt });
|
|
24
|
-
} catch { return null; }
|
|
25
|
-
}
|
|
26
|
-
/** Role and sender are host fields, never inferred from a message's text. Start must separately authorize. */
|
|
27
|
-
export function effectTrialMessage(message) {
|
|
28
|
-
if (!message || message.role !== "status" || message.sender !== "agend" || typeof message.instance !== "string"
|
|
29
|
-
|| message.instance.length < 1 || message.instance.length > 128) return null;
|
|
30
|
-
return checkEffectTrialOffer(message.effectTrial);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function chatEffectIdentity(host) {
|
|
34
|
-
try {
|
|
35
|
-
const b = host.document.body.dataset, x = JSON.parse(host.document.documentElement.getAttribute("data-prefs") || "null");
|
|
36
|
-
if (b.mode !== "full" || b.tier === "guest" || b.publicLink === "1" || b.webTransport === "poll" || !["person", "operator"].includes(x?.ownerScope) || x.unreadable === true || typeof x.ownerEpoch !== "string" || !/^[a-f0-9]{16}$/.test(x.ownerEpoch)) return null;
|
|
37
|
-
return Object.freeze({ epoch: x.ownerEpoch, scope: x.ownerScope });
|
|
38
|
-
} catch { return null; }
|
|
39
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/** #1629: parent-owned Canvas Worker transport; the Chat adapter owns product admission. */
|
|
2
|
-
import { maySendEffectEvent } from "./effect-policy.js";
|
|
3
|
-
const plain = v => !!v && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);
|
|
4
|
-
const exact = (v, keys) => plain(v) && Object.keys(v).sort().join(",") === keys.slice().sort().join(",");
|
|
5
|
-
const finite = v => typeof v === "number" && Number.isFinite(v);
|
|
6
|
-
const bytes = v => new TextEncoder().encode(v).byteLength;
|
|
7
|
-
|
|
8
|
-
/** Snapshot only host-validated code, primitives and raster bytes; no HTML or DOM interface exists. */
|
|
9
|
-
function snapshotRender(raw) {
|
|
10
|
-
if (!exact(raw, ["code", "params", "instance", "width", "height", "dpr", "assets", "wants"]) || typeof raw.code !== "string" || !plain(raw.params) || !Array.isArray(raw.assets) || raw.assets.length > 16 || !Array.isArray(raw.wants) || Array.from(raw.wants).some(v => !["pointer", "state", "reply", "mention", "theme"].includes(v)) || new Set(raw.wants).size !== raw.wants.length) return null;
|
|
11
|
-
if (raw.instance !== null && (typeof raw.instance !== "string" || raw.instance.length < 1 || raw.instance.length > 128)) return null;
|
|
12
|
-
if (!Number.isInteger(raw.width) || raw.width < 1 || raw.width > 4096 || !Number.isInteger(raw.height) || raw.height < 1 || raw.height > 4096 || raw.width * raw.height > 8388608 || !finite(raw.dpr) || raw.dpr < 0.5 || raw.dpr > 3) return null;
|
|
13
|
-
const params = Object.create(null), keys = Object.keys(raw.params);
|
|
14
|
-
if (keys.length > 16) return null;
|
|
15
|
-
for (const key of keys) {
|
|
16
|
-
const value = raw.params[key];
|
|
17
|
-
if (!/^[a-z][a-z0-9_]{0,31}$/.test(key) || !(typeof value === "boolean" || finite(value) || typeof value === "string" && value.length <= 128)) return null;
|
|
18
|
-
params[key] = value;
|
|
19
|
-
}
|
|
20
|
-
let total = bytes(raw.code) + bytes(JSON.stringify(params));
|
|
21
|
-
if (total > 1048576) return null;
|
|
22
|
-
const assets = [], names = new Set();
|
|
23
|
-
for (const a of raw.assets) {
|
|
24
|
-
if (!exact(a, ["name", "mime", "bytes"]) || typeof a.name !== "string" || !/^assets\/[a-z0-9][a-z0-9_-]{0,63}\.(?:png|jpe?g|webp|gif)$/.test(a.name) || names.has(a.name) || !["image/png", "image/jpeg", "image/webp", "image/gif"].includes(a.mime) || !(a.bytes instanceof Uint8Array)) return null;
|
|
25
|
-
total += a.bytes.byteLength;
|
|
26
|
-
if (total > 1048576) return null;
|
|
27
|
-
names.add(a.name);
|
|
28
|
-
assets.push({ name: a.name, mime: a.mime, bytes: new Uint8Array(a.bytes) });
|
|
29
|
-
}
|
|
30
|
-
return { code: raw.code, params: { ...params }, instance: raw.instance, width: raw.width, height: raw.height, dpr: raw.dpr, assets, wants: [...raw.wants] };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Listener transport only; this does not grant HTML-preview or ordinary-effect consent. */
|
|
34
|
-
export function effectTransportReady(host, origin, boot) {
|
|
35
|
-
try {
|
|
36
|
-
const target = new URL(origin), page = new URL(host.location.origin);
|
|
37
|
-
return origin === target.origin && ["http:", "https:"].includes(target.protocol) && ["http:", "https:"].includes(page.protocol)
|
|
38
|
-
&& target.origin !== page.origin && !(page.protocol === "https:" && target.protocol !== "https:") && typeof boot === "string" && /^[0-9a-f]{32}$/.test(boot);
|
|
39
|
-
} catch { return false; }
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** #2118: same URL origin, opaque realm. This helper does not alter product admission or HTML preview consent. */
|
|
43
|
-
export function effectBootstrapReady(host, origin, boot) {
|
|
44
|
-
try {
|
|
45
|
-
const target = new URL(origin), page = new URL(host.location.origin);
|
|
46
|
-
return origin === target.origin && target.origin === page.origin && ["http:", "https:"].includes(target.protocol)
|
|
47
|
-
&& typeof boot === "string" && /^[0-9a-f]{32}$/.test(boot);
|
|
48
|
-
} catch { return false; }
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* All options come from the host, never from an agent event or manifest origin. The registry supplies an admitted
|
|
53
|
-
* root and geometry in a later integration. This primitive alone does not implement caps, clipping or profiling.
|
|
54
|
-
*/
|
|
55
|
-
export function createEffectFrame(options = {}) { return ownedEffectFrame(options, false); }
|
|
56
|
-
|
|
57
|
-
/** Product transport: the same-listener trusted document still has an opaque sandboxed realm. */
|
|
58
|
-
export function createOpaqueEffectFrame(options = {}) { return ownedEffectFrame(options, true); }
|
|
59
|
-
|
|
60
|
-
function ownedEffectFrame({ window: host, root, origin, boot, render, clock = () => performance.now(), onReady = () => {}, onRemove = () => {} } = {}, sameListener) {
|
|
61
|
-
if (!(sameListener ? effectBootstrapReady : effectTransportReady)(host, origin, boot)) return { ok: false, reason: sameListener ? "bootstrap_unknown" : "preview_unknown" };
|
|
62
|
-
const payload = snapshotRender(render);
|
|
63
|
-
if (!payload || !host || !root || root.ownerDocument !== host.document || typeof root.append !== "function" || typeof host.MessageChannel !== "function" || typeof clock !== "function" || typeof onReady !== "function" || typeof onRemove !== "function") return { ok: false, reason: "unsupported" };
|
|
64
|
-
let mounted;
|
|
65
|
-
try { mounted = clock(); } catch { return { ok: false, reason: "clock_unavailable" }; }
|
|
66
|
-
if (!finite(mounted) || mounted < 0) return { ok: false, reason: "clock_unavailable" };
|
|
67
|
-
const frame = host.document.createElement("iframe");
|
|
68
|
-
frame.setAttribute("sandbox", "allow-scripts");
|
|
69
|
-
frame.setAttribute("tabindex", "-1");
|
|
70
|
-
frame.setAttribute("aria-hidden", "true");
|
|
71
|
-
frame.setAttribute("referrerpolicy", "no-referrer");
|
|
72
|
-
frame.className = "agend-effect-frame";
|
|
73
|
-
let removed = false, ready = false, transferred = false, loads = 0, channel = null, timer = null, last = mounted;
|
|
74
|
-
const time = () => {
|
|
75
|
-
let now; try { now = clock(); } catch { return null; }
|
|
76
|
-
if (!finite(now) || now < last) return null;
|
|
77
|
-
last = now; return now;
|
|
78
|
-
};
|
|
79
|
-
const state = () => ({ ready, removed, loads });
|
|
80
|
-
const remove = reason => {
|
|
81
|
-
if (removed) return;
|
|
82
|
-
removed = true;
|
|
83
|
-
host.removeEventListener("message", hello);
|
|
84
|
-
frame.removeEventListener("load", load);
|
|
85
|
-
if (timer !== null) host.clearTimeout(timer);
|
|
86
|
-
if (channel) {
|
|
87
|
-
channel.port1.removeEventListener("message", portReady);
|
|
88
|
-
try { channel.port1.close(); } catch { /* already detached */ }
|
|
89
|
-
try { channel.port2.close(); } catch { /* already transferred */ }
|
|
90
|
-
}
|
|
91
|
-
frame.remove();
|
|
92
|
-
try { onRemove(reason); } catch { /* host callback cannot revive a removed frame */ }
|
|
93
|
-
};
|
|
94
|
-
const load = () => { loads++; if (loads > 1) remove("navigation"); };
|
|
95
|
-
const current = () => {
|
|
96
|
-
if (removed) return false;
|
|
97
|
-
if (frame.parentNode !== root || frame.isConnected !== true) { remove("detached"); return false; }
|
|
98
|
-
const now = time();
|
|
99
|
-
if (now === null) { remove("clock_unavailable"); return false; }
|
|
100
|
-
if (!ready && now - mounted >= 10000) { remove("startup_timeout"); return false; }
|
|
101
|
-
return true;
|
|
102
|
-
};
|
|
103
|
-
const portReady = event => {
|
|
104
|
-
if (removed || ready) return;
|
|
105
|
-
let data; try { data = event.data; } catch { return; }
|
|
106
|
-
if (!exact(data, ["type"]) || data.type !== "ready") return;
|
|
107
|
-
if (!current()) return;
|
|
108
|
-
ready = true;
|
|
109
|
-
host.clearTimeout(timer); timer = null;
|
|
110
|
-
channel.port1.removeEventListener("message", portReady);
|
|
111
|
-
try { onReady(); } catch { remove("host_callback"); }
|
|
112
|
-
};
|
|
113
|
-
const hello = event => {
|
|
114
|
-
if (removed || transferred) return;
|
|
115
|
-
let data; try { data = event.data; } catch { return; }
|
|
116
|
-
// Opaque sandbox messages have origin null. Only this exact WindowProxy and boot may receive the private port.
|
|
117
|
-
if (event.source !== frame.contentWindow || event.origin !== "null" || event.ports?.length || !exact(data, ["v", "type", "ch", "boot"]) || data.v !== 1 || data.type !== "ready" || data.ch !== null || data.boot !== boot) return;
|
|
118
|
-
if (!current()) return;
|
|
119
|
-
transferred = true;
|
|
120
|
-
host.removeEventListener("message", hello);
|
|
121
|
-
try {
|
|
122
|
-
channel = new host.MessageChannel();
|
|
123
|
-
channel.port1.addEventListener("message", portReady);
|
|
124
|
-
channel.port1.start();
|
|
125
|
-
const nonce = new Uint8Array(16); host.crypto.getRandomValues(nonce);
|
|
126
|
-
const ch = Array.from(nonce, b => b.toString(16).padStart(2, "0")).join("");
|
|
127
|
-
const { wants, ...message } = payload;
|
|
128
|
-
// '*' is required for the opaque destination; the source WindowProxy was checked immediately above.
|
|
129
|
-
frame.contentWindow.postMessage({ v: 1, type: "render", ch, ...message }, "*", [channel.port2]);
|
|
130
|
-
} catch { remove("transport"); }
|
|
131
|
-
};
|
|
132
|
-
const deadline = () => {
|
|
133
|
-
if (!current() || ready) return;
|
|
134
|
-
timer = host.setTimeout(deadline, Math.max(1, 10000 - (last - mounted)));
|
|
135
|
-
};
|
|
136
|
-
host.addEventListener("message", hello);
|
|
137
|
-
frame.addEventListener("load", load);
|
|
138
|
-
timer = host.setTimeout(deadline, 10000);
|
|
139
|
-
try { frame.src = origin + (sameListener ? "/effect-bootstrap" : "/effect"); root.append(frame); } catch { remove("mount_failed"); }
|
|
140
|
-
return { ok: true, element: frame, state, remove,
|
|
141
|
-
send(event) {
|
|
142
|
-
if (!ready || !current() || !maySendEffectEvent(event, { instance: payload.instance, wants: payload.wants })) return false;
|
|
143
|
-
try { channel.port1.postMessage(event); return true; } catch { remove("transport"); return false; }
|
|
144
|
-
},
|
|
145
|
-
};
|
|
146
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/** #1629: host-only fixed viewport geometry and trust holes through the already-loaded, fixed same-origin app stylesheet. No inline attributes. */
|
|
2
|
-
import { effectHostStyle } from "./effect-host-style.js";
|
|
3
|
-
import { effectTrustClip } from "./effect-policy.js";
|
|
4
|
-
import { effectStockSnapshot, effectStockAllows } from "./effect-stock-snapshot.js";
|
|
5
|
-
let serial = 0;
|
|
6
|
-
// Native CSSOM canonicalizes SVG command whitespace. Compare bounded tokens, never whitespace-stripped digits.
|
|
7
|
-
function sameClip(expected, written) {
|
|
8
|
-
if (expected === "none") return written === "none";
|
|
9
|
-
const tokens = value => {
|
|
10
|
-
if (typeof value !== "string" || value.length > 100000) return null;
|
|
11
|
-
const m = /^path\(\s*evenodd\s*,\s*["']([^"']*)["']\s*\)$/.exec(value);
|
|
12
|
-
if (!m) return null;
|
|
13
|
-
const out = [], rest = m[1].replace(/[MHVZ]|-?\d+(?:\.\d+)?/g, token => { out.push(/[MHVZ]/.test(token) ? token : Number(token)); return ""; });
|
|
14
|
-
return /^[\s,]*$/.test(rest) ? JSON.stringify(out) : null;
|
|
15
|
-
};
|
|
16
|
-
const a = tokens(expected), b = tokens(written);
|
|
17
|
-
return a !== null && a === b;
|
|
18
|
-
}
|
|
19
|
-
const number = n => typeof n === "number" && Number.isFinite(n) && Math.abs(n) <= 1e7;
|
|
20
|
-
/** A fixed frame must share viewport axes. Unknown transformed/filter/contained ancestors fail closed. */
|
|
21
|
-
export function hostEffectSpace(host, element) {
|
|
22
|
-
try {
|
|
23
|
-
if (element?.ownerDocument !== host.document || element.isConnected !== true || typeof host.getComputedStyle !== "function") return false;
|
|
24
|
-
let el = element, seen = 0;
|
|
25
|
-
while (el) {
|
|
26
|
-
if (++seen > 128 || el.ownerDocument !== host.document) return false;
|
|
27
|
-
const s = host.getComputedStyle(el);
|
|
28
|
-
for (const key of ["transform", "perspective", "filter", "backdropFilter", "rotate", "scale", "translate", "offsetPath"]) if (s[key] !== undefined && s[key] !== "" && s[key] !== "none") return false;
|
|
29
|
-
if (s.contain !== "none" || s.willChange !== "auto" || s.contentVisibility !== undefined && s.contentVisibility !== "" && s.contentVisibility !== "visible" || s.zoom !== undefined && !["", "normal", "1"].includes(s.zoom)) return false;
|
|
30
|
-
if (el === host.document.documentElement) return true;
|
|
31
|
-
el = el.parentElement;
|
|
32
|
-
}
|
|
33
|
-
return false;
|
|
34
|
-
} catch { return false; }
|
|
35
|
-
}
|
|
36
|
-
export function hostEffectRect(element) {
|
|
37
|
-
try {
|
|
38
|
-
if (element?.isConnected !== true) return null;
|
|
39
|
-
const r = element.getBoundingClientRect();
|
|
40
|
-
if (![r.x, r.y, r.width, r.height].every(number) || r.width < 1 || r.height < 1) return null;
|
|
41
|
-
const x = Math.floor(r.x), y = Math.floor(r.y), width = Math.ceil(r.x + r.width) - x, height = Math.ceil(r.y + r.height) - y;
|
|
42
|
-
return width <= 4096 && height <= 4096 && width * height <= 8388608 ? { x, y, width, height } : null;
|
|
43
|
-
} catch { return null; }
|
|
44
|
-
}
|
|
45
|
-
export function createEffectGeometry({ window: host } = {}) {
|
|
46
|
-
let sheet;
|
|
47
|
-
try {
|
|
48
|
-
if (!host?.document || host.CSS?.supports("clip-path", 'path(evenodd, "M0 0H1V1H0Z")') !== true) return { ok: false, reason: "geometry_unsupported" };
|
|
49
|
-
sheet = effectHostStyle(host); if (!sheet) throw Error("app stylesheet unavailable");
|
|
50
|
-
} catch { return { ok: false, reason: "geometry_unsupported" }; }
|
|
51
|
-
const bound = new Set(); let disposed = false;
|
|
52
|
-
function bind(frame, { layer, by } = {}) {
|
|
53
|
-
if (disposed || !hostEffectSpace(host, frame) || !["front", "behind"].includes(layer) || !frame.classList) {
|
|
54
|
-
// The caller must also close its factory handle. Never leave its own rejected frame painting.
|
|
55
|
-
if (frame?.ownerDocument === host.document && typeof frame.remove === "function") frame.remove();
|
|
56
|
-
return { ok: false, reason: "geometry_unknown" };
|
|
57
|
-
}
|
|
58
|
-
const token = `agend-effect-geometry-${++serial}`;
|
|
59
|
-
let rule;
|
|
60
|
-
try {
|
|
61
|
-
const at = sheet.insertRule(`.${token}{}`, sheet.cssRules.length); rule = sheet.cssRules[at];
|
|
62
|
-
if (!rule?.style) throw Error("rule unavailable");
|
|
63
|
-
for (const [key, value] of Object.entries({ position: "fixed", border: "0px", margin: "0px", padding: "0px", "pointer-events": "none", "z-index": layer === "front" ? "20" : "0", opacity: layer === "front" && by !== "human" ? "0.85" : "1", visibility: "hidden", "background-color": "transparent", "box-sizing": "border-box", "box-shadow": "none", outline: "none", transition: "none", animation: "none", "min-width": "0px", "min-height": "0px", "max-width": "none", "max-height": "none" })) rule.style.setProperty(key, value, "important");
|
|
64
|
-
frame.classList.add(token);
|
|
65
|
-
// Binding is accepted BEFORE the first sync/ready. Prove its initial hidden state before the caller may yield.
|
|
66
|
-
if (host.getComputedStyle(frame).visibility !== "hidden") throw Error("initial hiding cascade lost");
|
|
67
|
-
} catch {
|
|
68
|
-
try { const at = [...sheet.cssRules].indexOf(rule); if (at >= 0) sheet.deleteRule(at); } catch { /* cleanup cannot precede physical refusal */ }
|
|
69
|
-
try { frame.classList.remove(token); } catch { /* the refused frame will not remain in the document */ }
|
|
70
|
-
frame.remove();
|
|
71
|
-
return { ok: false, reason: "geometry_unknown" };
|
|
72
|
-
}
|
|
73
|
-
let dead = false, shown = false;
|
|
74
|
-
const hide = () => {
|
|
75
|
-
shown = false;
|
|
76
|
-
try {
|
|
77
|
-
rule.style.setProperty("visibility", "hidden", "important");
|
|
78
|
-
if (frame.isConnected === true && host.getComputedStyle(frame).visibility !== "hidden") { frame.remove(); return false; }
|
|
79
|
-
return true;
|
|
80
|
-
} catch { frame.remove(); return false; }
|
|
81
|
-
};
|
|
82
|
-
const handle = { ok: true, hide,
|
|
83
|
-
/** Same-turn host paint snapshot is mandatory, including behind/human placements. No DOM fallback. */
|
|
84
|
-
sync(rect, stock) {
|
|
85
|
-
const refuse = () => { hide(); return false; };
|
|
86
|
-
const proof = effectStockSnapshot(stock);
|
|
87
|
-
if (!effectStockAllows(proof, { layer, by })) return refuse();
|
|
88
|
-
if (dead || disposed || !hostEffectSpace(host, frame) || !rect || ![rect.x, rect.y, rect.width, rect.height].every(number) || ![rect.x, rect.y, rect.width, rect.height].every(Number.isInteger) || rect.width < 1 || rect.height < 1 || rect.width > 4096 || rect.height > 4096 || rect.width * rect.height > 8388608) return refuse();
|
|
89
|
-
let path = "none";
|
|
90
|
-
if (layer === "front" && by !== "human") {
|
|
91
|
-
const clipped = effectTrustClip(rect, proof.rects);
|
|
92
|
-
if (!clipped.ok) return refuse();
|
|
93
|
-
path = clipped.path;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const matches = visibility => {
|
|
98
|
-
const style = host.getComputedStyle(frame), actual = frame.getBoundingClientRect();
|
|
99
|
-
return style.position === "fixed" && style.pointerEvents === "none" && style.visibility === visibility
|
|
100
|
-
&& style.opacity === (layer === "front" && by !== "human" ? "0.85" : "1") && style.zIndex === (layer === "front" ? "20" : "0")
|
|
101
|
-
&& sameClip(path, rule.style.getPropertyValue("clip-path")) && sameClip(path, style.clipPath)
|
|
102
|
-
&& ["x", "y", "width", "height"].every(key => number(actual[key]) && Math.abs(actual[key] - rect[key]) < 0.02)
|
|
103
|
-
&& frame.isConnected === true && frame.ownerDocument === host.document;
|
|
104
|
-
};
|
|
105
|
-
// A fresh proof and actual viewport/clip are still checked every frame. Rewriting an
|
|
106
|
-
// already verified presentation makes WebKit repeatedly invalidate the iframe's paint.
|
|
107
|
-
if (shown && matches("visible")) return true;
|
|
108
|
-
// Changed or unproven geometry is hidden before writes and verified before reveal.
|
|
109
|
-
if (!hide()) return false;
|
|
110
|
-
for (const key of ["x", "y", "width", "height"]) rule.style.setProperty(key === "x" ? "left" : key === "y" ? "top" : key, `${rect[key]}px`, "important");
|
|
111
|
-
rule.style.setProperty("clip-path", path, "important");
|
|
112
|
-
if (!sameClip(path, rule.style.getPropertyValue("clip-path"))) return false;
|
|
113
|
-
// The stylesheet can lose the cascade to another host rule. Verify the actual viewport and clip
|
|
114
|
-
// before revealing: holes expressed in planned coordinates cannot protect a transformed frame.
|
|
115
|
-
if (!matches("hidden")) return false;
|
|
116
|
-
rule.style.setProperty("visibility", "visible", "important");
|
|
117
|
-
shown = true;
|
|
118
|
-
return true;
|
|
119
|
-
} catch { hide(); return false; }
|
|
120
|
-
},
|
|
121
|
-
remove() {
|
|
122
|
-
if (dead) return; dead = true; hide(); bound.delete(handle);
|
|
123
|
-
if (frame.isConnected === true) frame.remove();
|
|
124
|
-
try { frame.classList.remove(token); const at = [...sheet.cssRules].indexOf(rule); if (at >= 0) sheet.deleteRule(at); } catch { frame.remove(); }
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
bound.add(handle); return handle;
|
|
128
|
-
}
|
|
129
|
-
return { ok: true, bind,
|
|
130
|
-
hide() { for (const h of bound) h.hide(); },
|
|
131
|
-
dispose() {
|
|
132
|
-
if (disposed) return; disposed = true;
|
|
133
|
-
for (const h of [...bound]) h.remove();
|
|
134
|
-
// The host link and its other rules remain intact. Each binding removed only its own rule.
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/** Fixed same-origin host stylesheet only. No new sheet, inline style, owner CSS or supplied URL. */
|
|
2
|
-
export function effectHostStyle(host) {
|
|
3
|
-
try {
|
|
4
|
-
const link = host?.document?.querySelector('link[rel="stylesheet"][href="/assets/app.css"]');
|
|
5
|
-
const href = new URL("/assets/app.css", host.location.origin).href;
|
|
6
|
-
if (!link || link.tagName !== "LINK" || link.ownerDocument !== host.document || link.isConnected !== true || link.disabled !== false || !link.sheet || link.sheet.ownerNode !== link || link.sheet.href !== href || link.sheet.disabled !== false) return null;
|
|
7
|
-
const sheet = link.sheet;
|
|
8
|
-
return typeof sheet.insertRule === "function" && typeof sheet.deleteRule === "function" && sheet.cssRules ? sheet : null;
|
|
9
|
-
} catch { return null; }
|
|
10
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/** #1629: host-only personal region targets on the currently proven stock shell. No selection, code or frame authority. */
|
|
2
|
-
import { createEffectGeometry, hostEffectRect, hostEffectSpace } from "./effect-geometry.js";
|
|
3
|
-
import { effectStockSnapshot } from "./effect-stock-snapshot.js";
|
|
4
|
-
const finite = n => typeof n === "number" && Number.isFinite(n);
|
|
5
|
-
function viewport(host) {
|
|
6
|
-
const width = host.innerWidth, height = host.innerHeight;
|
|
7
|
-
return [width, height].every(n => Number.isInteger(n) && n >= 1 && n <= 4096) && width * height <= 8388608 ? { x: 0, y: 0, width, height } : null;
|
|
8
|
-
}
|
|
9
|
-
/** An anchored box uses edge padding; center ignores the offset. Oversized boxes shrink within that padding. */
|
|
10
|
-
export function ownerEffectBoxRect(box, size) {
|
|
11
|
-
try {
|
|
12
|
-
if (!box || ![Object.prototype, null].includes(Object.getPrototypeOf(box))) return null;
|
|
13
|
-
const fields = Object.getOwnPropertyDescriptors(box), keys = Reflect.ownKeys(fields);
|
|
14
|
-
if (keys.length !== 4 || !["anchor", "width", "height", "offset"].every(k => fields[k] && Object.hasOwn(fields[k], "value"))) return null;
|
|
15
|
-
const b = Object.fromEntries(Object.entries(fields).map(([k, d]) => [k, d.value]));
|
|
16
|
-
if (!["top-left", "top-right", "bottom-left", "bottom-right", "center"].includes(b.anchor) || ![b.width, b.height].every(n => Number.isInteger(n) && n >= 32 && n <= 480) || !Number.isInteger(b.offset) || b.offset < 0 || b.offset > 64 || !size || ![size.width, size.height].every(n => Number.isInteger(n) && n >= 1 && n <= 4096) || size.width * size.height > 8388608) return null;
|
|
17
|
-
const pad = b.anchor === "center" ? 0 : b.offset, width = Math.min(b.width, size.width - pad * 2), height = Math.min(b.height, size.height - pad * 2);
|
|
18
|
-
if (width < 1 || height < 1) return null;
|
|
19
|
-
const x = b.anchor === "center" ? Math.floor((size.width - width) / 2) : b.anchor.endsWith("right") ? size.width - pad - width : pad;
|
|
20
|
-
const y = b.anchor === "center" ? Math.floor((size.height - height) / 2) : b.anchor.startsWith("bottom") ? size.height - pad - height : pad;
|
|
21
|
-
return { x, y, width, height };
|
|
22
|
-
} catch { return null; }
|
|
23
|
-
}
|
|
24
|
-
/** Caller supplies the authenticated lease and fresh stock proof. Header/sidebar are real stock elements;
|
|
25
|
-
* page/box are empty hidden host DOM references, never author DOM. They consume no frame/cap slot.
|
|
26
|
-
* A null result requires removing that descriptor; this primitive cannot retire a caller-owned effect frame. */
|
|
27
|
-
export function createOwnerEffectRegions({ window: host, current, factories = {} } = {}) {
|
|
28
|
-
let dead = false, root = null, geometry = null;
|
|
29
|
-
const targets = new Map();
|
|
30
|
-
const dispose = () => { if (dead) return; dead = true; geometry?.dispose?.(); root?.remove(); targets.clear(); };
|
|
31
|
-
const refuse = () => { for (const row of targets.values()) row.binding.hide(); return null; };
|
|
32
|
-
const unavailable = () => ({ ok: false, dispose });
|
|
33
|
-
const alive = () => { if (dead) return false; const valid = current() === true; return !dead && valid && host.document.body.dataset.mode === "full"; };
|
|
34
|
-
try {
|
|
35
|
-
if (!host?.document || typeof current !== "function" || typeof host.getComputedStyle !== "function" || !alive()) return unavailable();
|
|
36
|
-
root = host.document.createElement("div"); root.className = "agend-effect-layer"; host.document.body.append(root);
|
|
37
|
-
geometry = (factories.geometry ?? createEffectGeometry)({ window: host }); if (!geometry.ok) { dispose(); return unavailable(); }
|
|
38
|
-
} catch { dispose(); return unavailable(); }
|
|
39
|
-
function panel() {
|
|
40
|
-
const nodes = host.document.querySelectorAll(".main > .panel"); if (nodes.length !== 1) return null;
|
|
41
|
-
const el = nodes[0], kind = el.getAttribute("data-effect-panel");
|
|
42
|
-
return kind === null ? el.classList.contains("p-chat") ? el : null
|
|
43
|
-
: ["fleet", "needs", "settings", "view", "log", "details"].includes(kind) && el.classList.contains(`p-${kind}`) ? el : null;
|
|
44
|
-
}
|
|
45
|
-
function resolve(slot, box, stock) {
|
|
46
|
-
try {
|
|
47
|
-
if (!alive() || !effectStockSnapshot(stock)) return refuse();
|
|
48
|
-
const surface = panel(), size = viewport(host); if (!surface || !size) return refuse();
|
|
49
|
-
if (slot === "fx.header" || slot === "fx.sidebar") {
|
|
50
|
-
const selector = slot === "fx.header" ? ".main > .panel > .panel-head[data-trust=header]" : ".shell > .sidebar";
|
|
51
|
-
const nodes = host.document.querySelectorAll(selector); if (nodes.length !== 1) return null;
|
|
52
|
-
const el = nodes[0], s = host.getComputedStyle(el), rect = hostEffectRect(el);
|
|
53
|
-
return alive() && panel() === surface && s.display !== "none" && s.visibility === "visible" && hostEffectSpace(host, el) && rect && rect.x + rect.width > 0 && rect.y + rect.height > 0 && rect.x < size.width && rect.y < size.height ? el : null;
|
|
54
|
-
}
|
|
55
|
-
if (!["fx.page", "fx.box"].includes(slot)) return null;
|
|
56
|
-
const rect = slot === "fx.page" ? size : ownerEffectBoxRect(box, size); if (!rect) return refuse();
|
|
57
|
-
let row = targets.get(slot);
|
|
58
|
-
if (!row) {
|
|
59
|
-
const element = host.document.createElement("div"); element.className = "agend-owner-effect-target"; element.setAttribute("aria-hidden", "true"); root.append(element);
|
|
60
|
-
const binding = geometry.bind(element, { layer: "behind", by: "human" });
|
|
61
|
-
if (!binding.ok) { element.remove(); return null; }
|
|
62
|
-
row = { element, binding }; targets.set(slot, row);
|
|
63
|
-
}
|
|
64
|
-
// Empty reference only: the synchronous existing hidden-first verification is followed by hide before yielding.
|
|
65
|
-
// The future registry owns the actual effect's layer/provenance/caps and independent fresh paint check.
|
|
66
|
-
if (!row.binding.sync(rect, stock)) { row.binding.remove(); targets.delete(slot); return null; }
|
|
67
|
-
row.binding.hide();
|
|
68
|
-
const actual = hostEffectRect(row.element);
|
|
69
|
-
if (!alive() || panel() !== surface || row.element.isConnected !== true || host.getComputedStyle(row.element).visibility !== "hidden" || !actual || !["x", "y", "width", "height"].every(k => finite(actual[k]) && actual[k] === rect[k])) { row.binding.remove(); targets.delete(slot); return null; }
|
|
70
|
-
return row.element;
|
|
71
|
-
} catch { return refuse(); }
|
|
72
|
-
}
|
|
73
|
-
return { ok: true, root, resolve, dispose };
|
|
74
|
-
}
|