@mikuexe/annotator-react 0.1.0 → 0.2.1
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/CHANGELOG.md +25 -0
- package/README.md +69 -27
- package/dist/index.cjs +553 -110
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -6
- package/dist/index.d.ts +18 -6
- package/dist/index.js +551 -110
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.cjs
CHANGED
|
@@ -22,12 +22,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
22
22
|
var index_exports = {};
|
|
23
23
|
__export(index_exports, {
|
|
24
24
|
SourceAnnotator: () => SourceAnnotator,
|
|
25
|
+
captureAnnotationTarget: () => captureAnnotationTarget,
|
|
25
26
|
captureElementAnnotation: () => captureElementAnnotation,
|
|
26
27
|
copyTextToClipboard: () => copyTextToClipboard,
|
|
27
28
|
createAnnotationCollection: () => createAnnotationCollection,
|
|
28
29
|
formatAnnotationCollection: () => formatAnnotationCollection,
|
|
29
30
|
formatMarkdown: () => formatMarkdown,
|
|
30
31
|
getElementSelector: () => getElementSelector,
|
|
32
|
+
getPageContext: () => getPageContext,
|
|
31
33
|
trimText: () => trimText
|
|
32
34
|
});
|
|
33
35
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -42,13 +44,18 @@ var MAX_TEXT_LENGTH = 240;
|
|
|
42
44
|
var MAX_HTML_LENGTH = 640;
|
|
43
45
|
var MAX_SELECTOR_DEPTH = 6;
|
|
44
46
|
async function captureElementAnnotation(element, note, id = createAnnotationId()) {
|
|
47
|
+
return {
|
|
48
|
+
id,
|
|
49
|
+
note,
|
|
50
|
+
targets: [await captureAnnotationTarget(element)]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function captureAnnotationTarget(element) {
|
|
45
54
|
const elementInfo = await safeResolveElementInfo(element);
|
|
46
55
|
const source = normalizeSource(elementInfo?.source, elementInfo?.componentName);
|
|
47
56
|
const sourceStack = normalizeSourceStack(elementInfo?.stack, source);
|
|
48
57
|
const componentPath = getComponentPath(sourceStack, source);
|
|
49
58
|
return {
|
|
50
|
-
id,
|
|
51
|
-
note,
|
|
52
59
|
source,
|
|
53
60
|
sourceStack,
|
|
54
61
|
componentPath,
|
|
@@ -202,10 +209,19 @@ async function copyTextToClipboard(text) {
|
|
|
202
209
|
|
|
203
210
|
// src/format.ts
|
|
204
211
|
var TASK_FRAMING = "Please update the UI based on these source-linked annotations.";
|
|
205
|
-
function createAnnotationCollection(annotations) {
|
|
212
|
+
function createAnnotationCollection(annotations, page = getPageContext()) {
|
|
206
213
|
return {
|
|
207
214
|
annotations,
|
|
208
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
215
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
216
|
+
page
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function getPageContext(targetDocument) {
|
|
220
|
+
const activeDocument = targetDocument ?? (typeof document === "undefined" ? null : document);
|
|
221
|
+
const location = activeDocument?.location;
|
|
222
|
+
return {
|
|
223
|
+
domain: location?.hostname ?? "",
|
|
224
|
+
path: location?.pathname ?? ""
|
|
209
225
|
};
|
|
210
226
|
}
|
|
211
227
|
function formatAnnotationCollection(collection, output = "markdown") {
|
|
@@ -225,43 +241,28 @@ ${formatJson(collection)}
|
|
|
225
241
|
\`\`\``;
|
|
226
242
|
}
|
|
227
243
|
function formatMarkdown(collection) {
|
|
228
|
-
const lines = [
|
|
244
|
+
const lines = [
|
|
245
|
+
TASK_FRAMING,
|
|
246
|
+
"",
|
|
247
|
+
`Collected at: ${collection.createdAt}`,
|
|
248
|
+
`Domain: ${collection.page.domain}`,
|
|
249
|
+
`Path: ${collection.page.path}`,
|
|
250
|
+
""
|
|
251
|
+
];
|
|
229
252
|
if (collection.annotations.length === 0) {
|
|
230
253
|
lines.push("No annotations were collected.");
|
|
231
254
|
return lines.join("\n");
|
|
232
255
|
}
|
|
233
256
|
collection.annotations.forEach((annotation, index) => {
|
|
234
|
-
const source = formatSource(annotation);
|
|
235
|
-
const sourceStack = formatSourceStack(annotation);
|
|
236
|
-
const nearestComponent = annotation.source?.componentName;
|
|
237
|
-
const ownerPath = annotation.componentPath.join(" \u203A ");
|
|
238
257
|
lines.push(`## Annotation ${index + 1}`);
|
|
239
258
|
lines.push("");
|
|
240
259
|
lines.push(`ID: ${annotation.id}`);
|
|
241
260
|
lines.push(`Note: ${annotation.note || "(no note provided)"}`);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
if (ownerPath && ownerPath !== nearestComponent) {
|
|
249
|
-
lines.push(`React owner path: ${ownerPath}`);
|
|
250
|
-
}
|
|
251
|
-
if (sourceStack.length) {
|
|
252
|
-
lines.push("React source stack:");
|
|
253
|
-
sourceStack.forEach((frame) => lines.push(`- ${frame}`));
|
|
254
|
-
}
|
|
255
|
-
lines.push(`Element tag: ${annotation.element.tagName}`);
|
|
256
|
-
if (annotation.element.html) {
|
|
257
|
-
lines.push(`Element HTML: ${annotation.element.html}`);
|
|
258
|
-
}
|
|
259
|
-
if (annotation.element.text) {
|
|
260
|
-
lines.push(`Element text: ${annotation.element.text}`);
|
|
261
|
-
}
|
|
262
|
-
if (annotation.element.selector) {
|
|
263
|
-
lines.push(`Selector: ${annotation.element.selector}`);
|
|
264
|
-
}
|
|
261
|
+
annotation.targets.forEach((target, targetIndex) => {
|
|
262
|
+
const isSingleTarget = annotation.targets.length === 1;
|
|
263
|
+
const label = isSingleTarget ? "" : `Target ${targetIndex + 1} `;
|
|
264
|
+
appendTargetMarkdown(lines, target, label);
|
|
265
|
+
});
|
|
265
266
|
lines.push("");
|
|
266
267
|
});
|
|
267
268
|
return lines.join("\n").trimEnd();
|
|
@@ -269,8 +270,37 @@ function formatMarkdown(collection) {
|
|
|
269
270
|
function formatJson(collection) {
|
|
270
271
|
return JSON.stringify(collection, null, 2);
|
|
271
272
|
}
|
|
272
|
-
function
|
|
273
|
-
const source =
|
|
273
|
+
function appendTargetMarkdown(lines, target, label) {
|
|
274
|
+
const source = formatSource(target);
|
|
275
|
+
const sourceStack = formatSourceStack(target);
|
|
276
|
+
const nearestComponent = target.source?.componentName;
|
|
277
|
+
const ownerPath = target.componentPath.join(" \u203A ");
|
|
278
|
+
if (source) {
|
|
279
|
+
lines.push(`${label}Source: ${source}`);
|
|
280
|
+
}
|
|
281
|
+
if (nearestComponent) {
|
|
282
|
+
lines.push(`${label}Nearest React component: ${nearestComponent}`);
|
|
283
|
+
}
|
|
284
|
+
if (ownerPath && ownerPath !== nearestComponent) {
|
|
285
|
+
lines.push(`${label}React owner path: ${ownerPath}`);
|
|
286
|
+
}
|
|
287
|
+
if (sourceStack.length) {
|
|
288
|
+
lines.push(`${label}React source stack:`);
|
|
289
|
+
sourceStack.forEach((frame) => lines.push(`- ${frame}`));
|
|
290
|
+
}
|
|
291
|
+
lines.push(`${label}Element tag: ${target.element.tagName}`);
|
|
292
|
+
if (target.element.html) {
|
|
293
|
+
lines.push(`${label}Element HTML: ${target.element.html}`);
|
|
294
|
+
}
|
|
295
|
+
if (target.element.text) {
|
|
296
|
+
lines.push(`${label}Element text: ${target.element.text}`);
|
|
297
|
+
}
|
|
298
|
+
if (target.element.selector) {
|
|
299
|
+
lines.push(`${label}Selector: ${target.element.selector}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function formatSource(target) {
|
|
303
|
+
const source = target.source;
|
|
274
304
|
if (!source?.filePath) {
|
|
275
305
|
return "";
|
|
276
306
|
}
|
|
@@ -278,8 +308,8 @@ function formatSource(annotation) {
|
|
|
278
308
|
const column = source.columnNumber ? `:${source.columnNumber}` : "";
|
|
279
309
|
return `${source.filePath}${line}${column}`;
|
|
280
310
|
}
|
|
281
|
-
function formatSourceStack(
|
|
282
|
-
return
|
|
311
|
+
function formatSourceStack(target) {
|
|
312
|
+
return target.sourceStack.map((frame) => {
|
|
283
313
|
const location = formatSourceFrame(frame);
|
|
284
314
|
const component = frame.componentName ? ` (${frame.componentName})` : "";
|
|
285
315
|
return location ? `${location}${component}` : frame.componentName || "";
|
|
@@ -299,10 +329,22 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
299
329
|
var ROOT_ATTR = "data-mikuexe-annotator-root";
|
|
300
330
|
var DEFAULT_HOTKEY = "alt+a";
|
|
301
331
|
var DEFAULT_OUTPUT = "markdown";
|
|
332
|
+
var BLOCKED_INTERACTION_EVENTS = [
|
|
333
|
+
"pointerdown",
|
|
334
|
+
"pointerup",
|
|
335
|
+
"mousedown",
|
|
336
|
+
"mouseup",
|
|
337
|
+
"dblclick",
|
|
338
|
+
"auxclick",
|
|
339
|
+
"contextmenu",
|
|
340
|
+
"touchstart",
|
|
341
|
+
"touchend"
|
|
342
|
+
];
|
|
302
343
|
function SourceAnnotator({
|
|
303
344
|
enabled = true,
|
|
304
345
|
hotkey = DEFAULT_HOTKEY,
|
|
305
346
|
output = DEFAULT_OUTPUT,
|
|
347
|
+
target,
|
|
306
348
|
onCollect,
|
|
307
349
|
renderToaster = true
|
|
308
350
|
}) {
|
|
@@ -311,17 +353,41 @@ function SourceAnnotator({
|
|
|
311
353
|
const [selected, setSelected] = (0, import_react.useState)(null);
|
|
312
354
|
const [note, setNote] = (0, import_react.useState)("");
|
|
313
355
|
const [annotations, setAnnotations] = (0, import_react.useState)([]);
|
|
356
|
+
const [previewedAnnotation, setPreviewedAnnotation] = (0, import_react.useState)(null);
|
|
314
357
|
const [status, setStatus] = (0, import_react.useState)(null);
|
|
358
|
+
const [linkingAnnotationId, setLinkingAnnotationId] = (0, import_react.useState)(null);
|
|
315
359
|
const selectedRef = (0, import_react.useRef)(null);
|
|
360
|
+
const resolvedTarget = useResolvedTarget(target);
|
|
316
361
|
selectedRef.current = selected;
|
|
317
|
-
|
|
318
|
-
()
|
|
319
|
-
|
|
320
|
-
|
|
362
|
+
(0, import_react.useEffect)(() => {
|
|
363
|
+
setHoverRect(null);
|
|
364
|
+
setSelected(null);
|
|
365
|
+
setAnnotations([]);
|
|
366
|
+
setPreviewedAnnotation(null);
|
|
367
|
+
setNote("");
|
|
368
|
+
setStatus(null);
|
|
369
|
+
setLinkingAnnotationId(null);
|
|
370
|
+
}, [resolvedTarget.document, resolvedTarget.frameElement]);
|
|
321
371
|
const refreshTrackedRects = (0, import_react.useCallback)(() => {
|
|
322
372
|
setHoverRect(null);
|
|
323
|
-
setSelected(
|
|
324
|
-
|
|
373
|
+
setSelected(
|
|
374
|
+
(current) => current ? {
|
|
375
|
+
...current,
|
|
376
|
+
targets: current.targets.map((targetEntry) => ({
|
|
377
|
+
...targetEntry,
|
|
378
|
+
rect: getRect(targetEntry.element, targetEntry.frameElement)
|
|
379
|
+
}))
|
|
380
|
+
} : current
|
|
381
|
+
);
|
|
382
|
+
setAnnotations(
|
|
383
|
+
(existing) => existing.map((annotation) => ({
|
|
384
|
+
...annotation,
|
|
385
|
+
targets: annotation.targets.map((targetEntry) => ({
|
|
386
|
+
...targetEntry,
|
|
387
|
+
rect: getRect(targetEntry.targetElement, targetEntry.frameElement)
|
|
388
|
+
}))
|
|
389
|
+
}))
|
|
390
|
+
);
|
|
325
391
|
}, []);
|
|
326
392
|
(0, import_react.useEffect)(() => {
|
|
327
393
|
if (!enabled) {
|
|
@@ -336,67 +402,209 @@ function SourceAnnotator({
|
|
|
336
402
|
event.preventDefault();
|
|
337
403
|
setIsAnnotating((current) => enabled && !current);
|
|
338
404
|
};
|
|
405
|
+
if (typeof document === "undefined") {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
339
408
|
document.addEventListener("keydown", onKeyDown);
|
|
340
|
-
|
|
341
|
-
|
|
409
|
+
if (resolvedTarget.document && resolvedTarget.document !== document) {
|
|
410
|
+
resolvedTarget.document.addEventListener("keydown", onKeyDown);
|
|
411
|
+
}
|
|
412
|
+
return () => {
|
|
413
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
414
|
+
if (resolvedTarget.document && resolvedTarget.document !== document) {
|
|
415
|
+
resolvedTarget.document.removeEventListener("keydown", onKeyDown);
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
}, [enabled, hotkey, resolvedTarget.document]);
|
|
419
|
+
const handleElementSelection = (0, import_react.useCallback)(
|
|
420
|
+
async (eventTarget, frameElement, extendSelection) => {
|
|
421
|
+
if (linkingAnnotationId) {
|
|
422
|
+
const rect2 = getRect(eventTarget, frameElement);
|
|
423
|
+
setStatus("Resolving linked element\u2026");
|
|
424
|
+
try {
|
|
425
|
+
const targetData = await captureAnnotationTarget(eventTarget);
|
|
426
|
+
setAnnotations(
|
|
427
|
+
(existing) => existing.map((annotation) => {
|
|
428
|
+
if (annotation.id !== linkingAnnotationId) {
|
|
429
|
+
return annotation;
|
|
430
|
+
}
|
|
431
|
+
if (annotation.targets.some((targetEntry) => targetEntry.targetElement === eventTarget)) {
|
|
432
|
+
return annotation;
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
...annotation,
|
|
436
|
+
targets: [
|
|
437
|
+
...annotation.targets,
|
|
438
|
+
{
|
|
439
|
+
targetElement: eventTarget,
|
|
440
|
+
rect: rect2,
|
|
441
|
+
frameElement,
|
|
442
|
+
data: targetData
|
|
443
|
+
}
|
|
444
|
+
]
|
|
445
|
+
};
|
|
446
|
+
})
|
|
447
|
+
);
|
|
448
|
+
setLinkingAnnotationId(null);
|
|
449
|
+
setPreviewedAnnotation(null);
|
|
450
|
+
setStatus("Element linked to annotation.");
|
|
451
|
+
} catch {
|
|
452
|
+
setStatus("Element linked without source info.");
|
|
453
|
+
setAnnotations(
|
|
454
|
+
(existing) => existing.map((annotation) => {
|
|
455
|
+
if (annotation.id !== linkingAnnotationId) {
|
|
456
|
+
return annotation;
|
|
457
|
+
}
|
|
458
|
+
if (annotation.targets.some((targetEntry) => targetEntry.targetElement === eventTarget)) {
|
|
459
|
+
return annotation;
|
|
460
|
+
}
|
|
461
|
+
return {
|
|
462
|
+
...annotation,
|
|
463
|
+
targets: [
|
|
464
|
+
...annotation.targets,
|
|
465
|
+
{
|
|
466
|
+
targetElement: eventTarget,
|
|
467
|
+
rect: rect2,
|
|
468
|
+
frameElement,
|
|
469
|
+
data: {
|
|
470
|
+
source: null,
|
|
471
|
+
sourceStack: [],
|
|
472
|
+
componentPath: [],
|
|
473
|
+
element: {
|
|
474
|
+
tagName: eventTarget.tagName.toLowerCase(),
|
|
475
|
+
text: eventTarget.textContent?.trim() ?? "",
|
|
476
|
+
html: "",
|
|
477
|
+
selector: ""
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
]
|
|
482
|
+
};
|
|
483
|
+
})
|
|
484
|
+
);
|
|
485
|
+
setLinkingAnnotationId(null);
|
|
486
|
+
}
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const rect = getRect(eventTarget, frameElement);
|
|
490
|
+
const shouldAppend = extendSelection && Boolean(selectedRef.current) && !selectedRef.current?.editingId;
|
|
491
|
+
setSelected((current) => {
|
|
492
|
+
if (shouldAppend && current) {
|
|
493
|
+
if (current.targets.some((targetEntry) => targetEntry.element === eventTarget)) {
|
|
494
|
+
return current;
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
...current,
|
|
498
|
+
targets: [...current.targets, { element: eventTarget, rect, frameElement, target: null, loading: true }]
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
targets: [{ element: eventTarget, rect, frameElement, target: null, loading: true }]
|
|
503
|
+
};
|
|
504
|
+
});
|
|
505
|
+
if (!shouldAppend) {
|
|
506
|
+
setNote("");
|
|
507
|
+
}
|
|
508
|
+
setStatus("Resolving source\u2026");
|
|
509
|
+
try {
|
|
510
|
+
const targetData = await captureAnnotationTarget(eventTarget);
|
|
511
|
+
setSelected((current) => {
|
|
512
|
+
if (!current) {
|
|
513
|
+
return current;
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
...current,
|
|
517
|
+
targets: current.targets.map(
|
|
518
|
+
(targetEntry) => targetEntry.element === eventTarget ? { ...targetEntry, target: targetData, loading: false } : targetEntry
|
|
519
|
+
)
|
|
520
|
+
};
|
|
521
|
+
});
|
|
522
|
+
setStatus(targetData.source ? "Source captured." : "Element captured without source info.");
|
|
523
|
+
} catch {
|
|
524
|
+
setSelected((current) => {
|
|
525
|
+
if (!current) {
|
|
526
|
+
return current;
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
...current,
|
|
530
|
+
targets: current.targets.map(
|
|
531
|
+
(targetEntry) => targetEntry.element === eventTarget ? { ...targetEntry, loading: false } : targetEntry
|
|
532
|
+
)
|
|
533
|
+
};
|
|
534
|
+
});
|
|
535
|
+
setStatus("Element captured without source info.");
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
[linkingAnnotationId]
|
|
539
|
+
);
|
|
342
540
|
(0, import_react.useEffect)(() => {
|
|
343
|
-
if (!enabled || !isAnnotating) {
|
|
541
|
+
if (!enabled || !isAnnotating || !resolvedTarget.document) {
|
|
344
542
|
setHoverRect(null);
|
|
345
543
|
return;
|
|
346
544
|
}
|
|
545
|
+
const activeDocument = resolvedTarget.document;
|
|
347
546
|
const onPointerOver = (event) => {
|
|
348
|
-
const
|
|
349
|
-
if (!
|
|
547
|
+
const eventTarget = getAnnotatableTarget(event.target, activeDocument);
|
|
548
|
+
if (!eventTarget) {
|
|
350
549
|
setHoverRect(null);
|
|
351
550
|
return;
|
|
352
551
|
}
|
|
353
|
-
setHoverRect(getRect(
|
|
552
|
+
setHoverRect(getRect(eventTarget, resolvedTarget.frameElement));
|
|
553
|
+
};
|
|
554
|
+
const suppressInteraction = (event) => {
|
|
555
|
+
const eventTarget = getAnnotatableTarget(event.target, activeDocument);
|
|
556
|
+
if (!eventTarget) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
event.preventDefault();
|
|
560
|
+
event.stopPropagation();
|
|
561
|
+
event.stopImmediatePropagation();
|
|
354
562
|
};
|
|
355
563
|
const onClick = (event) => {
|
|
356
|
-
const
|
|
357
|
-
if (!
|
|
564
|
+
const eventTarget = getAnnotatableTarget(event.target, activeDocument);
|
|
565
|
+
if (!eventTarget) {
|
|
358
566
|
return;
|
|
359
567
|
}
|
|
360
568
|
event.preventDefault();
|
|
361
569
|
event.stopPropagation();
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
setNote("");
|
|
365
|
-
setStatus("Resolving source\u2026");
|
|
366
|
-
captureElementAnnotation(target, "").then((annotation) => {
|
|
367
|
-
setSelected((current) => {
|
|
368
|
-
if (current?.element !== target) {
|
|
369
|
-
return current;
|
|
370
|
-
}
|
|
371
|
-
return { ...current, annotation, loading: false };
|
|
372
|
-
});
|
|
373
|
-
setStatus(annotation.source ? "Source captured." : "Element captured without source info.");
|
|
374
|
-
}).catch(() => {
|
|
375
|
-
setSelected((current) => current?.element === target ? { ...current, loading: false } : current);
|
|
376
|
-
setStatus("Element captured without source info.");
|
|
377
|
-
});
|
|
570
|
+
event.stopImmediatePropagation();
|
|
571
|
+
void handleElementSelection(eventTarget, resolvedTarget.frameElement, shouldExtendSelection(event));
|
|
378
572
|
};
|
|
379
|
-
|
|
380
|
-
|
|
573
|
+
const onFramePointerLeave = () => {
|
|
574
|
+
setHoverRect(null);
|
|
575
|
+
};
|
|
576
|
+
activeDocument.addEventListener("pointerover", onPointerOver, true);
|
|
577
|
+
activeDocument.addEventListener("click", onClick, true);
|
|
578
|
+
BLOCKED_INTERACTION_EVENTS.forEach((eventName) => activeDocument.addEventListener(eventName, suppressInteraction, true));
|
|
579
|
+
resolvedTarget.frameElement?.addEventListener("pointerleave", onFramePointerLeave);
|
|
381
580
|
return () => {
|
|
382
|
-
|
|
383
|
-
|
|
581
|
+
activeDocument.removeEventListener("pointerover", onPointerOver, true);
|
|
582
|
+
activeDocument.removeEventListener("click", onClick, true);
|
|
583
|
+
BLOCKED_INTERACTION_EVENTS.forEach((eventName) => activeDocument.removeEventListener(eventName, suppressInteraction, true));
|
|
584
|
+
resolvedTarget.frameElement?.removeEventListener("pointerleave", onFramePointerLeave);
|
|
384
585
|
};
|
|
385
|
-
}, [enabled, isAnnotating]);
|
|
586
|
+
}, [enabled, handleElementSelection, isAnnotating, resolvedTarget]);
|
|
386
587
|
(0, import_react.useEffect)(() => {
|
|
387
|
-
if (!enabled || !isAnnotating) {
|
|
588
|
+
if (!enabled || !isAnnotating || !resolvedTarget.document) {
|
|
388
589
|
return;
|
|
389
590
|
}
|
|
390
|
-
|
|
591
|
+
const activeDocument = resolvedTarget.document;
|
|
592
|
+
activeDocument.addEventListener("scroll", refreshTrackedRects, true);
|
|
593
|
+
if (typeof document !== "undefined" && activeDocument !== document) {
|
|
594
|
+
document.addEventListener("scroll", refreshTrackedRects, true);
|
|
595
|
+
}
|
|
391
596
|
window.addEventListener("resize", refreshTrackedRects);
|
|
392
597
|
return () => {
|
|
393
|
-
|
|
598
|
+
activeDocument.removeEventListener("scroll", refreshTrackedRects, true);
|
|
599
|
+
if (typeof document !== "undefined" && activeDocument !== document) {
|
|
600
|
+
document.removeEventListener("scroll", refreshTrackedRects, true);
|
|
601
|
+
}
|
|
394
602
|
window.removeEventListener("resize", refreshTrackedRects);
|
|
395
603
|
};
|
|
396
|
-
}, [enabled, isAnnotating, refreshTrackedRects]);
|
|
604
|
+
}, [enabled, isAnnotating, refreshTrackedRects, resolvedTarget.document]);
|
|
397
605
|
const addAnnotation = (0, import_react.useCallback)(async () => {
|
|
398
606
|
const current = selectedRef.current;
|
|
399
|
-
if (!current || current.loading) {
|
|
607
|
+
if (!current || current.targets.some((targetEntry) => targetEntry.loading)) {
|
|
400
608
|
return;
|
|
401
609
|
}
|
|
402
610
|
const trimmedNote = note.trim();
|
|
@@ -404,14 +612,67 @@ function SourceAnnotator({
|
|
|
404
612
|
setStatus("Add a note before saving this annotation.");
|
|
405
613
|
return;
|
|
406
614
|
}
|
|
407
|
-
const
|
|
408
|
-
|
|
615
|
+
const currentTargets = await Promise.all(
|
|
616
|
+
current.targets.map(async (targetEntry) => {
|
|
617
|
+
if (targetEntry.target) {
|
|
618
|
+
return targetEntry;
|
|
619
|
+
}
|
|
620
|
+
const annotation = await captureElementAnnotation(targetEntry.element, trimmedNote);
|
|
621
|
+
return { ...targetEntry, target: annotation.targets[0], loading: false };
|
|
622
|
+
})
|
|
623
|
+
);
|
|
624
|
+
const storedAnnotation = {
|
|
625
|
+
id: current.editingId ?? createAnnotationId(),
|
|
626
|
+
note: trimmedNote,
|
|
627
|
+
targets: currentTargets.map((targetEntry) => ({
|
|
628
|
+
targetElement: targetEntry.element,
|
|
629
|
+
rect: getRect(targetEntry.element, targetEntry.frameElement),
|
|
630
|
+
frameElement: targetEntry.frameElement,
|
|
631
|
+
data: targetEntry.target
|
|
632
|
+
}))
|
|
633
|
+
};
|
|
634
|
+
setAnnotations((existing) => {
|
|
635
|
+
if (!current.editingId) {
|
|
636
|
+
return [...existing, storedAnnotation];
|
|
637
|
+
}
|
|
638
|
+
return existing.map((item) => item.id === current.editingId ? storedAnnotation : item);
|
|
639
|
+
});
|
|
409
640
|
setSelected(null);
|
|
410
641
|
setNote("");
|
|
411
|
-
|
|
642
|
+
setPreviewedAnnotation(null);
|
|
643
|
+
setStatus(current.editingId ? "Annotation updated." : "Annotation saved.");
|
|
412
644
|
}, [note]);
|
|
645
|
+
const editAnnotation = (0, import_react.useCallback)((annotation) => {
|
|
646
|
+
setLinkingAnnotationId(null);
|
|
647
|
+
setSelected({
|
|
648
|
+
editingId: annotation.id,
|
|
649
|
+
targets: annotation.targets.map((targetEntry) => ({
|
|
650
|
+
element: targetEntry.targetElement,
|
|
651
|
+
rect: getRect(targetEntry.targetElement, targetEntry.frameElement),
|
|
652
|
+
frameElement: targetEntry.frameElement,
|
|
653
|
+
target: targetEntry.data,
|
|
654
|
+
loading: false
|
|
655
|
+
}))
|
|
656
|
+
});
|
|
657
|
+
setNote(annotation.note);
|
|
658
|
+
setPreviewedAnnotation(null);
|
|
659
|
+
setStatus("Editing annotation.");
|
|
660
|
+
}, []);
|
|
661
|
+
const startLinkingAnnotation = (0, import_react.useCallback)((annotationId) => {
|
|
662
|
+
setSelected(null);
|
|
663
|
+
setPreviewedAnnotation(null);
|
|
664
|
+
setLinkingAnnotationId(annotationId);
|
|
665
|
+
setStatus("Click another element to link it to this annotation.");
|
|
666
|
+
}, []);
|
|
667
|
+
const deleteAnnotation = (0, import_react.useCallback)((annotationId) => {
|
|
668
|
+
setAnnotations((existing) => existing.filter((annotation) => annotation.id !== annotationId));
|
|
669
|
+
setSelected((current) => current?.editingId === annotationId ? null : current);
|
|
670
|
+
setPreviewedAnnotation((current) => current?.id === annotationId ? null : current);
|
|
671
|
+
setLinkingAnnotationId((current) => current === annotationId ? null : current);
|
|
672
|
+
setStatus("Annotation deleted.");
|
|
673
|
+
}, []);
|
|
413
674
|
const collect = (0, import_react.useCallback)(async () => {
|
|
414
|
-
const payload = createAnnotationCollection(annotations
|
|
675
|
+
const payload = createAnnotationCollection(stripStoredAnnotations(annotations), getPageContext(resolvedTarget.document));
|
|
415
676
|
const text = formatAnnotationCollection(payload, output);
|
|
416
677
|
try {
|
|
417
678
|
await copyTextToClipboard(text);
|
|
@@ -419,14 +680,17 @@ function SourceAnnotator({
|
|
|
419
680
|
setIsAnnotating(false);
|
|
420
681
|
setSelected(null);
|
|
421
682
|
setHoverRect(null);
|
|
683
|
+
setAnnotations([]);
|
|
684
|
+
setPreviewedAnnotation(null);
|
|
422
685
|
setNote("");
|
|
686
|
+
setStatus(null);
|
|
687
|
+
setLinkingAnnotationId(null);
|
|
423
688
|
import_sonner.toast.success("Annotations copied", { description: `${payload.annotations.length} copied to clipboard.` });
|
|
424
|
-
setStatus(`Copied ${payload.annotations.length} annotation${payload.annotations.length === 1 ? "" : "s"}.`);
|
|
425
689
|
} catch (error) {
|
|
426
690
|
import_sonner.toast.error("Copy failed", { description: error instanceof Error ? error.message : "Clipboard copy failed." });
|
|
427
691
|
setStatus(error instanceof Error ? error.message : "Clipboard copy failed.");
|
|
428
692
|
}
|
|
429
|
-
}, [annotations, onCollect, output]);
|
|
693
|
+
}, [annotations, onCollect, output, resolvedTarget.document]);
|
|
430
694
|
if (!enabled) {
|
|
431
695
|
return null;
|
|
432
696
|
}
|
|
@@ -444,11 +708,25 @@ function SourceAnnotator({
|
|
|
444
708
|
}
|
|
445
709
|
),
|
|
446
710
|
isAnnotating && hoverRect ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { rect: hoverRect, kind: "hover" }) : null,
|
|
447
|
-
selected
|
|
448
|
-
isAnnotating ? annotations.map(
|
|
449
|
-
|
|
711
|
+
selected?.targets.map((targetEntry, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { rect: targetEntry.rect, kind: "selected" }, index)),
|
|
712
|
+
isAnnotating ? annotations.map(
|
|
713
|
+
(annotation, index) => annotation.targets.map((targetEntry, targetIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
714
|
+
Pin,
|
|
715
|
+
{
|
|
716
|
+
annotation,
|
|
717
|
+
rect: targetEntry.rect,
|
|
718
|
+
index,
|
|
719
|
+
onEdit: editAnnotation,
|
|
720
|
+
onPreview: setPreviewedAnnotation,
|
|
721
|
+
onPreviewEnd: () => setPreviewedAnnotation(null)
|
|
722
|
+
},
|
|
723
|
+
`${annotation.id}:${targetIndex}`
|
|
724
|
+
))
|
|
725
|
+
) : null,
|
|
726
|
+
isAnnotating && previewedAnnotation ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnnotationPreview, { annotation: previewedAnnotation }) : null,
|
|
727
|
+
selected?.targets.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: getPopoverStyle(selected.targets[selected.targets.length - 1].rect), role: "dialog", "aria-label": "Add source annotation", children: [
|
|
450
728
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.popoverTitle, children: "Annotation" }),
|
|
451
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.metaText, children:
|
|
729
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.metaText, children: formatSelectedTargets(selected.targets) }),
|
|
452
730
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
453
731
|
"textarea",
|
|
454
732
|
{
|
|
@@ -462,17 +740,26 @@ function SourceAnnotator({
|
|
|
462
740
|
),
|
|
463
741
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.popoverActions, children: [
|
|
464
742
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => setSelected(null), style: styles.secondaryButton, children: "Cancel" }),
|
|
465
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: addAnnotation, style: styles.primaryButton, disabled: selected.loading, children: "Save note" })
|
|
743
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: addAnnotation, style: styles.primaryButton, disabled: selected.targets.some((targetEntry) => targetEntry.loading), children: selected.editingId ? "Update note" : "Save note" })
|
|
466
744
|
] })
|
|
467
745
|
] }) : null,
|
|
468
746
|
isAnnotating ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { style: styles.panel, "aria-label": "Collected annotations", children: [
|
|
469
747
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.panelHeader, children: [
|
|
470
748
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "Annotations" }),
|
|
471
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.badge, children:
|
|
749
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.badge, children: annotations.length })
|
|
472
750
|
] }),
|
|
473
|
-
annotations.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { style: styles.annotationList, children: annotations.map((annotation) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { style: styles.annotationItem, children: [
|
|
474
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
475
|
-
|
|
751
|
+
annotations.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { style: styles.annotationList, children: annotations.map((annotation, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { style: styles.annotationItem, children: [
|
|
752
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.annotationContent, children: [
|
|
753
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.noteText, children: annotation.note }),
|
|
754
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.metaText, children: formatStoredAnnotationSummary(annotation) })
|
|
755
|
+
] }),
|
|
756
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.annotationActions, children: [
|
|
757
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: () => startLinkingAnnotation(annotation.id), style: styles.linkButton, children: "Link element" }),
|
|
758
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", onClick: () => deleteAnnotation(annotation.id), style: styles.deleteButton, children: [
|
|
759
|
+
"Delete annotation ",
|
|
760
|
+
index + 1
|
|
761
|
+
] })
|
|
762
|
+
] })
|
|
476
763
|
] }, annotation.id)) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.emptyText, children: "Hover an element, click it, then add a note." }),
|
|
477
764
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: collect, style: styles.collectButton, disabled: !annotations.length, children: "Collect" }),
|
|
478
765
|
status ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.status, children: status }) : null
|
|
@@ -483,6 +770,7 @@ function Box({ rect, kind }) {
|
|
|
483
770
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
484
771
|
"div",
|
|
485
772
|
{
|
|
773
|
+
"data-mikuexe-annotator-box": kind,
|
|
486
774
|
style: {
|
|
487
775
|
...styles.box,
|
|
488
776
|
...kind === "selected" ? styles.selectedBox : styles.hoverBox,
|
|
@@ -494,37 +782,115 @@ function Box({ rect, kind }) {
|
|
|
494
782
|
}
|
|
495
783
|
);
|
|
496
784
|
}
|
|
497
|
-
function Pin({
|
|
785
|
+
function Pin({
|
|
786
|
+
annotation,
|
|
787
|
+
rect,
|
|
788
|
+
index,
|
|
789
|
+
onEdit,
|
|
790
|
+
onPreview,
|
|
791
|
+
onPreviewEnd
|
|
792
|
+
}) {
|
|
498
793
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
499
|
-
"
|
|
794
|
+
"button",
|
|
500
795
|
{
|
|
501
|
-
|
|
796
|
+
type: "button",
|
|
797
|
+
style: { ...styles.pin, top: Math.max(8, rect.top - 10), left: Math.max(8, rect.left - 10) },
|
|
502
798
|
title: annotation.note,
|
|
799
|
+
"aria-label": `Edit annotation ${index + 1}`,
|
|
800
|
+
onClick: () => onEdit(annotation),
|
|
801
|
+
onMouseOver: () => onPreview(annotation),
|
|
802
|
+
onMouseOut: onPreviewEnd,
|
|
803
|
+
onFocus: () => onPreview(annotation),
|
|
804
|
+
onBlur: onPreviewEnd,
|
|
503
805
|
children: index + 1
|
|
504
806
|
}
|
|
505
807
|
);
|
|
506
808
|
}
|
|
507
|
-
function
|
|
508
|
-
|
|
809
|
+
function AnnotationPreview({ annotation }) {
|
|
810
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { role: "tooltip", style: getPreviewStyle(annotation.targets[0]?.rect ?? { top: 8, left: 8, width: 0, height: 0 }), children: [
|
|
811
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.noteText, children: annotation.note }),
|
|
812
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.metaText, children: formatStoredAnnotationSummary(annotation) })
|
|
813
|
+
] });
|
|
814
|
+
}
|
|
815
|
+
function stripStoredAnnotations(annotations) {
|
|
816
|
+
return annotations.map((annotation) => ({
|
|
817
|
+
id: annotation.id,
|
|
818
|
+
note: annotation.note,
|
|
819
|
+
targets: annotation.targets.map((targetEntry) => targetEntry.data)
|
|
820
|
+
}));
|
|
821
|
+
}
|
|
822
|
+
function getAnnotatableTarget(target, ownerDocument) {
|
|
823
|
+
if (!isElement(target, ownerDocument)) {
|
|
509
824
|
return null;
|
|
510
825
|
}
|
|
511
826
|
if (target.closest(`[${ROOT_ATTR}]`)) {
|
|
512
827
|
return null;
|
|
513
828
|
}
|
|
514
|
-
if (target ===
|
|
829
|
+
if (target === ownerDocument.body || target === ownerDocument.documentElement) {
|
|
515
830
|
return null;
|
|
516
831
|
}
|
|
517
832
|
return target;
|
|
518
833
|
}
|
|
519
|
-
function getRect(element) {
|
|
834
|
+
function getRect(element, frameElement) {
|
|
520
835
|
const rect = element.getBoundingClientRect();
|
|
836
|
+
const frameRect = frameElement?.getBoundingClientRect();
|
|
521
837
|
return {
|
|
522
|
-
top: rect.top,
|
|
523
|
-
left: rect.left,
|
|
838
|
+
top: rect.top + (frameRect?.top ?? 0),
|
|
839
|
+
left: rect.left + (frameRect?.left ?? 0),
|
|
524
840
|
width: rect.width,
|
|
525
841
|
height: rect.height
|
|
526
842
|
};
|
|
527
843
|
}
|
|
844
|
+
function useResolvedTarget(target) {
|
|
845
|
+
const [navigationVersion, setNavigationVersion] = (0, import_react.useState)(0);
|
|
846
|
+
const resolvedTarget = (0, import_react.useMemo)(() => resolveTarget(target), [target, navigationVersion]);
|
|
847
|
+
const currentDocumentRef = (0, import_react.useRef)(resolvedTarget.document);
|
|
848
|
+
currentDocumentRef.current = resolvedTarget.document;
|
|
849
|
+
(0, import_react.useEffect)(() => {
|
|
850
|
+
if (typeof HTMLIFrameElement !== "undefined" && target instanceof HTMLIFrameElement) {
|
|
851
|
+
const updateTarget = () => {
|
|
852
|
+
const nextTarget = resolveTarget(target);
|
|
853
|
+
if (nextTarget.document !== currentDocumentRef.current) {
|
|
854
|
+
setNavigationVersion((version) => version + 1);
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
target.addEventListener("load", updateTarget);
|
|
858
|
+
return () => target.removeEventListener("load", updateTarget);
|
|
859
|
+
}
|
|
860
|
+
}, [target]);
|
|
861
|
+
return resolvedTarget;
|
|
862
|
+
}
|
|
863
|
+
function resolveTarget(target) {
|
|
864
|
+
const hostDocument = typeof document === "undefined" ? null : document;
|
|
865
|
+
if (typeof HTMLIFrameElement !== "undefined" && target instanceof HTMLIFrameElement) {
|
|
866
|
+
const frameDocument = target.contentDocument;
|
|
867
|
+
if (!frameDocument) {
|
|
868
|
+
console.warn("@mikuexe/annotator-react: SourceAnnotator target iframe must be same-origin; iframe contentDocument is not accessible.");
|
|
869
|
+
return {
|
|
870
|
+
document: null,
|
|
871
|
+
frameElement: target
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
return {
|
|
875
|
+
document: frameDocument,
|
|
876
|
+
frameElement: target
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
if (typeof Document !== "undefined" && target instanceof Document) {
|
|
880
|
+
return {
|
|
881
|
+
document: target,
|
|
882
|
+
frameElement: null
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
return {
|
|
886
|
+
document: hostDocument,
|
|
887
|
+
frameElement: null
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
function isElement(target, ownerDocument) {
|
|
891
|
+
const elementConstructor = ownerDocument.defaultView?.Element ?? Element;
|
|
892
|
+
return target instanceof elementConstructor;
|
|
893
|
+
}
|
|
528
894
|
function getPopoverStyle(rect) {
|
|
529
895
|
const top = Math.min(window.innerHeight - 260, rect.top + rect.height + 8);
|
|
530
896
|
const left = Math.min(window.innerWidth - 340, Math.max(8, rect.left));
|
|
@@ -534,17 +900,45 @@ function getPopoverStyle(rect) {
|
|
|
534
900
|
left
|
|
535
901
|
};
|
|
536
902
|
}
|
|
537
|
-
function
|
|
538
|
-
const
|
|
539
|
-
|
|
903
|
+
function getPreviewStyle(rect) {
|
|
904
|
+
const top = Math.min(window.innerHeight - 120, rect.top + rect.height + 8);
|
|
905
|
+
const left = Math.min(window.innerWidth - 260, Math.max(8, rect.left));
|
|
906
|
+
return {
|
|
907
|
+
...styles.preview,
|
|
908
|
+
top: Math.max(8, top),
|
|
909
|
+
left
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
function formatSelectedTargets(targets) {
|
|
913
|
+
if (!targets.length) {
|
|
914
|
+
return "Source unavailable";
|
|
915
|
+
}
|
|
916
|
+
if (targets.length === 1) {
|
|
917
|
+
return formatTargetSource(targets[0].target);
|
|
918
|
+
}
|
|
919
|
+
const resolvedCount = targets.filter((targetEntry) => targetEntry.target).length;
|
|
920
|
+
return `${targets.length} elements selected \xB7 ${resolvedCount}/${targets.length} resolved`;
|
|
921
|
+
}
|
|
922
|
+
function formatStoredAnnotationSummary(annotation) {
|
|
923
|
+
const firstTarget = annotation.targets[0]?.data;
|
|
924
|
+
const targetSummary = formatTargetSource(firstTarget);
|
|
925
|
+
const linkedSummary = annotation.targets.length > 1 ? ` \xB7 ${annotation.targets.length} linked elements` : "";
|
|
926
|
+
return `${targetSummary}${linkedSummary}`;
|
|
927
|
+
}
|
|
928
|
+
function formatTargetSource(target) {
|
|
929
|
+
const componentPath = target?.componentPath.length ? target.componentPath.join(" \u203A ") : null;
|
|
930
|
+
if (!target) {
|
|
540
931
|
return "Source unavailable";
|
|
541
932
|
}
|
|
542
|
-
if (!
|
|
543
|
-
return `${
|
|
933
|
+
if (!target.source?.filePath) {
|
|
934
|
+
return `${target.element.selector} \xB7 source unavailable`;
|
|
544
935
|
}
|
|
545
|
-
const line =
|
|
936
|
+
const line = target.source.lineNumber ? `:${target.source.lineNumber}` : "";
|
|
546
937
|
const component = componentPath ? ` \xB7 ${componentPath}` : "";
|
|
547
|
-
return `${
|
|
938
|
+
return `${target.source.filePath}${line}${component}`;
|
|
939
|
+
}
|
|
940
|
+
function shouldExtendSelection(event) {
|
|
941
|
+
return event.metaKey || event.ctrlKey;
|
|
548
942
|
}
|
|
549
943
|
function matchesHotkey(event, hotkey) {
|
|
550
944
|
const parts = hotkey.toLowerCase().split("+").map((part) => part.trim()).filter(Boolean);
|
|
@@ -606,6 +1000,7 @@ var styles = {
|
|
|
606
1000
|
},
|
|
607
1001
|
popover: {
|
|
608
1002
|
position: "fixed",
|
|
1003
|
+
zIndex: 2,
|
|
609
1004
|
width: 320,
|
|
610
1005
|
pointerEvents: "auto",
|
|
611
1006
|
background: "#ffffff",
|
|
@@ -658,6 +1053,7 @@ var styles = {
|
|
|
658
1053
|
},
|
|
659
1054
|
panel: {
|
|
660
1055
|
position: "fixed",
|
|
1056
|
+
zIndex: 1,
|
|
661
1057
|
right: 16,
|
|
662
1058
|
bottom: 68,
|
|
663
1059
|
width: 300,
|
|
@@ -694,11 +1090,23 @@ var styles = {
|
|
|
694
1090
|
overflow: "auto"
|
|
695
1091
|
},
|
|
696
1092
|
annotationItem: {
|
|
1093
|
+
display: "grid",
|
|
1094
|
+
gap: 8,
|
|
1095
|
+
alignItems: "start",
|
|
697
1096
|
marginBottom: 8
|
|
698
1097
|
},
|
|
1098
|
+
annotationContent: {
|
|
1099
|
+
minWidth: 0
|
|
1100
|
+
},
|
|
1101
|
+
annotationActions: {
|
|
1102
|
+
display: "flex",
|
|
1103
|
+
gap: 8,
|
|
1104
|
+
flexWrap: "wrap"
|
|
1105
|
+
},
|
|
699
1106
|
noteText: {
|
|
700
1107
|
color: "#0f172a",
|
|
701
|
-
fontWeight: 650
|
|
1108
|
+
fontWeight: 650,
|
|
1109
|
+
overflowWrap: "anywhere"
|
|
702
1110
|
},
|
|
703
1111
|
emptyText: {
|
|
704
1112
|
color: "#64748b",
|
|
@@ -719,31 +1127,66 @@ var styles = {
|
|
|
719
1127
|
color: "#475569",
|
|
720
1128
|
fontSize: 12
|
|
721
1129
|
},
|
|
1130
|
+
linkButton: {
|
|
1131
|
+
border: "1px solid #bfdbfe",
|
|
1132
|
+
borderRadius: 999,
|
|
1133
|
+
background: "#eff6ff",
|
|
1134
|
+
color: "#1d4ed8",
|
|
1135
|
+
padding: "4px 7px",
|
|
1136
|
+
fontSize: 11,
|
|
1137
|
+
fontWeight: 750,
|
|
1138
|
+
cursor: "pointer"
|
|
1139
|
+
},
|
|
1140
|
+
deleteButton: {
|
|
1141
|
+
border: "1px solid #fecaca",
|
|
1142
|
+
borderRadius: 999,
|
|
1143
|
+
background: "#fff1f2",
|
|
1144
|
+
color: "#be123c",
|
|
1145
|
+
padding: "4px 7px",
|
|
1146
|
+
fontSize: 11,
|
|
1147
|
+
fontWeight: 750,
|
|
1148
|
+
cursor: "pointer"
|
|
1149
|
+
},
|
|
1150
|
+
preview: {
|
|
1151
|
+
position: "fixed",
|
|
1152
|
+
maxWidth: 240,
|
|
1153
|
+
pointerEvents: "none",
|
|
1154
|
+
background: "#ffffff",
|
|
1155
|
+
border: "1px solid #cbd5e1",
|
|
1156
|
+
borderRadius: 10,
|
|
1157
|
+
padding: 10,
|
|
1158
|
+
boxShadow: "0 14px 35px rgba(15, 23, 42, 0.18)"
|
|
1159
|
+
},
|
|
722
1160
|
pin: {
|
|
723
1161
|
position: "fixed",
|
|
1162
|
+
border: 0,
|
|
724
1163
|
width: 20,
|
|
725
1164
|
height: 20,
|
|
726
1165
|
borderRadius: 999,
|
|
727
1166
|
display: "inline-flex",
|
|
728
1167
|
alignItems: "center",
|
|
729
1168
|
justifyContent: "center",
|
|
730
|
-
pointerEvents: "
|
|
1169
|
+
pointerEvents: "auto",
|
|
731
1170
|
background: "#f97316",
|
|
732
1171
|
color: "#ffffff",
|
|
733
1172
|
fontSize: 12,
|
|
734
1173
|
fontWeight: 800,
|
|
735
|
-
boxShadow: "0 8px 18px rgba(15, 23, 42, 0.2)"
|
|
1174
|
+
boxShadow: "0 8px 18px rgba(15, 23, 42, 0.2)",
|
|
1175
|
+
cursor: "pointer",
|
|
1176
|
+
padding: 0
|
|
736
1177
|
}
|
|
737
1178
|
};
|
|
738
1179
|
// Annotate the CommonJS export names for ESM import in node:
|
|
739
1180
|
0 && (module.exports = {
|
|
740
1181
|
SourceAnnotator,
|
|
1182
|
+
captureAnnotationTarget,
|
|
741
1183
|
captureElementAnnotation,
|
|
742
1184
|
copyTextToClipboard,
|
|
743
1185
|
createAnnotationCollection,
|
|
744
1186
|
formatAnnotationCollection,
|
|
745
1187
|
formatMarkdown,
|
|
746
1188
|
getElementSelector,
|
|
1189
|
+
getPageContext,
|
|
747
1190
|
trimText
|
|
748
1191
|
});
|
|
749
1192
|
//# sourceMappingURL=index.cjs.map
|