@useclickly/react 1.0.2 → 1.0.4
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/index.cjs +333 -1232
- package/dist/index.js +284 -1166
- package/dist/internal/Toolbar.d.ts.map +1 -1
- package/dist/internal/icons.d.ts +2 -0
- package/dist/internal/icons.d.ts.map +1 -1
- package/dist/internal/styles.d.ts +1 -1
- package/dist/internal/styles.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,1027 +1,19 @@
|
|
|
1
|
-
//
|
|
1
|
+
// ../react/src/Clickly.tsx
|
|
2
2
|
import { useEffect as useEffect7, useState as useState7 } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
|
+
import {
|
|
5
|
+
Overlay,
|
|
6
|
+
SelectionEngine,
|
|
7
|
+
createShadowHost
|
|
8
|
+
} from "@useclickly/core";
|
|
4
9
|
|
|
5
|
-
//
|
|
6
|
-
function pickElementAt(doc, x, y, excludeHost) {
|
|
7
|
-
if (typeof doc.elementsFromPoint !== "function") return null;
|
|
8
|
-
const chain = doc.elementsFromPoint(x, y);
|
|
9
|
-
for (const el of chain) {
|
|
10
|
-
if (!isInExcludedSubtree(el, excludeHost)) return el;
|
|
11
|
-
}
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
function pickElementsInRect(root, rect, excludeHost) {
|
|
15
|
-
const out = [];
|
|
16
|
-
const stack = [root];
|
|
17
|
-
while (stack.length) {
|
|
18
|
-
const el = stack.pop();
|
|
19
|
-
if (isInExcludedSubtree(el, excludeHost)) continue;
|
|
20
|
-
const box = el.getBoundingClientRect();
|
|
21
|
-
if (box.width > 0 && box.height > 0 && containedIn(box, rect)) {
|
|
22
|
-
out.push(el);
|
|
23
|
-
}
|
|
24
|
-
for (let i = 0; i < el.children.length; i++) {
|
|
25
|
-
const child = el.children[i];
|
|
26
|
-
if (child) stack.push(child);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return out;
|
|
30
|
-
}
|
|
31
|
-
function containedIn(el, sel) {
|
|
32
|
-
return el.left >= sel.x && el.top >= sel.y && el.right <= sel.x + sel.width && el.bottom <= sel.y + sel.height;
|
|
33
|
-
}
|
|
34
|
-
function isInExcludedSubtree(el, host) {
|
|
35
|
-
if (!host) return false;
|
|
36
|
-
let cur = el;
|
|
37
|
-
while (cur) {
|
|
38
|
-
if (cur === host) return true;
|
|
39
|
-
cur = cur.parentNode;
|
|
40
|
-
}
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
function manhattan(a, b) {
|
|
44
|
-
return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
45
|
-
}
|
|
46
|
-
function rectFromPoints(start, current) {
|
|
47
|
-
const x = Math.min(start.x, current.x);
|
|
48
|
-
const y = Math.min(start.y, current.y);
|
|
49
|
-
const width = Math.abs(current.x - start.x);
|
|
50
|
-
const height = Math.abs(current.y - start.y);
|
|
51
|
-
return { x, y, width, height };
|
|
52
|
-
}
|
|
53
|
-
var DRAG_THRESHOLD_PX = 12;
|
|
54
|
-
var initialState = { kind: "idle" };
|
|
55
|
-
function reduce(state, event) {
|
|
56
|
-
if (event.type === "DEACTIVATE") return { kind: "idle" };
|
|
57
|
-
if (event.type === "ESCAPE") {
|
|
58
|
-
if (state.kind === "idle") return state;
|
|
59
|
-
if (state.kind === "annotating") return resumeInspect(
|
|
60
|
-
[],
|
|
61
|
-
/* mode */
|
|
62
|
-
void 0
|
|
63
|
-
);
|
|
64
|
-
if (state.kind === "inspect" && state.pinned.length > 0) {
|
|
65
|
-
return { ...state, pinned: [], hoverTarget: null };
|
|
66
|
-
}
|
|
67
|
-
return resumeInspect("pinned" in state ? state.pinned : []);
|
|
68
|
-
}
|
|
69
|
-
if (event.type === "CLEAR_PINNED") {
|
|
70
|
-
if (state.kind === "idle") return state;
|
|
71
|
-
if (state.kind === "inspect") return { ...state, pinned: [] };
|
|
72
|
-
return state;
|
|
73
|
-
}
|
|
74
|
-
switch (state.kind) {
|
|
75
|
-
case "idle":
|
|
76
|
-
if (event.type === "ACTIVATE") {
|
|
77
|
-
return {
|
|
78
|
-
kind: "inspect",
|
|
79
|
-
mode: event.mode ?? "single",
|
|
80
|
-
hoverTarget: null,
|
|
81
|
-
pinned: []
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
return state;
|
|
85
|
-
case "inspect":
|
|
86
|
-
if (event.type === "MODE_CHANGE") return { ...state, mode: event.mode };
|
|
87
|
-
if (event.type === "POINTER_MOVE") return { ...state, hoverTarget: event.target };
|
|
88
|
-
if (event.type === "POINTER_DOWN") {
|
|
89
|
-
return {
|
|
90
|
-
kind: "pressed",
|
|
91
|
-
mode: state.mode,
|
|
92
|
-
start: event.point,
|
|
93
|
-
target: event.target,
|
|
94
|
-
additive: event.additive,
|
|
95
|
-
pinned: state.pinned
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
if (event.type === "ANNOTATE_PINNED") {
|
|
99
|
-
if (state.pinned.length === 0) return state;
|
|
100
|
-
return {
|
|
101
|
-
kind: "annotating",
|
|
102
|
-
selection: { kind: "multi", elements: state.pinned },
|
|
103
|
-
pinned: state.pinned
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
return state;
|
|
107
|
-
case "pressed":
|
|
108
|
-
if (event.type === "POINTER_MOVE") {
|
|
109
|
-
if (manhattan(state.start, event.point) >= DRAG_THRESHOLD_PX) {
|
|
110
|
-
return {
|
|
111
|
-
kind: "dragging",
|
|
112
|
-
mode: state.mode,
|
|
113
|
-
start: state.start,
|
|
114
|
-
current: event.point,
|
|
115
|
-
pinned: state.pinned
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
return state;
|
|
119
|
-
}
|
|
120
|
-
if (event.type === "POINTER_UP") {
|
|
121
|
-
if (!state.target) {
|
|
122
|
-
return resumeInspect(state.pinned, state.mode);
|
|
123
|
-
}
|
|
124
|
-
if (state.additive || state.mode === "multi") {
|
|
125
|
-
const nextPinned = togglePinned(state.pinned, state.target);
|
|
126
|
-
return {
|
|
127
|
-
kind: "inspect",
|
|
128
|
-
mode: state.mode,
|
|
129
|
-
hoverTarget: state.target,
|
|
130
|
-
pinned: nextPinned
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
return {
|
|
134
|
-
kind: "annotating",
|
|
135
|
-
selection: { kind: "single", element: state.target },
|
|
136
|
-
pinned: state.pinned
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
return state;
|
|
140
|
-
case "dragging":
|
|
141
|
-
if (event.type === "POINTER_MOVE") return { ...state, current: event.point };
|
|
142
|
-
if (event.type === "POINTER_UP") {
|
|
143
|
-
const rect = rectFromPoints(state.start, state.current);
|
|
144
|
-
const selection = { kind: "area", rect, elements: [] };
|
|
145
|
-
return { kind: "annotating", selection, pinned: state.pinned };
|
|
146
|
-
}
|
|
147
|
-
return state;
|
|
148
|
-
case "annotating":
|
|
149
|
-
if (event.type === "COMMIT") {
|
|
150
|
-
return resumeInspect([]);
|
|
151
|
-
}
|
|
152
|
-
return state;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
function resumeInspect(pinned, mode) {
|
|
156
|
-
return {
|
|
157
|
-
kind: "inspect",
|
|
158
|
-
mode: mode ?? "single",
|
|
159
|
-
hoverTarget: null,
|
|
160
|
-
pinned
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
function togglePinned(pinned, el) {
|
|
164
|
-
const idx = pinned.indexOf(el);
|
|
165
|
-
if (idx === -1) return [...pinned, el];
|
|
166
|
-
const next = pinned.slice();
|
|
167
|
-
next.splice(idx, 1);
|
|
168
|
-
return next;
|
|
169
|
-
}
|
|
170
|
-
var SelectionEngine = class {
|
|
171
|
-
state = initialState;
|
|
172
|
-
listeners = /* @__PURE__ */ new Set();
|
|
173
|
-
doc;
|
|
174
|
-
host;
|
|
175
|
-
raf;
|
|
176
|
-
caf;
|
|
177
|
-
searchRoot;
|
|
178
|
-
pendingPointer = null;
|
|
179
|
-
rafHandle = null;
|
|
180
|
-
/** Guard for the pointermove RAF coalescer. Separate from `rafHandle`
|
|
181
|
-
* because a synchronous `raf` (used in tests) returns a handle the cb
|
|
182
|
-
* has already invalidated — boolean is the safe sentinel. */
|
|
183
|
-
rafPending = false;
|
|
184
|
-
attached = false;
|
|
185
|
-
boundHandlers = [];
|
|
186
|
-
constructor(deps = {}) {
|
|
187
|
-
this.doc = deps.document ?? (typeof document !== "undefined" ? document : null);
|
|
188
|
-
if (!this.doc) {
|
|
189
|
-
throw new Error("SelectionEngine: no Document available (pass `deps.document`).");
|
|
190
|
-
}
|
|
191
|
-
this.host = deps.host ?? null;
|
|
192
|
-
this.raf = deps.raf ?? ((cb) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(cb) : setTimeout(() => cb(performance.now()), 16));
|
|
193
|
-
this.caf = deps.caf ?? ((h) => {
|
|
194
|
-
if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(h);
|
|
195
|
-
else clearTimeout(h);
|
|
196
|
-
});
|
|
197
|
-
this.searchRoot = deps.searchRoot ?? this.doc.body;
|
|
198
|
-
}
|
|
199
|
-
/* ─── Subscribable<EngineState> ───────────────────────────────── */
|
|
200
|
-
subscribe(listener) {
|
|
201
|
-
this.listeners.add(listener);
|
|
202
|
-
return () => this.listeners.delete(listener);
|
|
203
|
-
}
|
|
204
|
-
getSnapshot() {
|
|
205
|
-
return this.state;
|
|
206
|
-
}
|
|
207
|
-
/* ─── Public control ──────────────────────────────────────────── */
|
|
208
|
-
activate(mode) {
|
|
209
|
-
perfMark("clickly:engine:activate");
|
|
210
|
-
this.dispatch(mode ? { type: "ACTIVATE", mode } : { type: "ACTIVATE" });
|
|
211
|
-
this.attach();
|
|
212
|
-
}
|
|
213
|
-
deactivate() {
|
|
214
|
-
perfMark("clickly:engine:deactivate");
|
|
215
|
-
this.detach();
|
|
216
|
-
this.dispatch({ type: "DEACTIVATE" });
|
|
217
|
-
}
|
|
218
|
-
setMode(mode) {
|
|
219
|
-
this.dispatch({ type: "MODE_CHANGE", mode });
|
|
220
|
-
}
|
|
221
|
-
commit() {
|
|
222
|
-
this.dispatch({ type: "COMMIT" });
|
|
223
|
-
}
|
|
224
|
-
clearPinned() {
|
|
225
|
-
this.dispatch({ type: "CLEAR_PINNED" });
|
|
226
|
-
}
|
|
227
|
-
/** Open the annotation popup populated with everything currently pinned. */
|
|
228
|
-
annotatePinned() {
|
|
229
|
-
this.dispatch({ type: "ANNOTATE_PINNED" });
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Returns the resolved selection from the current annotating state, with
|
|
233
|
-
* area-mode element enumeration filled in (the reducer leaves it empty).
|
|
234
|
-
* Returns null if not currently annotating.
|
|
235
|
-
*/
|
|
236
|
-
resolveSelection() {
|
|
237
|
-
if (this.state.kind !== "annotating") return null;
|
|
238
|
-
const sel = this.state.selection;
|
|
239
|
-
if (sel.kind !== "area") return sel;
|
|
240
|
-
const elements = pickElementsInRect(this.searchRoot, sel.rect, this.host);
|
|
241
|
-
return { ...sel, elements };
|
|
242
|
-
}
|
|
243
|
-
/* ─── Lifecycle ───────────────────────────────────────────────── */
|
|
244
|
-
attach() {
|
|
245
|
-
if (this.attached) return;
|
|
246
|
-
this.attached = true;
|
|
247
|
-
const win = this.doc.defaultView ?? globalThis;
|
|
248
|
-
this.bind(this.doc, "pointermove", this.onPointerMove, { passive: true });
|
|
249
|
-
this.bind(this.doc, "pointerdown", this.onPointerDown);
|
|
250
|
-
this.bind(this.doc, "pointerup", this.onPointerUp);
|
|
251
|
-
this.bind(win, "keydown", this.onKeyDown);
|
|
252
|
-
}
|
|
253
|
-
detach() {
|
|
254
|
-
if (!this.attached) return;
|
|
255
|
-
for (const [t, ev, fn, opts] of this.boundHandlers) t.removeEventListener(ev, fn, opts);
|
|
256
|
-
this.boundHandlers = [];
|
|
257
|
-
this.attached = false;
|
|
258
|
-
if (this.rafHandle !== null) {
|
|
259
|
-
this.caf(this.rafHandle);
|
|
260
|
-
this.rafHandle = null;
|
|
261
|
-
}
|
|
262
|
-
this.rafPending = false;
|
|
263
|
-
}
|
|
264
|
-
destroy() {
|
|
265
|
-
this.detach();
|
|
266
|
-
this.listeners.clear();
|
|
267
|
-
}
|
|
268
|
-
bind(target, ev, fn, opts) {
|
|
269
|
-
target.addEventListener(ev, fn, opts);
|
|
270
|
-
this.boundHandlers.push([target, ev, fn, opts]);
|
|
271
|
-
}
|
|
272
|
-
/* ─── DOM event handlers ──────────────────────────────────────── */
|
|
273
|
-
onPointerMove = (e) => {
|
|
274
|
-
this.pendingPointer = { x: e.clientX, y: e.clientY };
|
|
275
|
-
if (this.rafPending) return;
|
|
276
|
-
this.rafPending = true;
|
|
277
|
-
this.rafHandle = this.raf(() => {
|
|
278
|
-
this.rafPending = false;
|
|
279
|
-
this.rafHandle = null;
|
|
280
|
-
const pt = this.pendingPointer;
|
|
281
|
-
this.pendingPointer = null;
|
|
282
|
-
if (!pt) return;
|
|
283
|
-
const target = pickElementAt(this.doc, pt.x, pt.y, this.host);
|
|
284
|
-
this.dispatch({ type: "POINTER_MOVE", point: pt, target });
|
|
285
|
-
});
|
|
286
|
-
};
|
|
287
|
-
onPointerDown = (e) => {
|
|
288
|
-
if (this.host && e.composedPath().includes(this.host)) return;
|
|
289
|
-
const target = pickElementAt(this.doc, e.clientX, e.clientY, this.host);
|
|
290
|
-
this.dispatch({
|
|
291
|
-
type: "POINTER_DOWN",
|
|
292
|
-
point: { x: e.clientX, y: e.clientY },
|
|
293
|
-
target,
|
|
294
|
-
additive: e.shiftKey || e.metaKey || e.ctrlKey
|
|
295
|
-
});
|
|
296
|
-
};
|
|
297
|
-
onPointerUp = (e) => {
|
|
298
|
-
this.dispatch({ type: "POINTER_UP", point: { x: e.clientX, y: e.clientY } });
|
|
299
|
-
};
|
|
300
|
-
onKeyDown = (e) => {
|
|
301
|
-
if (e.key === "Escape") this.dispatch({ type: "ESCAPE" });
|
|
302
|
-
};
|
|
303
|
-
/* ─── Reducer plumbing ────────────────────────────────────────── */
|
|
304
|
-
dispatch(event) {
|
|
305
|
-
const next = reduce(this.state, event);
|
|
306
|
-
if (next === this.state) return;
|
|
307
|
-
this.state = next;
|
|
308
|
-
for (const l of this.listeners) l(next);
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
function perfMark(name) {
|
|
312
|
-
if (typeof performance !== "undefined" && typeof performance.mark === "function") {
|
|
313
|
-
try {
|
|
314
|
-
performance.mark(name);
|
|
315
|
-
} catch {
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
var OVERLAY_CSS = `
|
|
320
|
-
:host {
|
|
321
|
-
--clickly-hover: #06b6d4;
|
|
322
|
-
--clickly-pinned: #f59e0b;
|
|
323
|
-
--clickly-selected: #10b981;
|
|
324
|
-
--clickly-marquee-stroke: #10b981;
|
|
325
|
-
--clickly-marquee-fill: rgba(16, 185, 129, 0.10);
|
|
326
|
-
--clickly-label-bg: rgba(15, 23, 42, 0.92);
|
|
327
|
-
--clickly-label-fg: #f8fafc;
|
|
328
|
-
--clickly-shadow: 0 0 0 1px rgba(255,255,255,0.5);
|
|
329
|
-
|
|
330
|
-
all: initial;
|
|
331
|
-
position: fixed;
|
|
332
|
-
inset: 0;
|
|
333
|
-
z-index: 2147483647;
|
|
334
|
-
pointer-events: none;
|
|
335
|
-
contain: layout style paint;
|
|
336
|
-
isolation: isolate;
|
|
337
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
.layer {
|
|
341
|
-
position: fixed;
|
|
342
|
-
left: 0;
|
|
343
|
-
top: 0;
|
|
344
|
-
width: 0;
|
|
345
|
-
height: 0;
|
|
346
|
-
pointer-events: none;
|
|
347
|
-
will-change: transform, width, height, opacity;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
.marker {
|
|
351
|
-
position: fixed;
|
|
352
|
-
left: 0;
|
|
353
|
-
top: 0;
|
|
354
|
-
box-sizing: border-box;
|
|
355
|
-
border-radius: 2px;
|
|
356
|
-
pointer-events: none;
|
|
357
|
-
will-change: transform, width, height, opacity;
|
|
358
|
-
transition: opacity 80ms linear;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
.marker[hidden] { display: none; }
|
|
362
|
-
|
|
363
|
-
.marker.hover { box-shadow: 0 0 0 2px var(--clickly-hover), var(--clickly-shadow); }
|
|
364
|
-
.marker.pinned { box-shadow: 0 0 0 2px var(--clickly-pinned), var(--clickly-shadow); }
|
|
365
|
-
.marker.selected { box-shadow: 0 0 0 2px var(--clickly-selected), var(--clickly-shadow); }
|
|
366
|
-
|
|
367
|
-
/* Marquee \u2014 used during drag (dashed) AND for the committed union
|
|
368
|
-
box around multi/area selections (solid). */
|
|
369
|
-
.marker.marquee {
|
|
370
|
-
border: 2px dashed var(--clickly-marquee-stroke);
|
|
371
|
-
background: var(--clickly-marquee-fill);
|
|
372
|
-
border-radius: 8px;
|
|
373
|
-
}
|
|
374
|
-
.marker.marquee.is-committed {
|
|
375
|
-
border-style: solid;
|
|
376
|
-
box-shadow: 0 0 0 1px rgba(16, 185, 129, 0.18);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
.label {
|
|
380
|
-
position: fixed;
|
|
381
|
-
left: 0;
|
|
382
|
-
top: 0;
|
|
383
|
-
padding: 2px 6px;
|
|
384
|
-
background: var(--clickly-label-bg);
|
|
385
|
-
color: var(--clickly-label-fg);
|
|
386
|
-
font-size: 11px;
|
|
387
|
-
line-height: 1.4;
|
|
388
|
-
border-radius: 4px;
|
|
389
|
-
white-space: nowrap;
|
|
390
|
-
pointer-events: none;
|
|
391
|
-
user-select: none;
|
|
392
|
-
max-width: 50vw;
|
|
393
|
-
overflow: hidden;
|
|
394
|
-
text-overflow: ellipsis;
|
|
395
|
-
}
|
|
396
|
-
`;
|
|
397
|
-
var HOST_TAG = "clickly-root";
|
|
398
|
-
function createShadowHost(deps = {}) {
|
|
399
|
-
const doc = deps.document ?? (typeof document !== "undefined" ? document : null);
|
|
400
|
-
if (!doc) throw new Error("createShadowHost: no Document available");
|
|
401
|
-
const existing = doc.querySelector(HOST_TAG);
|
|
402
|
-
const host = existing ?? doc.createElement(HOST_TAG);
|
|
403
|
-
if (!existing) doc.body.appendChild(host);
|
|
404
|
-
const root = host.shadowRoot ?? host.attachShadow({ mode: "open" });
|
|
405
|
-
if (!root.querySelector("style[data-clickly]")) {
|
|
406
|
-
const style = doc.createElement("style");
|
|
407
|
-
style.setAttribute("data-clickly", "");
|
|
408
|
-
style.textContent = OVERLAY_CSS;
|
|
409
|
-
root.appendChild(style);
|
|
410
|
-
}
|
|
411
|
-
return {
|
|
412
|
-
host,
|
|
413
|
-
root,
|
|
414
|
-
destroy() {
|
|
415
|
-
host.remove();
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
var OverlayRenderer = class {
|
|
420
|
-
root;
|
|
421
|
-
document;
|
|
422
|
-
layer;
|
|
423
|
-
hover;
|
|
424
|
-
hoverLabel;
|
|
425
|
-
/** Marquee rect — used both for live drag AND for the union box
|
|
426
|
-
* drawn around multi-element selections after commit. */
|
|
427
|
-
marquee;
|
|
428
|
-
/** Pinned/selected single-element rings (recycled across renders). */
|
|
429
|
-
pinnedPool = [];
|
|
430
|
-
selectedPool = [];
|
|
431
|
-
/** Last rendered state — used to re-render on scroll without a state change. */
|
|
432
|
-
lastState = null;
|
|
433
|
-
constructor(root, document2) {
|
|
434
|
-
this.root = root;
|
|
435
|
-
this.document = document2 ?? root.ownerDocument ?? (typeof globalThis !== "undefined" && globalThis.document ? globalThis.document : null);
|
|
436
|
-
if (!this.document) throw new Error("OverlayRenderer: no Document available");
|
|
437
|
-
this.layer = this.div("layer");
|
|
438
|
-
this.root.appendChild(this.layer);
|
|
439
|
-
this.hover = this.div("marker hover");
|
|
440
|
-
this.hover.hidden = true;
|
|
441
|
-
this.layer.appendChild(this.hover);
|
|
442
|
-
this.hoverLabel = this.div("label");
|
|
443
|
-
this.hoverLabel.hidden = true;
|
|
444
|
-
this.layer.appendChild(this.hoverLabel);
|
|
445
|
-
this.marquee = this.div("marker marquee");
|
|
446
|
-
this.marquee.hidden = true;
|
|
447
|
-
this.layer.appendChild(this.marquee);
|
|
448
|
-
this.document.addEventListener("scroll", this.onScroll, {
|
|
449
|
-
passive: true,
|
|
450
|
-
capture: true
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
onScroll = () => {
|
|
454
|
-
if (this.lastState) this.renderState(this.lastState);
|
|
455
|
-
};
|
|
456
|
-
render(state) {
|
|
457
|
-
this.lastState = state;
|
|
458
|
-
this.renderState(state);
|
|
459
|
-
}
|
|
460
|
-
renderState(state) {
|
|
461
|
-
this.renderSingle(this.hover, state.hover);
|
|
462
|
-
if (state.hover) {
|
|
463
|
-
this.hoverLabel.hidden = false;
|
|
464
|
-
this.hoverLabel.textContent = describeElement(state.hover);
|
|
465
|
-
const rect = state.hover.getBoundingClientRect();
|
|
466
|
-
const labelY = rect.top - 20 < 0 ? rect.bottom + 4 : rect.top - 20;
|
|
467
|
-
moveTo(this.hoverLabel, rect.left, labelY);
|
|
468
|
-
} else {
|
|
469
|
-
this.hoverLabel.hidden = true;
|
|
470
|
-
}
|
|
471
|
-
this.renderList(this.pinnedPool, state.pinned, "marker pinned");
|
|
472
|
-
const sel = state.selection ?? [];
|
|
473
|
-
const isMultiUnion = sel.length > 1 || state.marquee !== null && sel.length === 0;
|
|
474
|
-
if (sel.length === 1) {
|
|
475
|
-
this.renderList(this.selectedPool, [sel[0]], "marker selected");
|
|
476
|
-
} else {
|
|
477
|
-
this.renderList(this.selectedPool, [], "marker selected");
|
|
478
|
-
}
|
|
479
|
-
const marqueeRect = state.marquee ?? (sel.length > 1 ? unionOf(sel) : null);
|
|
480
|
-
if (marqueeRect) {
|
|
481
|
-
this.marquee.classList.toggle("is-committed", state.marquee === null);
|
|
482
|
-
this.placeRect(this.marquee, marqueeRect);
|
|
483
|
-
this.marquee.hidden = false;
|
|
484
|
-
} else {
|
|
485
|
-
this.marquee.hidden = true;
|
|
486
|
-
}
|
|
487
|
-
void isMultiUnion;
|
|
488
|
-
}
|
|
489
|
-
destroy() {
|
|
490
|
-
this.document.removeEventListener("scroll", this.onScroll, { capture: true });
|
|
491
|
-
this.lastState = null;
|
|
492
|
-
this.layer.remove();
|
|
493
|
-
}
|
|
494
|
-
/* ─── Internals ───────────────────────────────────────────────── */
|
|
495
|
-
renderSingle(node, target) {
|
|
496
|
-
if (!target) {
|
|
497
|
-
node.hidden = true;
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
const r = target.getBoundingClientRect();
|
|
501
|
-
if (r.width === 0 && r.height === 0) {
|
|
502
|
-
node.hidden = true;
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
this.placeRect(node, { x: r.left, y: r.top, width: r.width, height: r.height });
|
|
506
|
-
node.hidden = false;
|
|
507
|
-
}
|
|
508
|
-
renderList(pool, targets, className) {
|
|
509
|
-
while (pool.length < targets.length) {
|
|
510
|
-
const el = this.div(className);
|
|
511
|
-
el.hidden = true;
|
|
512
|
-
this.layer.appendChild(el);
|
|
513
|
-
pool.push(el);
|
|
514
|
-
}
|
|
515
|
-
for (let i = 0; i < pool.length; i++) {
|
|
516
|
-
const node = pool[i];
|
|
517
|
-
const target = targets[i];
|
|
518
|
-
if (!target) {
|
|
519
|
-
node.hidden = true;
|
|
520
|
-
continue;
|
|
521
|
-
}
|
|
522
|
-
const r = target.getBoundingClientRect();
|
|
523
|
-
if (r.width === 0 && r.height === 0) {
|
|
524
|
-
node.hidden = true;
|
|
525
|
-
continue;
|
|
526
|
-
}
|
|
527
|
-
this.placeRect(node, { x: r.left, y: r.top, width: r.width, height: r.height });
|
|
528
|
-
node.hidden = false;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
placeRect(node, rect) {
|
|
532
|
-
moveTo(node, rect.x, rect.y);
|
|
533
|
-
node.style.width = `${rect.width}px`;
|
|
534
|
-
node.style.height = `${rect.height}px`;
|
|
535
|
-
}
|
|
536
|
-
div(className) {
|
|
537
|
-
const el = this.document.createElement("div");
|
|
538
|
-
el.className = className;
|
|
539
|
-
return el;
|
|
540
|
-
}
|
|
541
|
-
};
|
|
542
|
-
function moveTo(node, x, y) {
|
|
543
|
-
const tx = Math.round(x);
|
|
544
|
-
const ty = Math.round(y);
|
|
545
|
-
node.style.transform = `translate3d(${tx}px, ${ty}px, 0)`;
|
|
546
|
-
}
|
|
547
|
-
var TAG_LABELS = {
|
|
548
|
-
p: "paragraph",
|
|
549
|
-
h1: "heading",
|
|
550
|
-
h2: "heading",
|
|
551
|
-
h3: "heading",
|
|
552
|
-
h4: "heading",
|
|
553
|
-
h5: "heading",
|
|
554
|
-
h6: "heading",
|
|
555
|
-
a: "link",
|
|
556
|
-
button: "button",
|
|
557
|
-
input: "input",
|
|
558
|
-
textarea: "textarea",
|
|
559
|
-
select: "select",
|
|
560
|
-
img: "image",
|
|
561
|
-
video: "video",
|
|
562
|
-
audio: "audio",
|
|
563
|
-
form: "form",
|
|
564
|
-
nav: "nav",
|
|
565
|
-
header: "header",
|
|
566
|
-
footer: "footer",
|
|
567
|
-
main: "main",
|
|
568
|
-
section: "section",
|
|
569
|
-
article: "article",
|
|
570
|
-
aside: "aside",
|
|
571
|
-
ul: "list",
|
|
572
|
-
ol: "list",
|
|
573
|
-
li: "list item",
|
|
574
|
-
table: "table",
|
|
575
|
-
thead: "table head",
|
|
576
|
-
tbody: "table body",
|
|
577
|
-
tr: "table row",
|
|
578
|
-
td: "cell",
|
|
579
|
-
th: "header cell",
|
|
580
|
-
span: "span",
|
|
581
|
-
div: "div",
|
|
582
|
-
label: "label",
|
|
583
|
-
code: "code",
|
|
584
|
-
pre: "code block",
|
|
585
|
-
blockquote: "quote",
|
|
586
|
-
strong: "bold",
|
|
587
|
-
em: "italic",
|
|
588
|
-
kbd: "key",
|
|
589
|
-
svg: "svg",
|
|
590
|
-
canvas: "canvas"
|
|
591
|
-
};
|
|
592
|
-
function describeElement(el) {
|
|
593
|
-
const tag = el.tagName.toLowerCase();
|
|
594
|
-
const type = TAG_LABELS[tag] ?? tag;
|
|
595
|
-
const text = (el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
596
|
-
if (text.length > 0) {
|
|
597
|
-
const preview = text.length > 48 ? text.slice(0, 48) + "\u2026" : text;
|
|
598
|
-
return `${type}: "${preview}"`;
|
|
599
|
-
}
|
|
600
|
-
if (el.id) return `${type}: #${el.id}`;
|
|
601
|
-
const cls = el.classList[0];
|
|
602
|
-
if (cls) return `${type}: .${cls}`;
|
|
603
|
-
return type;
|
|
604
|
-
}
|
|
605
|
-
function unionOf(elements) {
|
|
606
|
-
let minX = Infinity;
|
|
607
|
-
let minY = Infinity;
|
|
608
|
-
let maxX = -Infinity;
|
|
609
|
-
let maxY = -Infinity;
|
|
610
|
-
let any = false;
|
|
611
|
-
for (const el of elements) {
|
|
612
|
-
const r = el.getBoundingClientRect();
|
|
613
|
-
if (r.width === 0 && r.height === 0) continue;
|
|
614
|
-
any = true;
|
|
615
|
-
if (r.left < minX) minX = r.left;
|
|
616
|
-
if (r.top < minY) minY = r.top;
|
|
617
|
-
if (r.right > maxX) maxX = r.right;
|
|
618
|
-
if (r.bottom > maxY) maxY = r.bottom;
|
|
619
|
-
}
|
|
620
|
-
if (!any) return null;
|
|
621
|
-
const PAD = 4;
|
|
622
|
-
return {
|
|
623
|
-
x: minX - PAD,
|
|
624
|
-
y: minY - PAD,
|
|
625
|
-
width: maxX - minX + PAD * 2,
|
|
626
|
-
height: maxY - minY + PAD * 2
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
var emptyRenderState = {
|
|
630
|
-
hover: null,
|
|
631
|
-
pinned: [],
|
|
632
|
-
selection: null,
|
|
633
|
-
marquee: null
|
|
634
|
-
};
|
|
635
|
-
var Overlay = class {
|
|
636
|
-
renderer;
|
|
637
|
-
engine;
|
|
638
|
-
doc;
|
|
639
|
-
win;
|
|
640
|
-
searchRoot;
|
|
641
|
-
excludeHost;
|
|
642
|
-
raf;
|
|
643
|
-
caf;
|
|
644
|
-
state = emptyRenderState;
|
|
645
|
-
unsubscribe = null;
|
|
646
|
-
rafHandle = null;
|
|
647
|
-
/** Guard for scheduleRender re-entry. Tracked separately from `rafHandle`
|
|
648
|
-
* because a synchronous `raf` (used in tests) returns a handle the cb has
|
|
649
|
-
* already invalidated — boolean is the safe sentinel. */
|
|
650
|
-
renderPending = false;
|
|
651
|
-
destroyed = false;
|
|
652
|
-
bound = [];
|
|
653
|
-
constructor(opts) {
|
|
654
|
-
this.engine = opts.engine;
|
|
655
|
-
this.doc = opts.document ?? opts.root.ownerDocument ?? null;
|
|
656
|
-
if (!this.doc) throw new Error("Overlay: no Document available");
|
|
657
|
-
this.win = this.doc.defaultView ?? globalThis;
|
|
658
|
-
this.searchRoot = opts.searchRoot ?? this.doc.body;
|
|
659
|
-
this.excludeHost = opts.excludeHost ?? null;
|
|
660
|
-
this.raf = opts.raf ?? ((cb) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(cb) : setTimeout(() => cb(performance.now()), 16));
|
|
661
|
-
this.caf = opts.caf ?? ((h) => {
|
|
662
|
-
if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(h);
|
|
663
|
-
else clearTimeout(h);
|
|
664
|
-
});
|
|
665
|
-
this.renderer = new OverlayRenderer(opts.root, this.doc);
|
|
666
|
-
this.bindEvent(this.doc, "scroll", this.onReposition, { passive: true, capture: true });
|
|
667
|
-
this.bindEvent(this.win, "resize", this.onReposition, { passive: true });
|
|
668
|
-
this.unsubscribe = this.engine.subscribe((s) => this.onEngineState(s));
|
|
669
|
-
this.onEngineState(this.engine.getSnapshot());
|
|
670
|
-
}
|
|
671
|
-
destroy() {
|
|
672
|
-
this.destroyed = true;
|
|
673
|
-
if (this.unsubscribe) this.unsubscribe();
|
|
674
|
-
this.unsubscribe = null;
|
|
675
|
-
if (this.rafHandle !== null) this.caf(this.rafHandle);
|
|
676
|
-
this.rafHandle = null;
|
|
677
|
-
for (const [t, ev, fn, opts] of this.bound) t.removeEventListener(ev, fn, opts);
|
|
678
|
-
this.bound = [];
|
|
679
|
-
this.renderer.destroy();
|
|
680
|
-
}
|
|
681
|
-
/* ─── Internals ───────────────────────────────────────────────── */
|
|
682
|
-
onReposition = () => {
|
|
683
|
-
if (this.destroyed) return;
|
|
684
|
-
if (hasAnythingToTrack(this.state)) this.scheduleRender();
|
|
685
|
-
};
|
|
686
|
-
onEngineState(s) {
|
|
687
|
-
this.state = this.derive(s);
|
|
688
|
-
this.scheduleRender();
|
|
689
|
-
}
|
|
690
|
-
/** Engine state → render state. */
|
|
691
|
-
derive(s) {
|
|
692
|
-
switch (s.kind) {
|
|
693
|
-
case "idle":
|
|
694
|
-
return emptyRenderState;
|
|
695
|
-
case "inspect":
|
|
696
|
-
return { hover: s.hoverTarget, pinned: s.pinned, selection: null, marquee: null };
|
|
697
|
-
case "pressed":
|
|
698
|
-
return { hover: s.target, pinned: s.pinned, selection: null, marquee: null };
|
|
699
|
-
case "dragging": {
|
|
700
|
-
const rect = rectFromPoints(s.start, s.current);
|
|
701
|
-
return { hover: null, pinned: s.pinned, selection: null, marquee: rect };
|
|
702
|
-
}
|
|
703
|
-
case "annotating": {
|
|
704
|
-
const sel = s.selection;
|
|
705
|
-
const elements = sel.kind === "area" ? pickElementsInRect(this.searchRoot, sel.rect, this.excludeHost) : sel.kind === "multi" ? sel.elements : [sel.element];
|
|
706
|
-
return { hover: null, pinned: s.pinned, selection: elements, marquee: null };
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
scheduleRender() {
|
|
711
|
-
if (this.destroyed || this.renderPending) return;
|
|
712
|
-
this.renderPending = true;
|
|
713
|
-
this.rafHandle = this.raf(() => {
|
|
714
|
-
this.renderPending = false;
|
|
715
|
-
this.rafHandle = null;
|
|
716
|
-
if (this.destroyed) return;
|
|
717
|
-
this.renderer.render(this.state);
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
|
-
bindEvent(target, ev, fn, opts) {
|
|
721
|
-
target.addEventListener(ev, fn, opts);
|
|
722
|
-
this.bound.push([target, ev, fn, opts]);
|
|
723
|
-
}
|
|
724
|
-
};
|
|
725
|
-
function hasAnythingToTrack(s) {
|
|
726
|
-
return s.hover !== null || s.pinned.length > 0 || s.selection !== null && s.selection.length > 0 || s.marquee !== null;
|
|
727
|
-
}
|
|
728
|
-
var FIBER_PROP_PREFIX = "__reactFiber$";
|
|
729
|
-
function getFiber(node) {
|
|
730
|
-
if (!node) return null;
|
|
731
|
-
for (const key of Object.keys(node)) {
|
|
732
|
-
if (key.startsWith(FIBER_PROP_PREFIX)) return node[key];
|
|
733
|
-
}
|
|
734
|
-
return null;
|
|
735
|
-
}
|
|
736
|
-
function getComponentChain(node) {
|
|
737
|
-
const fiber = getFiber(node);
|
|
738
|
-
if (!fiber) return [];
|
|
739
|
-
const names = [];
|
|
740
|
-
let cur = fiber;
|
|
741
|
-
while (cur) {
|
|
742
|
-
const name = getComponentName(cur);
|
|
743
|
-
if (name) names.unshift(name);
|
|
744
|
-
cur = cur.return;
|
|
745
|
-
}
|
|
746
|
-
return dedupeConsecutive(names);
|
|
747
|
-
}
|
|
748
|
-
function getComponentName(fiber) {
|
|
749
|
-
const t = fiber.type;
|
|
750
|
-
if (!t) return null;
|
|
751
|
-
if (typeof t === "string") return null;
|
|
752
|
-
if (typeof t === "function") {
|
|
753
|
-
const fn = t;
|
|
754
|
-
return fn.displayName || fn.name || null;
|
|
755
|
-
}
|
|
756
|
-
if (typeof t === "object") {
|
|
757
|
-
const o = t;
|
|
758
|
-
if (o.displayName) return o.displayName;
|
|
759
|
-
if (o.render) return o.render.displayName || o.render.name || null;
|
|
760
|
-
if (o.type) return o.type.displayName || o.type.name || null;
|
|
761
|
-
}
|
|
762
|
-
return null;
|
|
763
|
-
}
|
|
764
|
-
function dedupeConsecutive(names) {
|
|
765
|
-
const out = [];
|
|
766
|
-
for (const n of names) {
|
|
767
|
-
if (out[out.length - 1] !== n) out.push(n);
|
|
768
|
-
}
|
|
769
|
-
return out;
|
|
770
|
-
}
|
|
771
|
-
function getSourceInfo(node) {
|
|
772
|
-
const fiber = getFiber(node);
|
|
773
|
-
if (!fiber) return null;
|
|
774
|
-
let cur = fiber._debugOwner;
|
|
775
|
-
while (cur) {
|
|
776
|
-
if (cur._debugSource) return cur._debugSource;
|
|
777
|
-
cur = cur._debugOwner;
|
|
778
|
-
}
|
|
779
|
-
cur = fiber;
|
|
780
|
-
while (cur) {
|
|
781
|
-
if (cur._debugSource) return cur._debugSource;
|
|
782
|
-
cur = cur.return;
|
|
783
|
-
}
|
|
784
|
-
return null;
|
|
785
|
-
}
|
|
786
|
-
function collectAccessibility(el) {
|
|
787
|
-
const parts = [];
|
|
788
|
-
const role = el.getAttribute("role");
|
|
789
|
-
if (role) parts.push(`role=${role}`);
|
|
790
|
-
for (const attr of Array.from(el.attributes)) {
|
|
791
|
-
if (attr.name.startsWith("aria-") && attr.value) {
|
|
792
|
-
parts.push(`${attr.name}=${attr.value}`);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
const tabindex = el.getAttribute("tabindex");
|
|
796
|
-
if (tabindex !== null && tabindex !== "") parts.push(`tabindex=${tabindex}`);
|
|
797
|
-
const title = el.getAttribute("title");
|
|
798
|
-
if (title) parts.push(`title=${title}`);
|
|
799
|
-
return parts.join("; ");
|
|
800
|
-
}
|
|
801
|
-
var GROUPS = {
|
|
802
|
-
layout: [
|
|
803
|
-
"display",
|
|
804
|
-
"position",
|
|
805
|
-
"top",
|
|
806
|
-
"right",
|
|
807
|
-
"bottom",
|
|
808
|
-
"left",
|
|
809
|
-
"width",
|
|
810
|
-
"height",
|
|
811
|
-
"margin",
|
|
812
|
-
"padding",
|
|
813
|
-
"box-sizing"
|
|
814
|
-
],
|
|
815
|
-
visual: [
|
|
816
|
-
"color",
|
|
817
|
-
"background-color",
|
|
818
|
-
"border",
|
|
819
|
-
"border-radius",
|
|
820
|
-
"outline"
|
|
821
|
-
],
|
|
822
|
-
text: [
|
|
823
|
-
"font-family",
|
|
824
|
-
"font-size",
|
|
825
|
-
"font-weight",
|
|
826
|
-
"line-height",
|
|
827
|
-
"letter-spacing",
|
|
828
|
-
"text-align",
|
|
829
|
-
"white-space"
|
|
830
|
-
],
|
|
831
|
-
flexgrid: [
|
|
832
|
-
"flex-direction",
|
|
833
|
-
"justify-content",
|
|
834
|
-
"align-items",
|
|
835
|
-
"gap",
|
|
836
|
-
"grid-template-columns",
|
|
837
|
-
"grid-template-rows"
|
|
838
|
-
],
|
|
839
|
-
effects: ["transform", "box-shadow", "filter"],
|
|
840
|
-
misc: ["cursor", "z-index", "overflow", "opacity", "transition", "animation"]
|
|
841
|
-
};
|
|
842
|
-
var TIER_GROUPS = {
|
|
843
|
-
compact: [],
|
|
844
|
-
standard: ["layout", "visual", "text"],
|
|
845
|
-
detailed: ["layout", "visual", "text", "flexgrid", "effects"],
|
|
846
|
-
forensic: ["layout", "visual", "text", "flexgrid", "effects", "misc"]
|
|
847
|
-
};
|
|
848
|
-
function collectComputedStyles(el, detail) {
|
|
849
|
-
if (detail === "compact") return {};
|
|
850
|
-
const doc = el.ownerDocument;
|
|
851
|
-
const win = doc?.defaultView ?? globalThis;
|
|
852
|
-
if (typeof win.getComputedStyle !== "function") return {};
|
|
853
|
-
const cs = win.getComputedStyle(el);
|
|
854
|
-
const props = /* @__PURE__ */ new Set();
|
|
855
|
-
for (const g of TIER_GROUPS[detail]) {
|
|
856
|
-
for (const p of GROUPS[g]) props.add(p);
|
|
857
|
-
}
|
|
858
|
-
const out = {};
|
|
859
|
-
for (const p of props) {
|
|
860
|
-
const v = cs.getPropertyValue(p);
|
|
861
|
-
if (!v) continue;
|
|
862
|
-
const trimmed = v.trim();
|
|
863
|
-
if (isUninterestingDefault(p, trimmed)) continue;
|
|
864
|
-
out[p] = trimmed;
|
|
865
|
-
}
|
|
866
|
-
return out;
|
|
867
|
-
}
|
|
868
|
-
function isUninterestingDefault(prop, value) {
|
|
869
|
-
if (!value || value === "none" || value === "auto" || value === "normal") return true;
|
|
870
|
-
if (value === "0px" || value === "0%") return true;
|
|
871
|
-
if (value === "rgba(0, 0, 0, 0)") return true;
|
|
872
|
-
if (prop === "color" || prop === "background-color") {
|
|
873
|
-
return value === "rgba(0, 0, 0, 0)";
|
|
874
|
-
}
|
|
875
|
-
return false;
|
|
876
|
-
}
|
|
877
|
-
var SHORT_MAX_DEPTH = 5;
|
|
878
|
-
var FULL_MAX_DEPTH = 8;
|
|
879
|
-
function buildSelector(target, doc = target.ownerDocument ?? document) {
|
|
880
|
-
if (target.id && isStableId(target.id)) {
|
|
881
|
-
const escaped = cssEscape(target.id);
|
|
882
|
-
const id = `#${escaped}`;
|
|
883
|
-
return { short: id, full: id };
|
|
884
|
-
}
|
|
885
|
-
const segments = [];
|
|
886
|
-
let cur = target;
|
|
887
|
-
let short = null;
|
|
888
|
-
for (let depth = 0; cur && depth < FULL_MAX_DEPTH; depth++) {
|
|
889
|
-
segments.unshift(segmentFor(cur));
|
|
890
|
-
const candidate = segments.join(" > ");
|
|
891
|
-
if (short === null && depth < SHORT_MAX_DEPTH && isUnique(doc, candidate)) {
|
|
892
|
-
short = candidate;
|
|
893
|
-
}
|
|
894
|
-
cur = cur.parentElement;
|
|
895
|
-
if (!cur || cur === doc.documentElement || cur.tagName.toLowerCase() === "html") break;
|
|
896
|
-
}
|
|
897
|
-
const full = segments.join(" > ");
|
|
898
|
-
return { short: short ?? full, full };
|
|
899
|
-
}
|
|
900
|
-
function segmentFor(el) {
|
|
901
|
-
const tag = el.tagName.toLowerCase();
|
|
902
|
-
if (el.id && isStableId(el.id)) return `${tag}#${cssEscape(el.id)}`;
|
|
903
|
-
const classes = Array.from(el.classList).filter(isUseableClass).slice(0, 3);
|
|
904
|
-
let segment = classes.length ? `${tag}.${classes.map(cssEscape).join(".")}` : tag;
|
|
905
|
-
const parent = el.parentElement;
|
|
906
|
-
if (parent) {
|
|
907
|
-
const sameTag = Array.from(parent.children).filter(
|
|
908
|
-
(c) => c.tagName === el.tagName
|
|
909
|
-
);
|
|
910
|
-
if (sameTag.length > 1) {
|
|
911
|
-
const idx = sameTag.indexOf(el) + 1;
|
|
912
|
-
if (idx > 0) segment += `:nth-of-type(${idx})`;
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
return segment;
|
|
916
|
-
}
|
|
917
|
-
function isUseableClass(c) {
|
|
918
|
-
if (!c) return false;
|
|
919
|
-
if (c.length > 30) return false;
|
|
920
|
-
if (/^css-[a-z0-9]+$/i.test(c)) return false;
|
|
921
|
-
if (/^jsx-\d+$/i.test(c)) return false;
|
|
922
|
-
if (/^_.+_[a-z0-9]{5,}$/i.test(c)) return false;
|
|
923
|
-
if (/^\d/.test(c)) return false;
|
|
924
|
-
return true;
|
|
925
|
-
}
|
|
926
|
-
function isStableId(id) {
|
|
927
|
-
if (/^:[a-z]\d+:/i.test(id)) return false;
|
|
928
|
-
if (/^radix-/i.test(id)) return false;
|
|
929
|
-
if (/^headlessui-/i.test(id)) return false;
|
|
930
|
-
return true;
|
|
931
|
-
}
|
|
932
|
-
function isUnique(doc, selector) {
|
|
933
|
-
try {
|
|
934
|
-
return doc.querySelectorAll(selector).length === 1;
|
|
935
|
-
} catch {
|
|
936
|
-
return false;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
function cssEscape(s) {
|
|
940
|
-
const g = globalThis;
|
|
941
|
-
if (g.CSS?.escape) return g.CSS.escape(s);
|
|
942
|
-
return s.replace(/[^a-zA-Z0-9_-]/g, (c) => "\\" + c);
|
|
943
|
-
}
|
|
944
|
-
var NEARBY_MAX = 200;
|
|
945
|
-
function collectNearbyText(el) {
|
|
946
|
-
const own = readText(el);
|
|
947
|
-
if (own) return truncate(own, NEARBY_MAX);
|
|
948
|
-
const parent = el.parentElement;
|
|
949
|
-
if (parent) {
|
|
950
|
-
const t = readText(parent);
|
|
951
|
-
if (t) return truncate(t, NEARBY_MAX);
|
|
952
|
-
}
|
|
953
|
-
return "";
|
|
954
|
-
}
|
|
955
|
-
function collectSelectedText(doc = document) {
|
|
956
|
-
const win = doc.defaultView ?? globalThis;
|
|
957
|
-
if (typeof win.getSelection !== "function") return "";
|
|
958
|
-
const sel = win.getSelection();
|
|
959
|
-
if (!sel) return "";
|
|
960
|
-
return sel.toString().trim();
|
|
961
|
-
}
|
|
962
|
-
function readText(el) {
|
|
963
|
-
const t = el.innerText ?? el.textContent ?? "";
|
|
964
|
-
return t.replace(/\s+/g, " ").trim();
|
|
965
|
-
}
|
|
966
|
-
function truncate(s, n) {
|
|
967
|
-
return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
968
|
-
}
|
|
969
|
-
var POSITIONED_FIXED = /* @__PURE__ */ new Set(["fixed", "sticky"]);
|
|
970
|
-
function collectMetadata(el, options = {}) {
|
|
971
|
-
perfMark2("clickly:metadata:collect");
|
|
972
|
-
const detail = options.detail ?? "standard";
|
|
973
|
-
const includeReact = options.includeReact !== false && detail !== "compact";
|
|
974
|
-
const doc = options.document ?? el.ownerDocument ?? document;
|
|
975
|
-
const { short, full } = buildSelector(el, doc);
|
|
976
|
-
const rect = el.getBoundingClientRect();
|
|
977
|
-
const boundingBox = { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
|
|
978
|
-
const reactComponents = includeReact ? getComponentChain(el).join(" > ") : "";
|
|
979
|
-
const source = includeReact ? getSourceInfo(el) : null;
|
|
980
|
-
return {
|
|
981
|
-
element: el.tagName.toLowerCase(),
|
|
982
|
-
elementPath: short,
|
|
983
|
-
fullPath: full,
|
|
984
|
-
cssClasses: typeof el.className === "string" ? el.className.trim() : "",
|
|
985
|
-
computedStyles: collectComputedStyles(el, detail),
|
|
986
|
-
accessibility: collectAccessibility(el),
|
|
987
|
-
nearbyText: collectNearbyText(el),
|
|
988
|
-
selectedText: options.selectedText ?? collectSelectedText(doc),
|
|
989
|
-
boundingBox,
|
|
990
|
-
isFixed: hasFixedAncestor(el),
|
|
991
|
-
reactComponents,
|
|
992
|
-
sourceFile: source?.fileName ?? "",
|
|
993
|
-
sourceLine: source?.lineNumber ?? 0,
|
|
994
|
-
sourceColumn: source?.columnNumber ?? 0
|
|
995
|
-
};
|
|
996
|
-
}
|
|
997
|
-
function perfMark2(name) {
|
|
998
|
-
if (typeof performance !== "undefined" && typeof performance.mark === "function") {
|
|
999
|
-
try {
|
|
1000
|
-
performance.mark(name);
|
|
1001
|
-
} catch {
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
function hasFixedAncestor(el) {
|
|
1006
|
-
const doc = el.ownerDocument;
|
|
1007
|
-
const win = doc?.defaultView ?? globalThis;
|
|
1008
|
-
if (typeof win.getComputedStyle !== "function") return false;
|
|
1009
|
-
let cur = el;
|
|
1010
|
-
for (let i = 0; cur && i < 8; i++) {
|
|
1011
|
-
const pos = win.getComputedStyle(cur).getPropertyValue("position");
|
|
1012
|
-
if (POSITIONED_FIXED.has(pos)) return true;
|
|
1013
|
-
cur = cur.parentElement;
|
|
1014
|
-
}
|
|
1015
|
-
return false;
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
// packages/react/src/internal/ClicklyRoot.tsx
|
|
10
|
+
// ../react/src/internal/ClicklyRoot.tsx
|
|
1019
11
|
import { useEffect as useEffect6, useState as useState6 } from "react";
|
|
1020
12
|
|
|
1021
|
-
//
|
|
13
|
+
// ../react/src/internal/Toolbar.tsx
|
|
1022
14
|
import { useRef as useRef4, useState as useState3 } from "react";
|
|
1023
15
|
|
|
1024
|
-
//
|
|
16
|
+
// ../react/src/hooks/useDraggable.ts
|
|
1025
17
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
1026
18
|
function useDraggable(defaultPos, size) {
|
|
1027
19
|
const [position, setPosition] = useState(defaultPos);
|
|
@@ -1078,7 +70,7 @@ function clamp(n, lo, hi) {
|
|
|
1078
70
|
return Math.max(lo, Math.min(hi, n));
|
|
1079
71
|
}
|
|
1080
72
|
|
|
1081
|
-
//
|
|
73
|
+
// ../react/src/state/useEngineState.ts
|
|
1082
74
|
import { useSyncExternalStore } from "react";
|
|
1083
75
|
function useEngineState(engine) {
|
|
1084
76
|
return useSyncExternalStore(
|
|
@@ -1090,118 +82,9 @@ function useEngineState(engine) {
|
|
|
1090
82
|
}
|
|
1091
83
|
var IDLE = { kind: "idle" };
|
|
1092
84
|
|
|
1093
|
-
//
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
1097
|
-
const setState = (partial, replace) => {
|
|
1098
|
-
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
1099
|
-
if (!Object.is(nextState, state)) {
|
|
1100
|
-
const previousState = state;
|
|
1101
|
-
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
1102
|
-
listeners.forEach((listener) => listener(state, previousState));
|
|
1103
|
-
}
|
|
1104
|
-
};
|
|
1105
|
-
const getState = () => state;
|
|
1106
|
-
const getInitialState = () => initialState2;
|
|
1107
|
-
const subscribe = (listener) => {
|
|
1108
|
-
listeners.add(listener);
|
|
1109
|
-
return () => listeners.delete(listener);
|
|
1110
|
-
};
|
|
1111
|
-
const api = { setState, getState, getInitialState, subscribe };
|
|
1112
|
-
const initialState2 = state = createState(setState, getState, api);
|
|
1113
|
-
return api;
|
|
1114
|
-
};
|
|
1115
|
-
var createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);
|
|
1116
|
-
|
|
1117
|
-
// node_modules/.pnpm/zustand@5.0.14_@types+react@18.3.31_react@18.3.1/node_modules/zustand/esm/react.mjs
|
|
1118
|
-
import React from "react";
|
|
1119
|
-
var identity = (arg) => arg;
|
|
1120
|
-
function useStore(api, selector = identity) {
|
|
1121
|
-
const slice = React.useSyncExternalStore(
|
|
1122
|
-
api.subscribe,
|
|
1123
|
-
React.useCallback(() => selector(api.getState()), [api, selector]),
|
|
1124
|
-
React.useCallback(() => selector(api.getInitialState()), [api, selector])
|
|
1125
|
-
);
|
|
1126
|
-
React.useDebugValue(slice);
|
|
1127
|
-
return slice;
|
|
1128
|
-
}
|
|
1129
|
-
var createImpl = (createState) => {
|
|
1130
|
-
const api = createStore(createState);
|
|
1131
|
-
const useBoundStore = (selector) => useStore(api, selector);
|
|
1132
|
-
Object.assign(useBoundStore, api);
|
|
1133
|
-
return useBoundStore;
|
|
1134
|
-
};
|
|
1135
|
-
var create = ((createState) => createState ? createImpl(createState) : createImpl);
|
|
1136
|
-
|
|
1137
|
-
// node_modules/.pnpm/zustand@5.0.14_@types+react@18.3.31_react@18.3.1/node_modules/zustand/esm/react/shallow.mjs
|
|
1138
|
-
import React2 from "react";
|
|
1139
|
-
|
|
1140
|
-
// node_modules/.pnpm/zustand@5.0.14_@types+react@18.3.31_react@18.3.1/node_modules/zustand/esm/vanilla/shallow.mjs
|
|
1141
|
-
var isIterable = (obj) => Symbol.iterator in obj;
|
|
1142
|
-
var hasIterableEntries = (value) => (
|
|
1143
|
-
// HACK: avoid checking entries type
|
|
1144
|
-
"entries" in value
|
|
1145
|
-
);
|
|
1146
|
-
var compareEntries = (valueA, valueB) => {
|
|
1147
|
-
const mapA = valueA instanceof Map ? valueA : new Map(valueA.entries());
|
|
1148
|
-
const mapB = valueB instanceof Map ? valueB : new Map(valueB.entries());
|
|
1149
|
-
if (mapA.size !== mapB.size) {
|
|
1150
|
-
return false;
|
|
1151
|
-
}
|
|
1152
|
-
for (const [key, value] of mapA) {
|
|
1153
|
-
if (!mapB.has(key) || !Object.is(value, mapB.get(key))) {
|
|
1154
|
-
return false;
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
return true;
|
|
1158
|
-
};
|
|
1159
|
-
var compareIterables = (valueA, valueB) => {
|
|
1160
|
-
const iteratorA = valueA[Symbol.iterator]();
|
|
1161
|
-
const iteratorB = valueB[Symbol.iterator]();
|
|
1162
|
-
let nextA = iteratorA.next();
|
|
1163
|
-
let nextB = iteratorB.next();
|
|
1164
|
-
while (!nextA.done && !nextB.done) {
|
|
1165
|
-
if (!Object.is(nextA.value, nextB.value)) {
|
|
1166
|
-
return false;
|
|
1167
|
-
}
|
|
1168
|
-
nextA = iteratorA.next();
|
|
1169
|
-
nextB = iteratorB.next();
|
|
1170
|
-
}
|
|
1171
|
-
return !!nextA.done && !!nextB.done;
|
|
1172
|
-
};
|
|
1173
|
-
function shallow(valueA, valueB) {
|
|
1174
|
-
if (Object.is(valueA, valueB)) {
|
|
1175
|
-
return true;
|
|
1176
|
-
}
|
|
1177
|
-
if (typeof valueA !== "object" || valueA === null || typeof valueB !== "object" || valueB === null) {
|
|
1178
|
-
return false;
|
|
1179
|
-
}
|
|
1180
|
-
if (Object.getPrototypeOf(valueA) !== Object.getPrototypeOf(valueB)) {
|
|
1181
|
-
return false;
|
|
1182
|
-
}
|
|
1183
|
-
if (isIterable(valueA) && isIterable(valueB)) {
|
|
1184
|
-
if (hasIterableEntries(valueA) && hasIterableEntries(valueB)) {
|
|
1185
|
-
return compareEntries(valueA, valueB);
|
|
1186
|
-
}
|
|
1187
|
-
return compareIterables(valueA, valueB);
|
|
1188
|
-
}
|
|
1189
|
-
return compareEntries(
|
|
1190
|
-
{ entries: () => Object.entries(valueA) },
|
|
1191
|
-
{ entries: () => Object.entries(valueB) }
|
|
1192
|
-
);
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
// node_modules/.pnpm/zustand@5.0.14_@types+react@18.3.31_react@18.3.1/node_modules/zustand/esm/react/shallow.mjs
|
|
1196
|
-
function useShallow(selector) {
|
|
1197
|
-
const prev = React2.useRef(void 0);
|
|
1198
|
-
return (state) => {
|
|
1199
|
-
const next = selector(state);
|
|
1200
|
-
return shallow(prev.current, next) ? prev.current : prev.current = next;
|
|
1201
|
-
};
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
// packages/react/src/state/annotations.ts
|
|
85
|
+
// ../react/src/state/annotations.ts
|
|
86
|
+
import { create } from "zustand";
|
|
87
|
+
import { useShallow } from "zustand/react/shallow";
|
|
1205
88
|
var useAnnotations = create((set, get) => ({
|
|
1206
89
|
byId: {},
|
|
1207
90
|
order: [],
|
|
@@ -1232,7 +115,8 @@ function useAnnotationsList() {
|
|
|
1232
115
|
);
|
|
1233
116
|
}
|
|
1234
117
|
|
|
1235
|
-
//
|
|
118
|
+
// ../react/src/state/settings.ts
|
|
119
|
+
import { create as create2 } from "zustand";
|
|
1236
120
|
var DEFAULTS = {
|
|
1237
121
|
outputDetail: "standard",
|
|
1238
122
|
copyOnAdd: true,
|
|
@@ -1258,7 +142,7 @@ function persist(s) {
|
|
|
1258
142
|
} catch {
|
|
1259
143
|
}
|
|
1260
144
|
}
|
|
1261
|
-
var useSettings =
|
|
145
|
+
var useSettings = create2((set) => ({
|
|
1262
146
|
...load(),
|
|
1263
147
|
set: (patch) => set((cur) => {
|
|
1264
148
|
const next = { ...cur, ...patch };
|
|
@@ -1271,7 +155,7 @@ var useSettings = create((set) => ({
|
|
|
1271
155
|
}
|
|
1272
156
|
}));
|
|
1273
157
|
|
|
1274
|
-
//
|
|
158
|
+
// ../react/src/output/markdown.ts
|
|
1275
159
|
function annotationsToMarkdown(annotations, detail = "standard") {
|
|
1276
160
|
if (!annotations.length) return "(no annotations)";
|
|
1277
161
|
return annotations.map((a, i) => formatOne(a, i + 1, detail)).join("\n\n");
|
|
@@ -1294,7 +178,7 @@ function formatOne(a, index, detail) {
|
|
|
1294
178
|
}
|
|
1295
179
|
if (detail === "detailed" || detail === "forensic") {
|
|
1296
180
|
if (a.reactComponents) lines.push(`**React:** ${a.reactComponents}`);
|
|
1297
|
-
if (a.nearbyText) lines.push(`**Nearby text:** ${
|
|
181
|
+
if (a.nearbyText) lines.push(`**Nearby text:** ${truncate(a.nearbyText, 120)}`);
|
|
1298
182
|
}
|
|
1299
183
|
if (detail === "forensic" && a.computedStyles) {
|
|
1300
184
|
lines.push("**Computed styles:**\n```css\n" + a.computedStyles + "\n```");
|
|
@@ -1306,11 +190,11 @@ function formatOne(a, index, detail) {
|
|
|
1306
190
|
if (a.severity) lines.push(`**Severity:** ${a.severity}`);
|
|
1307
191
|
return lines.join("\n");
|
|
1308
192
|
}
|
|
1309
|
-
function
|
|
193
|
+
function truncate(s, n) {
|
|
1310
194
|
return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
1311
195
|
}
|
|
1312
196
|
|
|
1313
|
-
//
|
|
197
|
+
// ../react/src/internal/icons.tsx
|
|
1314
198
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1315
199
|
function Icon({
|
|
1316
200
|
children,
|
|
@@ -1332,6 +216,18 @@ function Icon({
|
|
|
1332
216
|
}
|
|
1333
217
|
);
|
|
1334
218
|
}
|
|
219
|
+
var IconFreeze = () => /* @__PURE__ */ jsxs(Icon, { size: 15, children: [
|
|
220
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "12", y2: "22" }),
|
|
221
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "12", x2: "22", y2: "12" }),
|
|
222
|
+
/* @__PURE__ */ jsx("line", { x1: "5", y1: "5", x2: "19", y2: "19" }),
|
|
223
|
+
/* @__PURE__ */ jsx("line", { x1: "19", y1: "5", x2: "5", y2: "19" }),
|
|
224
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "2", fill: "currentColor", stroke: "none" })
|
|
225
|
+
] });
|
|
226
|
+
var IconInfo = () => /* @__PURE__ */ jsxs(Icon, { size: 14, children: [
|
|
227
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
228
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "8", x2: "12", y2: "8", strokeWidth: "2.5" }),
|
|
229
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "12", x2: "12", y2: "16" })
|
|
230
|
+
] });
|
|
1335
231
|
var IconCursor = () => /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx("path", { d: "M4 4l6 16 2-7 7-2z" }) });
|
|
1336
232
|
var IconLayers = () => /* @__PURE__ */ jsxs(Icon, { children: [
|
|
1337
233
|
/* @__PURE__ */ jsx("path", { d: "M12 2l9 5-9 5-9-5 9-5z" }),
|
|
@@ -1359,7 +255,7 @@ var IconClose = () => /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(
|
|
|
1359
255
|
var IconCheck = () => /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx("path", { d: "M5 12l5 5L20 7" }) });
|
|
1360
256
|
var IconList = () => /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }) });
|
|
1361
257
|
|
|
1362
|
-
//
|
|
258
|
+
// ../react/src/internal/SettingsPopover.tsx
|
|
1363
259
|
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
1364
260
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1365
261
|
function SettingsPopover({ anchor, width, onClose }) {
|
|
@@ -1460,7 +356,7 @@ function SettingsPopover({ anchor, width, onClose }) {
|
|
|
1460
356
|
] });
|
|
1461
357
|
}
|
|
1462
358
|
|
|
1463
|
-
//
|
|
359
|
+
// ../react/src/internal/AnnotationList.tsx
|
|
1464
360
|
import { useEffect as useEffect3, useRef as useRef3, useState as useState2 } from "react";
|
|
1465
361
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1466
362
|
function AnnotationList({ anchor, width, onClose }) {
|
|
@@ -1548,9 +444,48 @@ function AnnotationCard({
|
|
|
1548
444
|
] });
|
|
1549
445
|
}
|
|
1550
446
|
|
|
1551
|
-
//
|
|
447
|
+
// ../react/src/internal/Toolbar.tsx
|
|
1552
448
|
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1553
|
-
var TOOLBAR_SIZE = { width:
|
|
449
|
+
var TOOLBAR_SIZE = { width: 360, height: 44 };
|
|
450
|
+
var SHORTCUTS = [
|
|
451
|
+
{ keys: ["\u2318", "\u21E7", "F"], label: "Toggle toolbar" },
|
|
452
|
+
{ keys: ["1"], label: "Single select mode" },
|
|
453
|
+
{ keys: ["2"], label: "Multi select mode" },
|
|
454
|
+
{ keys: ["3"], label: "Area drag mode" },
|
|
455
|
+
{ keys: ["\u21B5"], label: "Annotate pinned elements" },
|
|
456
|
+
{ keys: ["Esc"], label: "Cancel / clear / close" },
|
|
457
|
+
{ keys: ["\u2318", "\u21B5"], label: "Submit annotation" },
|
|
458
|
+
{ keys: ["C"], label: "Copy all annotations" },
|
|
459
|
+
{ keys: ["X"], label: "Clear all annotations" }
|
|
460
|
+
];
|
|
461
|
+
function ShortcutsPanel() {
|
|
462
|
+
const [visible, setVisible] = useState3(false);
|
|
463
|
+
return /* @__PURE__ */ jsxs4(
|
|
464
|
+
"span",
|
|
465
|
+
{
|
|
466
|
+
className: "clickly-tip shortcuts-trigger",
|
|
467
|
+
onMouseEnter: () => setVisible(true),
|
|
468
|
+
onMouseLeave: () => setVisible(false),
|
|
469
|
+
children: [
|
|
470
|
+
/* @__PURE__ */ jsx4(
|
|
471
|
+
"button",
|
|
472
|
+
{
|
|
473
|
+
className: "clickly-btn icon-only",
|
|
474
|
+
"aria-label": "Show keyboard shortcuts",
|
|
475
|
+
children: /* @__PURE__ */ jsx4(IconInfo, {})
|
|
476
|
+
}
|
|
477
|
+
),
|
|
478
|
+
visible && /* @__PURE__ */ jsxs4("div", { className: "shortcuts-panel", role: "tooltip", children: [
|
|
479
|
+
/* @__PURE__ */ jsx4("div", { className: "shortcuts-title", children: "Keyboard shortcuts" }),
|
|
480
|
+
SHORTCUTS.map((s) => /* @__PURE__ */ jsxs4("div", { className: "shortcuts-row", children: [
|
|
481
|
+
/* @__PURE__ */ jsx4("span", { className: "shortcuts-label", children: s.label }),
|
|
482
|
+
/* @__PURE__ */ jsx4("span", { className: "shortcuts-keys", children: s.keys.map((k) => /* @__PURE__ */ jsx4("kbd", { children: k }, k)) })
|
|
483
|
+
] }, s.label))
|
|
484
|
+
] })
|
|
485
|
+
]
|
|
486
|
+
}
|
|
487
|
+
);
|
|
488
|
+
}
|
|
1554
489
|
function Tip({
|
|
1555
490
|
label,
|
|
1556
491
|
shortcut,
|
|
@@ -1564,7 +499,7 @@ function Tip({
|
|
|
1564
499
|
] })
|
|
1565
500
|
] });
|
|
1566
501
|
}
|
|
1567
|
-
function Toolbar({ engine, onCollapse }) {
|
|
502
|
+
function Toolbar({ engine, onCollapse, isClosing, onCloseEnd, frozen, onFreezeToggle }) {
|
|
1568
503
|
const state = useEngineState(engine);
|
|
1569
504
|
const annotations = useAnnotationsList();
|
|
1570
505
|
const clearAnnotations = useAnnotations((s) => s.clear);
|
|
@@ -1597,8 +532,9 @@ function Toolbar({ engine, onCollapse }) {
|
|
|
1597
532
|
"div",
|
|
1598
533
|
{
|
|
1599
534
|
ref: anchorRef,
|
|
1600
|
-
className:
|
|
535
|
+
className: `clickly-toolbar${isClosing ? " is-closing" : ""}`,
|
|
1601
536
|
style: { left: position.x, top: position.y, width: TOOLBAR_SIZE.width },
|
|
537
|
+
onAnimationEnd: isClosing ? onCloseEnd : void 0,
|
|
1602
538
|
"aria-label": "Clickly toolbar",
|
|
1603
539
|
children: [
|
|
1604
540
|
/* @__PURE__ */ jsx4(Tip, { label: "Move", children: /* @__PURE__ */ jsx4(
|
|
@@ -1654,6 +590,16 @@ function Toolbar({ engine, onCollapse }) {
|
|
|
1654
590
|
) }),
|
|
1655
591
|
/* @__PURE__ */ jsx4("div", { className: "divider" })
|
|
1656
592
|
] }),
|
|
593
|
+
/* @__PURE__ */ jsx4(Tip, { label: frozen ? "Unfreeze animations" : "Freeze animations", children: /* @__PURE__ */ jsx4(
|
|
594
|
+
"button",
|
|
595
|
+
{
|
|
596
|
+
className: `clickly-btn icon-only${frozen ? " is-freeze" : ""}`,
|
|
597
|
+
onClick: onFreezeToggle,
|
|
598
|
+
"aria-label": frozen ? "Unfreeze page animations" : "Freeze page animations",
|
|
599
|
+
"aria-pressed": frozen,
|
|
600
|
+
children: /* @__PURE__ */ jsx4(IconFreeze, {})
|
|
601
|
+
}
|
|
602
|
+
) }),
|
|
1657
603
|
/* @__PURE__ */ jsx4(Tip, { label: "Annotations", shortcut: "L", children: /* @__PURE__ */ jsxs4(
|
|
1658
604
|
"button",
|
|
1659
605
|
{
|
|
@@ -1687,6 +633,7 @@ function Toolbar({ engine, onCollapse }) {
|
|
|
1687
633
|
}
|
|
1688
634
|
) }),
|
|
1689
635
|
/* @__PURE__ */ jsx4("div", { className: "divider" }),
|
|
636
|
+
/* @__PURE__ */ jsx4(ShortcutsPanel, {}),
|
|
1690
637
|
/* @__PURE__ */ jsx4(Tip, { label: "Settings", children: /* @__PURE__ */ jsx4(
|
|
1691
638
|
"button",
|
|
1692
639
|
{
|
|
@@ -1727,7 +674,7 @@ function Toolbar({ engine, onCollapse }) {
|
|
|
1727
674
|
);
|
|
1728
675
|
}
|
|
1729
676
|
|
|
1730
|
-
//
|
|
677
|
+
// ../react/src/internal/CollapsedFAB.tsx
|
|
1731
678
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1732
679
|
function CollapsedFAB({ onExpand }) {
|
|
1733
680
|
const annotations = useAnnotationsList();
|
|
@@ -1746,9 +693,27 @@ function CollapsedFAB({ onExpand }) {
|
|
|
1746
693
|
);
|
|
1747
694
|
}
|
|
1748
695
|
|
|
1749
|
-
//
|
|
696
|
+
// ../react/src/internal/AnnotationPopup.tsx
|
|
1750
697
|
import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef5, useState as useState4 } from "react";
|
|
1751
|
-
|
|
698
|
+
|
|
699
|
+
// ../../node_modules/.pnpm/nanoid@5.1.16/node_modules/nanoid/url-alphabet/index.js
|
|
700
|
+
var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
701
|
+
|
|
702
|
+
// ../../node_modules/.pnpm/nanoid@5.1.16/node_modules/nanoid/index.browser.js
|
|
703
|
+
var nanoid = (size = 21) => {
|
|
704
|
+
let id = "";
|
|
705
|
+
let bytes = crypto.getRandomValues(new Uint8Array(size |= 0));
|
|
706
|
+
while (size--) {
|
|
707
|
+
id += urlAlphabet[bytes[size] & 63];
|
|
708
|
+
}
|
|
709
|
+
return id;
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
// ../react/src/internal/AnnotationPopup.tsx
|
|
713
|
+
import {
|
|
714
|
+
collectMetadata,
|
|
715
|
+
collectComputedStyles
|
|
716
|
+
} from "@useclickly/core";
|
|
1752
717
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1753
718
|
var POPUP_W = 320;
|
|
1754
719
|
var POPUP_H_EST = 240;
|
|
@@ -2024,7 +989,7 @@ function anchorRect(sel) {
|
|
|
2024
989
|
if (sel.elements.length === 0) return null;
|
|
2025
990
|
return sel.elements[0].getBoundingClientRect();
|
|
2026
991
|
}
|
|
2027
|
-
var
|
|
992
|
+
var TAG_LABELS = {
|
|
2028
993
|
p: "paragraph",
|
|
2029
994
|
h1: "heading",
|
|
2030
995
|
h2: "heading",
|
|
@@ -2066,9 +1031,9 @@ var TAG_LABELS2 = {
|
|
|
2066
1031
|
svg: "svg",
|
|
2067
1032
|
canvas: "canvas"
|
|
2068
1033
|
};
|
|
2069
|
-
function
|
|
1034
|
+
function describeElement(el) {
|
|
2070
1035
|
const tag = el.tagName.toLowerCase();
|
|
2071
|
-
const type =
|
|
1036
|
+
const type = TAG_LABELS[tag] ?? tag;
|
|
2072
1037
|
const text = (el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
2073
1038
|
if (text.length > 0) {
|
|
2074
1039
|
const preview = text.length > 48 ? text.slice(0, 48) + "\u2026" : text;
|
|
@@ -2080,13 +1045,13 @@ function describeElement2(el) {
|
|
|
2080
1045
|
return type;
|
|
2081
1046
|
}
|
|
2082
1047
|
function describeSelection(sel) {
|
|
2083
|
-
if (sel.kind === "single") return
|
|
1048
|
+
if (sel.kind === "single") return describeElement(sel.element);
|
|
2084
1049
|
if (sel.kind === "area") return `area \xB7 ${sel.elements.length} element(s)`;
|
|
2085
|
-
if (sel.elements.length === 1) return
|
|
1050
|
+
if (sel.elements.length === 1) return describeElement(sel.elements[0]);
|
|
2086
1051
|
return `${sel.elements.length} element(s)`;
|
|
2087
1052
|
}
|
|
2088
1053
|
|
|
2089
|
-
//
|
|
1054
|
+
// ../react/src/internal/AnnotationPins.tsx
|
|
2090
1055
|
import { useEffect as useEffect5, useRef as useRef6, useState as useState5 } from "react";
|
|
2091
1056
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2092
1057
|
function AnnotationPins() {
|
|
@@ -2111,7 +1076,7 @@ function AnnotationPins() {
|
|
|
2111
1076
|
}, []);
|
|
2112
1077
|
return /* @__PURE__ */ jsx7(Fragment2, { children: annotations.map((a, i) => /* @__PURE__ */ jsx7(Pin, { number: i + 1, annotation: a }, a.id)) });
|
|
2113
1078
|
}
|
|
2114
|
-
var
|
|
1079
|
+
var TAG_LABELS2 = {
|
|
2115
1080
|
p: "paragraph",
|
|
2116
1081
|
h1: "heading",
|
|
2117
1082
|
h2: "heading",
|
|
@@ -2151,7 +1116,7 @@ var TAG_LABELS3 = {
|
|
|
2151
1116
|
function pinLabel(annotation) {
|
|
2152
1117
|
const raw = (annotation.element ?? "").toLowerCase();
|
|
2153
1118
|
const tag = raw.split(/[.#\s]/)[0] ?? "";
|
|
2154
|
-
return (
|
|
1119
|
+
return (TAG_LABELS2[tag] ?? tag) || "element";
|
|
2155
1120
|
}
|
|
2156
1121
|
function parseStyles(raw) {
|
|
2157
1122
|
if (!raw) return [];
|
|
@@ -2329,13 +1294,22 @@ function resolvePosition(a) {
|
|
|
2329
1294
|
return null;
|
|
2330
1295
|
}
|
|
2331
1296
|
|
|
2332
|
-
//
|
|
1297
|
+
// ../react/src/internal/ClicklyRoot.tsx
|
|
2333
1298
|
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2334
1299
|
function ClicklyRoot({
|
|
2335
1300
|
engine,
|
|
2336
1301
|
host
|
|
2337
1302
|
}) {
|
|
2338
1303
|
const [expanded, setExpanded] = useState6(false);
|
|
1304
|
+
const [toolbarClosing, setToolbarClosing] = useState6(false);
|
|
1305
|
+
const [frozen, setFrozen] = useState6(false);
|
|
1306
|
+
const collapse = () => {
|
|
1307
|
+
setToolbarClosing(true);
|
|
1308
|
+
};
|
|
1309
|
+
const onToolbarCloseEnd = () => {
|
|
1310
|
+
setToolbarClosing(false);
|
|
1311
|
+
setExpanded(false);
|
|
1312
|
+
};
|
|
2339
1313
|
const clearAnnotations = useAnnotations((s) => s.clear);
|
|
2340
1314
|
const outputDetail = useSettings((s) => s.outputDetail);
|
|
2341
1315
|
const markerColor = useSettings((s) => s.markerColor);
|
|
@@ -2343,6 +1317,32 @@ function ClicklyRoot({
|
|
|
2343
1317
|
useEffect6(() => {
|
|
2344
1318
|
host.style.setProperty("--clickly-hover", markerColor);
|
|
2345
1319
|
}, [host, markerColor]);
|
|
1320
|
+
useEffect6(() => {
|
|
1321
|
+
const STYLE_ID = "clickly-freeze-animations";
|
|
1322
|
+
const gsap = window.gsap;
|
|
1323
|
+
if (frozen) {
|
|
1324
|
+
if (!document.getElementById(STYLE_ID)) {
|
|
1325
|
+
const el = document.createElement("style");
|
|
1326
|
+
el.id = STYLE_ID;
|
|
1327
|
+
el.textContent = `
|
|
1328
|
+
*, *::before, *::after {
|
|
1329
|
+
animation-play-state: paused !important;
|
|
1330
|
+
transition-duration: 0ms !important;
|
|
1331
|
+
transition-delay: 0ms !important;
|
|
1332
|
+
}
|
|
1333
|
+
`;
|
|
1334
|
+
document.head.appendChild(el);
|
|
1335
|
+
}
|
|
1336
|
+
if (gsap?.globalTimeline) gsap.globalTimeline.pause();
|
|
1337
|
+
} else {
|
|
1338
|
+
document.getElementById(STYLE_ID)?.remove();
|
|
1339
|
+
if (gsap?.globalTimeline) gsap.globalTimeline.resume();
|
|
1340
|
+
}
|
|
1341
|
+
return () => {
|
|
1342
|
+
document.getElementById(STYLE_ID)?.remove();
|
|
1343
|
+
if (gsap?.globalTimeline) gsap.globalTimeline.resume();
|
|
1344
|
+
};
|
|
1345
|
+
}, [frozen]);
|
|
2346
1346
|
useEffect6(() => {
|
|
2347
1347
|
if (expanded) {
|
|
2348
1348
|
if (engine.getSnapshot().kind === "idle") engine.activate("single");
|
|
@@ -2377,12 +1377,13 @@ function ClicklyRoot({
|
|
|
2377
1377
|
if (active && (active.tagName === "TEXTAREA" || active.tagName === "INPUT")) return;
|
|
2378
1378
|
if (e.key.toLowerCase() === "f" && e.shiftKey && (e.metaKey || e.ctrlKey)) {
|
|
2379
1379
|
e.preventDefault();
|
|
2380
|
-
|
|
1380
|
+
if (expanded) collapse();
|
|
1381
|
+
else setExpanded(true);
|
|
2381
1382
|
return;
|
|
2382
1383
|
}
|
|
2383
1384
|
if (e.key === "Escape") {
|
|
2384
1385
|
if (engine.getSnapshot().kind === "annotating") return;
|
|
2385
|
-
if (expanded)
|
|
1386
|
+
if (expanded) collapse();
|
|
2386
1387
|
return;
|
|
2387
1388
|
}
|
|
2388
1389
|
if (!expanded) return;
|
|
@@ -2413,14 +1414,24 @@ function ClicklyRoot({
|
|
|
2413
1414
|
}, [clearAnnotations, outputDetail, expanded, engine]);
|
|
2414
1415
|
return /* @__PURE__ */ jsxs8("div", { className: "clickly-ui", children: [
|
|
2415
1416
|
/* @__PURE__ */ jsx8(AnnotationPins, {}),
|
|
2416
|
-
expanded ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
2417
|
-
/* @__PURE__ */ jsx8(
|
|
2418
|
-
|
|
1417
|
+
expanded || toolbarClosing ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1418
|
+
/* @__PURE__ */ jsx8(
|
|
1419
|
+
Toolbar,
|
|
1420
|
+
{
|
|
1421
|
+
engine,
|
|
1422
|
+
onCollapse: collapse,
|
|
1423
|
+
isClosing: toolbarClosing,
|
|
1424
|
+
onCloseEnd: onToolbarCloseEnd,
|
|
1425
|
+
frozen,
|
|
1426
|
+
onFreezeToggle: () => setFrozen((v) => !v)
|
|
1427
|
+
}
|
|
1428
|
+
),
|
|
1429
|
+
!toolbarClosing && /* @__PURE__ */ jsx8(AnnotationPopup, { engine })
|
|
2419
1430
|
] }) : /* @__PURE__ */ jsx8(CollapsedFAB, { onExpand: () => setExpanded(true) })
|
|
2420
1431
|
] });
|
|
2421
1432
|
}
|
|
2422
1433
|
|
|
2423
|
-
//
|
|
1434
|
+
// ../react/src/internal/styles.ts
|
|
2424
1435
|
var REACT_UI_CSS = `
|
|
2425
1436
|
.clickly-ui, .clickly-ui * { box-sizing: border-box; }
|
|
2426
1437
|
|
|
@@ -2446,6 +1457,11 @@ var REACT_UI_CSS = `
|
|
|
2446
1457
|
0 0 0 1px rgba(255,255,255,0.06) inset;
|
|
2447
1458
|
transition: transform 140ms ease, box-shadow 140ms ease;
|
|
2448
1459
|
z-index: 1;
|
|
1460
|
+
animation: clickly-fab-open 280ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
|
|
1461
|
+
}
|
|
1462
|
+
@keyframes clickly-fab-open {
|
|
1463
|
+
from { opacity: 0; transform: scale(0.5); }
|
|
1464
|
+
to { opacity: 1; transform: scale(1); }
|
|
2449
1465
|
}
|
|
2450
1466
|
.clickly-fab:hover {
|
|
2451
1467
|
transform: scale(1.06);
|
|
@@ -2492,12 +1508,21 @@ var REACT_UI_CSS = `
|
|
|
2492
1508
|
pointer-events: auto;
|
|
2493
1509
|
user-select: none;
|
|
2494
1510
|
z-index: 1;
|
|
2495
|
-
|
|
1511
|
+
transform-origin: bottom right;
|
|
1512
|
+
animation: clickly-toolbar-open 240ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
1513
|
+
}
|
|
1514
|
+
.clickly-toolbar.is-closing {
|
|
1515
|
+
animation: clickly-toolbar-close 200ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
1516
|
+
pointer-events: none;
|
|
2496
1517
|
}
|
|
2497
1518
|
|
|
2498
|
-
@keyframes clickly-
|
|
2499
|
-
from { opacity: 0; transform:
|
|
2500
|
-
to { opacity: 1; transform:
|
|
1519
|
+
@keyframes clickly-toolbar-open {
|
|
1520
|
+
from { opacity: 0; transform: scale(0.82) translateY(10px); }
|
|
1521
|
+
to { opacity: 1; transform: scale(1) translateY(0); }
|
|
1522
|
+
}
|
|
1523
|
+
@keyframes clickly-toolbar-close {
|
|
1524
|
+
from { opacity: 1; transform: scale(1) translateY(0); }
|
|
1525
|
+
to { opacity: 0; transform: scale(0.82) translateY(10px); }
|
|
2501
1526
|
}
|
|
2502
1527
|
|
|
2503
1528
|
.clickly-toolbar .grip {
|
|
@@ -2548,6 +1573,14 @@ var REACT_UI_CSS = `
|
|
|
2548
1573
|
.clickly-btn.is-active:hover { background: #0284c7; }
|
|
2549
1574
|
.clickly-btn[disabled] { opacity: 0.28; cursor: not-allowed; pointer-events: none; }
|
|
2550
1575
|
|
|
1576
|
+
/* Freeze-animations active state \u2014 icy blue */
|
|
1577
|
+
.clickly-btn.is-freeze {
|
|
1578
|
+
background: rgba(99, 179, 237, 0.18);
|
|
1579
|
+
color: #63b3ed;
|
|
1580
|
+
box-shadow: 0 0 0 1px rgba(99,179,237,0.35), 0 2px 8px rgba(99,179,237,0.2);
|
|
1581
|
+
}
|
|
1582
|
+
.clickly-btn.is-freeze:hover { background: rgba(99, 179, 237, 0.26); }
|
|
1583
|
+
|
|
2551
1584
|
.clickly-btn.icon-only {
|
|
2552
1585
|
width: 32px;
|
|
2553
1586
|
padding: 0;
|
|
@@ -2625,6 +1658,91 @@ var REACT_UI_CSS = `
|
|
|
2625
1658
|
color: #94a3b8;
|
|
2626
1659
|
}
|
|
2627
1660
|
|
|
1661
|
+
/* \u2500\u2500\u2500 Shortcuts panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
1662
|
+
|
|
1663
|
+
.shortcuts-trigger {
|
|
1664
|
+
position: relative;
|
|
1665
|
+
display: inline-flex;
|
|
1666
|
+
align-items: center;
|
|
1667
|
+
justify-content: center;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
.shortcuts-panel {
|
|
1671
|
+
position: absolute;
|
|
1672
|
+
bottom: calc(100% + 12px);
|
|
1673
|
+
left: 50%;
|
|
1674
|
+
transform: translateX(-50%);
|
|
1675
|
+
width: 260px;
|
|
1676
|
+
background: rgba(9, 14, 28, 0.97);
|
|
1677
|
+
border: 1px solid rgba(255,255,255,0.08);
|
|
1678
|
+
border-radius: 12px;
|
|
1679
|
+
box-shadow: 0 16px 40px rgba(0,0,0,0.45);
|
|
1680
|
+
padding: 10px;
|
|
1681
|
+
z-index: 10;
|
|
1682
|
+
animation: clickly-fade-in 100ms ease-out;
|
|
1683
|
+
pointer-events: none;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
/* Arrow pointing down */
|
|
1687
|
+
.shortcuts-panel::after {
|
|
1688
|
+
content: "";
|
|
1689
|
+
position: absolute;
|
|
1690
|
+
top: 100%;
|
|
1691
|
+
left: 50%;
|
|
1692
|
+
transform: translateX(-50%);
|
|
1693
|
+
border: 6px solid transparent;
|
|
1694
|
+
border-top-color: rgba(9, 14, 28, 0.97);
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
.shortcuts-title {
|
|
1698
|
+
font: 600 11px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
1699
|
+
color: #64748b;
|
|
1700
|
+
text-transform: uppercase;
|
|
1701
|
+
letter-spacing: 0.06em;
|
|
1702
|
+
padding: 2px 4px 8px;
|
|
1703
|
+
border-bottom: 1px solid rgba(255,255,255,0.06);
|
|
1704
|
+
margin-bottom: 6px;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
.shortcuts-row {
|
|
1708
|
+
display: flex;
|
|
1709
|
+
align-items: center;
|
|
1710
|
+
justify-content: space-between;
|
|
1711
|
+
padding: 4px 4px;
|
|
1712
|
+
border-radius: 6px;
|
|
1713
|
+
gap: 8px;
|
|
1714
|
+
}
|
|
1715
|
+
.shortcuts-row:hover { background: rgba(255,255,255,0.04); }
|
|
1716
|
+
|
|
1717
|
+
.shortcuts-label {
|
|
1718
|
+
font: 12px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
1719
|
+
color: #94a3b8;
|
|
1720
|
+
flex: 1;
|
|
1721
|
+
min-width: 0;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
.shortcuts-keys {
|
|
1725
|
+
display: flex;
|
|
1726
|
+
gap: 3px;
|
|
1727
|
+
align-items: center;
|
|
1728
|
+
flex-shrink: 0;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
.shortcuts-keys kbd {
|
|
1732
|
+
display: inline-flex;
|
|
1733
|
+
align-items: center;
|
|
1734
|
+
justify-content: center;
|
|
1735
|
+
min-width: 20px;
|
|
1736
|
+
height: 20px;
|
|
1737
|
+
padding: 0 5px;
|
|
1738
|
+
background: rgba(255,255,255,0.08);
|
|
1739
|
+
border: 1px solid rgba(255,255,255,0.10);
|
|
1740
|
+
border-bottom-width: 2px;
|
|
1741
|
+
border-radius: 5px;
|
|
1742
|
+
font: 11px/1 ui-monospace, "SF Mono", Menlo, monospace;
|
|
1743
|
+
color: #e2e8f0;
|
|
1744
|
+
}
|
|
1745
|
+
|
|
2628
1746
|
/* \u2500\u2500\u2500 Popup & popovers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2629
1747
|
|
|
2630
1748
|
.clickly-popup, .clickly-popover {
|
|
@@ -3376,7 +2494,7 @@ var REACT_UI_CSS = `
|
|
|
3376
2494
|
}
|
|
3377
2495
|
`;
|
|
3378
2496
|
|
|
3379
|
-
//
|
|
2497
|
+
// ../react/src/internal/globalStyles.ts
|
|
3380
2498
|
var GLOBAL_PAGE_CSS = `
|
|
3381
2499
|
body[data-clickly-active] {
|
|
3382
2500
|
-webkit-user-select: none !important;
|
|
@@ -3397,7 +2515,7 @@ body[data-clickly-annotating] {
|
|
|
3397
2515
|
}
|
|
3398
2516
|
`;
|
|
3399
2517
|
|
|
3400
|
-
//
|
|
2518
|
+
// ../react/src/Clickly.tsx
|
|
3401
2519
|
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
3402
2520
|
function Clickly({ className } = {}) {
|
|
3403
2521
|
const [mount, setMount] = useState7(null);
|