@guidelytech/guide-runtime 0.8.0 → 0.10.0
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/capture.cjs +160 -34
- package/dist/capture.cjs.map +1 -1
- package/dist/capture.js +9 -14
- package/dist/capture.js.map +1 -1
- package/dist/chunk-GUH2PQJ7.js +462 -0
- package/dist/chunk-GUH2PQJ7.js.map +1 -0
- package/dist/chunk-KJRXCITD.js +515 -0
- package/dist/chunk-KJRXCITD.js.map +1 -0
- package/dist/chunk-PG7CMBHD.js +330 -0
- package/dist/chunk-PG7CMBHD.js.map +1 -0
- package/dist/chunk-VNJPNGZ4.js +301 -0
- package/dist/chunk-VNJPNGZ4.js.map +1 -0
- package/dist/index.cjs +356 -64
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -8
- package/dist/index.d.ts +62 -8
- package/dist/index.js +107 -23
- package/dist/index.js.map +1 -1
- package/dist/messenger.cjs +586 -35
- package/dist/messenger.cjs.map +1 -1
- package/dist/messenger.d.cts +21 -1
- package/dist/messenger.d.ts +21 -1
- package/dist/messenger.js +527 -23
- package/dist/messenger.js.map +1 -1
- package/package.json +5 -5
package/dist/capture.cjs
CHANGED
|
@@ -76,6 +76,12 @@ var CAPTURE_STYLES = `
|
|
|
76
76
|
// src/selectors.ts
|
|
77
77
|
var import_runtime = require("@guidelytech/shared/runtime");
|
|
78
78
|
var HOST_ID = "guidely-root";
|
|
79
|
+
function hasLayout(doc) {
|
|
80
|
+
const de = doc.documentElement;
|
|
81
|
+
if (!de) return false;
|
|
82
|
+
const r = de.getBoundingClientRect();
|
|
83
|
+
return r.width > 0 || r.height > 0;
|
|
84
|
+
}
|
|
79
85
|
function normalizeText(text) {
|
|
80
86
|
return (text || "").replace(/\s+/g, " ").trim();
|
|
81
87
|
}
|
|
@@ -92,6 +98,56 @@ function safeQueryAll(root, selector) {
|
|
|
92
98
|
return [];
|
|
93
99
|
}
|
|
94
100
|
}
|
|
101
|
+
var SHADOW_SEPARATOR = " >>> ";
|
|
102
|
+
var SHADOW_SPLIT = /\s*>>>\s*/;
|
|
103
|
+
function isShadowRoot(node) {
|
|
104
|
+
return !!node && node.nodeType === 11 && !!node.host;
|
|
105
|
+
}
|
|
106
|
+
function shadowRootOf(el) {
|
|
107
|
+
const root = el.getRootNode?.();
|
|
108
|
+
return isShadowRoot(root) ? root : null;
|
|
109
|
+
}
|
|
110
|
+
function composedTarget(e) {
|
|
111
|
+
const path = typeof e.composedPath === "function" ? e.composedPath() : [];
|
|
112
|
+
const first = path[0] ?? e.target;
|
|
113
|
+
if (!first) return null;
|
|
114
|
+
return first.nodeType === 1 ? first : first.parentElement;
|
|
115
|
+
}
|
|
116
|
+
var shadowRootCache = null;
|
|
117
|
+
function shadowRootsIn(root) {
|
|
118
|
+
const cached = shadowRootCache?.get(root);
|
|
119
|
+
if (cached) return cached;
|
|
120
|
+
const out = [];
|
|
121
|
+
const visit = (scope) => {
|
|
122
|
+
const own = scope.nodeType === 1 ? scope.shadowRoot : null;
|
|
123
|
+
const hosts = own ? [scope] : [];
|
|
124
|
+
for (const el of safeQueryAll(scope, "*")) if (el.shadowRoot) hosts.push(el);
|
|
125
|
+
for (const host of hosts) {
|
|
126
|
+
if (host.id === HOST_ID || !host.shadowRoot || out.includes(host.shadowRoot)) continue;
|
|
127
|
+
out.push(host.shadowRoot);
|
|
128
|
+
visit(host.shadowRoot);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
visit(root);
|
|
132
|
+
shadowRootCache?.set(root, out);
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
function queryAllDeep(root, selector) {
|
|
136
|
+
const out = safeQueryAll(root, selector);
|
|
137
|
+
for (const sr of shadowRootsIn(root)) out.push(...safeQueryAll(sr, selector));
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
function queryCss(root, selector, deep) {
|
|
141
|
+
if (!selector.includes(">>>")) return deep ? queryAllDeep(root, selector) : safeQueryAll(root, selector);
|
|
142
|
+
const steps = selector.split(SHADOW_SPLIT).filter(Boolean);
|
|
143
|
+
let scopes = [root];
|
|
144
|
+
let matches = [];
|
|
145
|
+
steps.forEach((step, i) => {
|
|
146
|
+
matches = Array.from(new Set(scopes.flatMap((scope) => queryAllDeep(scope, step))));
|
|
147
|
+
if (i < steps.length - 1) scopes = matches.map((el) => el.shadowRoot).filter((sr) => !!sr);
|
|
148
|
+
});
|
|
149
|
+
return matches;
|
|
150
|
+
}
|
|
95
151
|
function escapeCss(value) {
|
|
96
152
|
const g = globalThis;
|
|
97
153
|
if (g.CSS && typeof g.CSS.escape === "function") return g.CSS.escape(value);
|
|
@@ -107,14 +163,15 @@ function isStableId(id) {
|
|
|
107
163
|
function isStableClass(cls) {
|
|
108
164
|
return !!cls && !GENERATED_CLASS.test(cls) && cls.length <= 40;
|
|
109
165
|
}
|
|
110
|
-
function
|
|
111
|
-
const all = safeQueryAll(
|
|
166
|
+
function uniqueWithin(root, selector, el) {
|
|
167
|
+
const all = safeQueryAll(root, selector);
|
|
112
168
|
return all.length === 1 && all[0] === el;
|
|
113
169
|
}
|
|
114
170
|
function attrSelector(name, value) {
|
|
115
171
|
return `[${name}='${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}']`;
|
|
116
172
|
}
|
|
117
|
-
function cssPath(el,
|
|
173
|
+
function cssPath(el, root) {
|
|
174
|
+
const doc = el.ownerDocument;
|
|
118
175
|
const segments = [];
|
|
119
176
|
let cur = el;
|
|
120
177
|
while (cur && cur.nodeType === 1 && cur !== doc.body && cur !== doc.documentElement) {
|
|
@@ -134,20 +191,48 @@ function cssPath(el, doc) {
|
|
|
134
191
|
}
|
|
135
192
|
segments.unshift(seg);
|
|
136
193
|
const candidate = segments.join(" > ");
|
|
137
|
-
if (
|
|
194
|
+
if (uniqueWithin(root, candidate, el)) return candidate;
|
|
138
195
|
cur = parent;
|
|
139
196
|
}
|
|
140
197
|
const full = segments.join(" > ");
|
|
141
|
-
return
|
|
198
|
+
return uniqueWithin(root, full, el) ? full : null;
|
|
199
|
+
}
|
|
200
|
+
function shadowHostChain(el) {
|
|
201
|
+
const steps = [];
|
|
202
|
+
for (let root = shadowRootOf(el); root; root = shadowRootOf(root.host)) {
|
|
203
|
+
const host = root.host;
|
|
204
|
+
const step = cssPath(host, shadowRootOf(host) ?? host.ownerDocument);
|
|
205
|
+
if (!step) return null;
|
|
206
|
+
steps.unshift(step);
|
|
207
|
+
}
|
|
208
|
+
return steps.join(SHADOW_SEPARATOR);
|
|
209
|
+
}
|
|
210
|
+
function selectorFor(el, selector) {
|
|
211
|
+
const root = shadowRootOf(el);
|
|
212
|
+
if (!root) return uniqueWithin(el.ownerDocument, selector, el) ? selector : null;
|
|
213
|
+
if (!uniqueWithin(root, selector, el)) return null;
|
|
214
|
+
const doc = el.ownerDocument;
|
|
215
|
+
const only = (sel) => {
|
|
216
|
+
const all = queryCss(doc, sel, true);
|
|
217
|
+
return all.length === 1 && all[0] === el;
|
|
218
|
+
};
|
|
219
|
+
if (only(selector)) return selector;
|
|
220
|
+
const chain = shadowHostChain(el);
|
|
221
|
+
const scoped = chain ? chain + SHADOW_SEPARATOR + selector : null;
|
|
222
|
+
return scoped && only(scoped) ? scoped : null;
|
|
223
|
+
}
|
|
224
|
+
function shadowText(el) {
|
|
225
|
+
if (!el.shadowRoot) return "";
|
|
226
|
+
const parts = Array.from(el.shadowRoot.children).filter((c) => !/^(STYLE|SCRIPT|TEMPLATE)$/.test(c.tagName));
|
|
227
|
+
return normalizeText(parts.map((c) => c.innerText ?? c.textContent).join(" "));
|
|
142
228
|
}
|
|
143
229
|
function textSnapshotOf(el) {
|
|
144
230
|
const he = el;
|
|
145
|
-
const own = normalizeText(he.innerText ?? el.textContent);
|
|
231
|
+
const own = normalizeText(he.innerText ?? el.textContent) || shadowText(el);
|
|
146
232
|
const alt = normalizeText(el.getAttribute("aria-label") || el.getAttribute("title") || el.getAttribute("placeholder") || el.getAttribute("alt") || el.value);
|
|
147
233
|
return (own || alt).slice(0, 200);
|
|
148
234
|
}
|
|
149
235
|
function generateSelectors(el) {
|
|
150
|
-
const doc = el.ownerDocument;
|
|
151
236
|
const out = [];
|
|
152
237
|
const seen = /* @__PURE__ */ new Set();
|
|
153
238
|
const push = (s) => {
|
|
@@ -158,9 +243,15 @@ function generateSelectors(el) {
|
|
|
158
243
|
}
|
|
159
244
|
};
|
|
160
245
|
const tag = el.tagName.toLowerCase();
|
|
246
|
+
const pushFor = (kind, selector, value, label) => {
|
|
247
|
+
const resolved = selectorFor(el, selector);
|
|
248
|
+
if (resolved === selector) push({ kind, value, label });
|
|
249
|
+
else if (resolved) push({ kind: "css", value: resolved, label });
|
|
250
|
+
return !!resolved;
|
|
251
|
+
};
|
|
161
252
|
const guidelyId = el.getAttribute("data-guidely-id");
|
|
162
|
-
if (guidelyId
|
|
163
|
-
if (isStableId(el.id)
|
|
253
|
+
if (guidelyId) pushFor("guidely-id", attrSelector("data-guidely-id", guidelyId), guidelyId, `Guidely id ${guidelyId}`);
|
|
254
|
+
if (isStableId(el.id)) pushFor("id", `#${escapeCss(el.id)}`, `#${el.id}`, `id ${el.id}`);
|
|
164
255
|
const dataAttrs = Array.from(el.attributes).filter((a) => a.name.startsWith("data-") && !IGNORED_DATA_ATTRS.test(a.name) && a.value && a.value.length <= 120 && !/\d{5,}/.test(a.value));
|
|
165
256
|
dataAttrs.sort((a, b) => {
|
|
166
257
|
const ia = PREFERRED_DATA_ATTRS.indexOf(a.name);
|
|
@@ -169,42 +260,75 @@ function generateSelectors(el) {
|
|
|
169
260
|
});
|
|
170
261
|
for (const a of dataAttrs) {
|
|
171
262
|
const sel = attrSelector(a.name, a.value);
|
|
172
|
-
if (
|
|
173
|
-
else if (uniqueIn(doc, tag + sel, el)) push({ kind: "data-attr", value: tag + sel, label: `${a.name}=${a.value}` });
|
|
263
|
+
if (!pushFor("data-attr", sel, sel, `${a.name}=${a.value}`)) pushFor("data-attr", tag + sel, tag + sel, `${a.name}=${a.value}`);
|
|
174
264
|
}
|
|
175
265
|
const aria = el.getAttribute("aria-label");
|
|
176
266
|
if (aria && aria.length <= 120) {
|
|
177
267
|
const sel = attrSelector("aria-label", aria);
|
|
178
|
-
if (
|
|
179
|
-
else if (uniqueIn(doc, tag + sel, el)) push({ kind: "aria", value: tag + sel, label: aria });
|
|
268
|
+
if (!pushFor("aria", sel, sel, aria)) pushFor("aria", tag + sel, tag + sel, aria);
|
|
180
269
|
}
|
|
181
270
|
const name = el.getAttribute("name");
|
|
182
|
-
if (name
|
|
271
|
+
if (name) pushFor("css", tag + attrSelector("name", name), tag + attrSelector("name", name), `${tag} name=${name}`);
|
|
183
272
|
const role = el.getAttribute("role");
|
|
184
273
|
if (role) {
|
|
185
274
|
const title = el.getAttribute("title") || el.getAttribute("aria-labelledby");
|
|
186
275
|
const sel = title ? `[role='${role}']${attrSelector(el.getAttribute("title") ? "title" : "aria-labelledby", title)}` : `[role='${role}']`;
|
|
187
|
-
|
|
276
|
+
pushFor("css", sel, sel, `role ${role}`);
|
|
188
277
|
}
|
|
189
278
|
if (tag === "a") {
|
|
190
279
|
const href = el.getAttribute("href");
|
|
191
|
-
if (href && href.length <= 200
|
|
280
|
+
if (href && href.length <= 200) pushFor("css", `a${attrSelector("href", href)}`, `a${attrSelector("href", href)}`, `link ${href}`);
|
|
192
281
|
}
|
|
193
|
-
const path = cssPath(el,
|
|
194
|
-
if (path)
|
|
282
|
+
const path = cssPath(el, shadowRootOf(el) ?? el.ownerDocument);
|
|
283
|
+
if (path) pushFor("css", path, path, "css path");
|
|
195
284
|
if (/^(button|a|h[1-6]|label|summary)$/.test(tag) || role === "button" || role === "link" || role === "tab" || role === "menuitem") {
|
|
196
285
|
const text = normalizeText(el.innerText ?? el.textContent);
|
|
197
286
|
if (text && text.length <= 80) push({ kind: "text", value: text, label: `text "${text}"` });
|
|
198
287
|
}
|
|
199
288
|
return out.slice(0, 10);
|
|
200
289
|
}
|
|
290
|
+
function visualRect(el) {
|
|
291
|
+
const own = el.getBoundingClientRect();
|
|
292
|
+
if (!el.shadowRoot) return own;
|
|
293
|
+
const boxes = [own, ...Array.from(el.shadowRoot.children).map((c) => c.getBoundingClientRect())].filter((r) => r.width > 0 && r.height > 0);
|
|
294
|
+
if (boxes.length < 2) return boxes[0] ?? own;
|
|
295
|
+
const left = Math.min(...boxes.map((r) => r.left));
|
|
296
|
+
const top = Math.min(...boxes.map((r) => r.top));
|
|
297
|
+
const right = Math.max(...boxes.map((r) => r.right));
|
|
298
|
+
const bottom = Math.max(...boxes.map((r) => r.bottom));
|
|
299
|
+
return new DOMRect(left, top, right - left, bottom - top);
|
|
300
|
+
}
|
|
301
|
+
var ANCHOR_MAX_AREA_RATIO = 4;
|
|
302
|
+
var CONTROL_SELECTOR = "button, a[href], input, select, textarea, [role='button'], [role='link'], [role='checkbox'], [role='radio'], [role='switch'], [role='tab'], [role='menuitem'], [role='combobox'], [role='option']";
|
|
303
|
+
function componentAnchor(el) {
|
|
304
|
+
if (!hasLayout(el.ownerDocument)) return el;
|
|
305
|
+
let cur = el;
|
|
306
|
+
for (let root = shadowRootOf(cur); root; root = shadowRootOf(cur)) {
|
|
307
|
+
const host = root.host;
|
|
308
|
+
if (host.id === HOST_ID) break;
|
|
309
|
+
let control = null;
|
|
310
|
+
try {
|
|
311
|
+
control = cur.closest(CONTROL_SELECTOR);
|
|
312
|
+
} catch {
|
|
313
|
+
}
|
|
314
|
+
const r = visualRect(control ?? cur);
|
|
315
|
+
const area = r.width * r.height;
|
|
316
|
+
const h = visualRect(host);
|
|
317
|
+
const hostArea = h.width * h.height;
|
|
318
|
+
const inside = r.left >= h.left - 1 && r.top >= h.top - 1 && r.right <= h.right + 1 && r.bottom <= h.bottom + 1;
|
|
319
|
+
if (!area || !inside || hostArea > area * ANCHOR_MAX_AREA_RATIO) break;
|
|
320
|
+
cur = host;
|
|
321
|
+
}
|
|
322
|
+
return cur;
|
|
323
|
+
}
|
|
201
324
|
function describeElement(el) {
|
|
202
|
-
const
|
|
325
|
+
const target = componentAnchor(el);
|
|
326
|
+
const r = visualRect(target);
|
|
203
327
|
return {
|
|
204
|
-
selectors: generateSelectors(
|
|
205
|
-
textSnapshot: textSnapshotOf(el),
|
|
328
|
+
selectors: generateSelectors(target),
|
|
329
|
+
textSnapshot: textSnapshotOf(target) || textSnapshotOf(el),
|
|
206
330
|
rect: { x: r.x, y: r.y, width: r.width, height: r.height },
|
|
207
|
-
tag:
|
|
331
|
+
tag: target.tagName.toLowerCase()
|
|
208
332
|
};
|
|
209
333
|
}
|
|
210
334
|
|
|
@@ -780,7 +904,15 @@ var STYLES = `
|
|
|
780
904
|
.g-rc-item .g-rc-title{font-weight:500}
|
|
781
905
|
.g-rc-item .g-rc-sum{font-size:12px;color:var(--g-muted);margin-top:2px}
|
|
782
906
|
.g-rc-item .g-rc-icon{flex:none;width:20px;text-align:center;color:var(--g-primary)}
|
|
907
|
+
.g-rc-row{display:flex;align-items:center;gap:6px;padding-right:16px}
|
|
908
|
+
.g-rc-row>.g-rc-item{flex:1;min-width:0;padding-right:4px}
|
|
909
|
+
.g-rc-row:hover{background:var(--g-subtle)}
|
|
910
|
+
.g-rc-row:hover>.g-rc-item{background:transparent}
|
|
911
|
+
.g-rc-showme{flex:none;min-height:32px;padding:4px 10px;font-size:12px;white-space:nowrap}
|
|
912
|
+
.g-help-showme{margin:4px 0 12px}
|
|
783
913
|
.g-rc-article{padding:16px 20px}
|
|
914
|
+
.g-resource-center .g-panel{transition:width .2s ease,max-height .2s ease}
|
|
915
|
+
.g-resource-center .g-panel.g-panel-reading{width:min(720px,calc(100vw - 32px));max-height:calc(100vh - 100px)}
|
|
784
916
|
.g-rc-back{margin-bottom:8px}
|
|
785
917
|
.g-rc-empty{padding:24px 20px;color:var(--g-muted);text-align:center}
|
|
786
918
|
.g-faq-a{padding:0 20px 12px 50px;font-size:13px;color:var(--g-muted)}
|
|
@@ -940,12 +1072,6 @@ function button(doc, text, cls, attr) {
|
|
|
940
1072
|
b.setAttribute(attr, "");
|
|
941
1073
|
return b;
|
|
942
1074
|
}
|
|
943
|
-
function eventTarget(e) {
|
|
944
|
-
const path = typeof e.composedPath === "function" ? e.composedPath() : [];
|
|
945
|
-
const first = path[0] ?? e.target;
|
|
946
|
-
if (!first) return null;
|
|
947
|
-
return first.nodeType === 1 ? first : first.parentElement;
|
|
948
|
-
}
|
|
949
1075
|
function deepActiveElementIn(el) {
|
|
950
1076
|
const root = el.getRootNode();
|
|
951
1077
|
return root.activeElement ?? null;
|
|
@@ -1089,7 +1215,7 @@ function startCapture(options) {
|
|
|
1089
1215
|
};
|
|
1090
1216
|
const onPointerDown = (e) => {
|
|
1091
1217
|
if (!recording() || e.button !== 0) return;
|
|
1092
|
-
const target =
|
|
1218
|
+
const target = composedTarget(e);
|
|
1093
1219
|
if (!target || isGuidelyElement(target)) return;
|
|
1094
1220
|
const active = deepActiveElement(doc);
|
|
1095
1221
|
if (active && active !== target && !active.contains(target) && isTextEntry(active)) flushField(active);
|
|
@@ -1113,7 +1239,7 @@ function startCapture(options) {
|
|
|
1113
1239
|
};
|
|
1114
1240
|
const onChange = (e) => {
|
|
1115
1241
|
if (!recording()) return;
|
|
1116
|
-
const el =
|
|
1242
|
+
const el = composedTarget(e);
|
|
1117
1243
|
if (!el || isGuidelyElement(el)) return;
|
|
1118
1244
|
if (el.tagName === "SELECT") {
|
|
1119
1245
|
const sensitive = isSensitiveField(el, mask);
|
|
@@ -1130,19 +1256,19 @@ function startCapture(options) {
|
|
|
1130
1256
|
if (isTextEntry(el)) flushField(el);
|
|
1131
1257
|
};
|
|
1132
1258
|
const onFocusIn = (e) => {
|
|
1133
|
-
const el =
|
|
1259
|
+
const el = composedTarget(e);
|
|
1134
1260
|
if (el && !isGuidelyElement(el) && isTextEntry(el)) {
|
|
1135
1261
|
const host = el.tagName === "INPUT" || el.tagName === "TEXTAREA" ? el : actionTarget(el);
|
|
1136
1262
|
baseline.set(host, fieldValue(host));
|
|
1137
1263
|
}
|
|
1138
1264
|
};
|
|
1139
1265
|
const onFocusOut = (e) => {
|
|
1140
|
-
const el =
|
|
1266
|
+
const el = composedTarget(e);
|
|
1141
1267
|
if (el && el.tagName !== "INPUT" && el.tagName !== "TEXTAREA" && isTextEntry(el)) flushField(el);
|
|
1142
1268
|
};
|
|
1143
1269
|
const onKeyDown = (e) => {
|
|
1144
1270
|
if (e.key !== "Enter" || e.isComposing) return;
|
|
1145
|
-
const el =
|
|
1271
|
+
const el = composedTarget(e);
|
|
1146
1272
|
if (el && (el.tagName === "INPUT" || (e.metaKey || e.ctrlKey) && isTextEntry(el))) flushField(el);
|
|
1147
1273
|
};
|
|
1148
1274
|
const checkUrl = () => {
|
|
@@ -1161,7 +1287,7 @@ function startCapture(options) {
|
|
|
1161
1287
|
movedAt = performance.now();
|
|
1162
1288
|
};
|
|
1163
1289
|
const onInput = (e) => {
|
|
1164
|
-
const el =
|
|
1290
|
+
const el = composedTarget(e);
|
|
1165
1291
|
if (el && !isGuidelyElement(el)) typedAt = performance.now();
|
|
1166
1292
|
};
|
|
1167
1293
|
const expand = (open) => {
|