@useclickly/react 1.0.3 → 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 +179 -1264
- package/dist/index.js +147 -1215
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,17 +15,9 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
|
-
//
|
|
20
|
+
// ../react/src/index.ts
|
|
31
21
|
var index_exports = {};
|
|
32
22
|
__export(index_exports, {
|
|
33
23
|
Clickly: () => Clickly,
|
|
@@ -39,1030 +29,18 @@ __export(index_exports, {
|
|
|
39
29
|
});
|
|
40
30
|
module.exports = __toCommonJS(index_exports);
|
|
41
31
|
|
|
42
|
-
//
|
|
43
|
-
var
|
|
32
|
+
// ../react/src/Clickly.tsx
|
|
33
|
+
var import_react9 = require("react");
|
|
44
34
|
var import_react_dom = require("react-dom");
|
|
35
|
+
var import_core2 = require("@useclickly/core");
|
|
45
36
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
if (typeof doc.elementsFromPoint !== "function") return null;
|
|
49
|
-
const chain = doc.elementsFromPoint(x, y);
|
|
50
|
-
for (const el of chain) {
|
|
51
|
-
if (!isInExcludedSubtree(el, excludeHost)) return el;
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
function pickElementsInRect(root, rect, excludeHost) {
|
|
56
|
-
const out = [];
|
|
57
|
-
const stack = [root];
|
|
58
|
-
while (stack.length) {
|
|
59
|
-
const el = stack.pop();
|
|
60
|
-
if (isInExcludedSubtree(el, excludeHost)) continue;
|
|
61
|
-
const box = el.getBoundingClientRect();
|
|
62
|
-
if (box.width > 0 && box.height > 0 && containedIn(box, rect)) {
|
|
63
|
-
out.push(el);
|
|
64
|
-
}
|
|
65
|
-
for (let i = 0; i < el.children.length; i++) {
|
|
66
|
-
const child = el.children[i];
|
|
67
|
-
if (child) stack.push(child);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return out;
|
|
71
|
-
}
|
|
72
|
-
function containedIn(el, sel) {
|
|
73
|
-
return el.left >= sel.x && el.top >= sel.y && el.right <= sel.x + sel.width && el.bottom <= sel.y + sel.height;
|
|
74
|
-
}
|
|
75
|
-
function isInExcludedSubtree(el, host) {
|
|
76
|
-
if (!host) return false;
|
|
77
|
-
let cur = el;
|
|
78
|
-
while (cur) {
|
|
79
|
-
if (cur === host) return true;
|
|
80
|
-
cur = cur.parentNode;
|
|
81
|
-
}
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
function manhattan(a, b) {
|
|
85
|
-
return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
86
|
-
}
|
|
87
|
-
function rectFromPoints(start, current) {
|
|
88
|
-
const x = Math.min(start.x, current.x);
|
|
89
|
-
const y = Math.min(start.y, current.y);
|
|
90
|
-
const width = Math.abs(current.x - start.x);
|
|
91
|
-
const height = Math.abs(current.y - start.y);
|
|
92
|
-
return { x, y, width, height };
|
|
93
|
-
}
|
|
94
|
-
var DRAG_THRESHOLD_PX = 12;
|
|
95
|
-
var initialState = { kind: "idle" };
|
|
96
|
-
function reduce(state, event) {
|
|
97
|
-
if (event.type === "DEACTIVATE") return { kind: "idle" };
|
|
98
|
-
if (event.type === "ESCAPE") {
|
|
99
|
-
if (state.kind === "idle") return state;
|
|
100
|
-
if (state.kind === "annotating") return resumeInspect(
|
|
101
|
-
[],
|
|
102
|
-
/* mode */
|
|
103
|
-
void 0
|
|
104
|
-
);
|
|
105
|
-
if (state.kind === "inspect" && state.pinned.length > 0) {
|
|
106
|
-
return { ...state, pinned: [], hoverTarget: null };
|
|
107
|
-
}
|
|
108
|
-
return resumeInspect("pinned" in state ? state.pinned : []);
|
|
109
|
-
}
|
|
110
|
-
if (event.type === "CLEAR_PINNED") {
|
|
111
|
-
if (state.kind === "idle") return state;
|
|
112
|
-
if (state.kind === "inspect") return { ...state, pinned: [] };
|
|
113
|
-
return state;
|
|
114
|
-
}
|
|
115
|
-
switch (state.kind) {
|
|
116
|
-
case "idle":
|
|
117
|
-
if (event.type === "ACTIVATE") {
|
|
118
|
-
return {
|
|
119
|
-
kind: "inspect",
|
|
120
|
-
mode: event.mode ?? "single",
|
|
121
|
-
hoverTarget: null,
|
|
122
|
-
pinned: []
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
return state;
|
|
126
|
-
case "inspect":
|
|
127
|
-
if (event.type === "MODE_CHANGE") return { ...state, mode: event.mode };
|
|
128
|
-
if (event.type === "POINTER_MOVE") return { ...state, hoverTarget: event.target };
|
|
129
|
-
if (event.type === "POINTER_DOWN") {
|
|
130
|
-
return {
|
|
131
|
-
kind: "pressed",
|
|
132
|
-
mode: state.mode,
|
|
133
|
-
start: event.point,
|
|
134
|
-
target: event.target,
|
|
135
|
-
additive: event.additive,
|
|
136
|
-
pinned: state.pinned
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
if (event.type === "ANNOTATE_PINNED") {
|
|
140
|
-
if (state.pinned.length === 0) return state;
|
|
141
|
-
return {
|
|
142
|
-
kind: "annotating",
|
|
143
|
-
selection: { kind: "multi", elements: state.pinned },
|
|
144
|
-
pinned: state.pinned
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
return state;
|
|
148
|
-
case "pressed":
|
|
149
|
-
if (event.type === "POINTER_MOVE") {
|
|
150
|
-
if (manhattan(state.start, event.point) >= DRAG_THRESHOLD_PX) {
|
|
151
|
-
return {
|
|
152
|
-
kind: "dragging",
|
|
153
|
-
mode: state.mode,
|
|
154
|
-
start: state.start,
|
|
155
|
-
current: event.point,
|
|
156
|
-
pinned: state.pinned
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
return state;
|
|
160
|
-
}
|
|
161
|
-
if (event.type === "POINTER_UP") {
|
|
162
|
-
if (!state.target) {
|
|
163
|
-
return resumeInspect(state.pinned, state.mode);
|
|
164
|
-
}
|
|
165
|
-
if (state.additive || state.mode === "multi") {
|
|
166
|
-
const nextPinned = togglePinned(state.pinned, state.target);
|
|
167
|
-
return {
|
|
168
|
-
kind: "inspect",
|
|
169
|
-
mode: state.mode,
|
|
170
|
-
hoverTarget: state.target,
|
|
171
|
-
pinned: nextPinned
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
return {
|
|
175
|
-
kind: "annotating",
|
|
176
|
-
selection: { kind: "single", element: state.target },
|
|
177
|
-
pinned: state.pinned
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
return state;
|
|
181
|
-
case "dragging":
|
|
182
|
-
if (event.type === "POINTER_MOVE") return { ...state, current: event.point };
|
|
183
|
-
if (event.type === "POINTER_UP") {
|
|
184
|
-
const rect = rectFromPoints(state.start, state.current);
|
|
185
|
-
const selection = { kind: "area", rect, elements: [] };
|
|
186
|
-
return { kind: "annotating", selection, pinned: state.pinned };
|
|
187
|
-
}
|
|
188
|
-
return state;
|
|
189
|
-
case "annotating":
|
|
190
|
-
if (event.type === "COMMIT") {
|
|
191
|
-
return resumeInspect([]);
|
|
192
|
-
}
|
|
193
|
-
return state;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
function resumeInspect(pinned, mode) {
|
|
197
|
-
return {
|
|
198
|
-
kind: "inspect",
|
|
199
|
-
mode: mode ?? "single",
|
|
200
|
-
hoverTarget: null,
|
|
201
|
-
pinned
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
function togglePinned(pinned, el) {
|
|
205
|
-
const idx = pinned.indexOf(el);
|
|
206
|
-
if (idx === -1) return [...pinned, el];
|
|
207
|
-
const next = pinned.slice();
|
|
208
|
-
next.splice(idx, 1);
|
|
209
|
-
return next;
|
|
210
|
-
}
|
|
211
|
-
var SelectionEngine = class {
|
|
212
|
-
state = initialState;
|
|
213
|
-
listeners = /* @__PURE__ */ new Set();
|
|
214
|
-
doc;
|
|
215
|
-
host;
|
|
216
|
-
raf;
|
|
217
|
-
caf;
|
|
218
|
-
searchRoot;
|
|
219
|
-
pendingPointer = null;
|
|
220
|
-
rafHandle = null;
|
|
221
|
-
/** Guard for the pointermove RAF coalescer. Separate from `rafHandle`
|
|
222
|
-
* because a synchronous `raf` (used in tests) returns a handle the cb
|
|
223
|
-
* has already invalidated — boolean is the safe sentinel. */
|
|
224
|
-
rafPending = false;
|
|
225
|
-
attached = false;
|
|
226
|
-
boundHandlers = [];
|
|
227
|
-
constructor(deps = {}) {
|
|
228
|
-
this.doc = deps.document ?? (typeof document !== "undefined" ? document : null);
|
|
229
|
-
if (!this.doc) {
|
|
230
|
-
throw new Error("SelectionEngine: no Document available (pass `deps.document`).");
|
|
231
|
-
}
|
|
232
|
-
this.host = deps.host ?? null;
|
|
233
|
-
this.raf = deps.raf ?? ((cb) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(cb) : setTimeout(() => cb(performance.now()), 16));
|
|
234
|
-
this.caf = deps.caf ?? ((h) => {
|
|
235
|
-
if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(h);
|
|
236
|
-
else clearTimeout(h);
|
|
237
|
-
});
|
|
238
|
-
this.searchRoot = deps.searchRoot ?? this.doc.body;
|
|
239
|
-
}
|
|
240
|
-
/* ─── Subscribable<EngineState> ───────────────────────────────── */
|
|
241
|
-
subscribe(listener) {
|
|
242
|
-
this.listeners.add(listener);
|
|
243
|
-
return () => this.listeners.delete(listener);
|
|
244
|
-
}
|
|
245
|
-
getSnapshot() {
|
|
246
|
-
return this.state;
|
|
247
|
-
}
|
|
248
|
-
/* ─── Public control ──────────────────────────────────────────── */
|
|
249
|
-
activate(mode) {
|
|
250
|
-
perfMark("clickly:engine:activate");
|
|
251
|
-
this.dispatch(mode ? { type: "ACTIVATE", mode } : { type: "ACTIVATE" });
|
|
252
|
-
this.attach();
|
|
253
|
-
}
|
|
254
|
-
deactivate() {
|
|
255
|
-
perfMark("clickly:engine:deactivate");
|
|
256
|
-
this.detach();
|
|
257
|
-
this.dispatch({ type: "DEACTIVATE" });
|
|
258
|
-
}
|
|
259
|
-
setMode(mode) {
|
|
260
|
-
this.dispatch({ type: "MODE_CHANGE", mode });
|
|
261
|
-
}
|
|
262
|
-
commit() {
|
|
263
|
-
this.dispatch({ type: "COMMIT" });
|
|
264
|
-
}
|
|
265
|
-
clearPinned() {
|
|
266
|
-
this.dispatch({ type: "CLEAR_PINNED" });
|
|
267
|
-
}
|
|
268
|
-
/** Open the annotation popup populated with everything currently pinned. */
|
|
269
|
-
annotatePinned() {
|
|
270
|
-
this.dispatch({ type: "ANNOTATE_PINNED" });
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Returns the resolved selection from the current annotating state, with
|
|
274
|
-
* area-mode element enumeration filled in (the reducer leaves it empty).
|
|
275
|
-
* Returns null if not currently annotating.
|
|
276
|
-
*/
|
|
277
|
-
resolveSelection() {
|
|
278
|
-
if (this.state.kind !== "annotating") return null;
|
|
279
|
-
const sel = this.state.selection;
|
|
280
|
-
if (sel.kind !== "area") return sel;
|
|
281
|
-
const elements = pickElementsInRect(this.searchRoot, sel.rect, this.host);
|
|
282
|
-
return { ...sel, elements };
|
|
283
|
-
}
|
|
284
|
-
/* ─── Lifecycle ───────────────────────────────────────────────── */
|
|
285
|
-
attach() {
|
|
286
|
-
if (this.attached) return;
|
|
287
|
-
this.attached = true;
|
|
288
|
-
const win = this.doc.defaultView ?? globalThis;
|
|
289
|
-
this.bind(this.doc, "pointermove", this.onPointerMove, { passive: true });
|
|
290
|
-
this.bind(this.doc, "pointerdown", this.onPointerDown);
|
|
291
|
-
this.bind(this.doc, "pointerup", this.onPointerUp);
|
|
292
|
-
this.bind(win, "keydown", this.onKeyDown);
|
|
293
|
-
}
|
|
294
|
-
detach() {
|
|
295
|
-
if (!this.attached) return;
|
|
296
|
-
for (const [t, ev, fn, opts] of this.boundHandlers) t.removeEventListener(ev, fn, opts);
|
|
297
|
-
this.boundHandlers = [];
|
|
298
|
-
this.attached = false;
|
|
299
|
-
if (this.rafHandle !== null) {
|
|
300
|
-
this.caf(this.rafHandle);
|
|
301
|
-
this.rafHandle = null;
|
|
302
|
-
}
|
|
303
|
-
this.rafPending = false;
|
|
304
|
-
}
|
|
305
|
-
destroy() {
|
|
306
|
-
this.detach();
|
|
307
|
-
this.listeners.clear();
|
|
308
|
-
}
|
|
309
|
-
bind(target, ev, fn, opts) {
|
|
310
|
-
target.addEventListener(ev, fn, opts);
|
|
311
|
-
this.boundHandlers.push([target, ev, fn, opts]);
|
|
312
|
-
}
|
|
313
|
-
/* ─── DOM event handlers ──────────────────────────────────────── */
|
|
314
|
-
onPointerMove = (e) => {
|
|
315
|
-
this.pendingPointer = { x: e.clientX, y: e.clientY };
|
|
316
|
-
if (this.rafPending) return;
|
|
317
|
-
this.rafPending = true;
|
|
318
|
-
this.rafHandle = this.raf(() => {
|
|
319
|
-
this.rafPending = false;
|
|
320
|
-
this.rafHandle = null;
|
|
321
|
-
const pt = this.pendingPointer;
|
|
322
|
-
this.pendingPointer = null;
|
|
323
|
-
if (!pt) return;
|
|
324
|
-
const target = pickElementAt(this.doc, pt.x, pt.y, this.host);
|
|
325
|
-
this.dispatch({ type: "POINTER_MOVE", point: pt, target });
|
|
326
|
-
});
|
|
327
|
-
};
|
|
328
|
-
onPointerDown = (e) => {
|
|
329
|
-
if (this.host && e.composedPath().includes(this.host)) return;
|
|
330
|
-
const target = pickElementAt(this.doc, e.clientX, e.clientY, this.host);
|
|
331
|
-
this.dispatch({
|
|
332
|
-
type: "POINTER_DOWN",
|
|
333
|
-
point: { x: e.clientX, y: e.clientY },
|
|
334
|
-
target,
|
|
335
|
-
additive: e.shiftKey || e.metaKey || e.ctrlKey
|
|
336
|
-
});
|
|
337
|
-
};
|
|
338
|
-
onPointerUp = (e) => {
|
|
339
|
-
this.dispatch({ type: "POINTER_UP", point: { x: e.clientX, y: e.clientY } });
|
|
340
|
-
};
|
|
341
|
-
onKeyDown = (e) => {
|
|
342
|
-
if (e.key === "Escape") this.dispatch({ type: "ESCAPE" });
|
|
343
|
-
};
|
|
344
|
-
/* ─── Reducer plumbing ────────────────────────────────────────── */
|
|
345
|
-
dispatch(event) {
|
|
346
|
-
const next = reduce(this.state, event);
|
|
347
|
-
if (next === this.state) return;
|
|
348
|
-
this.state = next;
|
|
349
|
-
for (const l of this.listeners) l(next);
|
|
350
|
-
}
|
|
351
|
-
};
|
|
352
|
-
function perfMark(name) {
|
|
353
|
-
if (typeof performance !== "undefined" && typeof performance.mark === "function") {
|
|
354
|
-
try {
|
|
355
|
-
performance.mark(name);
|
|
356
|
-
} catch {
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
var OVERLAY_CSS = `
|
|
361
|
-
:host {
|
|
362
|
-
--clickly-hover: #06b6d4;
|
|
363
|
-
--clickly-pinned: #f59e0b;
|
|
364
|
-
--clickly-selected: #10b981;
|
|
365
|
-
--clickly-marquee-stroke: #10b981;
|
|
366
|
-
--clickly-marquee-fill: rgba(16, 185, 129, 0.10);
|
|
367
|
-
--clickly-label-bg: rgba(15, 23, 42, 0.92);
|
|
368
|
-
--clickly-label-fg: #f8fafc;
|
|
369
|
-
--clickly-shadow: 0 0 0 1px rgba(255,255,255,0.5);
|
|
370
|
-
|
|
371
|
-
all: initial;
|
|
372
|
-
position: fixed;
|
|
373
|
-
inset: 0;
|
|
374
|
-
z-index: 2147483647;
|
|
375
|
-
pointer-events: none;
|
|
376
|
-
contain: layout style paint;
|
|
377
|
-
isolation: isolate;
|
|
378
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
.layer {
|
|
382
|
-
position: fixed;
|
|
383
|
-
left: 0;
|
|
384
|
-
top: 0;
|
|
385
|
-
width: 0;
|
|
386
|
-
height: 0;
|
|
387
|
-
pointer-events: none;
|
|
388
|
-
will-change: transform, width, height, opacity;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
.marker {
|
|
392
|
-
position: fixed;
|
|
393
|
-
left: 0;
|
|
394
|
-
top: 0;
|
|
395
|
-
box-sizing: border-box;
|
|
396
|
-
border-radius: 2px;
|
|
397
|
-
pointer-events: none;
|
|
398
|
-
will-change: transform, width, height, opacity;
|
|
399
|
-
transition: opacity 80ms linear;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
.marker[hidden] { display: none; }
|
|
403
|
-
|
|
404
|
-
.marker.hover { box-shadow: 0 0 0 2px var(--clickly-hover), var(--clickly-shadow); }
|
|
405
|
-
.marker.pinned { box-shadow: 0 0 0 2px var(--clickly-pinned), var(--clickly-shadow); }
|
|
406
|
-
.marker.selected { box-shadow: 0 0 0 2px var(--clickly-selected), var(--clickly-shadow); }
|
|
407
|
-
|
|
408
|
-
/* Marquee \u2014 used during drag (dashed) AND for the committed union
|
|
409
|
-
box around multi/area selections (solid). */
|
|
410
|
-
.marker.marquee {
|
|
411
|
-
border: 2px dashed var(--clickly-marquee-stroke);
|
|
412
|
-
background: var(--clickly-marquee-fill);
|
|
413
|
-
border-radius: 8px;
|
|
414
|
-
}
|
|
415
|
-
.marker.marquee.is-committed {
|
|
416
|
-
border-style: solid;
|
|
417
|
-
box-shadow: 0 0 0 1px rgba(16, 185, 129, 0.18);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
.label {
|
|
421
|
-
position: fixed;
|
|
422
|
-
left: 0;
|
|
423
|
-
top: 0;
|
|
424
|
-
padding: 2px 6px;
|
|
425
|
-
background: var(--clickly-label-bg);
|
|
426
|
-
color: var(--clickly-label-fg);
|
|
427
|
-
font-size: 11px;
|
|
428
|
-
line-height: 1.4;
|
|
429
|
-
border-radius: 4px;
|
|
430
|
-
white-space: nowrap;
|
|
431
|
-
pointer-events: none;
|
|
432
|
-
user-select: none;
|
|
433
|
-
max-width: 50vw;
|
|
434
|
-
overflow: hidden;
|
|
435
|
-
text-overflow: ellipsis;
|
|
436
|
-
}
|
|
437
|
-
`;
|
|
438
|
-
var HOST_TAG = "clickly-root";
|
|
439
|
-
function createShadowHost(deps = {}) {
|
|
440
|
-
const doc = deps.document ?? (typeof document !== "undefined" ? document : null);
|
|
441
|
-
if (!doc) throw new Error("createShadowHost: no Document available");
|
|
442
|
-
const existing = doc.querySelector(HOST_TAG);
|
|
443
|
-
const host = existing ?? doc.createElement(HOST_TAG);
|
|
444
|
-
if (!existing) doc.body.appendChild(host);
|
|
445
|
-
const root = host.shadowRoot ?? host.attachShadow({ mode: "open" });
|
|
446
|
-
if (!root.querySelector("style[data-clickly]")) {
|
|
447
|
-
const style = doc.createElement("style");
|
|
448
|
-
style.setAttribute("data-clickly", "");
|
|
449
|
-
style.textContent = OVERLAY_CSS;
|
|
450
|
-
root.appendChild(style);
|
|
451
|
-
}
|
|
452
|
-
return {
|
|
453
|
-
host,
|
|
454
|
-
root,
|
|
455
|
-
destroy() {
|
|
456
|
-
host.remove();
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
var OverlayRenderer = class {
|
|
461
|
-
root;
|
|
462
|
-
document;
|
|
463
|
-
layer;
|
|
464
|
-
hover;
|
|
465
|
-
hoverLabel;
|
|
466
|
-
/** Marquee rect — used both for live drag AND for the union box
|
|
467
|
-
* drawn around multi-element selections after commit. */
|
|
468
|
-
marquee;
|
|
469
|
-
/** Pinned/selected single-element rings (recycled across renders). */
|
|
470
|
-
pinnedPool = [];
|
|
471
|
-
selectedPool = [];
|
|
472
|
-
/** Last rendered state — used to re-render on scroll without a state change. */
|
|
473
|
-
lastState = null;
|
|
474
|
-
constructor(root, document2) {
|
|
475
|
-
this.root = root;
|
|
476
|
-
this.document = document2 ?? root.ownerDocument ?? (typeof globalThis !== "undefined" && globalThis.document ? globalThis.document : null);
|
|
477
|
-
if (!this.document) throw new Error("OverlayRenderer: no Document available");
|
|
478
|
-
this.layer = this.div("layer");
|
|
479
|
-
this.root.appendChild(this.layer);
|
|
480
|
-
this.hover = this.div("marker hover");
|
|
481
|
-
this.hover.hidden = true;
|
|
482
|
-
this.layer.appendChild(this.hover);
|
|
483
|
-
this.hoverLabel = this.div("label");
|
|
484
|
-
this.hoverLabel.hidden = true;
|
|
485
|
-
this.layer.appendChild(this.hoverLabel);
|
|
486
|
-
this.marquee = this.div("marker marquee");
|
|
487
|
-
this.marquee.hidden = true;
|
|
488
|
-
this.layer.appendChild(this.marquee);
|
|
489
|
-
this.document.addEventListener("scroll", this.onScroll, {
|
|
490
|
-
passive: true,
|
|
491
|
-
capture: true
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
onScroll = () => {
|
|
495
|
-
if (this.lastState) this.renderState(this.lastState);
|
|
496
|
-
};
|
|
497
|
-
render(state) {
|
|
498
|
-
this.lastState = state;
|
|
499
|
-
this.renderState(state);
|
|
500
|
-
}
|
|
501
|
-
renderState(state) {
|
|
502
|
-
this.renderSingle(this.hover, state.hover);
|
|
503
|
-
if (state.hover) {
|
|
504
|
-
this.hoverLabel.hidden = false;
|
|
505
|
-
this.hoverLabel.textContent = describeElement(state.hover);
|
|
506
|
-
const rect = state.hover.getBoundingClientRect();
|
|
507
|
-
const labelY = rect.top - 20 < 0 ? rect.bottom + 4 : rect.top - 20;
|
|
508
|
-
moveTo(this.hoverLabel, rect.left, labelY);
|
|
509
|
-
} else {
|
|
510
|
-
this.hoverLabel.hidden = true;
|
|
511
|
-
}
|
|
512
|
-
this.renderList(this.pinnedPool, state.pinned, "marker pinned");
|
|
513
|
-
const sel = state.selection ?? [];
|
|
514
|
-
const isMultiUnion = sel.length > 1 || state.marquee !== null && sel.length === 0;
|
|
515
|
-
if (sel.length === 1) {
|
|
516
|
-
this.renderList(this.selectedPool, [sel[0]], "marker selected");
|
|
517
|
-
} else {
|
|
518
|
-
this.renderList(this.selectedPool, [], "marker selected");
|
|
519
|
-
}
|
|
520
|
-
const marqueeRect = state.marquee ?? (sel.length > 1 ? unionOf(sel) : null);
|
|
521
|
-
if (marqueeRect) {
|
|
522
|
-
this.marquee.classList.toggle("is-committed", state.marquee === null);
|
|
523
|
-
this.placeRect(this.marquee, marqueeRect);
|
|
524
|
-
this.marquee.hidden = false;
|
|
525
|
-
} else {
|
|
526
|
-
this.marquee.hidden = true;
|
|
527
|
-
}
|
|
528
|
-
void isMultiUnion;
|
|
529
|
-
}
|
|
530
|
-
destroy() {
|
|
531
|
-
this.document.removeEventListener("scroll", this.onScroll, { capture: true });
|
|
532
|
-
this.lastState = null;
|
|
533
|
-
this.layer.remove();
|
|
534
|
-
}
|
|
535
|
-
/* ─── Internals ───────────────────────────────────────────────── */
|
|
536
|
-
renderSingle(node, target) {
|
|
537
|
-
if (!target) {
|
|
538
|
-
node.hidden = true;
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
const r = target.getBoundingClientRect();
|
|
542
|
-
if (r.width === 0 && r.height === 0) {
|
|
543
|
-
node.hidden = true;
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
this.placeRect(node, { x: r.left, y: r.top, width: r.width, height: r.height });
|
|
547
|
-
node.hidden = false;
|
|
548
|
-
}
|
|
549
|
-
renderList(pool, targets, className) {
|
|
550
|
-
while (pool.length < targets.length) {
|
|
551
|
-
const el = this.div(className);
|
|
552
|
-
el.hidden = true;
|
|
553
|
-
this.layer.appendChild(el);
|
|
554
|
-
pool.push(el);
|
|
555
|
-
}
|
|
556
|
-
for (let i = 0; i < pool.length; i++) {
|
|
557
|
-
const node = pool[i];
|
|
558
|
-
const target = targets[i];
|
|
559
|
-
if (!target) {
|
|
560
|
-
node.hidden = true;
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
const r = target.getBoundingClientRect();
|
|
564
|
-
if (r.width === 0 && r.height === 0) {
|
|
565
|
-
node.hidden = true;
|
|
566
|
-
continue;
|
|
567
|
-
}
|
|
568
|
-
this.placeRect(node, { x: r.left, y: r.top, width: r.width, height: r.height });
|
|
569
|
-
node.hidden = false;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
placeRect(node, rect) {
|
|
573
|
-
moveTo(node, rect.x, rect.y);
|
|
574
|
-
node.style.width = `${rect.width}px`;
|
|
575
|
-
node.style.height = `${rect.height}px`;
|
|
576
|
-
}
|
|
577
|
-
div(className) {
|
|
578
|
-
const el = this.document.createElement("div");
|
|
579
|
-
el.className = className;
|
|
580
|
-
return el;
|
|
581
|
-
}
|
|
582
|
-
};
|
|
583
|
-
function moveTo(node, x, y) {
|
|
584
|
-
const tx = Math.round(x);
|
|
585
|
-
const ty = Math.round(y);
|
|
586
|
-
node.style.transform = `translate3d(${tx}px, ${ty}px, 0)`;
|
|
587
|
-
}
|
|
588
|
-
var TAG_LABELS = {
|
|
589
|
-
p: "paragraph",
|
|
590
|
-
h1: "heading",
|
|
591
|
-
h2: "heading",
|
|
592
|
-
h3: "heading",
|
|
593
|
-
h4: "heading",
|
|
594
|
-
h5: "heading",
|
|
595
|
-
h6: "heading",
|
|
596
|
-
a: "link",
|
|
597
|
-
button: "button",
|
|
598
|
-
input: "input",
|
|
599
|
-
textarea: "textarea",
|
|
600
|
-
select: "select",
|
|
601
|
-
img: "image",
|
|
602
|
-
video: "video",
|
|
603
|
-
audio: "audio",
|
|
604
|
-
form: "form",
|
|
605
|
-
nav: "nav",
|
|
606
|
-
header: "header",
|
|
607
|
-
footer: "footer",
|
|
608
|
-
main: "main",
|
|
609
|
-
section: "section",
|
|
610
|
-
article: "article",
|
|
611
|
-
aside: "aside",
|
|
612
|
-
ul: "list",
|
|
613
|
-
ol: "list",
|
|
614
|
-
li: "list item",
|
|
615
|
-
table: "table",
|
|
616
|
-
thead: "table head",
|
|
617
|
-
tbody: "table body",
|
|
618
|
-
tr: "table row",
|
|
619
|
-
td: "cell",
|
|
620
|
-
th: "header cell",
|
|
621
|
-
span: "span",
|
|
622
|
-
div: "div",
|
|
623
|
-
label: "label",
|
|
624
|
-
code: "code",
|
|
625
|
-
pre: "code block",
|
|
626
|
-
blockquote: "quote",
|
|
627
|
-
strong: "bold",
|
|
628
|
-
em: "italic",
|
|
629
|
-
kbd: "key",
|
|
630
|
-
svg: "svg",
|
|
631
|
-
canvas: "canvas"
|
|
632
|
-
};
|
|
633
|
-
function describeElement(el) {
|
|
634
|
-
const tag = el.tagName.toLowerCase();
|
|
635
|
-
const type = TAG_LABELS[tag] ?? tag;
|
|
636
|
-
const text = (el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
637
|
-
if (text.length > 0) {
|
|
638
|
-
const preview = text.length > 48 ? text.slice(0, 48) + "\u2026" : text;
|
|
639
|
-
return `${type}: "${preview}"`;
|
|
640
|
-
}
|
|
641
|
-
if (el.id) return `${type}: #${el.id}`;
|
|
642
|
-
const cls = el.classList[0];
|
|
643
|
-
if (cls) return `${type}: .${cls}`;
|
|
644
|
-
return type;
|
|
645
|
-
}
|
|
646
|
-
function unionOf(elements) {
|
|
647
|
-
let minX = Infinity;
|
|
648
|
-
let minY = Infinity;
|
|
649
|
-
let maxX = -Infinity;
|
|
650
|
-
let maxY = -Infinity;
|
|
651
|
-
let any = false;
|
|
652
|
-
for (const el of elements) {
|
|
653
|
-
const r = el.getBoundingClientRect();
|
|
654
|
-
if (r.width === 0 && r.height === 0) continue;
|
|
655
|
-
any = true;
|
|
656
|
-
if (r.left < minX) minX = r.left;
|
|
657
|
-
if (r.top < minY) minY = r.top;
|
|
658
|
-
if (r.right > maxX) maxX = r.right;
|
|
659
|
-
if (r.bottom > maxY) maxY = r.bottom;
|
|
660
|
-
}
|
|
661
|
-
if (!any) return null;
|
|
662
|
-
const PAD = 4;
|
|
663
|
-
return {
|
|
664
|
-
x: minX - PAD,
|
|
665
|
-
y: minY - PAD,
|
|
666
|
-
width: maxX - minX + PAD * 2,
|
|
667
|
-
height: maxY - minY + PAD * 2
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
var emptyRenderState = {
|
|
671
|
-
hover: null,
|
|
672
|
-
pinned: [],
|
|
673
|
-
selection: null,
|
|
674
|
-
marquee: null
|
|
675
|
-
};
|
|
676
|
-
var Overlay = class {
|
|
677
|
-
renderer;
|
|
678
|
-
engine;
|
|
679
|
-
doc;
|
|
680
|
-
win;
|
|
681
|
-
searchRoot;
|
|
682
|
-
excludeHost;
|
|
683
|
-
raf;
|
|
684
|
-
caf;
|
|
685
|
-
state = emptyRenderState;
|
|
686
|
-
unsubscribe = null;
|
|
687
|
-
rafHandle = null;
|
|
688
|
-
/** Guard for scheduleRender re-entry. Tracked separately from `rafHandle`
|
|
689
|
-
* because a synchronous `raf` (used in tests) returns a handle the cb has
|
|
690
|
-
* already invalidated — boolean is the safe sentinel. */
|
|
691
|
-
renderPending = false;
|
|
692
|
-
destroyed = false;
|
|
693
|
-
bound = [];
|
|
694
|
-
constructor(opts) {
|
|
695
|
-
this.engine = opts.engine;
|
|
696
|
-
this.doc = opts.document ?? opts.root.ownerDocument ?? null;
|
|
697
|
-
if (!this.doc) throw new Error("Overlay: no Document available");
|
|
698
|
-
this.win = this.doc.defaultView ?? globalThis;
|
|
699
|
-
this.searchRoot = opts.searchRoot ?? this.doc.body;
|
|
700
|
-
this.excludeHost = opts.excludeHost ?? null;
|
|
701
|
-
this.raf = opts.raf ?? ((cb) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(cb) : setTimeout(() => cb(performance.now()), 16));
|
|
702
|
-
this.caf = opts.caf ?? ((h) => {
|
|
703
|
-
if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(h);
|
|
704
|
-
else clearTimeout(h);
|
|
705
|
-
});
|
|
706
|
-
this.renderer = new OverlayRenderer(opts.root, this.doc);
|
|
707
|
-
this.bindEvent(this.doc, "scroll", this.onReposition, { passive: true, capture: true });
|
|
708
|
-
this.bindEvent(this.win, "resize", this.onReposition, { passive: true });
|
|
709
|
-
this.unsubscribe = this.engine.subscribe((s) => this.onEngineState(s));
|
|
710
|
-
this.onEngineState(this.engine.getSnapshot());
|
|
711
|
-
}
|
|
712
|
-
destroy() {
|
|
713
|
-
this.destroyed = true;
|
|
714
|
-
if (this.unsubscribe) this.unsubscribe();
|
|
715
|
-
this.unsubscribe = null;
|
|
716
|
-
if (this.rafHandle !== null) this.caf(this.rafHandle);
|
|
717
|
-
this.rafHandle = null;
|
|
718
|
-
for (const [t, ev, fn, opts] of this.bound) t.removeEventListener(ev, fn, opts);
|
|
719
|
-
this.bound = [];
|
|
720
|
-
this.renderer.destroy();
|
|
721
|
-
}
|
|
722
|
-
/* ─── Internals ───────────────────────────────────────────────── */
|
|
723
|
-
onReposition = () => {
|
|
724
|
-
if (this.destroyed) return;
|
|
725
|
-
if (hasAnythingToTrack(this.state)) this.scheduleRender();
|
|
726
|
-
};
|
|
727
|
-
onEngineState(s) {
|
|
728
|
-
this.state = this.derive(s);
|
|
729
|
-
this.scheduleRender();
|
|
730
|
-
}
|
|
731
|
-
/** Engine state → render state. */
|
|
732
|
-
derive(s) {
|
|
733
|
-
switch (s.kind) {
|
|
734
|
-
case "idle":
|
|
735
|
-
return emptyRenderState;
|
|
736
|
-
case "inspect":
|
|
737
|
-
return { hover: s.hoverTarget, pinned: s.pinned, selection: null, marquee: null };
|
|
738
|
-
case "pressed":
|
|
739
|
-
return { hover: s.target, pinned: s.pinned, selection: null, marquee: null };
|
|
740
|
-
case "dragging": {
|
|
741
|
-
const rect = rectFromPoints(s.start, s.current);
|
|
742
|
-
return { hover: null, pinned: s.pinned, selection: null, marquee: rect };
|
|
743
|
-
}
|
|
744
|
-
case "annotating": {
|
|
745
|
-
const sel = s.selection;
|
|
746
|
-
const elements = sel.kind === "area" ? pickElementsInRect(this.searchRoot, sel.rect, this.excludeHost) : sel.kind === "multi" ? sel.elements : [sel.element];
|
|
747
|
-
return { hover: null, pinned: s.pinned, selection: elements, marquee: null };
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
scheduleRender() {
|
|
752
|
-
if (this.destroyed || this.renderPending) return;
|
|
753
|
-
this.renderPending = true;
|
|
754
|
-
this.rafHandle = this.raf(() => {
|
|
755
|
-
this.renderPending = false;
|
|
756
|
-
this.rafHandle = null;
|
|
757
|
-
if (this.destroyed) return;
|
|
758
|
-
this.renderer.render(this.state);
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
bindEvent(target, ev, fn, opts) {
|
|
762
|
-
target.addEventListener(ev, fn, opts);
|
|
763
|
-
this.bound.push([target, ev, fn, opts]);
|
|
764
|
-
}
|
|
765
|
-
};
|
|
766
|
-
function hasAnythingToTrack(s) {
|
|
767
|
-
return s.hover !== null || s.pinned.length > 0 || s.selection !== null && s.selection.length > 0 || s.marquee !== null;
|
|
768
|
-
}
|
|
769
|
-
var FIBER_PROP_PREFIX = "__reactFiber$";
|
|
770
|
-
function getFiber(node) {
|
|
771
|
-
if (!node) return null;
|
|
772
|
-
for (const key of Object.keys(node)) {
|
|
773
|
-
if (key.startsWith(FIBER_PROP_PREFIX)) return node[key];
|
|
774
|
-
}
|
|
775
|
-
return null;
|
|
776
|
-
}
|
|
777
|
-
function getComponentChain(node) {
|
|
778
|
-
const fiber = getFiber(node);
|
|
779
|
-
if (!fiber) return [];
|
|
780
|
-
const names = [];
|
|
781
|
-
let cur = fiber;
|
|
782
|
-
while (cur) {
|
|
783
|
-
const name = getComponentName(cur);
|
|
784
|
-
if (name) names.unshift(name);
|
|
785
|
-
cur = cur.return;
|
|
786
|
-
}
|
|
787
|
-
return dedupeConsecutive(names);
|
|
788
|
-
}
|
|
789
|
-
function getComponentName(fiber) {
|
|
790
|
-
const t = fiber.type;
|
|
791
|
-
if (!t) return null;
|
|
792
|
-
if (typeof t === "string") return null;
|
|
793
|
-
if (typeof t === "function") {
|
|
794
|
-
const fn = t;
|
|
795
|
-
return fn.displayName || fn.name || null;
|
|
796
|
-
}
|
|
797
|
-
if (typeof t === "object") {
|
|
798
|
-
const o = t;
|
|
799
|
-
if (o.displayName) return o.displayName;
|
|
800
|
-
if (o.render) return o.render.displayName || o.render.name || null;
|
|
801
|
-
if (o.type) return o.type.displayName || o.type.name || null;
|
|
802
|
-
}
|
|
803
|
-
return null;
|
|
804
|
-
}
|
|
805
|
-
function dedupeConsecutive(names) {
|
|
806
|
-
const out = [];
|
|
807
|
-
for (const n of names) {
|
|
808
|
-
if (out[out.length - 1] !== n) out.push(n);
|
|
809
|
-
}
|
|
810
|
-
return out;
|
|
811
|
-
}
|
|
812
|
-
function getSourceInfo(node) {
|
|
813
|
-
const fiber = getFiber(node);
|
|
814
|
-
if (!fiber) return null;
|
|
815
|
-
let cur = fiber._debugOwner;
|
|
816
|
-
while (cur) {
|
|
817
|
-
if (cur._debugSource) return cur._debugSource;
|
|
818
|
-
cur = cur._debugOwner;
|
|
819
|
-
}
|
|
820
|
-
cur = fiber;
|
|
821
|
-
while (cur) {
|
|
822
|
-
if (cur._debugSource) return cur._debugSource;
|
|
823
|
-
cur = cur.return;
|
|
824
|
-
}
|
|
825
|
-
return null;
|
|
826
|
-
}
|
|
827
|
-
function collectAccessibility(el) {
|
|
828
|
-
const parts = [];
|
|
829
|
-
const role = el.getAttribute("role");
|
|
830
|
-
if (role) parts.push(`role=${role}`);
|
|
831
|
-
for (const attr of Array.from(el.attributes)) {
|
|
832
|
-
if (attr.name.startsWith("aria-") && attr.value) {
|
|
833
|
-
parts.push(`${attr.name}=${attr.value}`);
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
const tabindex = el.getAttribute("tabindex");
|
|
837
|
-
if (tabindex !== null && tabindex !== "") parts.push(`tabindex=${tabindex}`);
|
|
838
|
-
const title = el.getAttribute("title");
|
|
839
|
-
if (title) parts.push(`title=${title}`);
|
|
840
|
-
return parts.join("; ");
|
|
841
|
-
}
|
|
842
|
-
var GROUPS = {
|
|
843
|
-
layout: [
|
|
844
|
-
"display",
|
|
845
|
-
"position",
|
|
846
|
-
"top",
|
|
847
|
-
"right",
|
|
848
|
-
"bottom",
|
|
849
|
-
"left",
|
|
850
|
-
"width",
|
|
851
|
-
"height",
|
|
852
|
-
"margin",
|
|
853
|
-
"padding",
|
|
854
|
-
"box-sizing"
|
|
855
|
-
],
|
|
856
|
-
visual: [
|
|
857
|
-
"color",
|
|
858
|
-
"background-color",
|
|
859
|
-
"border",
|
|
860
|
-
"border-radius",
|
|
861
|
-
"outline"
|
|
862
|
-
],
|
|
863
|
-
text: [
|
|
864
|
-
"font-family",
|
|
865
|
-
"font-size",
|
|
866
|
-
"font-weight",
|
|
867
|
-
"line-height",
|
|
868
|
-
"letter-spacing",
|
|
869
|
-
"text-align",
|
|
870
|
-
"white-space"
|
|
871
|
-
],
|
|
872
|
-
flexgrid: [
|
|
873
|
-
"flex-direction",
|
|
874
|
-
"justify-content",
|
|
875
|
-
"align-items",
|
|
876
|
-
"gap",
|
|
877
|
-
"grid-template-columns",
|
|
878
|
-
"grid-template-rows"
|
|
879
|
-
],
|
|
880
|
-
effects: ["transform", "box-shadow", "filter"],
|
|
881
|
-
misc: ["cursor", "z-index", "overflow", "opacity", "transition", "animation"]
|
|
882
|
-
};
|
|
883
|
-
var TIER_GROUPS = {
|
|
884
|
-
compact: [],
|
|
885
|
-
standard: ["layout", "visual", "text"],
|
|
886
|
-
detailed: ["layout", "visual", "text", "flexgrid", "effects"],
|
|
887
|
-
forensic: ["layout", "visual", "text", "flexgrid", "effects", "misc"]
|
|
888
|
-
};
|
|
889
|
-
function collectComputedStyles(el, detail) {
|
|
890
|
-
if (detail === "compact") return {};
|
|
891
|
-
const doc = el.ownerDocument;
|
|
892
|
-
const win = doc?.defaultView ?? globalThis;
|
|
893
|
-
if (typeof win.getComputedStyle !== "function") return {};
|
|
894
|
-
const cs = win.getComputedStyle(el);
|
|
895
|
-
const props = /* @__PURE__ */ new Set();
|
|
896
|
-
for (const g of TIER_GROUPS[detail]) {
|
|
897
|
-
for (const p of GROUPS[g]) props.add(p);
|
|
898
|
-
}
|
|
899
|
-
const out = {};
|
|
900
|
-
for (const p of props) {
|
|
901
|
-
const v = cs.getPropertyValue(p);
|
|
902
|
-
if (!v) continue;
|
|
903
|
-
const trimmed = v.trim();
|
|
904
|
-
if (isUninterestingDefault(p, trimmed)) continue;
|
|
905
|
-
out[p] = trimmed;
|
|
906
|
-
}
|
|
907
|
-
return out;
|
|
908
|
-
}
|
|
909
|
-
function isUninterestingDefault(prop, value) {
|
|
910
|
-
if (!value || value === "none" || value === "auto" || value === "normal") return true;
|
|
911
|
-
if (value === "0px" || value === "0%") return true;
|
|
912
|
-
if (value === "rgba(0, 0, 0, 0)") return true;
|
|
913
|
-
if (prop === "color" || prop === "background-color") {
|
|
914
|
-
return value === "rgba(0, 0, 0, 0)";
|
|
915
|
-
}
|
|
916
|
-
return false;
|
|
917
|
-
}
|
|
918
|
-
var SHORT_MAX_DEPTH = 5;
|
|
919
|
-
var FULL_MAX_DEPTH = 8;
|
|
920
|
-
function buildSelector(target, doc = target.ownerDocument ?? document) {
|
|
921
|
-
if (target.id && isStableId(target.id)) {
|
|
922
|
-
const escaped = cssEscape(target.id);
|
|
923
|
-
const id = `#${escaped}`;
|
|
924
|
-
return { short: id, full: id };
|
|
925
|
-
}
|
|
926
|
-
const segments = [];
|
|
927
|
-
let cur = target;
|
|
928
|
-
let short = null;
|
|
929
|
-
for (let depth = 0; cur && depth < FULL_MAX_DEPTH; depth++) {
|
|
930
|
-
segments.unshift(segmentFor(cur));
|
|
931
|
-
const candidate = segments.join(" > ");
|
|
932
|
-
if (short === null && depth < SHORT_MAX_DEPTH && isUnique(doc, candidate)) {
|
|
933
|
-
short = candidate;
|
|
934
|
-
}
|
|
935
|
-
cur = cur.parentElement;
|
|
936
|
-
if (!cur || cur === doc.documentElement || cur.tagName.toLowerCase() === "html") break;
|
|
937
|
-
}
|
|
938
|
-
const full = segments.join(" > ");
|
|
939
|
-
return { short: short ?? full, full };
|
|
940
|
-
}
|
|
941
|
-
function segmentFor(el) {
|
|
942
|
-
const tag = el.tagName.toLowerCase();
|
|
943
|
-
if (el.id && isStableId(el.id)) return `${tag}#${cssEscape(el.id)}`;
|
|
944
|
-
const classes = Array.from(el.classList).filter(isUseableClass).slice(0, 3);
|
|
945
|
-
let segment = classes.length ? `${tag}.${classes.map(cssEscape).join(".")}` : tag;
|
|
946
|
-
const parent = el.parentElement;
|
|
947
|
-
if (parent) {
|
|
948
|
-
const sameTag = Array.from(parent.children).filter(
|
|
949
|
-
(c) => c.tagName === el.tagName
|
|
950
|
-
);
|
|
951
|
-
if (sameTag.length > 1) {
|
|
952
|
-
const idx = sameTag.indexOf(el) + 1;
|
|
953
|
-
if (idx > 0) segment += `:nth-of-type(${idx})`;
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
return segment;
|
|
957
|
-
}
|
|
958
|
-
function isUseableClass(c) {
|
|
959
|
-
if (!c) return false;
|
|
960
|
-
if (c.length > 30) return false;
|
|
961
|
-
if (/^css-[a-z0-9]+$/i.test(c)) return false;
|
|
962
|
-
if (/^jsx-\d+$/i.test(c)) return false;
|
|
963
|
-
if (/^_.+_[a-z0-9]{5,}$/i.test(c)) return false;
|
|
964
|
-
if (/^\d/.test(c)) return false;
|
|
965
|
-
return true;
|
|
966
|
-
}
|
|
967
|
-
function isStableId(id) {
|
|
968
|
-
if (/^:[a-z]\d+:/i.test(id)) return false;
|
|
969
|
-
if (/^radix-/i.test(id)) return false;
|
|
970
|
-
if (/^headlessui-/i.test(id)) return false;
|
|
971
|
-
return true;
|
|
972
|
-
}
|
|
973
|
-
function isUnique(doc, selector) {
|
|
974
|
-
try {
|
|
975
|
-
return doc.querySelectorAll(selector).length === 1;
|
|
976
|
-
} catch {
|
|
977
|
-
return false;
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
function cssEscape(s) {
|
|
981
|
-
const g = globalThis;
|
|
982
|
-
if (g.CSS?.escape) return g.CSS.escape(s);
|
|
983
|
-
return s.replace(/[^a-zA-Z0-9_-]/g, (c) => "\\" + c);
|
|
984
|
-
}
|
|
985
|
-
var NEARBY_MAX = 200;
|
|
986
|
-
function collectNearbyText(el) {
|
|
987
|
-
const own = readText(el);
|
|
988
|
-
if (own) return truncate(own, NEARBY_MAX);
|
|
989
|
-
const parent = el.parentElement;
|
|
990
|
-
if (parent) {
|
|
991
|
-
const t = readText(parent);
|
|
992
|
-
if (t) return truncate(t, NEARBY_MAX);
|
|
993
|
-
}
|
|
994
|
-
return "";
|
|
995
|
-
}
|
|
996
|
-
function collectSelectedText(doc = document) {
|
|
997
|
-
const win = doc.defaultView ?? globalThis;
|
|
998
|
-
if (typeof win.getSelection !== "function") return "";
|
|
999
|
-
const sel = win.getSelection();
|
|
1000
|
-
if (!sel) return "";
|
|
1001
|
-
return sel.toString().trim();
|
|
1002
|
-
}
|
|
1003
|
-
function readText(el) {
|
|
1004
|
-
const t = el.innerText ?? el.textContent ?? "";
|
|
1005
|
-
return t.replace(/\s+/g, " ").trim();
|
|
1006
|
-
}
|
|
1007
|
-
function truncate(s, n) {
|
|
1008
|
-
return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
1009
|
-
}
|
|
1010
|
-
var POSITIONED_FIXED = /* @__PURE__ */ new Set(["fixed", "sticky"]);
|
|
1011
|
-
function collectMetadata(el, options = {}) {
|
|
1012
|
-
perfMark2("clickly:metadata:collect");
|
|
1013
|
-
const detail = options.detail ?? "standard";
|
|
1014
|
-
const includeReact = options.includeReact !== false && detail !== "compact";
|
|
1015
|
-
const doc = options.document ?? el.ownerDocument ?? document;
|
|
1016
|
-
const { short, full } = buildSelector(el, doc);
|
|
1017
|
-
const rect = el.getBoundingClientRect();
|
|
1018
|
-
const boundingBox = { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
|
|
1019
|
-
const reactComponents = includeReact ? getComponentChain(el).join(" > ") : "";
|
|
1020
|
-
const source = includeReact ? getSourceInfo(el) : null;
|
|
1021
|
-
return {
|
|
1022
|
-
element: el.tagName.toLowerCase(),
|
|
1023
|
-
elementPath: short,
|
|
1024
|
-
fullPath: full,
|
|
1025
|
-
cssClasses: typeof el.className === "string" ? el.className.trim() : "",
|
|
1026
|
-
computedStyles: collectComputedStyles(el, detail),
|
|
1027
|
-
accessibility: collectAccessibility(el),
|
|
1028
|
-
nearbyText: collectNearbyText(el),
|
|
1029
|
-
selectedText: options.selectedText ?? collectSelectedText(doc),
|
|
1030
|
-
boundingBox,
|
|
1031
|
-
isFixed: hasFixedAncestor(el),
|
|
1032
|
-
reactComponents,
|
|
1033
|
-
sourceFile: source?.fileName ?? "",
|
|
1034
|
-
sourceLine: source?.lineNumber ?? 0,
|
|
1035
|
-
sourceColumn: source?.columnNumber ?? 0
|
|
1036
|
-
};
|
|
1037
|
-
}
|
|
1038
|
-
function perfMark2(name) {
|
|
1039
|
-
if (typeof performance !== "undefined" && typeof performance.mark === "function") {
|
|
1040
|
-
try {
|
|
1041
|
-
performance.mark(name);
|
|
1042
|
-
} catch {
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
function hasFixedAncestor(el) {
|
|
1047
|
-
const doc = el.ownerDocument;
|
|
1048
|
-
const win = doc?.defaultView ?? globalThis;
|
|
1049
|
-
if (typeof win.getComputedStyle !== "function") return false;
|
|
1050
|
-
let cur = el;
|
|
1051
|
-
for (let i = 0; cur && i < 8; i++) {
|
|
1052
|
-
const pos = win.getComputedStyle(cur).getPropertyValue("position");
|
|
1053
|
-
if (POSITIONED_FIXED.has(pos)) return true;
|
|
1054
|
-
cur = cur.parentElement;
|
|
1055
|
-
}
|
|
1056
|
-
return false;
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
// packages/react/src/internal/ClicklyRoot.tsx
|
|
1060
|
-
var import_react10 = require("react");
|
|
37
|
+
// ../react/src/internal/ClicklyRoot.tsx
|
|
38
|
+
var import_react8 = require("react");
|
|
1061
39
|
|
|
1062
|
-
//
|
|
1063
|
-
var
|
|
40
|
+
// ../react/src/internal/Toolbar.tsx
|
|
41
|
+
var import_react5 = require("react");
|
|
1064
42
|
|
|
1065
|
-
//
|
|
43
|
+
// ../react/src/hooks/useDraggable.ts
|
|
1066
44
|
var import_react = require("react");
|
|
1067
45
|
function useDraggable(defaultPos, size) {
|
|
1068
46
|
const [position, setPosition] = (0, import_react.useState)(defaultPos);
|
|
@@ -1119,7 +97,7 @@ function clamp(n, lo, hi) {
|
|
|
1119
97
|
return Math.max(lo, Math.min(hi, n));
|
|
1120
98
|
}
|
|
1121
99
|
|
|
1122
|
-
//
|
|
100
|
+
// ../react/src/state/useEngineState.ts
|
|
1123
101
|
var import_react2 = require("react");
|
|
1124
102
|
function useEngineState(engine) {
|
|
1125
103
|
return (0, import_react2.useSyncExternalStore)(
|
|
@@ -1131,119 +109,10 @@ function useEngineState(engine) {
|
|
|
1131
109
|
}
|
|
1132
110
|
var IDLE = { kind: "idle" };
|
|
1133
111
|
|
|
1134
|
-
//
|
|
1135
|
-
var
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
const setState = (partial, replace) => {
|
|
1139
|
-
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
1140
|
-
if (!Object.is(nextState, state)) {
|
|
1141
|
-
const previousState = state;
|
|
1142
|
-
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
1143
|
-
listeners.forEach((listener) => listener(state, previousState));
|
|
1144
|
-
}
|
|
1145
|
-
};
|
|
1146
|
-
const getState = () => state;
|
|
1147
|
-
const getInitialState = () => initialState2;
|
|
1148
|
-
const subscribe = (listener) => {
|
|
1149
|
-
listeners.add(listener);
|
|
1150
|
-
return () => listeners.delete(listener);
|
|
1151
|
-
};
|
|
1152
|
-
const api = { setState, getState, getInitialState, subscribe };
|
|
1153
|
-
const initialState2 = state = createState(setState, getState, api);
|
|
1154
|
-
return api;
|
|
1155
|
-
};
|
|
1156
|
-
var createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);
|
|
1157
|
-
|
|
1158
|
-
// node_modules/.pnpm/zustand@5.0.14_@types+react@18.3.31_react@18.3.1/node_modules/zustand/esm/react.mjs
|
|
1159
|
-
var import_react3 = __toESM(require("react"), 1);
|
|
1160
|
-
var identity = (arg) => arg;
|
|
1161
|
-
function useStore(api, selector = identity) {
|
|
1162
|
-
const slice = import_react3.default.useSyncExternalStore(
|
|
1163
|
-
api.subscribe,
|
|
1164
|
-
import_react3.default.useCallback(() => selector(api.getState()), [api, selector]),
|
|
1165
|
-
import_react3.default.useCallback(() => selector(api.getInitialState()), [api, selector])
|
|
1166
|
-
);
|
|
1167
|
-
import_react3.default.useDebugValue(slice);
|
|
1168
|
-
return slice;
|
|
1169
|
-
}
|
|
1170
|
-
var createImpl = (createState) => {
|
|
1171
|
-
const api = createStore(createState);
|
|
1172
|
-
const useBoundStore = (selector) => useStore(api, selector);
|
|
1173
|
-
Object.assign(useBoundStore, api);
|
|
1174
|
-
return useBoundStore;
|
|
1175
|
-
};
|
|
1176
|
-
var create = ((createState) => createState ? createImpl(createState) : createImpl);
|
|
1177
|
-
|
|
1178
|
-
// node_modules/.pnpm/zustand@5.0.14_@types+react@18.3.31_react@18.3.1/node_modules/zustand/esm/react/shallow.mjs
|
|
1179
|
-
var import_react4 = __toESM(require("react"), 1);
|
|
1180
|
-
|
|
1181
|
-
// node_modules/.pnpm/zustand@5.0.14_@types+react@18.3.31_react@18.3.1/node_modules/zustand/esm/vanilla/shallow.mjs
|
|
1182
|
-
var isIterable = (obj) => Symbol.iterator in obj;
|
|
1183
|
-
var hasIterableEntries = (value) => (
|
|
1184
|
-
// HACK: avoid checking entries type
|
|
1185
|
-
"entries" in value
|
|
1186
|
-
);
|
|
1187
|
-
var compareEntries = (valueA, valueB) => {
|
|
1188
|
-
const mapA = valueA instanceof Map ? valueA : new Map(valueA.entries());
|
|
1189
|
-
const mapB = valueB instanceof Map ? valueB : new Map(valueB.entries());
|
|
1190
|
-
if (mapA.size !== mapB.size) {
|
|
1191
|
-
return false;
|
|
1192
|
-
}
|
|
1193
|
-
for (const [key, value] of mapA) {
|
|
1194
|
-
if (!mapB.has(key) || !Object.is(value, mapB.get(key))) {
|
|
1195
|
-
return false;
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
return true;
|
|
1199
|
-
};
|
|
1200
|
-
var compareIterables = (valueA, valueB) => {
|
|
1201
|
-
const iteratorA = valueA[Symbol.iterator]();
|
|
1202
|
-
const iteratorB = valueB[Symbol.iterator]();
|
|
1203
|
-
let nextA = iteratorA.next();
|
|
1204
|
-
let nextB = iteratorB.next();
|
|
1205
|
-
while (!nextA.done && !nextB.done) {
|
|
1206
|
-
if (!Object.is(nextA.value, nextB.value)) {
|
|
1207
|
-
return false;
|
|
1208
|
-
}
|
|
1209
|
-
nextA = iteratorA.next();
|
|
1210
|
-
nextB = iteratorB.next();
|
|
1211
|
-
}
|
|
1212
|
-
return !!nextA.done && !!nextB.done;
|
|
1213
|
-
};
|
|
1214
|
-
function shallow(valueA, valueB) {
|
|
1215
|
-
if (Object.is(valueA, valueB)) {
|
|
1216
|
-
return true;
|
|
1217
|
-
}
|
|
1218
|
-
if (typeof valueA !== "object" || valueA === null || typeof valueB !== "object" || valueB === null) {
|
|
1219
|
-
return false;
|
|
1220
|
-
}
|
|
1221
|
-
if (Object.getPrototypeOf(valueA) !== Object.getPrototypeOf(valueB)) {
|
|
1222
|
-
return false;
|
|
1223
|
-
}
|
|
1224
|
-
if (isIterable(valueA) && isIterable(valueB)) {
|
|
1225
|
-
if (hasIterableEntries(valueA) && hasIterableEntries(valueB)) {
|
|
1226
|
-
return compareEntries(valueA, valueB);
|
|
1227
|
-
}
|
|
1228
|
-
return compareIterables(valueA, valueB);
|
|
1229
|
-
}
|
|
1230
|
-
return compareEntries(
|
|
1231
|
-
{ entries: () => Object.entries(valueA) },
|
|
1232
|
-
{ entries: () => Object.entries(valueB) }
|
|
1233
|
-
);
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
// node_modules/.pnpm/zustand@5.0.14_@types+react@18.3.31_react@18.3.1/node_modules/zustand/esm/react/shallow.mjs
|
|
1237
|
-
function useShallow(selector) {
|
|
1238
|
-
const prev = import_react4.default.useRef(void 0);
|
|
1239
|
-
return (state) => {
|
|
1240
|
-
const next = selector(state);
|
|
1241
|
-
return shallow(prev.current, next) ? prev.current : prev.current = next;
|
|
1242
|
-
};
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
// packages/react/src/state/annotations.ts
|
|
1246
|
-
var useAnnotations = create((set, get) => ({
|
|
112
|
+
// ../react/src/state/annotations.ts
|
|
113
|
+
var import_zustand = require("zustand");
|
|
114
|
+
var import_shallow = require("zustand/react/shallow");
|
|
115
|
+
var useAnnotations = (0, import_zustand.create)((set, get) => ({
|
|
1247
116
|
byId: {},
|
|
1248
117
|
order: [],
|
|
1249
118
|
add: (a) => set((s) => ({
|
|
@@ -1269,11 +138,12 @@ var useAnnotations = create((set, get) => ({
|
|
|
1269
138
|
}));
|
|
1270
139
|
function useAnnotationsList() {
|
|
1271
140
|
return useAnnotations(
|
|
1272
|
-
useShallow((s) => s.order.map((id) => s.byId[id]).filter(Boolean))
|
|
141
|
+
(0, import_shallow.useShallow)((s) => s.order.map((id) => s.byId[id]).filter(Boolean))
|
|
1273
142
|
);
|
|
1274
143
|
}
|
|
1275
144
|
|
|
1276
|
-
//
|
|
145
|
+
// ../react/src/state/settings.ts
|
|
146
|
+
var import_zustand2 = require("zustand");
|
|
1277
147
|
var DEFAULTS = {
|
|
1278
148
|
outputDetail: "standard",
|
|
1279
149
|
copyOnAdd: true,
|
|
@@ -1299,7 +169,7 @@ function persist(s) {
|
|
|
1299
169
|
} catch {
|
|
1300
170
|
}
|
|
1301
171
|
}
|
|
1302
|
-
var useSettings = create((set) => ({
|
|
172
|
+
var useSettings = (0, import_zustand2.create)((set) => ({
|
|
1303
173
|
...load(),
|
|
1304
174
|
set: (patch) => set((cur) => {
|
|
1305
175
|
const next = { ...cur, ...patch };
|
|
@@ -1312,7 +182,7 @@ var useSettings = create((set) => ({
|
|
|
1312
182
|
}
|
|
1313
183
|
}));
|
|
1314
184
|
|
|
1315
|
-
//
|
|
185
|
+
// ../react/src/output/markdown.ts
|
|
1316
186
|
function annotationsToMarkdown(annotations, detail = "standard") {
|
|
1317
187
|
if (!annotations.length) return "(no annotations)";
|
|
1318
188
|
return annotations.map((a, i) => formatOne(a, i + 1, detail)).join("\n\n");
|
|
@@ -1335,7 +205,7 @@ function formatOne(a, index, detail) {
|
|
|
1335
205
|
}
|
|
1336
206
|
if (detail === "detailed" || detail === "forensic") {
|
|
1337
207
|
if (a.reactComponents) lines.push(`**React:** ${a.reactComponents}`);
|
|
1338
|
-
if (a.nearbyText) lines.push(`**Nearby text:** ${
|
|
208
|
+
if (a.nearbyText) lines.push(`**Nearby text:** ${truncate(a.nearbyText, 120)}`);
|
|
1339
209
|
}
|
|
1340
210
|
if (detail === "forensic" && a.computedStyles) {
|
|
1341
211
|
lines.push("**Computed styles:**\n```css\n" + a.computedStyles + "\n```");
|
|
@@ -1347,11 +217,11 @@ function formatOne(a, index, detail) {
|
|
|
1347
217
|
if (a.severity) lines.push(`**Severity:** ${a.severity}`);
|
|
1348
218
|
return lines.join("\n");
|
|
1349
219
|
}
|
|
1350
|
-
function
|
|
220
|
+
function truncate(s, n) {
|
|
1351
221
|
return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
1352
222
|
}
|
|
1353
223
|
|
|
1354
|
-
//
|
|
224
|
+
// ../react/src/internal/icons.tsx
|
|
1355
225
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1356
226
|
function Icon({
|
|
1357
227
|
children,
|
|
@@ -1412,13 +282,13 @@ var IconClose = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { childr
|
|
|
1412
282
|
var IconCheck = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 12l5 5L20 7" }) });
|
|
1413
283
|
var IconList = () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01" }) });
|
|
1414
284
|
|
|
1415
|
-
//
|
|
1416
|
-
var
|
|
285
|
+
// ../react/src/internal/SettingsPopover.tsx
|
|
286
|
+
var import_react3 = require("react");
|
|
1417
287
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1418
288
|
function SettingsPopover({ anchor, width, onClose }) {
|
|
1419
|
-
const ref = (0,
|
|
289
|
+
const ref = (0, import_react3.useRef)(null);
|
|
1420
290
|
const s = useSettings();
|
|
1421
|
-
(0,
|
|
291
|
+
(0, import_react3.useEffect)(() => {
|
|
1422
292
|
const onDown = (e) => {
|
|
1423
293
|
if (ref.current && e.composedPath().includes(ref.current)) return;
|
|
1424
294
|
onClose();
|
|
@@ -1513,15 +383,15 @@ function SettingsPopover({ anchor, width, onClose }) {
|
|
|
1513
383
|
] });
|
|
1514
384
|
}
|
|
1515
385
|
|
|
1516
|
-
//
|
|
1517
|
-
var
|
|
386
|
+
// ../react/src/internal/AnnotationList.tsx
|
|
387
|
+
var import_react4 = require("react");
|
|
1518
388
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1519
389
|
function AnnotationList({ anchor, width, onClose }) {
|
|
1520
390
|
const items = useAnnotationsList();
|
|
1521
391
|
const remove = useAnnotations((s) => s.remove);
|
|
1522
392
|
const outputDetail = useSettings((s) => s.outputDetail);
|
|
1523
|
-
const ref = (0,
|
|
1524
|
-
(0,
|
|
393
|
+
const ref = (0, import_react4.useRef)(null);
|
|
394
|
+
(0, import_react4.useEffect)(() => {
|
|
1525
395
|
const onDown = (e) => {
|
|
1526
396
|
if (ref.current && e.composedPath().includes(ref.current)) return;
|
|
1527
397
|
onClose();
|
|
@@ -1555,7 +425,7 @@ function AnnotationCard({
|
|
|
1555
425
|
outputDetail,
|
|
1556
426
|
onRemove
|
|
1557
427
|
}) {
|
|
1558
|
-
const [copied, setCopied] = (0,
|
|
428
|
+
const [copied, setCopied] = (0, import_react4.useState)(false);
|
|
1559
429
|
const copyOne = async () => {
|
|
1560
430
|
const md = formatOne(a, index, outputDetail);
|
|
1561
431
|
try {
|
|
@@ -1601,7 +471,7 @@ function AnnotationCard({
|
|
|
1601
471
|
] });
|
|
1602
472
|
}
|
|
1603
473
|
|
|
1604
|
-
//
|
|
474
|
+
// ../react/src/internal/Toolbar.tsx
|
|
1605
475
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1606
476
|
var TOOLBAR_SIZE = { width: 360, height: 44 };
|
|
1607
477
|
var SHORTCUTS = [
|
|
@@ -1616,7 +486,7 @@ var SHORTCUTS = [
|
|
|
1616
486
|
{ keys: ["X"], label: "Clear all annotations" }
|
|
1617
487
|
];
|
|
1618
488
|
function ShortcutsPanel() {
|
|
1619
|
-
const [visible, setVisible] = (0,
|
|
489
|
+
const [visible, setVisible] = (0, import_react5.useState)(false);
|
|
1620
490
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1621
491
|
"span",
|
|
1622
492
|
{
|
|
@@ -1656,45 +526,14 @@ function Tip({
|
|
|
1656
526
|
] })
|
|
1657
527
|
] });
|
|
1658
528
|
}
|
|
1659
|
-
function Toolbar({ engine, onCollapse }) {
|
|
529
|
+
function Toolbar({ engine, onCollapse, isClosing, onCloseEnd, frozen, onFreezeToggle }) {
|
|
1660
530
|
const state = useEngineState(engine);
|
|
1661
531
|
const annotations = useAnnotationsList();
|
|
1662
532
|
const clearAnnotations = useAnnotations((s) => s.clear);
|
|
1663
533
|
const outputDetail = useSettings((s) => s.outputDetail);
|
|
1664
|
-
const [showSettings, setShowSettings] = (0,
|
|
1665
|
-
const [showList, setShowList] = (0,
|
|
1666
|
-
const
|
|
1667
|
-
const anchorRef = (0, import_react7.useRef)(null);
|
|
1668
|
-
(0, import_react7.useEffect)(() => {
|
|
1669
|
-
const STYLE_ID = "clickly-freeze-animations";
|
|
1670
|
-
const gsap = window.gsap;
|
|
1671
|
-
if (frozen) {
|
|
1672
|
-
if (!document.getElementById(STYLE_ID)) {
|
|
1673
|
-
const el = document.createElement("style");
|
|
1674
|
-
el.id = STYLE_ID;
|
|
1675
|
-
el.textContent = `
|
|
1676
|
-
*, *::before, *::after {
|
|
1677
|
-
animation-play-state: paused !important;
|
|
1678
|
-
transition-duration: 0ms !important;
|
|
1679
|
-
transition-delay: 0ms !important;
|
|
1680
|
-
}
|
|
1681
|
-
`;
|
|
1682
|
-
document.head.appendChild(el);
|
|
1683
|
-
}
|
|
1684
|
-
if (gsap?.globalTimeline) {
|
|
1685
|
-
gsap.globalTimeline.pause();
|
|
1686
|
-
}
|
|
1687
|
-
} else {
|
|
1688
|
-
document.getElementById(STYLE_ID)?.remove();
|
|
1689
|
-
if (gsap?.globalTimeline) {
|
|
1690
|
-
gsap.globalTimeline.resume();
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
return () => {
|
|
1694
|
-
document.getElementById(STYLE_ID)?.remove();
|
|
1695
|
-
if (gsap?.globalTimeline) gsap.globalTimeline.resume();
|
|
1696
|
-
};
|
|
1697
|
-
}, [frozen]);
|
|
534
|
+
const [showSettings, setShowSettings] = (0, import_react5.useState)(false);
|
|
535
|
+
const [showList, setShowList] = (0, import_react5.useState)(false);
|
|
536
|
+
const anchorRef = (0, import_react5.useRef)(null);
|
|
1698
537
|
const { position, handleProps } = useDraggable(
|
|
1699
538
|
{
|
|
1700
539
|
x: Math.max(8, window.innerWidth - TOOLBAR_SIZE.width - 16),
|
|
@@ -1720,8 +559,9 @@ function Toolbar({ engine, onCollapse }) {
|
|
|
1720
559
|
"div",
|
|
1721
560
|
{
|
|
1722
561
|
ref: anchorRef,
|
|
1723
|
-
className:
|
|
562
|
+
className: `clickly-toolbar${isClosing ? " is-closing" : ""}`,
|
|
1724
563
|
style: { left: position.x, top: position.y, width: TOOLBAR_SIZE.width },
|
|
564
|
+
onAnimationEnd: isClosing ? onCloseEnd : void 0,
|
|
1725
565
|
"aria-label": "Clickly toolbar",
|
|
1726
566
|
children: [
|
|
1727
567
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tip, { label: "Move", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -1781,7 +621,7 @@ function Toolbar({ engine, onCollapse }) {
|
|
|
1781
621
|
"button",
|
|
1782
622
|
{
|
|
1783
623
|
className: `clickly-btn icon-only${frozen ? " is-freeze" : ""}`,
|
|
1784
|
-
onClick:
|
|
624
|
+
onClick: onFreezeToggle,
|
|
1785
625
|
"aria-label": frozen ? "Unfreeze page animations" : "Freeze page animations",
|
|
1786
626
|
"aria-pressed": frozen,
|
|
1787
627
|
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(IconFreeze, {})
|
|
@@ -1861,7 +701,7 @@ function Toolbar({ engine, onCollapse }) {
|
|
|
1861
701
|
);
|
|
1862
702
|
}
|
|
1863
703
|
|
|
1864
|
-
//
|
|
704
|
+
// ../react/src/internal/CollapsedFAB.tsx
|
|
1865
705
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1866
706
|
function CollapsedFAB({ onExpand }) {
|
|
1867
707
|
const annotations = useAnnotationsList();
|
|
@@ -1880,9 +720,24 @@ function CollapsedFAB({ onExpand }) {
|
|
|
1880
720
|
);
|
|
1881
721
|
}
|
|
1882
722
|
|
|
1883
|
-
//
|
|
1884
|
-
var
|
|
1885
|
-
|
|
723
|
+
// ../react/src/internal/AnnotationPopup.tsx
|
|
724
|
+
var import_react6 = require("react");
|
|
725
|
+
|
|
726
|
+
// ../../node_modules/.pnpm/nanoid@5.1.16/node_modules/nanoid/url-alphabet/index.js
|
|
727
|
+
var urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
728
|
+
|
|
729
|
+
// ../../node_modules/.pnpm/nanoid@5.1.16/node_modules/nanoid/index.browser.js
|
|
730
|
+
var nanoid = (size = 21) => {
|
|
731
|
+
let id = "";
|
|
732
|
+
let bytes = crypto.getRandomValues(new Uint8Array(size |= 0));
|
|
733
|
+
while (size--) {
|
|
734
|
+
id += urlAlphabet[bytes[size] & 63];
|
|
735
|
+
}
|
|
736
|
+
return id;
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
// ../react/src/internal/AnnotationPopup.tsx
|
|
740
|
+
var import_core = require("@useclickly/core");
|
|
1886
741
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1887
742
|
var POPUP_W = 320;
|
|
1888
743
|
var POPUP_H_EST = 240;
|
|
@@ -1891,15 +746,15 @@ function AnnotationPopup({ engine }) {
|
|
|
1891
746
|
const addAnnotation = useAnnotations((s) => s.add);
|
|
1892
747
|
const copyOnAdd = useSettings((s) => s.copyOnAdd);
|
|
1893
748
|
const outputDetail = useSettings((s) => s.outputDetail);
|
|
1894
|
-
const [comment, setComment] = (0,
|
|
1895
|
-
const [showStyles, setShowStyles] = (0,
|
|
1896
|
-
const [editMode, setEditMode] = (0,
|
|
1897
|
-
const [styles, setStyles] = (0,
|
|
1898
|
-
const [editedStyles, setEditedStyles] = (0,
|
|
1899
|
-
const targetElRef = (0,
|
|
1900
|
-
const taRef = (0,
|
|
1901
|
-
const popupRef = (0,
|
|
1902
|
-
(0,
|
|
749
|
+
const [comment, setComment] = (0, import_react6.useState)("");
|
|
750
|
+
const [showStyles, setShowStyles] = (0, import_react6.useState)(false);
|
|
751
|
+
const [editMode, setEditMode] = (0, import_react6.useState)(false);
|
|
752
|
+
const [styles, setStyles] = (0, import_react6.useState)({});
|
|
753
|
+
const [editedStyles, setEditedStyles] = (0, import_react6.useState)({});
|
|
754
|
+
const targetElRef = (0, import_react6.useRef)(null);
|
|
755
|
+
const taRef = (0, import_react6.useRef)(null);
|
|
756
|
+
const popupRef = (0, import_react6.useRef)(null);
|
|
757
|
+
(0, import_react6.useEffect)(() => {
|
|
1903
758
|
if (state.kind === "annotating") {
|
|
1904
759
|
setComment("");
|
|
1905
760
|
setShowStyles(false);
|
|
@@ -1909,14 +764,14 @@ function AnnotationPopup({ engine }) {
|
|
|
1909
764
|
const el = sel?.kind === "single" ? sel.element : sel?.kind === "multi" || sel?.kind === "area" ? sel.elements[0] : null;
|
|
1910
765
|
targetElRef.current = el instanceof HTMLElement ? el : null;
|
|
1911
766
|
if (el) {
|
|
1912
|
-
setStyles(collectComputedStyles(el, "standard"));
|
|
767
|
+
setStyles((0, import_core.collectComputedStyles)(el, "standard"));
|
|
1913
768
|
} else {
|
|
1914
769
|
setStyles({});
|
|
1915
770
|
}
|
|
1916
771
|
requestAnimationFrame(() => taRef.current?.focus());
|
|
1917
772
|
}
|
|
1918
773
|
}, [state.kind, engine]);
|
|
1919
|
-
(0,
|
|
774
|
+
(0, import_react6.useEffect)(() => {
|
|
1920
775
|
if (state.kind !== "annotating") return;
|
|
1921
776
|
const onDown = (e) => {
|
|
1922
777
|
if (popupRef.current && e.composedPath().includes(popupRef.current)) return;
|
|
@@ -1965,9 +820,9 @@ function AnnotationPopup({ engine }) {
|
|
|
1965
820
|
const isMulti = sel.kind !== "single";
|
|
1966
821
|
const showReact = useSettings.getState().showReactComponents;
|
|
1967
822
|
for (const el of elements) {
|
|
1968
|
-
const md = collectMetadata(el, { detail: outputDetail, includeReact: showReact });
|
|
823
|
+
const md = (0, import_core.collectMetadata)(el, { detail: outputDetail, includeReact: showReact });
|
|
1969
824
|
const annotation = {
|
|
1970
|
-
id: "ann_" +
|
|
825
|
+
id: "ann_" + nanoid(8),
|
|
1971
826
|
comment: sharedComment,
|
|
1972
827
|
element: md.element,
|
|
1973
828
|
elementPath: md.elementPath,
|
|
@@ -2000,8 +855,8 @@ function AnnotationPopup({ engine }) {
|
|
|
2000
855
|
}
|
|
2001
856
|
engine.commit();
|
|
2002
857
|
};
|
|
2003
|
-
const [placement, setPlacement] = (0,
|
|
2004
|
-
const calcPlacement = (0,
|
|
858
|
+
const [placement, setPlacement] = (0, import_react6.useState)(null);
|
|
859
|
+
const calcPlacement = (0, import_react6.useCallback)(() => {
|
|
2005
860
|
if (state.kind !== "annotating") {
|
|
2006
861
|
setPlacement(null);
|
|
2007
862
|
return;
|
|
@@ -2034,10 +889,10 @@ function AnnotationPopup({ engine }) {
|
|
|
2034
889
|
if (left + POPUP_W > vw) left = Math.max(8, vw - POPUP_W - 8);
|
|
2035
890
|
setPlacement({ top, left, label });
|
|
2036
891
|
}, [state, engine]);
|
|
2037
|
-
(0,
|
|
892
|
+
(0, import_react6.useEffect)(() => {
|
|
2038
893
|
calcPlacement();
|
|
2039
894
|
}, [calcPlacement]);
|
|
2040
|
-
(0,
|
|
895
|
+
(0, import_react6.useEffect)(() => {
|
|
2041
896
|
if (state.kind !== "annotating") return;
|
|
2042
897
|
const onScroll = () => calcPlacement();
|
|
2043
898
|
window.addEventListener("scroll", onScroll, { capture: true, passive: true });
|
|
@@ -2158,7 +1013,7 @@ function anchorRect(sel) {
|
|
|
2158
1013
|
if (sel.elements.length === 0) return null;
|
|
2159
1014
|
return sel.elements[0].getBoundingClientRect();
|
|
2160
1015
|
}
|
|
2161
|
-
var
|
|
1016
|
+
var TAG_LABELS = {
|
|
2162
1017
|
p: "paragraph",
|
|
2163
1018
|
h1: "heading",
|
|
2164
1019
|
h2: "heading",
|
|
@@ -2200,9 +1055,9 @@ var TAG_LABELS2 = {
|
|
|
2200
1055
|
svg: "svg",
|
|
2201
1056
|
canvas: "canvas"
|
|
2202
1057
|
};
|
|
2203
|
-
function
|
|
1058
|
+
function describeElement(el) {
|
|
2204
1059
|
const tag = el.tagName.toLowerCase();
|
|
2205
|
-
const type =
|
|
1060
|
+
const type = TAG_LABELS[tag] ?? tag;
|
|
2206
1061
|
const text = (el.textContent ?? "").replace(/\s+/g, " ").trim();
|
|
2207
1062
|
if (text.length > 0) {
|
|
2208
1063
|
const preview = text.length > 48 ? text.slice(0, 48) + "\u2026" : text;
|
|
@@ -2214,19 +1069,19 @@ function describeElement2(el) {
|
|
|
2214
1069
|
return type;
|
|
2215
1070
|
}
|
|
2216
1071
|
function describeSelection(sel) {
|
|
2217
|
-
if (sel.kind === "single") return
|
|
1072
|
+
if (sel.kind === "single") return describeElement(sel.element);
|
|
2218
1073
|
if (sel.kind === "area") return `area \xB7 ${sel.elements.length} element(s)`;
|
|
2219
|
-
if (sel.elements.length === 1) return
|
|
1074
|
+
if (sel.elements.length === 1) return describeElement(sel.elements[0]);
|
|
2220
1075
|
return `${sel.elements.length} element(s)`;
|
|
2221
1076
|
}
|
|
2222
1077
|
|
|
2223
|
-
//
|
|
2224
|
-
var
|
|
1078
|
+
// ../react/src/internal/AnnotationPins.tsx
|
|
1079
|
+
var import_react7 = require("react");
|
|
2225
1080
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
2226
1081
|
function AnnotationPins() {
|
|
2227
1082
|
const annotations = useAnnotationsList();
|
|
2228
|
-
const [, setVersion] = (0,
|
|
2229
|
-
(0,
|
|
1083
|
+
const [, setVersion] = (0, import_react7.useState)(0);
|
|
1084
|
+
(0, import_react7.useEffect)(() => {
|
|
2230
1085
|
let raf = null;
|
|
2231
1086
|
const update = () => {
|
|
2232
1087
|
if (raf !== null) return;
|
|
@@ -2245,7 +1100,7 @@ function AnnotationPins() {
|
|
|
2245
1100
|
}, []);
|
|
2246
1101
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: annotations.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Pin, { number: i + 1, annotation: a }, a.id)) });
|
|
2247
1102
|
}
|
|
2248
|
-
var
|
|
1103
|
+
var TAG_LABELS2 = {
|
|
2249
1104
|
p: "paragraph",
|
|
2250
1105
|
h1: "heading",
|
|
2251
1106
|
h2: "heading",
|
|
@@ -2285,7 +1140,7 @@ var TAG_LABELS3 = {
|
|
|
2285
1140
|
function pinLabel(annotation) {
|
|
2286
1141
|
const raw = (annotation.element ?? "").toLowerCase();
|
|
2287
1142
|
const tag = raw.split(/[.#\s]/)[0] ?? "";
|
|
2288
|
-
return (
|
|
1143
|
+
return (TAG_LABELS2[tag] ?? tag) || "element";
|
|
2289
1144
|
}
|
|
2290
1145
|
function parseStyles(raw) {
|
|
2291
1146
|
if (!raw) return [];
|
|
@@ -2298,12 +1153,12 @@ function parseStyles(raw) {
|
|
|
2298
1153
|
function Pin({ number, annotation }) {
|
|
2299
1154
|
const remove = useAnnotations((s) => s.remove);
|
|
2300
1155
|
const update = useAnnotations((s) => s.update);
|
|
2301
|
-
const [hovered, setHovered] = (0,
|
|
2302
|
-
const [editing, setEditing] = (0,
|
|
2303
|
-
const [draft, setDraft] = (0,
|
|
2304
|
-
const [showStyles, setShowStyles] = (0,
|
|
2305
|
-
const taRef = (0,
|
|
2306
|
-
const editRef = (0,
|
|
1156
|
+
const [hovered, setHovered] = (0, import_react7.useState)(false);
|
|
1157
|
+
const [editing, setEditing] = (0, import_react7.useState)(false);
|
|
1158
|
+
const [draft, setDraft] = (0, import_react7.useState)(annotation.comment);
|
|
1159
|
+
const [showStyles, setShowStyles] = (0, import_react7.useState)(false);
|
|
1160
|
+
const taRef = (0, import_react7.useRef)(null);
|
|
1161
|
+
const editRef = (0, import_react7.useRef)(null);
|
|
2307
1162
|
const styleEntries = parseStyles(annotation.computedStyles);
|
|
2308
1163
|
const label = pinLabel(annotation);
|
|
2309
1164
|
const headerLabel = annotation.isMultiSelect ? `${label} (multi-select)` : `${label}: "${annotation.elementPath}"`;
|
|
@@ -2326,7 +1181,7 @@ function Pin({ number, annotation }) {
|
|
|
2326
1181
|
if (e.key === "Escape") closeEdit();
|
|
2327
1182
|
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) save();
|
|
2328
1183
|
};
|
|
2329
|
-
(0,
|
|
1184
|
+
(0, import_react7.useEffect)(() => {
|
|
2330
1185
|
if (!editing) return;
|
|
2331
1186
|
const onDown = (e) => {
|
|
2332
1187
|
if (editRef.current && e.composedPath().includes(editRef.current)) return;
|
|
@@ -2335,7 +1190,7 @@ function Pin({ number, annotation }) {
|
|
|
2335
1190
|
window.addEventListener("pointerdown", onDown, true);
|
|
2336
1191
|
return () => window.removeEventListener("pointerdown", onDown, true);
|
|
2337
1192
|
}, [editing]);
|
|
2338
|
-
(0,
|
|
1193
|
+
(0, import_react7.useEffect)(() => {
|
|
2339
1194
|
if (!editing) return;
|
|
2340
1195
|
let el = null;
|
|
2341
1196
|
if (annotation.elementPath) {
|
|
@@ -2463,28 +1318,63 @@ function resolvePosition(a) {
|
|
|
2463
1318
|
return null;
|
|
2464
1319
|
}
|
|
2465
1320
|
|
|
2466
|
-
//
|
|
1321
|
+
// ../react/src/internal/ClicklyRoot.tsx
|
|
2467
1322
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2468
1323
|
function ClicklyRoot({
|
|
2469
1324
|
engine,
|
|
2470
1325
|
host
|
|
2471
1326
|
}) {
|
|
2472
|
-
const [expanded, setExpanded] = (0,
|
|
1327
|
+
const [expanded, setExpanded] = (0, import_react8.useState)(false);
|
|
1328
|
+
const [toolbarClosing, setToolbarClosing] = (0, import_react8.useState)(false);
|
|
1329
|
+
const [frozen, setFrozen] = (0, import_react8.useState)(false);
|
|
1330
|
+
const collapse = () => {
|
|
1331
|
+
setToolbarClosing(true);
|
|
1332
|
+
};
|
|
1333
|
+
const onToolbarCloseEnd = () => {
|
|
1334
|
+
setToolbarClosing(false);
|
|
1335
|
+
setExpanded(false);
|
|
1336
|
+
};
|
|
2473
1337
|
const clearAnnotations = useAnnotations((s) => s.clear);
|
|
2474
1338
|
const outputDetail = useSettings((s) => s.outputDetail);
|
|
2475
1339
|
const markerColor = useSettings((s) => s.markerColor);
|
|
2476
1340
|
const engineState = useEngineState(engine);
|
|
2477
|
-
(0,
|
|
1341
|
+
(0, import_react8.useEffect)(() => {
|
|
2478
1342
|
host.style.setProperty("--clickly-hover", markerColor);
|
|
2479
1343
|
}, [host, markerColor]);
|
|
2480
|
-
(0,
|
|
1344
|
+
(0, import_react8.useEffect)(() => {
|
|
1345
|
+
const STYLE_ID = "clickly-freeze-animations";
|
|
1346
|
+
const gsap = window.gsap;
|
|
1347
|
+
if (frozen) {
|
|
1348
|
+
if (!document.getElementById(STYLE_ID)) {
|
|
1349
|
+
const el = document.createElement("style");
|
|
1350
|
+
el.id = STYLE_ID;
|
|
1351
|
+
el.textContent = `
|
|
1352
|
+
*, *::before, *::after {
|
|
1353
|
+
animation-play-state: paused !important;
|
|
1354
|
+
transition-duration: 0ms !important;
|
|
1355
|
+
transition-delay: 0ms !important;
|
|
1356
|
+
}
|
|
1357
|
+
`;
|
|
1358
|
+
document.head.appendChild(el);
|
|
1359
|
+
}
|
|
1360
|
+
if (gsap?.globalTimeline) gsap.globalTimeline.pause();
|
|
1361
|
+
} else {
|
|
1362
|
+
document.getElementById(STYLE_ID)?.remove();
|
|
1363
|
+
if (gsap?.globalTimeline) gsap.globalTimeline.resume();
|
|
1364
|
+
}
|
|
1365
|
+
return () => {
|
|
1366
|
+
document.getElementById(STYLE_ID)?.remove();
|
|
1367
|
+
if (gsap?.globalTimeline) gsap.globalTimeline.resume();
|
|
1368
|
+
};
|
|
1369
|
+
}, [frozen]);
|
|
1370
|
+
(0, import_react8.useEffect)(() => {
|
|
2481
1371
|
if (expanded) {
|
|
2482
1372
|
if (engine.getSnapshot().kind === "idle") engine.activate("single");
|
|
2483
1373
|
} else {
|
|
2484
1374
|
if (engine.getSnapshot().kind !== "idle") engine.deactivate();
|
|
2485
1375
|
}
|
|
2486
1376
|
}, [expanded, engine]);
|
|
2487
|
-
(0,
|
|
1377
|
+
(0, import_react8.useEffect)(() => {
|
|
2488
1378
|
const active = engineState.kind !== "idle";
|
|
2489
1379
|
if (active) document.body.setAttribute("data-clickly-active", "");
|
|
2490
1380
|
else document.body.removeAttribute("data-clickly-active");
|
|
@@ -2502,7 +1392,7 @@ function ClicklyRoot({
|
|
|
2502
1392
|
document.body.removeAttribute("data-clickly-annotating");
|
|
2503
1393
|
};
|
|
2504
1394
|
}, [engineState]);
|
|
2505
|
-
(0,
|
|
1395
|
+
(0, import_react8.useEffect)(() => {
|
|
2506
1396
|
const onKey = (e) => {
|
|
2507
1397
|
const tag = e.target?.tagName;
|
|
2508
1398
|
if (tag === "INPUT" || tag === "TEXTAREA") return;
|
|
@@ -2511,12 +1401,13 @@ function ClicklyRoot({
|
|
|
2511
1401
|
if (active && (active.tagName === "TEXTAREA" || active.tagName === "INPUT")) return;
|
|
2512
1402
|
if (e.key.toLowerCase() === "f" && e.shiftKey && (e.metaKey || e.ctrlKey)) {
|
|
2513
1403
|
e.preventDefault();
|
|
2514
|
-
|
|
1404
|
+
if (expanded) collapse();
|
|
1405
|
+
else setExpanded(true);
|
|
2515
1406
|
return;
|
|
2516
1407
|
}
|
|
2517
1408
|
if (e.key === "Escape") {
|
|
2518
1409
|
if (engine.getSnapshot().kind === "annotating") return;
|
|
2519
|
-
if (expanded)
|
|
1410
|
+
if (expanded) collapse();
|
|
2520
1411
|
return;
|
|
2521
1412
|
}
|
|
2522
1413
|
if (!expanded) return;
|
|
@@ -2547,14 +1438,24 @@ function ClicklyRoot({
|
|
|
2547
1438
|
}, [clearAnnotations, outputDetail, expanded, engine]);
|
|
2548
1439
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "clickly-ui", children: [
|
|
2549
1440
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AnnotationPins, {}),
|
|
2550
|
-
expanded ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
2551
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2552
|
-
|
|
1441
|
+
expanded || toolbarClosing ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
1442
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1443
|
+
Toolbar,
|
|
1444
|
+
{
|
|
1445
|
+
engine,
|
|
1446
|
+
onCollapse: collapse,
|
|
1447
|
+
isClosing: toolbarClosing,
|
|
1448
|
+
onCloseEnd: onToolbarCloseEnd,
|
|
1449
|
+
frozen,
|
|
1450
|
+
onFreezeToggle: () => setFrozen((v) => !v)
|
|
1451
|
+
}
|
|
1452
|
+
),
|
|
1453
|
+
!toolbarClosing && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AnnotationPopup, { engine })
|
|
2553
1454
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CollapsedFAB, { onExpand: () => setExpanded(true) })
|
|
2554
1455
|
] });
|
|
2555
1456
|
}
|
|
2556
1457
|
|
|
2557
|
-
//
|
|
1458
|
+
// ../react/src/internal/styles.ts
|
|
2558
1459
|
var REACT_UI_CSS = `
|
|
2559
1460
|
.clickly-ui, .clickly-ui * { box-sizing: border-box; }
|
|
2560
1461
|
|
|
@@ -2580,6 +1481,11 @@ var REACT_UI_CSS = `
|
|
|
2580
1481
|
0 0 0 1px rgba(255,255,255,0.06) inset;
|
|
2581
1482
|
transition: transform 140ms ease, box-shadow 140ms ease;
|
|
2582
1483
|
z-index: 1;
|
|
1484
|
+
animation: clickly-fab-open 280ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
|
|
1485
|
+
}
|
|
1486
|
+
@keyframes clickly-fab-open {
|
|
1487
|
+
from { opacity: 0; transform: scale(0.5); }
|
|
1488
|
+
to { opacity: 1; transform: scale(1); }
|
|
2583
1489
|
}
|
|
2584
1490
|
.clickly-fab:hover {
|
|
2585
1491
|
transform: scale(1.06);
|
|
@@ -2626,12 +1532,21 @@ var REACT_UI_CSS = `
|
|
|
2626
1532
|
pointer-events: auto;
|
|
2627
1533
|
user-select: none;
|
|
2628
1534
|
z-index: 1;
|
|
2629
|
-
|
|
1535
|
+
transform-origin: bottom right;
|
|
1536
|
+
animation: clickly-toolbar-open 240ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
1537
|
+
}
|
|
1538
|
+
.clickly-toolbar.is-closing {
|
|
1539
|
+
animation: clickly-toolbar-close 200ms cubic-bezier(0.4, 0, 1, 1) both;
|
|
1540
|
+
pointer-events: none;
|
|
2630
1541
|
}
|
|
2631
1542
|
|
|
2632
|
-
@keyframes clickly-
|
|
2633
|
-
from { opacity: 0; transform:
|
|
2634
|
-
to { opacity: 1; transform:
|
|
1543
|
+
@keyframes clickly-toolbar-open {
|
|
1544
|
+
from { opacity: 0; transform: scale(0.82) translateY(10px); }
|
|
1545
|
+
to { opacity: 1; transform: scale(1) translateY(0); }
|
|
1546
|
+
}
|
|
1547
|
+
@keyframes clickly-toolbar-close {
|
|
1548
|
+
from { opacity: 1; transform: scale(1) translateY(0); }
|
|
1549
|
+
to { opacity: 0; transform: scale(0.82) translateY(10px); }
|
|
2635
1550
|
}
|
|
2636
1551
|
|
|
2637
1552
|
.clickly-toolbar .grip {
|
|
@@ -3603,7 +2518,7 @@ var REACT_UI_CSS = `
|
|
|
3603
2518
|
}
|
|
3604
2519
|
`;
|
|
3605
2520
|
|
|
3606
|
-
//
|
|
2521
|
+
// ../react/src/internal/globalStyles.ts
|
|
3607
2522
|
var GLOBAL_PAGE_CSS = `
|
|
3608
2523
|
body[data-clickly-active] {
|
|
3609
2524
|
-webkit-user-select: none !important;
|
|
@@ -3624,18 +2539,18 @@ body[data-clickly-annotating] {
|
|
|
3624
2539
|
}
|
|
3625
2540
|
`;
|
|
3626
2541
|
|
|
3627
|
-
//
|
|
2542
|
+
// ../react/src/Clickly.tsx
|
|
3628
2543
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
3629
2544
|
function Clickly({ className } = {}) {
|
|
3630
|
-
const [mount, setMount] = (0,
|
|
3631
|
-
(0,
|
|
2545
|
+
const [mount, setMount] = (0, import_react9.useState)(null);
|
|
2546
|
+
(0, import_react9.useEffect)(() => {
|
|
3632
2547
|
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
3633
2548
|
let shadow = null;
|
|
3634
2549
|
let engine = null;
|
|
3635
2550
|
let overlay = null;
|
|
3636
2551
|
let portal = null;
|
|
3637
2552
|
try {
|
|
3638
|
-
shadow = createShadowHost({ document });
|
|
2553
|
+
shadow = (0, import_core2.createShadowHost)({ document });
|
|
3639
2554
|
portal = document.createElement("div");
|
|
3640
2555
|
portal.setAttribute("data-clickly-react-root", "");
|
|
3641
2556
|
shadow.root.appendChild(portal);
|
|
@@ -3651,8 +2566,8 @@ function Clickly({ className } = {}) {
|
|
|
3651
2566
|
gstyle.textContent = GLOBAL_PAGE_CSS;
|
|
3652
2567
|
document.head.appendChild(gstyle);
|
|
3653
2568
|
}
|
|
3654
|
-
engine = new SelectionEngine({ document, host: shadow.host });
|
|
3655
|
-
overlay = new Overlay({
|
|
2569
|
+
engine = new import_core2.SelectionEngine({ document, host: shadow.host });
|
|
2570
|
+
overlay = new import_core2.Overlay({
|
|
3656
2571
|
engine,
|
|
3657
2572
|
root: shadow.root,
|
|
3658
2573
|
document,
|
|
@@ -3677,7 +2592,7 @@ function Clickly({ className } = {}) {
|
|
|
3677
2592
|
document.body.removeAttribute("data-clickly-mode");
|
|
3678
2593
|
};
|
|
3679
2594
|
}, []);
|
|
3680
|
-
(0,
|
|
2595
|
+
(0, import_react9.useEffect)(() => {
|
|
3681
2596
|
if (!mount || !className) return;
|
|
3682
2597
|
mount.shadow.host.className = className;
|
|
3683
2598
|
return () => {
|