agentation-vue 0.2.5 → 0.2.10
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/AgentationVue.d.vue.ts +1 -0
- package/dist/AgentationVue.vue +212 -57
- package/dist/AgentationVue.vue.d.ts +1 -0
- package/dist/components/AgentationToolbar.vue +1 -2
- package/dist/components/AnnotationInput.d.vue.ts +2 -0
- package/dist/components/AnnotationInput.vue +87 -12
- package/dist/components/AnnotationInput.vue.d.ts +2 -0
- package/dist/components/MentionDropdown.d.vue.ts +16 -0
- package/dist/components/MentionDropdown.vue +41 -0
- package/dist/components/MentionDropdown.vue.d.ts +16 -0
- package/dist/components/SettingsPanel.vue +2 -0
- package/dist/components/SettingsPopover.vue +3 -5
- package/dist/composables/useAnnotations.d.ts +5 -1
- package/dist/composables/useAnnotations.mjs +26 -4
- package/dist/composables/useElementDetection.mjs +2 -2
- package/dist/composables/useKeyboardShortcuts.mjs +25 -11
- package/dist/composables/useMentionDropdown.d.ts +21 -0
- package/dist/composables/useMentionDropdown.mjs +162 -0
- package/dist/composables/useMultiSelect.mjs +2 -2
- package/dist/composables/useSettings.d.ts +3 -0
- package/dist/composables/useSettings.mjs +19 -2
- package/dist/constants.d.ts +1 -0
- package/dist/constants.mjs +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.mjs +11 -2
- package/dist/styles/agentation.css +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/utils/agentation-tree.d.ts +2 -0
- package/dist/utils/agentation-tree.mjs +21 -0
- package/dist/utils/dom-inspector.d.ts +3 -0
- package/dist/utils/dom-inspector.mjs +11 -0
- package/dist/utils/mention.d.ts +13 -0
- package/dist/utils/mention.mjs +46 -0
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ type __VLS_Props = {
|
|
|
8
8
|
pageUrl?: string;
|
|
9
9
|
theme?: 'light' | 'dark' | 'auto';
|
|
10
10
|
activationKey?: 'none' | 'Meta' | 'Alt' | 'Shift';
|
|
11
|
+
disablePortal?: boolean;
|
|
11
12
|
};
|
|
12
13
|
declare const _default: import("vue-demi").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue-demi").ComponentOptionsMixin, import("vue-demi").ComponentOptionsMixin, {
|
|
13
14
|
copy: (markdown: string) => any;
|
package/dist/AgentationVue.vue
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
isVue2 as _isVue2,
|
|
4
|
+
computed,
|
|
5
|
+
defineComponent,
|
|
6
|
+
onBeforeUnmount,
|
|
7
|
+
onMounted,
|
|
8
|
+
ref,
|
|
9
|
+
watch
|
|
10
|
+
} from "vue-demi";
|
|
3
11
|
import AgentationToolbar from "./components/AgentationToolbar.vue";
|
|
4
12
|
import AnnotationInput from "./components/AnnotationInput.vue";
|
|
5
13
|
import AnnotationMarker from "./components/AnnotationMarker.vue";
|
|
@@ -10,15 +18,26 @@ import { useAnnotations } from "./composables/useAnnotations.mjs";
|
|
|
10
18
|
import { useAreaSelect } from "./composables/useAreaSelect.mjs";
|
|
11
19
|
import { useElementDetection } from "./composables/useElementDetection.mjs";
|
|
12
20
|
import { useInteractionMode } from "./composables/useInteractionMode.mjs";
|
|
13
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
DEFAULT_SHORTCUT_CONFIG,
|
|
23
|
+
useKeyboardShortcuts
|
|
24
|
+
} from "./composables/useKeyboardShortcuts.mjs";
|
|
14
25
|
import { useMarkerPositions } from "./composables/useMarkerPositions.mjs";
|
|
15
26
|
import { useMultiSelect } from "./composables/useMultiSelect.mjs";
|
|
16
27
|
import { useOutputFormatter } from "./composables/useOutputFormatter.mjs";
|
|
17
28
|
import { useSettings } from "./composables/useSettings.mjs";
|
|
18
29
|
import { useTextSelection } from "./composables/useTextSelection.mjs";
|
|
19
|
-
import {
|
|
30
|
+
import { isInsideAgentationTree } from "./utils/agentation-tree.mjs";
|
|
20
31
|
import { copyToClipboard } from "./utils/clipboard.mjs";
|
|
21
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
isFixed as checkIsFixed,
|
|
34
|
+
detectVueComponents,
|
|
35
|
+
getAccessibilityInfo,
|
|
36
|
+
getComputedStylesSummary,
|
|
37
|
+
getNearbyElements,
|
|
38
|
+
getNearbyText,
|
|
39
|
+
getRelevantComputedStyles
|
|
40
|
+
} from "./utils/dom-inspector.mjs";
|
|
22
41
|
import { createPortalContainer, destroyPortalContainer } from "./utils/portal.mjs";
|
|
23
42
|
import { getElementName, getElementPath } from "./utils/selectors.mjs";
|
|
24
43
|
import { boundingBoxToStyle } from "./utils/style.mjs";
|
|
@@ -30,7 +49,8 @@ const props = defineProps({
|
|
|
30
49
|
autoHideToolbar: { type: Boolean, required: false },
|
|
31
50
|
pageUrl: { type: String, required: false },
|
|
32
51
|
theme: { type: String, required: false },
|
|
33
|
-
activationKey: { type: String, required: false }
|
|
52
|
+
activationKey: { type: String, required: false },
|
|
53
|
+
disablePortal: { type: Boolean, required: false }
|
|
34
54
|
});
|
|
35
55
|
const emit = defineEmits(["annotation-add", "annotation-delete", "annotation-update", "annotations-clear", "copy"]);
|
|
36
56
|
const HISTORY_CHANGE_EVENT = "va:history-change";
|
|
@@ -59,8 +79,24 @@ const toolbarRef = ref(null);
|
|
|
59
79
|
const currentUrl = ref(props.pageUrl || getCurrentUrl());
|
|
60
80
|
const { settings } = useSettings();
|
|
61
81
|
const { mode, transition } = useInteractionMode();
|
|
62
|
-
const {
|
|
63
|
-
|
|
82
|
+
const {
|
|
83
|
+
annotations,
|
|
84
|
+
addAnnotation,
|
|
85
|
+
removeAnnotation,
|
|
86
|
+
updateAnnotation,
|
|
87
|
+
clearAnnotations,
|
|
88
|
+
restoreAnnotations,
|
|
89
|
+
setScopeUrl
|
|
90
|
+
} = useAnnotations(currentUrl.value);
|
|
91
|
+
const {
|
|
92
|
+
hoveredRect,
|
|
93
|
+
hoveredName,
|
|
94
|
+
hoveredComponentChain,
|
|
95
|
+
onMouseMove,
|
|
96
|
+
clearHighlight,
|
|
97
|
+
getElementUnderOverlay,
|
|
98
|
+
cleanup: cleanupDetection
|
|
99
|
+
} = useElementDetection(overlayEl, () => settings.showComponentTree);
|
|
64
100
|
const textSelection = useTextSelection(mode);
|
|
65
101
|
const multiSelect = useMultiSelect(mode, transition);
|
|
66
102
|
const areaSelect = useAreaSelect(mode, transition);
|
|
@@ -77,11 +113,17 @@ const editingAnnotation = ref(null);
|
|
|
77
113
|
const settingsOpen = ref(false);
|
|
78
114
|
const settingsAnchorEl = ref(null);
|
|
79
115
|
const copyFeedback = ref(false);
|
|
116
|
+
const undoFeedback = ref(false);
|
|
117
|
+
const undoSnapshot = ref([]);
|
|
118
|
+
let undoTimer = null;
|
|
119
|
+
const UNDO_TIMEOUT_MS = 5e3;
|
|
80
120
|
const toolbarDragging = ref(false);
|
|
81
121
|
const DRAG_END_SUPPRESSION_MS = 500;
|
|
82
122
|
const SETTINGS_CLOSE_SUPPRESSION_MS = 220;
|
|
83
123
|
let suppressInteractionsUntil = 0;
|
|
84
|
-
const effectiveBlockPageInteractions = computed(
|
|
124
|
+
const effectiveBlockPageInteractions = computed(
|
|
125
|
+
() => props.blockPageInteractions ?? settings.blockPageInteractions
|
|
126
|
+
);
|
|
85
127
|
const rootStyle = computed(() => {
|
|
86
128
|
const hex = settings.markerColor;
|
|
87
129
|
if (!hex)
|
|
@@ -105,7 +147,16 @@ const pendingMarkerY = computed(() => {
|
|
|
105
147
|
return 0;
|
|
106
148
|
return pendingPosition.value.y + (window.scrollY || document.documentElement.scrollTop);
|
|
107
149
|
});
|
|
108
|
-
const pendingIsSelection = computed(
|
|
150
|
+
const pendingIsSelection = computed(
|
|
151
|
+
() => mode.value === "input-open" && !editingAnnotation.value && (multiSelect.selectedElements.value.length > 0 || !!areaSelect.areaRect.value)
|
|
152
|
+
);
|
|
153
|
+
const mentionCandidates = computed(
|
|
154
|
+
() => annotations.value.map((ann, i) => ({
|
|
155
|
+
id: ann.id,
|
|
156
|
+
displayNumber: i + 1,
|
|
157
|
+
commentPreview: ann.comment.replace(/@\[\d+\]/g, "@\u2026").slice(0, 40) + (ann.comment.length > 40 ? "\u2026" : "")
|
|
158
|
+
})).filter((c) => !editingAnnotation.value || c.id !== editingAnnotation.value.id)
|
|
159
|
+
);
|
|
109
160
|
let portalContainer = null;
|
|
110
161
|
const isVue2 = _isVue2;
|
|
111
162
|
const PassThrough = defineComponent({
|
|
@@ -114,48 +165,81 @@ const PassThrough = defineComponent({
|
|
|
114
165
|
return (typeof slot === "function" ? slot() : slot?.[0]) || null;
|
|
115
166
|
}
|
|
116
167
|
});
|
|
117
|
-
const portalWrapper =
|
|
118
|
-
|
|
168
|
+
const portalWrapper = computed(
|
|
169
|
+
() => props.disablePortal || isVue2 ? PassThrough : "Teleport"
|
|
170
|
+
);
|
|
171
|
+
const portalProps = computed(
|
|
172
|
+
() => props.disablePortal || isVue2 ? {} : { to: "body" }
|
|
173
|
+
);
|
|
119
174
|
onMounted(() => {
|
|
120
|
-
if (isVue2 && rootEl.value) {
|
|
175
|
+
if (!props.disablePortal && isVue2 && rootEl.value) {
|
|
121
176
|
portalContainer = createPortalContainer();
|
|
122
177
|
portalContainer.appendChild(rootEl.value);
|
|
123
178
|
}
|
|
124
179
|
});
|
|
125
180
|
onBeforeUnmount(() => {
|
|
181
|
+
dismissUndo();
|
|
126
182
|
animPause.cleanup();
|
|
127
183
|
cleanupDetection();
|
|
128
184
|
if (portalContainer) {
|
|
129
185
|
destroyPortalContainer(portalContainer);
|
|
130
186
|
}
|
|
131
187
|
});
|
|
132
|
-
watch(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
watch(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
watch(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
188
|
+
watch(
|
|
189
|
+
() => props.outputDetail,
|
|
190
|
+
(v) => {
|
|
191
|
+
if (v)
|
|
192
|
+
settings.outputDetail = v;
|
|
193
|
+
},
|
|
194
|
+
{ immediate: true }
|
|
195
|
+
);
|
|
196
|
+
watch(
|
|
197
|
+
() => props.markerColor,
|
|
198
|
+
(v) => {
|
|
199
|
+
if (v)
|
|
200
|
+
settings.markerColor = v;
|
|
201
|
+
},
|
|
202
|
+
{ immediate: true }
|
|
203
|
+
);
|
|
204
|
+
watch(
|
|
205
|
+
() => props.theme,
|
|
206
|
+
(v) => {
|
|
207
|
+
if (v)
|
|
208
|
+
settings.theme = v;
|
|
209
|
+
},
|
|
210
|
+
{ immediate: true }
|
|
211
|
+
);
|
|
212
|
+
watch(
|
|
213
|
+
() => props.blockPageInteractions,
|
|
214
|
+
(v) => {
|
|
215
|
+
if (v)
|
|
216
|
+
settings.blockPageInteractions = v;
|
|
217
|
+
},
|
|
218
|
+
{ immediate: true }
|
|
219
|
+
);
|
|
220
|
+
watch(
|
|
221
|
+
() => props.autoHideToolbar,
|
|
222
|
+
(v) => {
|
|
223
|
+
if (v)
|
|
224
|
+
settings.autoHideToolbar = v;
|
|
225
|
+
},
|
|
226
|
+
{ immediate: true }
|
|
227
|
+
);
|
|
228
|
+
watch(
|
|
229
|
+
() => props.pageUrl,
|
|
230
|
+
(url) => {
|
|
231
|
+
syncUrlScope(url || getCurrentUrl());
|
|
232
|
+
},
|
|
233
|
+
{ immediate: true }
|
|
234
|
+
);
|
|
235
|
+
watch(
|
|
236
|
+
() => props.activationKey,
|
|
237
|
+
(v) => {
|
|
238
|
+
if (v !== void 0)
|
|
239
|
+
settings.activationKey = v;
|
|
240
|
+
},
|
|
241
|
+
{ immediate: true }
|
|
242
|
+
);
|
|
159
243
|
function onActivate() {
|
|
160
244
|
transition("inspect");
|
|
161
245
|
}
|
|
@@ -206,7 +290,10 @@ function onOverlayMouseUp(e) {
|
|
|
206
290
|
areaSelect.onMouseUp();
|
|
207
291
|
const rect = areaSelect.areaRect.value;
|
|
208
292
|
if (rect && rect.width > 10 && rect.height > 10) {
|
|
209
|
-
pendingPosition.value = {
|
|
293
|
+
pendingPosition.value = {
|
|
294
|
+
x: rect.x + rect.width / 2,
|
|
295
|
+
y: rect.y + rect.height / 2
|
|
296
|
+
};
|
|
210
297
|
pendingElementName.value = "Area selection";
|
|
211
298
|
pendingComponentChain.value = void 0;
|
|
212
299
|
pendingComputedStyles.value = void 0;
|
|
@@ -228,7 +315,9 @@ function onOverlayMouseUp(e) {
|
|
|
228
315
|
};
|
|
229
316
|
pendingElementName.value = `"${textResult.selectedText.slice(0, 30)}"`;
|
|
230
317
|
pendingComponentChain.value = getVueComponents(textResult.anchorElement);
|
|
231
|
-
pendingComputedStyles.value = getRelevantComputedStyles(
|
|
318
|
+
pendingComputedStyles.value = getRelevantComputedStyles(
|
|
319
|
+
textResult.anchorElement
|
|
320
|
+
);
|
|
232
321
|
pendingTarget.value = textResult.anchorElement;
|
|
233
322
|
pendingTextSelection.value = {
|
|
234
323
|
text: textResult.selectedText,
|
|
@@ -244,7 +333,7 @@ function onOverlayMouseUp(e) {
|
|
|
244
333
|
return;
|
|
245
334
|
}
|
|
246
335
|
const el = getElementUnderOverlay(e);
|
|
247
|
-
if (!el || el
|
|
336
|
+
if (!el || isInsideAgentationTree(el))
|
|
248
337
|
return;
|
|
249
338
|
pendingPosition.value = { x: e.clientX, y: e.clientY };
|
|
250
339
|
pendingElementName.value = getElementName(el);
|
|
@@ -281,7 +370,10 @@ function shouldUseDocumentFallbackEvents() {
|
|
|
281
370
|
return mode.value === "inspect" && !effectiveBlockPageInteractions.value && !isInteractionLocked();
|
|
282
371
|
}
|
|
283
372
|
function lockInteractionsTemporarily(durationMs) {
|
|
284
|
-
suppressInteractionsUntil = Math.max(
|
|
373
|
+
suppressInteractionsUntil = Math.max(
|
|
374
|
+
suppressInteractionsUntil,
|
|
375
|
+
Date.now() + durationMs
|
|
376
|
+
);
|
|
285
377
|
}
|
|
286
378
|
function isInteractionLocked() {
|
|
287
379
|
return settingsOpen.value || toolbarDragging.value || Date.now() < suppressInteractionsUntil;
|
|
@@ -296,6 +388,7 @@ function closeSettings(lockInteractions = true) {
|
|
|
296
388
|
function syncUrlScope(nextUrl) {
|
|
297
389
|
if (!nextUrl || currentUrl.value === nextUrl)
|
|
298
390
|
return;
|
|
391
|
+
dismissUndo();
|
|
299
392
|
currentUrl.value = nextUrl;
|
|
300
393
|
setScopeUrl(nextUrl);
|
|
301
394
|
}
|
|
@@ -335,7 +428,7 @@ function onDocumentClick(e) {
|
|
|
335
428
|
if (mode.value === "idle")
|
|
336
429
|
return;
|
|
337
430
|
const target = e.target;
|
|
338
|
-
if (!target || target
|
|
431
|
+
if (!target || isInsideAgentationTree(target, e))
|
|
339
432
|
return;
|
|
340
433
|
e.preventDefault();
|
|
341
434
|
e.stopPropagation();
|
|
@@ -394,7 +487,9 @@ function onInputAdd(comment) {
|
|
|
394
487
|
comment,
|
|
395
488
|
url,
|
|
396
489
|
element: "multi",
|
|
397
|
-
elementPath: `region at (${Math.round(boundingBox.x)}, ${Math.round(
|
|
490
|
+
elementPath: `region at (${Math.round(boundingBox.x)}, ${Math.round(
|
|
491
|
+
boundingBox.y
|
|
492
|
+
)})`,
|
|
398
493
|
isMultiSelect: true,
|
|
399
494
|
elements,
|
|
400
495
|
boundingBox,
|
|
@@ -470,7 +565,12 @@ function onInputAdd(comment) {
|
|
|
470
565
|
vueComponents: getVueComponents(el),
|
|
471
566
|
nearbyElements: getNearbyElements(el),
|
|
472
567
|
nearbyText: getNearbyText(el),
|
|
473
|
-
boundingBox: {
|
|
568
|
+
boundingBox: {
|
|
569
|
+
x: rect.x,
|
|
570
|
+
y: rect.y,
|
|
571
|
+
width: rect.width,
|
|
572
|
+
height: rect.height
|
|
573
|
+
},
|
|
474
574
|
cssClasses: detail === "forensic" ? Array.from(el.classList).join(" ") : void 0,
|
|
475
575
|
fullPath: detail === "forensic" ? getElementPath(el) : void 0,
|
|
476
576
|
computedStyles: detail === "forensic" ? getComputedStylesSummary(el) : void 0,
|
|
@@ -488,7 +588,11 @@ function onInputCancel() {
|
|
|
488
588
|
transition("inspect");
|
|
489
589
|
}
|
|
490
590
|
async function onCopy() {
|
|
491
|
-
const markdown = formatAnnotations(
|
|
591
|
+
const markdown = formatAnnotations(
|
|
592
|
+
annotations.value,
|
|
593
|
+
settings.outputDetail,
|
|
594
|
+
resolvedUrl.value
|
|
595
|
+
);
|
|
492
596
|
if (props.copyToClipboard !== false) {
|
|
493
597
|
const success = await copyToClipboard(markdown);
|
|
494
598
|
if (!success)
|
|
@@ -500,13 +604,37 @@ async function onCopy() {
|
|
|
500
604
|
}
|
|
501
605
|
emit("copy", markdown);
|
|
502
606
|
if (settings.clearAfterCopy) {
|
|
503
|
-
|
|
504
|
-
|
|
607
|
+
onClear();
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
function dismissUndo() {
|
|
611
|
+
undoFeedback.value = false;
|
|
612
|
+
undoSnapshot.value = [];
|
|
613
|
+
if (undoTimer) {
|
|
614
|
+
clearTimeout(undoTimer);
|
|
615
|
+
undoTimer = null;
|
|
505
616
|
}
|
|
506
617
|
}
|
|
618
|
+
function startUndoTimer() {
|
|
619
|
+
if (undoTimer)
|
|
620
|
+
clearTimeout(undoTimer);
|
|
621
|
+
undoTimer = setTimeout(() => dismissUndo(), UNDO_TIMEOUT_MS);
|
|
622
|
+
}
|
|
623
|
+
function onUndo() {
|
|
624
|
+
const snapshot = undoSnapshot.value;
|
|
625
|
+
dismissUndo();
|
|
626
|
+
if (snapshot.length > 0)
|
|
627
|
+
restoreAnnotations(snapshot);
|
|
628
|
+
}
|
|
507
629
|
function onClear() {
|
|
630
|
+
dismissUndo();
|
|
508
631
|
const cleared = clearAnnotations();
|
|
632
|
+
if (cleared.length === 0)
|
|
633
|
+
return;
|
|
509
634
|
emit("annotations-clear", cleared);
|
|
635
|
+
undoSnapshot.value = cleared;
|
|
636
|
+
undoFeedback.value = true;
|
|
637
|
+
startUndoTimer();
|
|
510
638
|
}
|
|
511
639
|
function onMarkerClick(ann) {
|
|
512
640
|
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
|
@@ -514,12 +642,16 @@ function onMarkerClick(ann) {
|
|
|
514
642
|
const markerY = ann.isFixed ? ann.y : ann.y - scrollTop;
|
|
515
643
|
editingAnnotation.value = ann;
|
|
516
644
|
pendingPosition.value = { x: markerX, y: markerY };
|
|
517
|
-
pendingElementName.value = getElementName(
|
|
645
|
+
pendingElementName.value = getElementName(
|
|
646
|
+
ann._targetRef?.deref() || document.createElement(ann.element)
|
|
647
|
+
);
|
|
518
648
|
pendingComponentChain.value = ann.vueComponents;
|
|
519
|
-
pendingComputedStyles.value = ann.computedStyles ? Object.fromEntries(
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
649
|
+
pendingComputedStyles.value = ann.computedStyles ? Object.fromEntries(
|
|
650
|
+
ann.computedStyles.split("\n").filter(Boolean).map((line) => {
|
|
651
|
+
const idx = line.indexOf(":");
|
|
652
|
+
return idx > -1 ? [line.slice(0, idx).trim(), line.slice(idx + 1).trim()] : [line, ""];
|
|
653
|
+
})
|
|
654
|
+
) : ann._targetRef?.deref() ? getRelevantComputedStyles(ann._targetRef.deref()) : void 0;
|
|
523
655
|
pendingTextSelection.value = null;
|
|
524
656
|
pendingTarget.value = ann._targetRef?.deref() || null;
|
|
525
657
|
transition("input-open");
|
|
@@ -595,7 +727,10 @@ onMounted(() => {
|
|
|
595
727
|
document.addEventListener("mousemove", onDocumentMouseMove, true);
|
|
596
728
|
document.addEventListener("mousedown", onDocumentMouseDown, true);
|
|
597
729
|
document.addEventListener("mouseup", onDocumentMouseUp, true);
|
|
598
|
-
document.addEventListener("wheel", onDocumentWheel, {
|
|
730
|
+
document.addEventListener("wheel", onDocumentWheel, {
|
|
731
|
+
passive: true,
|
|
732
|
+
capture: true
|
|
733
|
+
});
|
|
599
734
|
document.addEventListener("click", onDocumentClick, true);
|
|
600
735
|
});
|
|
601
736
|
onBeforeUnmount(() => {
|
|
@@ -612,14 +747,21 @@ onBeforeUnmount(() => {
|
|
|
612
747
|
|
|
613
748
|
<template>
|
|
614
749
|
<component :is="portalWrapper" v-bind="portalProps">
|
|
615
|
-
<div
|
|
750
|
+
<div
|
|
751
|
+
ref="rootEl"
|
|
752
|
+
data-agentation-vue
|
|
753
|
+
:data-va-theme="settings.theme !== 'auto' ? settings.theme : void 0"
|
|
754
|
+
:style="rootStyle"
|
|
755
|
+
>
|
|
616
756
|
<!-- Intercept overlay -->
|
|
617
757
|
<div
|
|
618
758
|
v-if="mode !== 'idle'"
|
|
619
759
|
ref="overlayEl"
|
|
620
760
|
class="__va-intercept"
|
|
621
761
|
:class="{ '__va-intercept--input-open': mode === 'input-open' }"
|
|
622
|
-
:style="
|
|
762
|
+
:style="
|
|
763
|
+
mode === 'inspect' && !effectiveBlockPageInteractions ? { pointerEvents: 'none' } : void 0
|
|
764
|
+
"
|
|
623
765
|
@mousemove="onOverlayMouseMove"
|
|
624
766
|
@mousedown="onOverlayMouseDown"
|
|
625
767
|
@mouseup="onOverlayMouseUp"
|
|
@@ -678,6 +820,7 @@ onBeforeUnmount(() => {
|
|
|
678
820
|
:computed-styles="pendingComputedStyles"
|
|
679
821
|
:initial-comment="editingAnnotation?.comment"
|
|
680
822
|
:is-editing="!!editingAnnotation"
|
|
823
|
+
:mention-candidates="mentionCandidates"
|
|
681
824
|
@add="onInputAdd"
|
|
682
825
|
@cancel="onInputCancel"
|
|
683
826
|
@delete="onInputDelete"
|
|
@@ -697,6 +840,18 @@ onBeforeUnmount(() => {
|
|
|
697
840
|
Copied!
|
|
698
841
|
</div>
|
|
699
842
|
|
|
843
|
+
<!-- Undo clear feedback -->
|
|
844
|
+
<div
|
|
845
|
+
v-if="undoFeedback"
|
|
846
|
+
class="__va-undo-feedback"
|
|
847
|
+
:class="{ '__va-undo-feedback--shifted': copyFeedback }"
|
|
848
|
+
>
|
|
849
|
+
<span>Annotations cleared</span>
|
|
850
|
+
<button type="button" class="__va-undo-btn" @click="onUndo">
|
|
851
|
+
Undo
|
|
852
|
+
</button>
|
|
853
|
+
</div>
|
|
854
|
+
|
|
700
855
|
<!-- Toolbar -->
|
|
701
856
|
<AgentationToolbar
|
|
702
857
|
ref="toolbarRef"
|
|
@@ -8,6 +8,7 @@ type __VLS_Props = {
|
|
|
8
8
|
pageUrl?: string;
|
|
9
9
|
theme?: 'light' | 'dark' | 'auto';
|
|
10
10
|
activationKey?: 'none' | 'Meta' | 'Alt' | 'Shift';
|
|
11
|
+
disablePortal?: boolean;
|
|
11
12
|
};
|
|
12
13
|
declare const _default: import("vue-demi").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue-demi").ComponentOptionsMixin, import("vue-demi").ComponentOptionsMixin, {
|
|
13
14
|
copy: (markdown: string) => any;
|
|
@@ -77,8 +77,7 @@ function onDeactivate() {
|
|
|
77
77
|
emit("deactivate");
|
|
78
78
|
}
|
|
79
79
|
function onClear() {
|
|
80
|
-
|
|
81
|
-
emit("clear");
|
|
80
|
+
emit("clear");
|
|
82
81
|
}
|
|
83
82
|
function onOpenSettings(e) {
|
|
84
83
|
const anchorEl = e.currentTarget instanceof HTMLElement ? e.currentTarget : null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { MentionCandidate } from '../utils/mention';
|
|
1
2
|
type __VLS_Props = {
|
|
2
3
|
position: {
|
|
3
4
|
x: number;
|
|
@@ -8,6 +9,7 @@ type __VLS_Props = {
|
|
|
8
9
|
computedStyles?: Record<string, string>;
|
|
9
10
|
initialComment?: string;
|
|
10
11
|
isEditing?: boolean;
|
|
12
|
+
mentionCandidates?: MentionCandidate[];
|
|
11
13
|
};
|
|
12
14
|
declare const _default: import("vue-demi").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue-demi").ComponentOptionsMixin, import("vue-demi").ComponentOptionsMixin, {
|
|
13
15
|
cancel: () => any;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed, onMounted, ref } from "vue-demi";
|
|
2
|
+
import { computed, onMounted, ref, toRef } from "vue-demi";
|
|
3
|
+
import { useMentionDropdown } from "../composables/useMentionDropdown.mjs";
|
|
4
|
+
import { hydrateMentions, serializeMentions } from "../utils/mention.mjs";
|
|
3
5
|
import ComponentChain from "./ComponentChain.vue";
|
|
6
|
+
import MentionDropdown from "./MentionDropdown.vue";
|
|
4
7
|
import VaButton from "./VaButton.vue";
|
|
5
8
|
import VaIcon from "./VaIcon.vue";
|
|
6
9
|
const props = defineProps({
|
|
@@ -9,12 +12,16 @@ const props = defineProps({
|
|
|
9
12
|
componentChain: { type: String, required: false },
|
|
10
13
|
computedStyles: { type: Object, required: false },
|
|
11
14
|
initialComment: { type: String, required: false },
|
|
12
|
-
isEditing: { type: Boolean, required: false }
|
|
15
|
+
isEditing: { type: Boolean, required: false },
|
|
16
|
+
mentionCandidates: { type: Array, required: false }
|
|
13
17
|
});
|
|
14
18
|
const emit = defineEmits(["add", "cancel", "delete"]);
|
|
15
|
-
const comment = ref(props.initialComment || "");
|
|
16
19
|
const inputEl = ref(null);
|
|
20
|
+
const commentText = ref(props.initialComment || "");
|
|
17
21
|
const computedStyleEntries = computed(() => Object.entries(props.computedStyles || {}));
|
|
22
|
+
const candidates = toRef(props, "mentionCandidates");
|
|
23
|
+
const safeCandidates = computed(() => candidates.value || []);
|
|
24
|
+
const mention = useMentionDropdown(inputEl, safeCandidates);
|
|
18
25
|
const inputStyle = computed(() => {
|
|
19
26
|
const x = Math.min(props.position.x, window.innerWidth - 380);
|
|
20
27
|
const y = Math.min(props.position.y + 20, window.innerHeight - 150);
|
|
@@ -23,14 +30,71 @@ const inputStyle = computed(() => {
|
|
|
23
30
|
top: `${Math.max(10, y)}px`
|
|
24
31
|
};
|
|
25
32
|
});
|
|
33
|
+
function autoResize() {
|
|
34
|
+
const el = inputEl.value;
|
|
35
|
+
if (!el)
|
|
36
|
+
return;
|
|
37
|
+
el.style.height = "auto";
|
|
38
|
+
el.style.height = `${el.scrollHeight}px`;
|
|
39
|
+
}
|
|
40
|
+
function getComment() {
|
|
41
|
+
const el = inputEl.value;
|
|
42
|
+
if (!el)
|
|
43
|
+
return "";
|
|
44
|
+
return serializeMentions(el);
|
|
45
|
+
}
|
|
46
|
+
function onInput() {
|
|
47
|
+
commentText.value = getComment();
|
|
48
|
+
autoResize();
|
|
49
|
+
mention.checkForTrigger();
|
|
50
|
+
}
|
|
51
|
+
function onKeyDown(e) {
|
|
52
|
+
if (mention.onKeyDown(e))
|
|
53
|
+
return;
|
|
54
|
+
if (e.key === "Enter" && !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
onAdd();
|
|
57
|
+
} else if (e.key === "Escape") {
|
|
58
|
+
if (mention.isOpen.value) {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
mention.close();
|
|
61
|
+
} else {
|
|
62
|
+
emit("cancel");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
26
66
|
function onAdd() {
|
|
27
|
-
const text =
|
|
67
|
+
const text = getComment().trim();
|
|
28
68
|
if (!text)
|
|
29
69
|
return;
|
|
30
70
|
emit("add", text);
|
|
31
71
|
}
|
|
72
|
+
function onPaste(e) {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
const text = e.clipboardData?.getData("text/plain") || "";
|
|
75
|
+
document.execCommand("insertText", false, text);
|
|
76
|
+
}
|
|
77
|
+
function onSelectCandidate(candidate) {
|
|
78
|
+
mention.selectCandidate(candidate);
|
|
79
|
+
}
|
|
32
80
|
onMounted(() => {
|
|
33
|
-
inputEl.value
|
|
81
|
+
const el = inputEl.value;
|
|
82
|
+
if (!el)
|
|
83
|
+
return;
|
|
84
|
+
if (props.initialComment) {
|
|
85
|
+
const html = hydrateMentions(props.initialComment, safeCandidates.value);
|
|
86
|
+
el.innerHTML = html;
|
|
87
|
+
}
|
|
88
|
+
el.focus();
|
|
89
|
+
const sel = window.getSelection();
|
|
90
|
+
if (sel && el.childNodes.length > 0) {
|
|
91
|
+
const range = document.createRange();
|
|
92
|
+
range.selectNodeContents(el);
|
|
93
|
+
range.collapse(false);
|
|
94
|
+
sel.removeAllRanges();
|
|
95
|
+
sel.addRange(range);
|
|
96
|
+
}
|
|
97
|
+
autoResize();
|
|
34
98
|
});
|
|
35
99
|
</script>
|
|
36
100
|
|
|
@@ -66,13 +130,24 @@ onMounted(() => {
|
|
|
66
130
|
<ComponentChain :chain="componentChain" variant="light" truncate="leaf" />
|
|
67
131
|
</div>
|
|
68
132
|
<span v-else class="__va-input-label">{{ elementName || "Annotation" }}</span>
|
|
69
|
-
<
|
|
133
|
+
<div
|
|
70
134
|
ref="inputEl"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
135
|
+
class="__va-input-editable"
|
|
136
|
+
contenteditable="true"
|
|
137
|
+
role="textbox"
|
|
138
|
+
aria-multiline="true"
|
|
139
|
+
data-placeholder="Add a comment..."
|
|
140
|
+
@input="onInput"
|
|
141
|
+
@keydown="onKeyDown"
|
|
142
|
+
@paste="onPaste"
|
|
143
|
+
/>
|
|
144
|
+
<MentionDropdown
|
|
145
|
+
:open="mention.isOpen.value"
|
|
146
|
+
:candidates="mention.filteredCandidates.value"
|
|
147
|
+
:active-index="mention.activeIndex.value"
|
|
148
|
+
:position="mention.dropdownPosition.value"
|
|
149
|
+
@select="onSelectCandidate"
|
|
150
|
+
/>
|
|
76
151
|
<div class="__va-input-actions">
|
|
77
152
|
<button
|
|
78
153
|
v-if="isEditing"
|
|
@@ -86,7 +161,7 @@ onMounted(() => {
|
|
|
86
161
|
<VaButton variant="secondary" @click="$emit('cancel')">
|
|
87
162
|
Cancel
|
|
88
163
|
</VaButton>
|
|
89
|
-
<VaButton :disabled="!
|
|
164
|
+
<VaButton :disabled="!commentText.trim()" @click="onAdd">
|
|
90
165
|
{{ isEditing ? "Save" : "Add" }}
|
|
91
166
|
</VaButton>
|
|
92
167
|
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { MentionCandidate } from '../utils/mention';
|
|
1
2
|
type __VLS_Props = {
|
|
2
3
|
position: {
|
|
3
4
|
x: number;
|
|
@@ -8,6 +9,7 @@ type __VLS_Props = {
|
|
|
8
9
|
computedStyles?: Record<string, string>;
|
|
9
10
|
initialComment?: string;
|
|
10
11
|
isEditing?: boolean;
|
|
12
|
+
mentionCandidates?: MentionCandidate[];
|
|
11
13
|
};
|
|
12
14
|
declare const _default: import("vue-demi").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue-demi").ComponentOptionsMixin, import("vue-demi").ComponentOptionsMixin, {
|
|
13
15
|
cancel: () => any;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MentionCandidate } from '../utils/mention';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
candidates: MentionCandidate[];
|
|
5
|
+
activeIndex: number;
|
|
6
|
+
position: {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
declare const _default: import("vue-demi").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue-demi").ComponentOptionsMixin, import("vue-demi").ComponentOptionsMixin, {
|
|
12
|
+
select: (candidate: MentionCandidate) => any;
|
|
13
|
+
}, string, import("vue-demi").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
14
|
+
onSelect?: ((candidate: MentionCandidate) => any) | undefined;
|
|
15
|
+
}>, {}, {}, {}, {}, string, import("vue-demi").ComponentProvideOptions, false, {}, any>;
|
|
16
|
+
export default _default;
|