agentation-vue 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +27 -0
- package/dist/AgentationVue.d.vue.ts +27 -0
- package/dist/AgentationVue.vue +839 -0
- package/dist/AgentationVue.vue.d.ts +27 -0
- package/dist/components/AgentationToolbar.d.vue.ts +36 -0
- package/dist/components/AgentationToolbar.vue +233 -0
- package/dist/components/AgentationToolbar.vue.d.ts +36 -0
- package/dist/components/AnnotationInput.d.vue.ts +21 -0
- package/dist/components/AnnotationInput.vue +105 -0
- package/dist/components/AnnotationInput.vue.d.ts +21 -0
- package/dist/components/AnnotationMarker.d.vue.ts +15 -0
- package/dist/components/AnnotationMarker.vue +46 -0
- package/dist/components/AnnotationMarker.vue.d.ts +15 -0
- package/dist/components/ComponentChain.d.vue.ts +10 -0
- package/dist/components/ComponentChain.vue +91 -0
- package/dist/components/ComponentChain.vue.d.ts +10 -0
- package/dist/components/ElementHighlight.d.vue.ts +9 -0
- package/dist/components/ElementHighlight.vue +33 -0
- package/dist/components/ElementHighlight.vue.d.ts +9 -0
- package/dist/components/SettingsPanel.d.vue.ts +10 -0
- package/dist/components/SettingsPanel.vue +143 -0
- package/dist/components/SettingsPanel.vue.d.ts +10 -0
- package/dist/components/SettingsPopover.d.vue.ts +14 -0
- package/dist/components/SettingsPopover.vue +235 -0
- package/dist/components/SettingsPopover.vue.d.ts +14 -0
- package/dist/components/VaButton.d.vue.ts +23 -0
- package/dist/components/VaButton.vue +19 -0
- package/dist/components/VaButton.vue.d.ts +23 -0
- package/dist/components/VaIcon.d.vue.ts +6 -0
- package/dist/components/VaIcon.vue +18 -0
- package/dist/components/VaIcon.vue.d.ts +6 -0
- package/dist/components/VaIconButton.d.vue.ts +25 -0
- package/dist/components/VaIconButton.vue +43 -0
- package/dist/components/VaIconButton.vue.d.ts +25 -0
- package/dist/components/VaToggle.d.vue.ts +10 -0
- package/dist/components/VaToggle.vue +23 -0
- package/dist/components/VaToggle.vue.d.ts +10 -0
- package/dist/composables/useAnimationPause.d.ts +7 -0
- package/dist/composables/useAnimationPause.js +52 -0
- package/dist/composables/useAnimationPause.mjs +43 -0
- package/dist/composables/useAnnotations.d.ts +105 -0
- package/dist/composables/useAnnotations.js +106 -0
- package/dist/composables/useAnnotations.mjs +108 -0
- package/dist/composables/useAreaSelect.d.ts +21 -0
- package/dist/composables/useAreaSelect.js +62 -0
- package/dist/composables/useAreaSelect.mjs +41 -0
- package/dist/composables/useElementDetection.d.ts +22 -0
- package/dist/composables/useElementDetection.js +85 -0
- package/dist/composables/useElementDetection.mjs +82 -0
- package/dist/composables/useInteractionMode.d.ts +5 -0
- package/dist/composables/useInteractionMode.js +29 -0
- package/dist/composables/useInteractionMode.mjs +20 -0
- package/dist/composables/useKeyboardShortcuts.d.ts +43 -0
- package/dist/composables/useKeyboardShortcuts.js +202 -0
- package/dist/composables/useKeyboardShortcuts.mjs +223 -0
- package/dist/composables/useMarkerPositions.d.ts +5 -0
- package/dist/composables/useMarkerPositions.js +45 -0
- package/dist/composables/useMarkerPositions.mjs +36 -0
- package/dist/composables/useMultiSelect.d.ts +20 -0
- package/dist/composables/useMultiSelect.js +108 -0
- package/dist/composables/useMultiSelect.mjs +85 -0
- package/dist/composables/useOutputFormatter.d.ts +5 -0
- package/dist/composables/useOutputFormatter.js +100 -0
- package/dist/composables/useOutputFormatter.mjs +91 -0
- package/dist/composables/useSettings.d.ts +14 -0
- package/dist/composables/useSettings.js +48 -0
- package/dist/composables/useSettings.mjs +38 -0
- package/dist/composables/useTextSelection.d.ts +11 -0
- package/dist/composables/useTextSelection.js +33 -0
- package/dist/composables/useTextSelection.mjs +22 -0
- package/dist/composables/useToolbarAutoHide.d.ts +19 -0
- package/dist/composables/useToolbarAutoHide.js +270 -0
- package/dist/composables/useToolbarAutoHide.mjs +208 -0
- package/dist/composables/useToolbarDragSnap.d.ts +30 -0
- package/dist/composables/useToolbarDragSnap.js +296 -0
- package/dist/composables/useToolbarDragSnap.mjs +245 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +8 -0
- package/dist/constants.mjs +2 -0
- package/dist/directives/vaTooltip.d.ts +22 -0
- package/dist/directives/vaTooltip.js +241 -0
- package/dist/directives/vaTooltip.mjs +257 -0
- package/dist/icons.d.ts +16 -0
- package/dist/icons.js +21 -0
- package/dist/icons.mjs +15 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +168 -0
- package/dist/index.mjs +30 -0
- package/dist/styles/agentation.css +1 -0
- package/dist/types.d.ts +70 -0
- package/dist/types.js +1 -0
- package/dist/types.mjs +0 -0
- package/dist/utils/clipboard.d.ts +1 -0
- package/dist/utils/clipboard.js +22 -0
- package/dist/utils/clipboard.mjs +16 -0
- package/dist/utils/dom-inspector.d.ts +7 -0
- package/dist/utils/dom-inspector.js +168 -0
- package/dist/utils/dom-inspector.mjs +242 -0
- package/dist/utils/math.d.ts +1 -0
- package/dist/utils/math.js +9 -0
- package/dist/utils/math.mjs +3 -0
- package/dist/utils/portal.d.ts +2 -0
- package/dist/utils/portal.js +18 -0
- package/dist/utils/portal.mjs +11 -0
- package/dist/utils/selectors.d.ts +3 -0
- package/dist/utils/selectors.js +103 -0
- package/dist/utils/selectors.mjs +105 -0
- package/dist/utils/style.d.ts +2 -0
- package/dist/utils/style.js +14 -0
- package/dist/utils/style.mjs +8 -0
- package/package.json +49 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useElementDetection = useElementDetection;
|
|
7
|
+
var _vueDemi = require("vue-demi");
|
|
8
|
+
var _constants = require("../constants");
|
|
9
|
+
var _domInspector = require("../utils/dom-inspector");
|
|
10
|
+
var _selectors = require("../utils/selectors");
|
|
11
|
+
function useElementDetection(overlayRef, showComponentTree) {
|
|
12
|
+
const hoveredElement = (0, _vueDemi.ref)(null);
|
|
13
|
+
const hoveredRect = (0, _vueDemi.ref)(null);
|
|
14
|
+
const hoveredName = (0, _vueDemi.ref)("");
|
|
15
|
+
const hoveredComponentChain = (0, _vueDemi.ref)();
|
|
16
|
+
let lastElement = null;
|
|
17
|
+
let rafId = null;
|
|
18
|
+
function getElementUnderOverlay(e) {
|
|
19
|
+
const overlay = overlayRef.value;
|
|
20
|
+
if (!overlay) return document.elementFromPoint(e.clientX, e.clientY);
|
|
21
|
+
const previousPointerEvents = overlay.style.pointerEvents;
|
|
22
|
+
overlay.style.pointerEvents = "none";
|
|
23
|
+
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
24
|
+
overlay.style.pointerEvents = previousPointerEvents;
|
|
25
|
+
return el;
|
|
26
|
+
}
|
|
27
|
+
function clearHighlight() {
|
|
28
|
+
hoveredElement.value = null;
|
|
29
|
+
hoveredRect.value = null;
|
|
30
|
+
hoveredName.value = "";
|
|
31
|
+
hoveredComponentChain.value = void 0;
|
|
32
|
+
lastElement = null;
|
|
33
|
+
}
|
|
34
|
+
function updateHighlight(el) {
|
|
35
|
+
const rect = el.getBoundingClientRect();
|
|
36
|
+
hoveredElement.value = el;
|
|
37
|
+
hoveredRect.value = {
|
|
38
|
+
x: rect.left,
|
|
39
|
+
y: rect.top,
|
|
40
|
+
width: rect.width,
|
|
41
|
+
height: rect.height
|
|
42
|
+
};
|
|
43
|
+
hoveredName.value = (0, _selectors.getElementName)(el);
|
|
44
|
+
if (showComponentTree?.()) {
|
|
45
|
+
hoveredComponentChain.value = (0, _domInspector.detectVueComponents)(el);
|
|
46
|
+
} else {
|
|
47
|
+
hoveredComponentChain.value = void 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function onMouseMove(e) {
|
|
51
|
+
if (rafId !== null) return;
|
|
52
|
+
rafId = requestAnimationFrame(() => {
|
|
53
|
+
rafId = null;
|
|
54
|
+
const el = getElementUnderOverlay(e);
|
|
55
|
+
if (el === lastElement) return;
|
|
56
|
+
if (el?.closest(_constants.VA_DATA_ATTR_SELECTOR)) {
|
|
57
|
+
clearHighlight();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
lastElement = el;
|
|
61
|
+
if (el) {
|
|
62
|
+
updateHighlight(el);
|
|
63
|
+
} else {
|
|
64
|
+
clearHighlight();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function cleanup() {
|
|
69
|
+
if (rafId !== null) {
|
|
70
|
+
cancelAnimationFrame(rafId);
|
|
71
|
+
rafId = null;
|
|
72
|
+
}
|
|
73
|
+
clearHighlight();
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
hoveredElement,
|
|
77
|
+
hoveredRect,
|
|
78
|
+
hoveredName,
|
|
79
|
+
hoveredComponentChain,
|
|
80
|
+
onMouseMove,
|
|
81
|
+
clearHighlight,
|
|
82
|
+
getElementUnderOverlay,
|
|
83
|
+
cleanup
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { ref } from "vue-demi";
|
|
2
|
+
import { VA_DATA_ATTR_SELECTOR } from "../constants.mjs";
|
|
3
|
+
import { detectVueComponents } from "../utils/dom-inspector.mjs";
|
|
4
|
+
import { getElementName } from "../utils/selectors.mjs";
|
|
5
|
+
export function useElementDetection(overlayRef, showComponentTree) {
|
|
6
|
+
const hoveredElement = ref(null);
|
|
7
|
+
const hoveredRect = ref(null);
|
|
8
|
+
const hoveredName = ref("");
|
|
9
|
+
const hoveredComponentChain = ref();
|
|
10
|
+
let lastElement = null;
|
|
11
|
+
let rafId = null;
|
|
12
|
+
function getElementUnderOverlay(e) {
|
|
13
|
+
const overlay = overlayRef.value;
|
|
14
|
+
if (!overlay)
|
|
15
|
+
return document.elementFromPoint(e.clientX, e.clientY);
|
|
16
|
+
const previousPointerEvents = overlay.style.pointerEvents;
|
|
17
|
+
overlay.style.pointerEvents = "none";
|
|
18
|
+
const el = document.elementFromPoint(e.clientX, e.clientY);
|
|
19
|
+
overlay.style.pointerEvents = previousPointerEvents;
|
|
20
|
+
return el;
|
|
21
|
+
}
|
|
22
|
+
function clearHighlight() {
|
|
23
|
+
hoveredElement.value = null;
|
|
24
|
+
hoveredRect.value = null;
|
|
25
|
+
hoveredName.value = "";
|
|
26
|
+
hoveredComponentChain.value = void 0;
|
|
27
|
+
lastElement = null;
|
|
28
|
+
}
|
|
29
|
+
function updateHighlight(el) {
|
|
30
|
+
const rect = el.getBoundingClientRect();
|
|
31
|
+
hoveredElement.value = el;
|
|
32
|
+
hoveredRect.value = {
|
|
33
|
+
x: rect.left,
|
|
34
|
+
y: rect.top,
|
|
35
|
+
width: rect.width,
|
|
36
|
+
height: rect.height
|
|
37
|
+
};
|
|
38
|
+
hoveredName.value = getElementName(el);
|
|
39
|
+
if (showComponentTree?.()) {
|
|
40
|
+
hoveredComponentChain.value = detectVueComponents(el);
|
|
41
|
+
} else {
|
|
42
|
+
hoveredComponentChain.value = void 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function onMouseMove(e) {
|
|
46
|
+
if (rafId !== null)
|
|
47
|
+
return;
|
|
48
|
+
rafId = requestAnimationFrame(() => {
|
|
49
|
+
rafId = null;
|
|
50
|
+
const el = getElementUnderOverlay(e);
|
|
51
|
+
if (el === lastElement)
|
|
52
|
+
return;
|
|
53
|
+
if (el?.closest(VA_DATA_ATTR_SELECTOR)) {
|
|
54
|
+
clearHighlight();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
lastElement = el;
|
|
58
|
+
if (el) {
|
|
59
|
+
updateHighlight(el);
|
|
60
|
+
} else {
|
|
61
|
+
clearHighlight();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function cleanup() {
|
|
66
|
+
if (rafId !== null) {
|
|
67
|
+
cancelAnimationFrame(rafId);
|
|
68
|
+
rafId = null;
|
|
69
|
+
}
|
|
70
|
+
clearHighlight();
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
hoveredElement,
|
|
74
|
+
hoveredRect,
|
|
75
|
+
hoveredName,
|
|
76
|
+
hoveredComponentChain,
|
|
77
|
+
onMouseMove,
|
|
78
|
+
clearHighlight,
|
|
79
|
+
getElementUnderOverlay,
|
|
80
|
+
cleanup
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useInteractionMode = useInteractionMode;
|
|
7
|
+
var _vueDemi = require("vue-demi");
|
|
8
|
+
function useInteractionMode() {
|
|
9
|
+
const mode = (0, _vueDemi.ref)("idle");
|
|
10
|
+
const allowedTransitions = {
|
|
11
|
+
"idle": ["inspect"],
|
|
12
|
+
"inspect": ["idle", "input-open", "multi-selecting", "area-selecting"],
|
|
13
|
+
"multi-selecting": ["input-open", "inspect"],
|
|
14
|
+
"area-selecting": ["input-open", "inspect"],
|
|
15
|
+
"input-open": ["inspect", "idle"]
|
|
16
|
+
};
|
|
17
|
+
function transition(to) {
|
|
18
|
+
if (allowedTransitions[mode.value]?.includes(to)) {
|
|
19
|
+
mode.value = to;
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
console.warn(`[agentation-vue] Invalid transition: ${mode.value} \u2192 ${to}`);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
mode,
|
|
27
|
+
transition
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ref } from "vue-demi";
|
|
2
|
+
export function useInteractionMode() {
|
|
3
|
+
const mode = ref("idle");
|
|
4
|
+
const allowedTransitions = {
|
|
5
|
+
"idle": ["inspect"],
|
|
6
|
+
"inspect": ["idle", "input-open", "multi-selecting", "area-selecting"],
|
|
7
|
+
"multi-selecting": ["input-open", "inspect"],
|
|
8
|
+
"area-selecting": ["input-open", "inspect"],
|
|
9
|
+
"input-open": ["inspect", "idle"]
|
|
10
|
+
};
|
|
11
|
+
function transition(to) {
|
|
12
|
+
if (allowedTransitions[mode.value]?.includes(to)) {
|
|
13
|
+
mode.value = to;
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
console.warn(`[agentation-vue] Invalid transition: ${mode.value} \u2192 ${to}`);
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return { mode, transition };
|
|
20
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ComputedRef, Ref } from 'vue-demi';
|
|
2
|
+
import type { InteractionMode } from '../types';
|
|
3
|
+
export type ShortcutAction = 'activate' | 'element-select' | 'area-select' | 'pause-animations' | 'copy' | 'clear' | 'settings' | 'minimize';
|
|
4
|
+
export interface DoubleTapConfig {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
key: string;
|
|
7
|
+
thresholdMs: number;
|
|
8
|
+
}
|
|
9
|
+
export interface KeyboardShortcutConfig {
|
|
10
|
+
enabledWhenClosed: boolean;
|
|
11
|
+
priorityWhenOpen: boolean;
|
|
12
|
+
doubleTap: DoubleTapConfig;
|
|
13
|
+
keymap: Partial<Record<ShortcutAction, string>>;
|
|
14
|
+
conflictPolicy: 'ignore-editables' | 'always-capture';
|
|
15
|
+
}
|
|
16
|
+
export interface ShortcutActionHandlers {
|
|
17
|
+
activate: () => void;
|
|
18
|
+
deactivate: () => void;
|
|
19
|
+
elementSelect: () => void;
|
|
20
|
+
areaSelect: () => void;
|
|
21
|
+
pauseAnimations: () => void;
|
|
22
|
+
copy: () => void;
|
|
23
|
+
clear: () => void;
|
|
24
|
+
openSettings: () => void;
|
|
25
|
+
inputCancel: () => void;
|
|
26
|
+
closeSettings: () => void;
|
|
27
|
+
}
|
|
28
|
+
export interface UseKeyboardShortcutsOptions {
|
|
29
|
+
mode: Ref<InteractionMode>;
|
|
30
|
+
settingsOpen: Ref<boolean>;
|
|
31
|
+
toolbarDragging: Ref<boolean>;
|
|
32
|
+
toolbarRef: Ref<{
|
|
33
|
+
expanded: boolean;
|
|
34
|
+
} | null>;
|
|
35
|
+
isInteractionLocked: () => boolean;
|
|
36
|
+
config: ComputedRef<KeyboardShortcutConfig>;
|
|
37
|
+
actions: ShortcutActionHandlers;
|
|
38
|
+
}
|
|
39
|
+
export interface KeyboardShortcutState {
|
|
40
|
+
cleanup: () => void;
|
|
41
|
+
}
|
|
42
|
+
export declare const DEFAULT_SHORTCUT_CONFIG: KeyboardShortcutConfig;
|
|
43
|
+
export declare function useKeyboardShortcuts(options: UseKeyboardShortcutsOptions): KeyboardShortcutState;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DEFAULT_SHORTCUT_CONFIG = void 0;
|
|
7
|
+
exports.useKeyboardShortcuts = useKeyboardShortcuts;
|
|
8
|
+
var _vueDemi = require("vue-demi");
|
|
9
|
+
var _constants = require("../constants");
|
|
10
|
+
const DEFAULT_KEYMAP = {
|
|
11
|
+
"activate": "",
|
|
12
|
+
"element-select": "v",
|
|
13
|
+
"area-select": "a",
|
|
14
|
+
"pause-animations": "p",
|
|
15
|
+
"copy": "c",
|
|
16
|
+
"clear": "Backspace",
|
|
17
|
+
"settings": "",
|
|
18
|
+
"minimize": "Escape"
|
|
19
|
+
};
|
|
20
|
+
const DEFAULT_SHORTCUT_CONFIG = exports.DEFAULT_SHORTCUT_CONFIG = {
|
|
21
|
+
enabledWhenClosed: true,
|
|
22
|
+
priorityWhenOpen: true,
|
|
23
|
+
doubleTap: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
key: "Shift",
|
|
26
|
+
thresholdMs: 280
|
|
27
|
+
},
|
|
28
|
+
keymap: {},
|
|
29
|
+
conflictPolicy: "ignore-editables"
|
|
30
|
+
};
|
|
31
|
+
const BROWSER_BLACKLIST = /* @__PURE__ */new Set(["Meta+l", "Meta+t", "Meta+w", "Meta+r", "Meta+n", "Meta+q", "Meta+Shift+t", "Meta+Shift+n", "Control+l", "Control+t", "Control+w", "Control+r", "Control+n", "Control+q", "Control+Shift+t", "Control+Shift+n", "Meta+c", "Meta+v", "Meta+x", "Meta+a", "Meta+z", "Control+c", "Control+v", "Control+x", "Control+a", "Control+z"]);
|
|
32
|
+
function useKeyboardShortcuts(options) {
|
|
33
|
+
const {
|
|
34
|
+
mode,
|
|
35
|
+
settingsOpen,
|
|
36
|
+
toolbarRef,
|
|
37
|
+
isInteractionLocked,
|
|
38
|
+
actions
|
|
39
|
+
} = options;
|
|
40
|
+
let lastActivationKeyUpTime = 0;
|
|
41
|
+
let listenerAttached = false;
|
|
42
|
+
let mergedKeymap = {
|
|
43
|
+
...DEFAULT_KEYMAP,
|
|
44
|
+
...options.config.value.keymap
|
|
45
|
+
};
|
|
46
|
+
(0, _vueDemi.watch)(options.config, cfg2 => {
|
|
47
|
+
mergedKeymap = {
|
|
48
|
+
...DEFAULT_KEYMAP,
|
|
49
|
+
...cfg2.keymap
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
function cfg() {
|
|
53
|
+
return options.config.value;
|
|
54
|
+
}
|
|
55
|
+
function getCurrentScope() {
|
|
56
|
+
if (mode.value === "idle" && (!toolbarRef.value || !toolbarRef.value.expanded)) {
|
|
57
|
+
return "closed";
|
|
58
|
+
}
|
|
59
|
+
if (settingsOpen.value) {
|
|
60
|
+
return "settings";
|
|
61
|
+
}
|
|
62
|
+
if (mode.value === "input-open") {
|
|
63
|
+
return "input";
|
|
64
|
+
}
|
|
65
|
+
return "open";
|
|
66
|
+
}
|
|
67
|
+
function isForeignEditable() {
|
|
68
|
+
const active = document.activeElement;
|
|
69
|
+
if (!active) return false;
|
|
70
|
+
const tag = active.tagName.toLowerCase();
|
|
71
|
+
const isEditable = tag === "input" || tag === "textarea" || active.isContentEditable;
|
|
72
|
+
if (!isEditable) return false;
|
|
73
|
+
return !active.closest(_constants.VA_DATA_ATTR_SELECTOR);
|
|
74
|
+
}
|
|
75
|
+
function isBrowserCombo(e) {
|
|
76
|
+
if (!e.metaKey && !e.ctrlKey) return false;
|
|
77
|
+
const parts = [];
|
|
78
|
+
if (e.metaKey) parts.push("Meta");
|
|
79
|
+
if (e.ctrlKey) parts.push("Control");
|
|
80
|
+
if (e.shiftKey) parts.push("Shift");
|
|
81
|
+
parts.push(e.key);
|
|
82
|
+
return BROWSER_BLACKLIST.has(parts.join("+"));
|
|
83
|
+
}
|
|
84
|
+
function findActionForKey(key) {
|
|
85
|
+
const normalized = key.toLowerCase();
|
|
86
|
+
for (const [action, mappedKey] of Object.entries(mergedKeymap)) {
|
|
87
|
+
if (!mappedKey) continue;
|
|
88
|
+
if (mappedKey.toLowerCase() === normalized || mappedKey === key) {
|
|
89
|
+
return action;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
function executeAction(action) {
|
|
95
|
+
switch (action) {
|
|
96
|
+
case "element-select":
|
|
97
|
+
actions.elementSelect();
|
|
98
|
+
break;
|
|
99
|
+
case "area-select":
|
|
100
|
+
actions.areaSelect();
|
|
101
|
+
break;
|
|
102
|
+
case "pause-animations":
|
|
103
|
+
actions.pauseAnimations();
|
|
104
|
+
break;
|
|
105
|
+
case "copy":
|
|
106
|
+
actions.copy();
|
|
107
|
+
break;
|
|
108
|
+
case "clear":
|
|
109
|
+
actions.clear();
|
|
110
|
+
break;
|
|
111
|
+
case "settings":
|
|
112
|
+
actions.openSettings();
|
|
113
|
+
break;
|
|
114
|
+
case "minimize":
|
|
115
|
+
actions.deactivate();
|
|
116
|
+
if (toolbarRef.value) toolbarRef.value.expanded = false;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function consume(e) {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
e.stopPropagation();
|
|
123
|
+
}
|
|
124
|
+
function onKeyDown(e) {
|
|
125
|
+
if (e.repeat) return;
|
|
126
|
+
if (isBrowserCombo(e)) return;
|
|
127
|
+
const hasModifier = e.metaKey || e.ctrlKey || e.altKey;
|
|
128
|
+
const scope = getCurrentScope();
|
|
129
|
+
if (scope === "closed") {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (scope === "settings") {
|
|
133
|
+
if (e.key === "Escape") {
|
|
134
|
+
consume(e);
|
|
135
|
+
actions.closeSettings();
|
|
136
|
+
}
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (scope === "input") {
|
|
140
|
+
if (e.key === "Escape") {
|
|
141
|
+
consume(e);
|
|
142
|
+
actions.inputCancel();
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (cfg().conflictPolicy === "ignore-editables" && isForeignEditable()) return;
|
|
147
|
+
if (isInteractionLocked()) return;
|
|
148
|
+
if (hasModifier) return;
|
|
149
|
+
const action = findActionForKey(e.key);
|
|
150
|
+
if (!action) return;
|
|
151
|
+
if (cfg().priorityWhenOpen) {
|
|
152
|
+
consume(e);
|
|
153
|
+
}
|
|
154
|
+
executeAction(action);
|
|
155
|
+
}
|
|
156
|
+
function onKeyUp(e) {
|
|
157
|
+
const {
|
|
158
|
+
doubleTap
|
|
159
|
+
} = cfg();
|
|
160
|
+
if (!doubleTap.enabled) return;
|
|
161
|
+
if (e.key !== doubleTap.key) return;
|
|
162
|
+
if (e.repeat) return;
|
|
163
|
+
const now = Date.now();
|
|
164
|
+
const delta = now - lastActivationKeyUpTime;
|
|
165
|
+
lastActivationKeyUpTime = now;
|
|
166
|
+
if (delta < doubleTap.thresholdMs && delta > 50) {
|
|
167
|
+
lastActivationKeyUpTime = 0;
|
|
168
|
+
const scope = getCurrentScope();
|
|
169
|
+
if (scope === "closed") {
|
|
170
|
+
actions.activate();
|
|
171
|
+
if (toolbarRef.value) toolbarRef.value.expanded = true;
|
|
172
|
+
} else if (scope === "open") {
|
|
173
|
+
actions.deactivate();
|
|
174
|
+
if (toolbarRef.value) toolbarRef.value.expanded = false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function onBlurOrVisibility() {
|
|
179
|
+
lastActivationKeyUpTime = 0;
|
|
180
|
+
}
|
|
181
|
+
function attach() {
|
|
182
|
+
if (listenerAttached) return;
|
|
183
|
+
listenerAttached = true;
|
|
184
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
185
|
+
document.addEventListener("keyup", onKeyUp, true);
|
|
186
|
+
window.addEventListener("blur", onBlurOrVisibility);
|
|
187
|
+
document.addEventListener("visibilitychange", onBlurOrVisibility);
|
|
188
|
+
}
|
|
189
|
+
function detach() {
|
|
190
|
+
if (!listenerAttached) return;
|
|
191
|
+
listenerAttached = false;
|
|
192
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
193
|
+
document.removeEventListener("keyup", onKeyUp, true);
|
|
194
|
+
window.removeEventListener("blur", onBlurOrVisibility);
|
|
195
|
+
document.removeEventListener("visibilitychange", onBlurOrVisibility);
|
|
196
|
+
}
|
|
197
|
+
(0, _vueDemi.onMounted)(attach);
|
|
198
|
+
(0, _vueDemi.onBeforeUnmount)(detach);
|
|
199
|
+
return {
|
|
200
|
+
cleanup: detach
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { onBeforeUnmount, onMounted, watch } from "vue-demi";
|
|
2
|
+
import { VA_DATA_ATTR_SELECTOR } from "../constants.mjs";
|
|
3
|
+
const DEFAULT_KEYMAP = {
|
|
4
|
+
"activate": "",
|
|
5
|
+
"element-select": "v",
|
|
6
|
+
"area-select": "a",
|
|
7
|
+
"pause-animations": "p",
|
|
8
|
+
"copy": "c",
|
|
9
|
+
"clear": "Backspace",
|
|
10
|
+
"settings": "",
|
|
11
|
+
"minimize": "Escape"
|
|
12
|
+
};
|
|
13
|
+
export const DEFAULT_SHORTCUT_CONFIG = {
|
|
14
|
+
enabledWhenClosed: true,
|
|
15
|
+
priorityWhenOpen: true,
|
|
16
|
+
doubleTap: { enabled: true, key: "Shift", thresholdMs: 280 },
|
|
17
|
+
keymap: {},
|
|
18
|
+
conflictPolicy: "ignore-editables"
|
|
19
|
+
};
|
|
20
|
+
const BROWSER_BLACKLIST = /* @__PURE__ */ new Set([
|
|
21
|
+
"Meta+l",
|
|
22
|
+
"Meta+t",
|
|
23
|
+
"Meta+w",
|
|
24
|
+
"Meta+r",
|
|
25
|
+
"Meta+n",
|
|
26
|
+
"Meta+q",
|
|
27
|
+
"Meta+Shift+t",
|
|
28
|
+
"Meta+Shift+n",
|
|
29
|
+
"Control+l",
|
|
30
|
+
"Control+t",
|
|
31
|
+
"Control+w",
|
|
32
|
+
"Control+r",
|
|
33
|
+
"Control+n",
|
|
34
|
+
"Control+q",
|
|
35
|
+
"Control+Shift+t",
|
|
36
|
+
"Control+Shift+n",
|
|
37
|
+
"Meta+c",
|
|
38
|
+
"Meta+v",
|
|
39
|
+
"Meta+x",
|
|
40
|
+
"Meta+a",
|
|
41
|
+
"Meta+z",
|
|
42
|
+
"Control+c",
|
|
43
|
+
"Control+v",
|
|
44
|
+
"Control+x",
|
|
45
|
+
"Control+a",
|
|
46
|
+
"Control+z"
|
|
47
|
+
]);
|
|
48
|
+
export function useKeyboardShortcuts(options) {
|
|
49
|
+
const { mode, settingsOpen, toolbarRef, isInteractionLocked, actions } = options;
|
|
50
|
+
let lastActivationKeyUpTime = 0;
|
|
51
|
+
let listenerAttached = false;
|
|
52
|
+
let mergedKeymap = { ...DEFAULT_KEYMAP, ...options.config.value.keymap };
|
|
53
|
+
watch(options.config, (cfg2) => {
|
|
54
|
+
mergedKeymap = { ...DEFAULT_KEYMAP, ...cfg2.keymap };
|
|
55
|
+
});
|
|
56
|
+
function cfg() {
|
|
57
|
+
return options.config.value;
|
|
58
|
+
}
|
|
59
|
+
function getCurrentScope() {
|
|
60
|
+
if (mode.value === "idle" && (!toolbarRef.value || !toolbarRef.value.expanded)) {
|
|
61
|
+
return "closed";
|
|
62
|
+
}
|
|
63
|
+
if (settingsOpen.value) {
|
|
64
|
+
return "settings";
|
|
65
|
+
}
|
|
66
|
+
if (mode.value === "input-open") {
|
|
67
|
+
return "input";
|
|
68
|
+
}
|
|
69
|
+
return "open";
|
|
70
|
+
}
|
|
71
|
+
function isForeignEditable() {
|
|
72
|
+
const active = document.activeElement;
|
|
73
|
+
if (!active)
|
|
74
|
+
return false;
|
|
75
|
+
const tag = active.tagName.toLowerCase();
|
|
76
|
+
const isEditable = tag === "input" || tag === "textarea" || active.isContentEditable;
|
|
77
|
+
if (!isEditable)
|
|
78
|
+
return false;
|
|
79
|
+
return !active.closest(VA_DATA_ATTR_SELECTOR);
|
|
80
|
+
}
|
|
81
|
+
function isBrowserCombo(e) {
|
|
82
|
+
if (!e.metaKey && !e.ctrlKey)
|
|
83
|
+
return false;
|
|
84
|
+
const parts = [];
|
|
85
|
+
if (e.metaKey)
|
|
86
|
+
parts.push("Meta");
|
|
87
|
+
if (e.ctrlKey)
|
|
88
|
+
parts.push("Control");
|
|
89
|
+
if (e.shiftKey)
|
|
90
|
+
parts.push("Shift");
|
|
91
|
+
parts.push(e.key);
|
|
92
|
+
return BROWSER_BLACKLIST.has(parts.join("+"));
|
|
93
|
+
}
|
|
94
|
+
function findActionForKey(key) {
|
|
95
|
+
const normalized = key.toLowerCase();
|
|
96
|
+
for (const [action, mappedKey] of Object.entries(mergedKeymap)) {
|
|
97
|
+
if (!mappedKey)
|
|
98
|
+
continue;
|
|
99
|
+
if (mappedKey.toLowerCase() === normalized || mappedKey === key) {
|
|
100
|
+
return action;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
function executeAction(action) {
|
|
106
|
+
switch (action) {
|
|
107
|
+
case "element-select":
|
|
108
|
+
actions.elementSelect();
|
|
109
|
+
break;
|
|
110
|
+
case "area-select":
|
|
111
|
+
actions.areaSelect();
|
|
112
|
+
break;
|
|
113
|
+
case "pause-animations":
|
|
114
|
+
actions.pauseAnimations();
|
|
115
|
+
break;
|
|
116
|
+
case "copy":
|
|
117
|
+
actions.copy();
|
|
118
|
+
break;
|
|
119
|
+
case "clear":
|
|
120
|
+
actions.clear();
|
|
121
|
+
break;
|
|
122
|
+
case "settings":
|
|
123
|
+
actions.openSettings();
|
|
124
|
+
break;
|
|
125
|
+
case "minimize":
|
|
126
|
+
actions.deactivate();
|
|
127
|
+
if (toolbarRef.value)
|
|
128
|
+
toolbarRef.value.expanded = false;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function consume(e) {
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
e.stopPropagation();
|
|
135
|
+
}
|
|
136
|
+
function onKeyDown(e) {
|
|
137
|
+
if (e.repeat)
|
|
138
|
+
return;
|
|
139
|
+
if (isBrowserCombo(e))
|
|
140
|
+
return;
|
|
141
|
+
const hasModifier = e.metaKey || e.ctrlKey || e.altKey;
|
|
142
|
+
const scope = getCurrentScope();
|
|
143
|
+
if (scope === "closed") {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (scope === "settings") {
|
|
147
|
+
if (e.key === "Escape") {
|
|
148
|
+
consume(e);
|
|
149
|
+
actions.closeSettings();
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (scope === "input") {
|
|
154
|
+
if (e.key === "Escape") {
|
|
155
|
+
consume(e);
|
|
156
|
+
actions.inputCancel();
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (cfg().conflictPolicy === "ignore-editables" && isForeignEditable())
|
|
161
|
+
return;
|
|
162
|
+
if (isInteractionLocked())
|
|
163
|
+
return;
|
|
164
|
+
if (hasModifier)
|
|
165
|
+
return;
|
|
166
|
+
const action = findActionForKey(e.key);
|
|
167
|
+
if (!action)
|
|
168
|
+
return;
|
|
169
|
+
if (cfg().priorityWhenOpen) {
|
|
170
|
+
consume(e);
|
|
171
|
+
}
|
|
172
|
+
executeAction(action);
|
|
173
|
+
}
|
|
174
|
+
function onKeyUp(e) {
|
|
175
|
+
const { doubleTap } = cfg();
|
|
176
|
+
if (!doubleTap.enabled)
|
|
177
|
+
return;
|
|
178
|
+
if (e.key !== doubleTap.key)
|
|
179
|
+
return;
|
|
180
|
+
if (e.repeat)
|
|
181
|
+
return;
|
|
182
|
+
const now = Date.now();
|
|
183
|
+
const delta = now - lastActivationKeyUpTime;
|
|
184
|
+
lastActivationKeyUpTime = now;
|
|
185
|
+
if (delta < doubleTap.thresholdMs && delta > 50) {
|
|
186
|
+
lastActivationKeyUpTime = 0;
|
|
187
|
+
const scope = getCurrentScope();
|
|
188
|
+
if (scope === "closed") {
|
|
189
|
+
actions.activate();
|
|
190
|
+
if (toolbarRef.value)
|
|
191
|
+
toolbarRef.value.expanded = true;
|
|
192
|
+
} else if (scope === "open") {
|
|
193
|
+
actions.deactivate();
|
|
194
|
+
if (toolbarRef.value)
|
|
195
|
+
toolbarRef.value.expanded = false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function onBlurOrVisibility() {
|
|
200
|
+
lastActivationKeyUpTime = 0;
|
|
201
|
+
}
|
|
202
|
+
function attach() {
|
|
203
|
+
if (listenerAttached)
|
|
204
|
+
return;
|
|
205
|
+
listenerAttached = true;
|
|
206
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
207
|
+
document.addEventListener("keyup", onKeyUp, true);
|
|
208
|
+
window.addEventListener("blur", onBlurOrVisibility);
|
|
209
|
+
document.addEventListener("visibilitychange", onBlurOrVisibility);
|
|
210
|
+
}
|
|
211
|
+
function detach() {
|
|
212
|
+
if (!listenerAttached)
|
|
213
|
+
return;
|
|
214
|
+
listenerAttached = false;
|
|
215
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
216
|
+
document.removeEventListener("keyup", onKeyUp, true);
|
|
217
|
+
window.removeEventListener("blur", onBlurOrVisibility);
|
|
218
|
+
document.removeEventListener("visibilitychange", onBlurOrVisibility);
|
|
219
|
+
}
|
|
220
|
+
onMounted(attach);
|
|
221
|
+
onBeforeUnmount(detach);
|
|
222
|
+
return { cleanup: detach };
|
|
223
|
+
}
|