@levo-so/site-runtime 0.1.113

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.
@@ -0,0 +1,224 @@
1
+ //#region src/inspector/dom.ts
2
+ var e = {
3
+ ring: 99997,
4
+ overlay: 99998,
5
+ label: 99999
6
+ }, t = {
7
+ hover: "#3b82f6",
8
+ hoverBg: "rgba(59,130,246,0.12)",
9
+ hoverLabelBg: "#1d4ed8",
10
+ select: "#16a34a",
11
+ selectGlow: "rgba(22,163,74,0.18)"
12
+ }, n = (e) => {
13
+ let t = e?.getAttribute("data-tsd-source");
14
+ return t ? t.replace(/^\//, "") : null;
15
+ }, r = (e) => Array.from(document.querySelectorAll("[data-tsd-source]")).filter((t) => n(t) === e), i = (e, t) => {
16
+ let n = r(t).indexOf(e);
17
+ return n < 0 ? 0 : n;
18
+ }, a = (e, t = 0) => {
19
+ let n = r(e);
20
+ return n[t] ?? n[0] ?? null;
21
+ }, o = (e, t) => {
22
+ let n = (e.getAttribute("data-staged-key") || "").split(/\s+/).filter(Boolean);
23
+ n.includes(t) || e.setAttribute("data-staged-key", [...n, t].join(" "));
24
+ }, s = (e, t) => {
25
+ let n = (e.getAttribute("data-staged-key") || "").split(/\s+/).filter((e) => e && e !== t);
26
+ n.length ? e.setAttribute("data-staged-key", n.join(" ")) : e.removeAttribute("data-staged-key");
27
+ }, c = (e, t) => {
28
+ let n = Object.entries(t.styles).filter(([e]) => /^-?[a-zA-Z][a-zA-Z0-9-]*$/.test(e)).map(([e, t]) => `${e}:${String(t).replace(/[;{}]/g, "")} !important;`).join("");
29
+ if (!n) return "";
30
+ let r = `[data-staged-key~="${CSS.escape(e)}"]{${n}}`, i = t.media?.replace(/[;{}]/g, "").trim();
31
+ return i ? `@media ${i}{${r}}` : r;
32
+ }, l = (e) => {
33
+ let t = e.tagName.toLowerCase();
34
+ return t === "input" ? {
35
+ checkbox: "Checkbox",
36
+ radio: "Radio button",
37
+ submit: "Button",
38
+ button: "Button",
39
+ range: "Slider",
40
+ file: "File upload"
41
+ }[(e.getAttribute("type") || "text").toLowerCase()] || "Input" : {
42
+ a: "Link",
43
+ button: "Button",
44
+ img: "Image",
45
+ picture: "Image",
46
+ svg: "Icon",
47
+ h1: "Heading",
48
+ h2: "Heading",
49
+ h3: "Heading",
50
+ h4: "Heading",
51
+ h5: "Heading",
52
+ h6: "Heading",
53
+ p: "Text",
54
+ span: "Text",
55
+ strong: "Text",
56
+ em: "Text",
57
+ ul: "List",
58
+ ol: "List",
59
+ li: "List item",
60
+ nav: "Navigation",
61
+ header: "Header",
62
+ footer: "Footer",
63
+ form: "Form",
64
+ label: "Label",
65
+ select: "Dropdown",
66
+ textarea: "Text field",
67
+ video: "Video",
68
+ section: "Section",
69
+ article: "Article",
70
+ main: "Section",
71
+ aside: "Sidebar",
72
+ div: "Container"
73
+ }[t] || `${t.charAt(0).toUpperCase()}${t.slice(1)}`;
74
+ }, u = 36, d = (e) => {
75
+ let t = l(e), n = ((e) => e.replace(/\s+/g, " ").trim())(e.getAttribute("aria-label") || e.getAttribute("alt") || e.getAttribute("title") || e.getAttribute("name") || "");
76
+ return n ? `${t} · ${n.length > u ? `${n.slice(0, u - 1).trimEnd()}…` : n}` : t;
77
+ }, f = (e) => {
78
+ let t = [], n = e;
79
+ for (; n && n.nodeType === 1 && n !== document.body && t.length < 8;) t.unshift(n.tagName.toLowerCase()), n = n.parentElement;
80
+ return t.join(" > ");
81
+ }, p = () => {
82
+ let n = document.createElement("div");
83
+ n.style.cssText = `position:fixed;pointer-events:none;z-index:${e.overlay};background:${t.hoverBg};outline:2px solid ${t.hover};border-radius:2px;display:none`;
84
+ let r = document.createElement("div");
85
+ r.style.cssText = `position:fixed;pointer-events:none;z-index:${e.label};background:${t.hoverLabelBg};color:#fff;font:11px/1.6 ui-monospace,monospace;padding:1px 6px;border-radius:4px;display:none;max-width:70vw;white-space:nowrap;overflow:hidden;text-overflow:ellipsis`;
86
+ let i = document.createElement("div");
87
+ i.style.cssText = `position:fixed;pointer-events:none;z-index:${e.ring};outline:2px solid ${t.select};outline-offset:1px;border-radius:3px;box-shadow:0 0 0 4px ${t.selectGlow};display:none`;
88
+ let a = document.createElement("div");
89
+ return a.style.cssText = `position:fixed;pointer-events:none;z-index:${e.label};background:${t.select};color:#fff;font:11px/1.6 ui-monospace,monospace;padding:1px 6px;border-radius:4px;display:none;max-width:70vw;white-space:nowrap;overflow:hidden;text-overflow:ellipsis`, {
90
+ overlay: n,
91
+ label: r,
92
+ ring: i,
93
+ ringLabel: a
94
+ };
95
+ }, m = (e, t, n) => {
96
+ let r = n.getBoundingClientRect();
97
+ e.style.display = "block", e.style.left = `${r.left}px`, e.style.top = `${r.top}px`, e.style.width = `${r.width}px`, e.style.height = `${r.height}px`, t.style.display = "block", t.textContent = d(n), t.style.left = `${Math.max(4, r.left)}px`, t.style.top = `${r.top > 26 ? r.top - 22 : r.bottom + 4}px`;
98
+ }, h = (e) => {
99
+ let t = (e instanceof Element ? e : null)?.closest("a[href]");
100
+ if (!t || t.target === "_blank") return null;
101
+ let n;
102
+ try {
103
+ n = new URL(t.href, location.href);
104
+ } catch {
105
+ return null;
106
+ }
107
+ return n.protocol !== "http:" && n.protocol !== "https:" || n.origin === location.origin ? null : n.href;
108
+ }, g = (e) => {
109
+ if (!e.childNodes.length) return !1;
110
+ let t = !1;
111
+ for (let n of e.childNodes) {
112
+ if (n.nodeType !== 3) return !1;
113
+ (n.textContent ?? "").trim() && (t = !0);
114
+ }
115
+ return t;
116
+ }, _ = 2e3, v = (e) => {
117
+ let t = [], r = e;
118
+ for (; r && r.nodeType === 1 && r !== document.body && t.length < 8;) {
119
+ let e = n(r);
120
+ e && t.unshift({
121
+ tag: r.tagName.toLowerCase(),
122
+ text: (r.textContent || "").trim().slice(0, 60),
123
+ source: e
124
+ }), r = r.parentElement;
125
+ }
126
+ return t;
127
+ }, y = (e) => {
128
+ let t = n(e), a = t ? r(t).length : 1, o = t ? i(e, t) : 0, s = t && g(e) ? (e.textContent ?? "").replace(/\s+/g, " ").trim().slice(0, _) : void 0, c = b(e);
129
+ return {
130
+ tag: e.tagName.toLowerCase(),
131
+ classes: typeof e.className == "string" ? e.className.trim() : "",
132
+ text: (e.innerText || "").trim().slice(0, 120),
133
+ label: d(e),
134
+ path: f(e),
135
+ source: t,
136
+ index: o,
137
+ occurrences: a,
138
+ ancestors: v(e),
139
+ ...s === void 0 ? {} : { fullText: s },
140
+ ...c ? { sourceBinding: c } : {}
141
+ };
142
+ }, b = (e) => {
143
+ let t = e.closest("[data-levo-source]");
144
+ if (!t) return;
145
+ let r = t.getAttribute("data-levo-source") ?? "", a = n(t), o;
146
+ try {
147
+ o = JSON.parse(r);
148
+ } catch {
149
+ o = void 0;
150
+ }
151
+ return {
152
+ raw: r,
153
+ element: {
154
+ source: a ?? "",
155
+ index: a ? i(t, a) : 0
156
+ },
157
+ ...o === void 0 ? {} : { config: o }
158
+ };
159
+ }, x = () => Array.from(document.querySelectorAll("[data-levo-source]")).map((e) => {
160
+ let t = e.getAttribute("data-levo-source") ?? "", r = n(e), a;
161
+ try {
162
+ a = JSON.parse(t);
163
+ } catch {
164
+ a = void 0;
165
+ }
166
+ return {
167
+ raw: t,
168
+ element: {
169
+ source: r ?? "",
170
+ index: r ? i(e, r) : 0
171
+ },
172
+ tag: e.tagName.toLowerCase(),
173
+ label: d(e),
174
+ ...a === void 0 ? {} : { config: a }
175
+ };
176
+ }), S = 14, C = 2e3, w = 100, T = 48, E = new Set([
177
+ "script",
178
+ "style",
179
+ "template",
180
+ "noscript"
181
+ ]), D = (e, t) => t.includes(e) || e.id === "studio-staged" || E.has(e.tagName.toLowerCase()), O = (e) => {
182
+ let t = !1, r = 0, i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map();
183
+ for (let e of Array.from(document.querySelectorAll("[data-tsd-source]"))) {
184
+ let t = n(e);
185
+ if (!t) continue;
186
+ let r = a.get(t) ?? 0;
187
+ i.set(e, r), a.set(t, r + 1);
188
+ }
189
+ let o = (e) => {
190
+ for (let t of e.childNodes) {
191
+ if (t.nodeType !== 3) continue;
192
+ let e = (t.textContent ?? "").replace(/\s+/g, " ").trim();
193
+ if (e) return e.length > T ? `${e.slice(0, T).trimEnd()}…` : e;
194
+ }
195
+ }, s = (a, c) => {
196
+ r++;
197
+ let l = n(a), u = o(a), d = Array.from(a.children).filter((t) => !D(t, e)), f = {
198
+ tag: a.tagName.toLowerCase(),
199
+ ...l ? {
200
+ source: l,
201
+ index: i.get(a) ?? 0
202
+ } : {},
203
+ ...u === void 0 ? {} : { text: u },
204
+ ...a.classList.contains("hidden") ? { hidden: !0 } : {},
205
+ childCount: d.length,
206
+ children: []
207
+ };
208
+ if (c >= S) return d.length && (t = !0), f;
209
+ for (let e of d) {
210
+ if (f.children.length >= w || r >= C) {
211
+ t = !0;
212
+ break;
213
+ }
214
+ f.children.push(s(e, c + 1));
215
+ }
216
+ return f;
217
+ };
218
+ return {
219
+ tree: s(document.body, 0),
220
+ truncated: t
221
+ };
222
+ };
223
+ //#endregion
224
+ export { t as COLOR, o as addStagedKey, O as buildLayersTree, p as createOverlayNodes, c as cssRuleFor, x as enumerateSources, h as externalLinkHref, g as isTextLeaf, a as nodeForSource, r as nodesForSource, m as positionBox, s as removeStagedKey, y as selectedPayload, i as sourceIndex, n as sourceOf };
@@ -0,0 +1,7 @@
1
+ import { IPreviewToEditor } from './protocol';
2
+ export type IEditorChannel = ReturnType<typeof createEditorChannel>;
3
+ export declare const createEditorChannel: () => {
4
+ send: (msg: IPreviewToEditor) => void;
5
+ acceptOrigin: (e: MessageEvent) => boolean;
6
+ };
7
+ //# sourceMappingURL=editorChannel.d.ts.map
@@ -0,0 +1,19 @@
1
+ //#region src/inspector/editorChannel.ts
2
+ var e = () => {
3
+ let e = null;
4
+ return {
5
+ send: (t) => {
6
+ parent.postMessage({
7
+ source: "web-chat-preview",
8
+ ...t
9
+ }, e ?? "*");
10
+ },
11
+ acceptOrigin: (t) => {
12
+ if (e === null) e = t.origin;
13
+ else if (t.origin !== e) return !1;
14
+ return !0;
15
+ }
16
+ };
17
+ };
18
+ //#endregion
19
+ export { e as createEditorChannel };
@@ -0,0 +1,8 @@
1
+ export type IHistoryWatcher = ReturnType<typeof createHistoryWatcher>;
2
+ export declare const createHistoryWatcher: (deps: {
3
+ /** Fired AFTER `location.pathname` actually changed (dedup handled here). */
4
+ onPathChange: () => void;
5
+ }) => {
6
+ teardown: () => void;
7
+ };
8
+ //# sourceMappingURL=historyWatcher.d.ts.map
@@ -0,0 +1,24 @@
1
+ //#region src/inspector/historyWatcher.ts
2
+ var e = (e) => {
3
+ let t = location.pathname, n = () => {
4
+ location.pathname !== t && (t = location.pathname, e.onPathChange());
5
+ }, r = !1;
6
+ if (!history.pushState.__inspectorPatched) {
7
+ let e = history.pushState, t = history.replaceState, i = function(...t) {
8
+ let r = e.apply(this, t);
9
+ return n(), r;
10
+ }, a = function(...e) {
11
+ let r = t.apply(this, e);
12
+ return n(), r;
13
+ };
14
+ i.__inspectorPatched = !0, i.__orig = e, a.__inspectorPatched = !0, a.__orig = t, history.pushState = i, history.replaceState = a, r = !0;
15
+ }
16
+ return window.addEventListener("popstate", n), { teardown: () => {
17
+ if (window.removeEventListener("popstate", n), r) {
18
+ let e = history.pushState, t = history.replaceState;
19
+ e.__inspectorPatched && e.__orig && (history.pushState = e.__orig), t.__inspectorPatched && t.__orig && (history.replaceState = t.__orig);
20
+ }
21
+ } };
22
+ };
23
+ //#endregion
24
+ export { e as createHistoryWatcher };
@@ -0,0 +1,2 @@
1
+ export declare function Inspector(): null;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,191 @@
1
+ import { createOverlayNodes as e, externalLinkHref as t, isTextLeaf as n, positionBox as r, selectedPayload as ee, sourceOf as te } from "./dom.js";
2
+ import { createEditorChannel as ne } from "./editorChannel.js";
3
+ import { createHistoryWatcher as re } from "./historyWatcher.js";
4
+ import { createMessageRouter as ie } from "./messageRouter.js";
5
+ import { createOriginalsStash as i } from "./originalsStash.js";
6
+ import { createStagedOverrides as a } from "./stagedOverrides.js";
7
+ import { createTextEditController as ae } from "./textEditController.js";
8
+ import { useEffect as o } from "react";
9
+ //#region src/inspector/index.tsx
10
+ function s() {
11
+ return o(() => c(), []), null;
12
+ }
13
+ function c() {
14
+ let o = !1, s = null, c = null, l = !1, u = null, d = i(), f = ne(), { overlay: p, label: m, ring: h, ringLabel: g } = e();
15
+ document.body.append(p, m, h, g);
16
+ let _ = [
17
+ p,
18
+ m,
19
+ h,
20
+ g
21
+ ], v = null, y = (e) => {
22
+ e && !v ? (v = document.createElement("style"), v.id = "studio-pick-cursor", v.textContent = "*,*::before,*::after{cursor:crosshair !important}", document.head.appendChild(v)) : !e && v && (v.remove(), v = null);
23
+ }, b = (e) => !e || _.includes(e) || e === document.body || e === document.documentElement, x = (e) => {
24
+ if (!e || b(e)) return null;
25
+ let t = te(e) ? e : e.closest("[data-tsd-source]");
26
+ return !t || b(t) || !n(t) ? null : t;
27
+ }, S = (e) => r(p, m, e), C = () => {
28
+ if (c) {
29
+ if (!c.isConnected) {
30
+ M();
31
+ return;
32
+ }
33
+ r(h, g, c);
34
+ }
35
+ }, w = 0, T = () => {
36
+ w || !c || l || (w = requestAnimationFrame(() => {
37
+ w = 0, C();
38
+ }));
39
+ }, E = new ResizeObserver(T), D = new MutationObserver((e) => {
40
+ for (let t of e) {
41
+ let e = t.target;
42
+ if (!_.some((t) => t === e) && !(t.type === "attributes" && t.attributeName === "data-staged-key")) {
43
+ T(), I.scheduleReapply();
44
+ return;
45
+ }
46
+ }
47
+ }), O = !1, k = () => {
48
+ O ||= (D.observe(document.documentElement, {
49
+ attributes: !0,
50
+ childList: !0,
51
+ characterData: !0,
52
+ subtree: !0
53
+ }), !0);
54
+ }, A = () => {
55
+ O &&= (D.disconnect(), !1);
56
+ }, j = () => {
57
+ E.disconnect(), c?.isConnected && (E.observe(c), k());
58
+ }, M = () => {
59
+ c = null, E.disconnect(), L(), w &&= (cancelAnimationFrame(w), 0), h.style.display = "none", g.style.display = "none";
60
+ }, N = (e) => S(e), P = () => {
61
+ p.style.display = "none", m.style.display = "none";
62
+ }, F = ae({
63
+ channel: f,
64
+ originals: d,
65
+ clearTextHover: () => {
66
+ u = null, P();
67
+ }
68
+ }), I = a({
69
+ originals: d,
70
+ editingNode: F.editingNode
71
+ }), L = () => {
72
+ c?.isConnected || I.count() ? k() : A();
73
+ }, R = (e) => {
74
+ e && l && z(!1), o = !!e, y(o), o || (s = null, p.style.display = "none", m.style.display = "none"), f.send({
75
+ type: "inspect-state",
76
+ enabled: o
77
+ });
78
+ }, z = (e) => {
79
+ l = !!e, l ? (o && R(!1), h.style.display = "none", g.style.display = "none") : (F.commitIfEditing(), P(), document.documentElement.style.cursor = "", c && C()), f.send({
80
+ type: "text-edit-state",
81
+ enabled: l
82
+ });
83
+ }, B = 0, V = 0, H = 0, oe = () => {
84
+ B = 0;
85
+ let e = document.elementFromPoint(V, H);
86
+ if (!b(e)) {
87
+ if (l) {
88
+ let t = x(e);
89
+ if (t && t === F.editingNode()) {
90
+ u = null, P(), document.documentElement.style.cursor = "";
91
+ return;
92
+ }
93
+ u = t, document.documentElement.style.cursor = t ? "text" : "", t ? N(t) : P();
94
+ return;
95
+ }
96
+ o && (s = e, e && S(e));
97
+ }
98
+ }, U = (e) => {
99
+ !o && !l || (V = e.clientX, H = e.clientY, B ||= requestAnimationFrame(oe));
100
+ }, W = 0, se = () => {
101
+ W = 0, o && s && S(s), l && !F.editingNode() && u?.isConnected && N(u), c && !l && C();
102
+ }, G = () => {
103
+ W ||= requestAnimationFrame(se);
104
+ }, K = (e) => {
105
+ let n = t(e.target);
106
+ return n ? (e.preventDefault(), e.stopPropagation(), window.open(n, "_blank", "noopener,noreferrer"), !0) : !1;
107
+ }, q = (e) => {
108
+ if (l) {
109
+ let t = x(document.elementFromPoint(e.clientX, e.clientY) || e.target);
110
+ if (!t || t === F.editingNode()) {
111
+ K(e);
112
+ return;
113
+ }
114
+ e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), F.startEdit(t);
115
+ return;
116
+ }
117
+ if (!o) {
118
+ K(e);
119
+ return;
120
+ }
121
+ e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation();
122
+ let t = s || e.target;
123
+ if (b(t)) return;
124
+ let n = t.closest("[data-tsd-source]") || t;
125
+ c = n, C(), j(), f.send({
126
+ type: "element-selected",
127
+ element: ee(n)
128
+ });
129
+ }, J = ie({
130
+ channel: f,
131
+ originals: d,
132
+ staged: I,
133
+ setInspect: R,
134
+ setTextEdit: z,
135
+ isInspecting: () => o,
136
+ isTextEditing: () => l,
137
+ editingNode: F.editingNode,
138
+ setSelected: (e) => {
139
+ c = e, j();
140
+ },
141
+ positionSelected: C,
142
+ clearSelection: M,
143
+ syncDomObserver: L,
144
+ positionHover: S,
145
+ hideTextHover: P,
146
+ startEditAtEnd: (e) => F.startEdit(e, !0),
147
+ editableTarget: x,
148
+ ownNodes: _
149
+ }), Y = [
150
+ "mousedown",
151
+ "mouseup",
152
+ "dblclick",
153
+ "auxclick",
154
+ "pointerdown",
155
+ "pointerup",
156
+ "mouseover",
157
+ "mouseout",
158
+ "mouseenter",
159
+ "mouseleave",
160
+ "pointerover",
161
+ "pointerout",
162
+ "pointerenter",
163
+ "pointerleave",
164
+ "touchstart",
165
+ "touchend",
166
+ "submit"
167
+ ], X = (e) => {
168
+ o && (e.cancelable && e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation());
169
+ }, Z = () => {
170
+ c && !l && C();
171
+ }, Q = () => {
172
+ let e = location.pathname.replace(/\/$/, "") || "/";
173
+ f.send({
174
+ type: "route",
175
+ route: e
176
+ });
177
+ }, ce = re({ onPathChange: () => {
178
+ M(), Q();
179
+ } }), $ = (e) => {
180
+ e.key !== "Escape" || F.editingNode() || (l ? z(!1) : o && R(!1));
181
+ };
182
+ window.addEventListener("mousemove", U, !0), window.addEventListener("scroll", G, !0), window.addEventListener("resize", Z), window.addEventListener("click", q, !0), window.addEventListener("keydown", $, !0), window.addEventListener("message", J.onMessage);
183
+ for (let e of Y) window.addEventListener(e, X, !0);
184
+ return Q(), f.send({ type: "ready" }), () => {
185
+ window.removeEventListener("mousemove", U, !0), window.removeEventListener("keydown", $, !0), window.removeEventListener("scroll", G, !0), window.removeEventListener("resize", Z), window.removeEventListener("click", q, !0), window.removeEventListener("message", J.onMessage);
186
+ for (let e of Y) window.removeEventListener(e, X, !0);
187
+ E.disconnect(), D.disconnect(), w && cancelAnimationFrame(w), B && cancelAnimationFrame(B), W && cancelAnimationFrame(W), I.teardown(), ce.teardown(), F.teardown(), document.documentElement.style.cursor = "", v?.remove(), p.remove(), m.remove(), h.remove(), g.remove();
188
+ };
189
+ }
190
+ //#endregion
191
+ export { s as Inspector };
@@ -0,0 +1,38 @@
1
+ import { IEditorChannel } from './editorChannel';
2
+ import { IOriginalsStash } from './originalsStash';
3
+ import { IStagedOverrides } from './stagedOverrides';
4
+ export interface IMessageRouterContext {
5
+ channel: IEditorChannel;
6
+ originals: IOriginalsStash;
7
+ staged: IStagedOverrides;
8
+ /** Enter/exit select mode (mutual exclusion + state echo live in the setter). */
9
+ setInspect: (on: boolean) => void;
10
+ /** Enter/exit text mode (mutual exclusion + state echo live in the setter). */
11
+ setTextEdit: (on: boolean) => void;
12
+ isInspecting: () => boolean;
13
+ isTextEditing: () => boolean;
14
+ /** The node currently in contenteditable, or null. */
15
+ editingNode: () => Element | null;
16
+ /** Adopt `node` as the persistent selection (ring bookkeeping + observers re-arm). */
17
+ setSelected: (node: Element) => void;
18
+ /** Redraw the selection ring (internally drops a disconnected selection). */
19
+ positionSelected: () => void;
20
+ /** Drop the persistent selection + its ring. */
21
+ clearSelection: () => void;
22
+ /** Re-arbitrate the document-wide MutationObserver (selection OR staged need it). */
23
+ syncDomObserver: () => void;
24
+ /** Draw the transient hover outline + label on `el`. */
25
+ positionHover: (el: Element) => void;
26
+ /** Hide the transient hover outline + label. */
27
+ hideTextHover: () => void;
28
+ /** Begin an in-place text edit on `el`, caret at the end (programmatic start). */
29
+ startEditAtEnd: (el: Element) => void;
30
+ /** The nearest editable text-leaf for `el`, or null (mirrors the click-path test). */
31
+ editableTarget: (el: Element | null) => Element | null;
32
+ /** The runtime's own overlay artifacts, skipped by the layers snapshot. */
33
+ ownNodes: readonly Element[];
34
+ }
35
+ export declare const createMessageRouter: (ctx: IMessageRouterContext) => {
36
+ onMessage: (e: MessageEvent) => void;
37
+ };
38
+ //# sourceMappingURL=messageRouter.d.ts.map
@@ -0,0 +1,134 @@
1
+ import { buildLayersTree as e, enumerateSources as t, nodeForSource as n, selectedPayload as r } from "./dom.js";
2
+ //#region src/inspector/messageRouter.ts
3
+ var i = (i) => {
4
+ let { channel: a, originals: o, staged: s } = i, c = a.send, l = null;
5
+ return { onMessage: (u) => {
6
+ let d = u.data;
7
+ if (!(!d || d.source !== "web-chat") && a.acceptOrigin(u)) {
8
+ if (d.type === "set-inspect") i.setInspect(d.enabled);
9
+ else if (d.type === "set-text-edit") i.setTextEdit(d.enabled);
10
+ else if (d.type === "revert-text") {
11
+ let e = String(d.element?.source || ""), t = Number.isInteger(d.element?.index) ? d.element.index : 0, r = o.restore(`${e}#${t}`), i = r == null ? null : n(e, t);
12
+ i && r != null && (i.textContent = r);
13
+ } else if (d.type === "stage-style") {
14
+ let e = String(d.stageId || ""), t = String(d.element?.source || ""), n = Number.isInteger(d.element?.index) ? d.element.index : 0, r = d.styles && typeof d.styles == "object" ? d.styles : null;
15
+ if (!e || /\s/.test(e) || !t || !r) return;
16
+ let a = {
17
+ kind: "style",
18
+ source: t,
19
+ index: n,
20
+ styles: r,
21
+ media: typeof d.media == "string" && d.media.trim() ? d.media : void 0
22
+ };
23
+ s.upsert(e, a), i.syncDomObserver(), c({
24
+ type: "staged-ack",
25
+ stageId: e
26
+ });
27
+ } else if (d.type === "stage-text") {
28
+ let e = String(d.stageId || ""), t = String(d.element?.source || ""), n = Number.isInteger(d.element?.index) ? d.element.index : 0;
29
+ if (!e || /\s/.test(e) || !t || typeof d.value != "string") return;
30
+ let r = {
31
+ kind: "text",
32
+ source: t,
33
+ index: n,
34
+ value: d.value
35
+ };
36
+ s.upsert(e, r), i.syncDomObserver(), c({
37
+ type: "staged-ack",
38
+ stageId: e
39
+ });
40
+ } else if (d.type === "clear-staged") {
41
+ if (d.stageId == null) {
42
+ let e = s.ids();
43
+ s.clearAll();
44
+ for (let t of e) c({
45
+ type: "staged-ack",
46
+ stageId: t
47
+ });
48
+ } else {
49
+ let e = String(d.stageId);
50
+ s.clearOne(e), c({
51
+ type: "staged-ack",
52
+ stageId: e
53
+ });
54
+ }
55
+ i.syncDomObserver();
56
+ } else if (d.type === "select-element") {
57
+ let e = String(d.element?.source || ""), t = Number.isInteger(d.element?.index) ? d.element.index : 0, a = e ? n(e, t) : null;
58
+ if (!a) {
59
+ c({
60
+ type: "select-miss",
61
+ element: {
62
+ source: e,
63
+ index: t
64
+ }
65
+ });
66
+ return;
67
+ }
68
+ l = null, i.hideTextHover(), i.setSelected(a), i.isTextEditing() || i.positionSelected(), a.scrollIntoView({
69
+ block: "center",
70
+ behavior: "smooth"
71
+ }), c({
72
+ type: "element-selected",
73
+ element: r(a)
74
+ });
75
+ } else if (d.type === "edit-element") {
76
+ let e = String(d.element?.source || ""), t = Number.isInteger(d.element?.index) ? d.element.index : 0, a = e ? n(e, t) : null;
77
+ if (!a) {
78
+ c({
79
+ type: "select-miss",
80
+ element: {
81
+ source: e,
82
+ index: t
83
+ }
84
+ });
85
+ return;
86
+ }
87
+ l = null, i.hideTextHover(), i.setSelected(a), a.scrollIntoView({
88
+ block: "center",
89
+ behavior: "smooth"
90
+ }), c({
91
+ type: "element-selected",
92
+ element: r(a)
93
+ }), i.isTextEditing() || i.setTextEdit(!0);
94
+ let o = i.editableTarget(a);
95
+ o && i.startEditAtEnd(o);
96
+ } else if (d.type === "highlight-element") {
97
+ let e = String(d.element?.source || ""), t = Number.isInteger(d.element?.index) ? d.element.index : 0, r = e ? n(e, t) : null;
98
+ if (!r) l = null, i.hideTextHover(), c({
99
+ type: "highlight-miss",
100
+ element: {
101
+ source: e,
102
+ index: t
103
+ }
104
+ });
105
+ else {
106
+ l = r, i.positionHover(r);
107
+ let e = r.getBoundingClientRect(), t = window.innerHeight || document.documentElement.clientHeight, n = window.innerWidth || document.documentElement.clientWidth;
108
+ (e.bottom < 0 || e.top > t || e.right < 0 || e.left > n) && r.scrollIntoView({
109
+ block: "center",
110
+ behavior: "smooth"
111
+ });
112
+ }
113
+ } else if (d.type === "clear-highlight") l && (l = null, !i.isInspecting() && !i.editingNode() && i.hideTextHover());
114
+ else if (d.type === "request-layers") {
115
+ let { tree: t, truncated: n } = e(i.ownNodes);
116
+ c({
117
+ type: "layers-tree",
118
+ tree: t,
119
+ truncated: n
120
+ });
121
+ } else if (d.type === "request-sources") c({
122
+ type: "sources-list",
123
+ sources: t()
124
+ });
125
+ else if (d.type === "clear-selection") i.clearSelection();
126
+ else if (d.type === "navigate") {
127
+ let e = String(d.route || "/");
128
+ /^\/(?!\/)[A-Za-z0-9\-._~/?#[\]@!$&'()*+,;=%]*$/.test(e) && location.assign(e);
129
+ }
130
+ }
131
+ } };
132
+ };
133
+ //#endregion
134
+ export { i as createMessageRouter };
@@ -0,0 +1,12 @@
1
+ export type IOriginalsStash = ReturnType<typeof createOriginalsStash>;
2
+ export declare const createOriginalsStash: () => {
3
+ /** Stash unconditionally (edit start) — a fresh edit re-captures the current text. */
4
+ capture: (key: string, text: string) => void;
5
+ /** Stash only if absent (staged re-apply) — never overwrite the true original. */
6
+ captureOnce: (key: string, text: string) => void;
7
+ /** Peek without consuming — commit keeps the stash for a possible later revert. */
8
+ get: (key: string) => string | undefined;
9
+ /** Consume: return the original AND drop it (cancel / clear-staged / revert-text). */
10
+ restore: (key: string) => string | undefined;
11
+ };
12
+ //# sourceMappingURL=originalsStash.d.ts.map