@sakana-y/vue-grab 0.0.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/LICENSE +21 -0
- package/dist/composables/index.d.ts +12 -0
- package/dist/composables/index.d.ts.map +1 -0
- package/dist/core/index.d.ts +27 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/floating-button/index.d.ts +58 -0
- package/dist/floating-button/index.d.ts.map +1 -0
- package/dist/hotkeys/index.d.ts +7 -0
- package/dist/hotkeys/index.d.ts.map +1 -0
- package/dist/index.cjs +197 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +493 -0
- package/dist/index.umd.js +197 -0
- package/dist/init.d.ts +11 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/overlay/index.d.ts +16 -0
- package/dist/overlay/index.d.ts.map +1 -0
- package/dist/plugin.d.ts +7 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/session.d.ts +17 -0
- package/dist/session.d.ts.map +1 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 sakana
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DeepReadonly, Ref } from 'vue';
|
|
2
|
+
import { GrabConfig, GrabResult } from '@sakana-y/vue-grab-shared';
|
|
3
|
+
export interface UseGrabReturn {
|
|
4
|
+
config: GrabConfig;
|
|
5
|
+
isActive: Readonly<Ref<boolean>>;
|
|
6
|
+
lastResult: DeepReadonly<Ref<GrabResult | null>>;
|
|
7
|
+
activate: () => void;
|
|
8
|
+
deactivate: () => void;
|
|
9
|
+
toggle: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function useGrab(): UseGrabReturn;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composables/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,YAAY,EAAE,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAKxE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IACjC,UAAU,EAAE,YAAY,CAAC,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC;IACjD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,OAAO,IAAI,aAAa,CA+BvC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { GrabConfig, GrabResult } from '@sakana-y/vue-grab-shared';
|
|
2
|
+
export declare class GrabEngine {
|
|
3
|
+
private config;
|
|
4
|
+
private overlay;
|
|
5
|
+
private callbacks;
|
|
6
|
+
private stateListeners;
|
|
7
|
+
private _isActive;
|
|
8
|
+
private prevCursor;
|
|
9
|
+
private handleMouseMove;
|
|
10
|
+
private handleClick;
|
|
11
|
+
private handleKeyDown;
|
|
12
|
+
constructor(config: GrabConfig);
|
|
13
|
+
get isActive(): boolean;
|
|
14
|
+
activate(): void;
|
|
15
|
+
deactivate(): void;
|
|
16
|
+
onGrab(cb: (result: GrabResult) => void): () => void;
|
|
17
|
+
onStateChange(cb: (active: boolean) => void): () => void;
|
|
18
|
+
toggle(): void;
|
|
19
|
+
destroy(): void;
|
|
20
|
+
updateConfig(config: Partial<GrabConfig>): void;
|
|
21
|
+
private shouldIgnore;
|
|
22
|
+
private getVueComponent;
|
|
23
|
+
private getComponentLabelFromInstance;
|
|
24
|
+
private getComponentStack;
|
|
25
|
+
private generateSelector;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAiB,MAAM,2BAA2B,CAAC;AAevF,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,SAAS,CAAgD;IACjE,OAAO,CAAC,cAAc,CAA6C;IACnE,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAM;IAGxB,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,WAAW,CAA0C;IAC7D,OAAO,CAAC,aAAa,CAA6C;gBAEtD,MAAM,EAAE,UAAU;IAI9B,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,QAAQ,IAAI,IAAI;IAyDhB,UAAU,IAAI,IAAI;IAsBlB,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;IAKpD,aAAa,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI;IAKxD,MAAM,IAAI,IAAI;IAKd,OAAO,IAAI,IAAI;IAMf,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI;IAI/C,OAAO,CAAC,YAAY;IA8BpB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,6BAA6B;IAQrC,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,gBAAgB;CA8BzB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { FloatingButtonConfig } from '@sakana-y/vue-grab-shared';
|
|
2
|
+
export declare const FAB_HOST_ID = "vue-grab-fab-host";
|
|
3
|
+
export declare class FloatingButton {
|
|
4
|
+
private host;
|
|
5
|
+
private shadowRoot;
|
|
6
|
+
private toolbarEl;
|
|
7
|
+
private btnEl;
|
|
8
|
+
private gearEl;
|
|
9
|
+
private panelEl;
|
|
10
|
+
private kbdEl;
|
|
11
|
+
private recordBtn;
|
|
12
|
+
private config;
|
|
13
|
+
private posX;
|
|
14
|
+
private posY;
|
|
15
|
+
private isDragging;
|
|
16
|
+
private wasDragged;
|
|
17
|
+
private dragPointerId;
|
|
18
|
+
private dragStartX;
|
|
19
|
+
private dragStartY;
|
|
20
|
+
private dragOffsetX;
|
|
21
|
+
private dragOffsetY;
|
|
22
|
+
private panelOpen;
|
|
23
|
+
private isRecording;
|
|
24
|
+
private currentHotkey;
|
|
25
|
+
private toggleCb;
|
|
26
|
+
private hotkeyChangeCb;
|
|
27
|
+
private boundPointerDown;
|
|
28
|
+
private boundPointerMove;
|
|
29
|
+
private boundPointerUp;
|
|
30
|
+
private boundDocClick;
|
|
31
|
+
private boundDocKeyDown;
|
|
32
|
+
private boundRecordKeyDown;
|
|
33
|
+
constructor(config: FloatingButtonConfig);
|
|
34
|
+
getCurrentHotkey(): string;
|
|
35
|
+
mount(): void;
|
|
36
|
+
destroy(): void;
|
|
37
|
+
setActive(active: boolean): void;
|
|
38
|
+
setHighlightColor(color: string): void;
|
|
39
|
+
setCurrentHotkey(combo: string): void;
|
|
40
|
+
onToggle(cb: () => void): void;
|
|
41
|
+
onHotkeyChange(cb: (combo: string) => void): void;
|
|
42
|
+
private onPointerDown;
|
|
43
|
+
private onPointerMove;
|
|
44
|
+
private onPointerUp;
|
|
45
|
+
private snapToEdge;
|
|
46
|
+
private applyPosition;
|
|
47
|
+
private getEdgeFromPosition;
|
|
48
|
+
private applyOrientation;
|
|
49
|
+
private togglePanel;
|
|
50
|
+
private openPanel;
|
|
51
|
+
private closePanel;
|
|
52
|
+
private positionPanel;
|
|
53
|
+
private toggleRecording;
|
|
54
|
+
private startRecording;
|
|
55
|
+
private stopRecording;
|
|
56
|
+
private updateHotkeyDisplay;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/floating-button/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAGtE,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAoO/C,qBAAa,cAAc;IACzB,OAAO,CAAC,IAAI,CAA4B;IACxC,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,MAAM,CAAuB;IAGrC,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,IAAI,CAAM;IAGlB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,WAAW,CAAK;IAGxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,aAAa,CAAM;IAG3B,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,cAAc,CAA0C;IAGhE,OAAO,CAAC,gBAAgB,CAA4C;IACpE,OAAO,CAAC,gBAAgB,CAA4C;IACpE,OAAO,CAAC,cAAc,CAA4C;IAClE,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,eAAe,CAA6C;IACpE,OAAO,CAAC,kBAAkB,CAA6C;gBAE3D,MAAM,EAAE,oBAAoB;IAiBxC,gBAAgB,IAAI,MAAM;IAI1B,KAAK,IAAI,IAAI;IAoIb,OAAO,IAAI,IAAI;IAuBf,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAShC,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAItC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKrC,QAAQ,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI;IAI9B,cAAc,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAMjD,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,aAAa;IAsBrB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,mBAAmB;CAI5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hotkeys/index.ts"],"names":[],"mappings":"AAmBA,wBAAgB,UAAU,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CASnD;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAsB;IAEtC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAoBnD,OAAO,IAAI,IAAI;CAIhB"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`vue`);var t=`#4f46e5`,n=`#ffffff`,r=`Alt+Shift+G`,i={highlightColor:t,labelTextColor:n,showTagHint:!0,maxHtmlLength:1e4,filter:{ignoreSelectors:[],ignoreTags:[],skipCommonComponents:!1},floatingButton:{enabled:!1,initialPosition:`top-center`,storageKey:`vue-grab-fab-pos`,hotkeyStorageKey:`vue-grab-hotkey`}};function a(e,t){let{filter:n,floatingButton:r,...i}=t;return{...e,...i,filter:{...e.filter,...n},floatingButton:{...e.floatingButton,...r}}}var o=Symbol(`vue-grab-config`);function s(e={}){let t=a(i,e);return{install(e){e.provide(o,t)}}}var c=`vue-grab-overlay-host`,l=class{host=null;shadowRoot=null;highlightBox=null;labelEl=null;config;constructor(e){this.config=e}mount(){if(this.host)return;this.host=document.createElement(`div`),this.host.id=c,this.host.style.cssText=`position:fixed;top:0;left:0;width:0;height:0;z-index:2147483647;pointer-events:none;`,document.body.appendChild(this.host),this.shadowRoot=this.host.attachShadow({mode:`open`}),this.host.style.setProperty(`--grab-color`,this.config.highlightColor),this.host.style.setProperty(`--grab-label-color`,this.config.labelTextColor);let e=document.createElement(`style`);e.textContent=`
|
|
2
|
+
.grab-highlight {
|
|
3
|
+
position: fixed;
|
|
4
|
+
pointer-events: none;
|
|
5
|
+
border: 2px solid var(--grab-color);
|
|
6
|
+
background: color-mix(in srgb, var(--grab-color) 12%, transparent);
|
|
7
|
+
border-radius: 2px;
|
|
8
|
+
transition: all 0.05s ease-out;
|
|
9
|
+
display: none;
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
}
|
|
12
|
+
.grab-label {
|
|
13
|
+
position: fixed;
|
|
14
|
+
pointer-events: none;
|
|
15
|
+
background: var(--grab-color);
|
|
16
|
+
color: var(--grab-label-color);
|
|
17
|
+
font-size: 11px;
|
|
18
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
19
|
+
padding: 2px 6px;
|
|
20
|
+
border-radius: 2px;
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
display: none;
|
|
23
|
+
line-height: 1.4;
|
|
24
|
+
}
|
|
25
|
+
`,this.shadowRoot.appendChild(e),this.highlightBox=document.createElement(`div`),this.highlightBox.className=`grab-highlight`,this.shadowRoot.appendChild(this.highlightBox),this.labelEl=document.createElement(`div`),this.labelEl.className=`grab-label`,this.shadowRoot.appendChild(this.labelEl)}highlight(e,t){if(!this.highlightBox||!this.labelEl)return;let n=e.getBoundingClientRect();this.highlightBox.style.top=`${n.top}px`,this.highlightBox.style.left=`${n.left}px`,this.highlightBox.style.width=`${n.width}px`,this.highlightBox.style.height=`${n.height}px`,this.highlightBox.style.display=`block`,this.config.showTagHint&&t?(this.labelEl.textContent=t,n.top>24?this.labelEl.style.top=`${n.top-20-4}px`:this.labelEl.style.top=`${n.bottom+4}px`,this.labelEl.style.left=`${n.left}px`,this.labelEl.style.display=`block`):this.labelEl.style.display=`none`}clearHighlight(){this.highlightBox&&(this.highlightBox.style.display=`none`),this.labelEl&&(this.labelEl.style.display=`none`)}destroy(){this.host&&(this.host.remove(),this.host=null,this.shadowRoot=null,this.highlightBox=null,this.labelEl=null)}};function u(e){let t=e.split(`+`).map(e=>e.trim().toLowerCase());return{key:t[t.length-1],alt:t.includes(`alt`),ctrl:t.includes(`ctrl`)||t.includes(`control`),shift:t.includes(`shift`),meta:t.includes(`meta`)||t.includes(`cmd`)||t.includes(`command`)}}function d(e){let t=[];e.ctrlKey&&t.push(`Ctrl`),e.altKey&&t.push(`Alt`),e.shiftKey&&t.push(`Shift`),e.metaKey&&t.push(`Meta`);let n=e.key.length===1?e.key.toUpperCase():e.key;return t.push(n),t.join(`+`)}var f=class{cleanups=[];register(e,t){let n=u(e),r=e=>{e.key.toLowerCase()===n.key&&e.altKey===n.alt&&e.ctrlKey===n.ctrl&&e.shiftKey===n.shift&&e.metaKey===n.meta&&(e.preventDefault(),t())};document.addEventListener(`keydown`,r,{capture:!0}),this.cleanups.push(()=>document.removeEventListener(`keydown`,r,{capture:!0}))}destroy(){this.cleanups.forEach(e=>e()),this.cleanups=[]}},p=`vue-grab-fab-host`,m=3,h=`left 0.3s ease, top 0.3s ease`,g=3,_=5;function v(){return g*window.innerHeight/window.innerWidth}var y={"bottom-right":{x:97,y:85},"bottom-left":{x:3,y:85},"top-right":{x:97,y:15},"top-left":{x:3,y:15},"top-center":{x:50,y:3}};function b(e,t,n){return Math.min(Math.max(e,t),n)}function x(e,t){if(!e)return null;try{let n=localStorage.getItem(e);return n?t(n):null}catch{return null}}function S(e,t){if(e)try{localStorage.setItem(e,t)}catch{}}function C(e){return x(e,e=>{let{x:t,y:n}=JSON.parse(e);return typeof t==`number`&&typeof n==`number`?{x:t,y:n}:null})}function w(e,t,n){S(e,JSON.stringify({x:t,y:n}))}function T(e){return x(e,e=>typeof e==`string`?e:null)}function E(e,t){S(e,t)}var D=`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="22" y1="12" x2="18" y2="12"/><line x1="6" y1="12" x2="2" y2="12"/><line x1="12" y1="6" x2="12" y2="2"/><line x1="12" y1="22" x2="12" y2="18"/><line x1="12" y1="15" x2="12" y2="9"/><line x1="9" y1="12" x2="15" y2="12"/></svg>`,O=`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.32 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>`,k=`
|
|
26
|
+
:host {
|
|
27
|
+
all: initial;
|
|
28
|
+
}
|
|
29
|
+
.toolbar {
|
|
30
|
+
display: inline-flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
height: 36px;
|
|
33
|
+
padding: 0 4px;
|
|
34
|
+
gap: 2px;
|
|
35
|
+
border-radius: 8px;
|
|
36
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
37
|
+
background: rgba(30,30,30,0.85);
|
|
38
|
+
backdrop-filter: blur(8px);
|
|
39
|
+
-webkit-backdrop-filter: blur(8px);
|
|
40
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.35);
|
|
41
|
+
cursor: grab;
|
|
42
|
+
user-select: none;
|
|
43
|
+
touch-action: none;
|
|
44
|
+
position: relative;
|
|
45
|
+
}
|
|
46
|
+
.toolbar.dragging {
|
|
47
|
+
cursor: grabbing;
|
|
48
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.5);
|
|
49
|
+
}
|
|
50
|
+
.toolbar-btn {
|
|
51
|
+
width: 28px;
|
|
52
|
+
height: 28px;
|
|
53
|
+
border-radius: 6px;
|
|
54
|
+
border: none;
|
|
55
|
+
background: transparent;
|
|
56
|
+
color: #a0a0a0;
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
justify-content: center;
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
transition: color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
|
|
62
|
+
}
|
|
63
|
+
.toolbar-btn:hover {
|
|
64
|
+
color: #e0e0e0;
|
|
65
|
+
background: rgba(255,255,255,0.08);
|
|
66
|
+
}
|
|
67
|
+
.grab-btn.active {
|
|
68
|
+
color: var(--grab-color, #4f46e5);
|
|
69
|
+
box-shadow: inset 0 0 0 1.5px var(--grab-color, #4f46e5);
|
|
70
|
+
background: color-mix(in srgb, var(--grab-color, #4f46e5) 12%, transparent);
|
|
71
|
+
}
|
|
72
|
+
.toolbar-divider {
|
|
73
|
+
width: 1px;
|
|
74
|
+
height: 18px;
|
|
75
|
+
background: rgba(255,255,255,0.12);
|
|
76
|
+
margin: 0 2px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.panel {
|
|
80
|
+
position: absolute;
|
|
81
|
+
width: 240px;
|
|
82
|
+
background: rgba(25,25,25,0.94);
|
|
83
|
+
backdrop-filter: blur(12px);
|
|
84
|
+
-webkit-backdrop-filter: blur(12px);
|
|
85
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
86
|
+
border-radius: 12px;
|
|
87
|
+
color: #e0e0e0;
|
|
88
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
89
|
+
font-size: 13px;
|
|
90
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
|
91
|
+
padding: 14px;
|
|
92
|
+
display: none;
|
|
93
|
+
z-index: 1;
|
|
94
|
+
}
|
|
95
|
+
.panel.open {
|
|
96
|
+
display: block;
|
|
97
|
+
}
|
|
98
|
+
.panel-header {
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
justify-content: space-between;
|
|
102
|
+
margin-bottom: 12px;
|
|
103
|
+
font-weight: 600;
|
|
104
|
+
font-size: 13px;
|
|
105
|
+
color: #fff;
|
|
106
|
+
}
|
|
107
|
+
.panel-close {
|
|
108
|
+
background: none;
|
|
109
|
+
border: none;
|
|
110
|
+
color: #888;
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
font-size: 16px;
|
|
113
|
+
line-height: 1;
|
|
114
|
+
padding: 0 2px;
|
|
115
|
+
}
|
|
116
|
+
.panel-close:hover {
|
|
117
|
+
color: #fff;
|
|
118
|
+
}
|
|
119
|
+
.panel-label {
|
|
120
|
+
font-size: 11px;
|
|
121
|
+
color: #888;
|
|
122
|
+
margin-bottom: 6px;
|
|
123
|
+
text-transform: uppercase;
|
|
124
|
+
letter-spacing: 0.5px;
|
|
125
|
+
}
|
|
126
|
+
.hotkey-row {
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
gap: 8px;
|
|
130
|
+
}
|
|
131
|
+
kbd {
|
|
132
|
+
display: inline-flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
background: rgba(255,255,255,0.08);
|
|
135
|
+
border: 1px solid rgba(255,255,255,0.15);
|
|
136
|
+
border-radius: 6px;
|
|
137
|
+
padding: 4px 10px;
|
|
138
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
139
|
+
font-size: 12px;
|
|
140
|
+
color: #ddd;
|
|
141
|
+
min-width: 80px;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
flex: 1;
|
|
144
|
+
}
|
|
145
|
+
kbd.recording {
|
|
146
|
+
border-color: var(--grab-color, #4f46e5);
|
|
147
|
+
box-shadow: 0 0 0 1px var(--grab-color, #4f46e5);
|
|
148
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
149
|
+
}
|
|
150
|
+
@keyframes pulse {
|
|
151
|
+
0%, 100% { opacity: 1; }
|
|
152
|
+
50% { opacity: 0.6; }
|
|
153
|
+
}
|
|
154
|
+
.record-btn {
|
|
155
|
+
background: rgba(255,255,255,0.08);
|
|
156
|
+
border: 1px solid rgba(255,255,255,0.15);
|
|
157
|
+
border-radius: 6px;
|
|
158
|
+
color: #ccc;
|
|
159
|
+
padding: 4px 10px;
|
|
160
|
+
font-size: 12px;
|
|
161
|
+
cursor: pointer;
|
|
162
|
+
white-space: nowrap;
|
|
163
|
+
font-family: inherit;
|
|
164
|
+
}
|
|
165
|
+
.record-btn:hover {
|
|
166
|
+
background: rgba(255,255,255,0.14);
|
|
167
|
+
color: #fff;
|
|
168
|
+
}
|
|
169
|
+
.panel-hint {
|
|
170
|
+
margin-top: 12px;
|
|
171
|
+
font-size: 11px;
|
|
172
|
+
color: #555;
|
|
173
|
+
text-align: center;
|
|
174
|
+
}
|
|
175
|
+
.toolbar.vertical {
|
|
176
|
+
flex-direction: column;
|
|
177
|
+
height: auto;
|
|
178
|
+
width: 36px;
|
|
179
|
+
padding: 4px 0;
|
|
180
|
+
}
|
|
181
|
+
.toolbar.vertical .toolbar-divider {
|
|
182
|
+
width: 18px;
|
|
183
|
+
height: 1px;
|
|
184
|
+
margin: 2px 0;
|
|
185
|
+
}
|
|
186
|
+
`,A=class{host=null;shadowRoot=null;toolbarEl=null;btnEl=null;gearEl=null;panelEl=null;kbdEl=null;recordBtn=null;config;posX=97;posY=85;isDragging=!1;wasDragged=!1;dragPointerId=-1;dragStartX=0;dragStartY=0;dragOffsetX=0;dragOffsetY=0;panelOpen=!1;isRecording=!1;currentHotkey=``;toggleCb=null;hotkeyChangeCb=null;boundPointerDown=null;boundPointerMove=null;boundPointerUp=null;boundDocClick=null;boundDocKeyDown=null;boundRecordKeyDown=null;constructor(e){this.config=e;let t=y[e.initialPosition]??y[`bottom-right`],n=C(e.storageKey);n?(this.posX=n.x,this.posY=n.y):(this.posX=t.x,this.posY=t.y,this.posX<=_?this.posX=v():this.posX>=100-_&&(this.posX=100-v())),this.currentHotkey=T(e.hotkeyStorageKey)??``}getCurrentHotkey(){return this.currentHotkey}mount(){if(this.host)return;this.host=document.createElement(`div`),this.host.id=p,this.host.style.cssText=`position:fixed;z-index:2147483646;pointer-events:auto;transform:translate(-50%,-50%);transition:${h};`,this.applyPosition(),document.body.appendChild(this.host),this.shadowRoot=this.host.attachShadow({mode:`open`});let e=document.createElement(`style`);e.textContent=k,this.shadowRoot.appendChild(e),this.toolbarEl=document.createElement(`div`),this.toolbarEl.className=`toolbar`,this.btnEl=document.createElement(`div`),this.btnEl.className=`toolbar-btn grab-btn`,this.btnEl.innerHTML=D,this.toolbarEl.appendChild(this.btnEl);let t=document.createElement(`div`);t.className=`toolbar-divider`,this.toolbarEl.appendChild(t),this.gearEl=document.createElement(`div`),this.gearEl.className=`toolbar-btn gear-btn`,this.gearEl.innerHTML=O,this.toolbarEl.appendChild(this.gearEl),this.panelEl=document.createElement(`div`),this.panelEl.className=`panel`,this.panelEl.innerHTML=`
|
|
187
|
+
<div class="panel-header">
|
|
188
|
+
<span>Settings</span>
|
|
189
|
+
<button class="panel-close" data-action="close">×</button>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="panel-label">Hotkey</div>
|
|
192
|
+
<div class="hotkey-row">
|
|
193
|
+
<kbd></kbd>
|
|
194
|
+
<button class="record-btn">Record</button>
|
|
195
|
+
</div>
|
|
196
|
+
<div class="panel-hint">Drag toolbar to reposition</div>
|
|
197
|
+
`,this.toolbarEl.appendChild(this.panelEl),this.shadowRoot.appendChild(this.toolbarEl),this.kbdEl=this.panelEl.querySelector(`kbd`),this.recordBtn=this.panelEl.querySelector(`.record-btn`),this.updateHotkeyDisplay(),this.applyOrientation(this.getEdgeFromPosition()),this.boundPointerDown=this.onPointerDown.bind(this),this.boundPointerMove=this.onPointerMove.bind(this),this.boundPointerUp=this.onPointerUp.bind(this),this.toolbarEl.addEventListener(`pointerdown`,this.boundPointerDown),this.toolbarEl.addEventListener(`pointermove`,this.boundPointerMove),this.toolbarEl.addEventListener(`pointerup`,this.boundPointerUp),this.btnEl.addEventListener(`click`,e=>{if(e.stopPropagation(),!this.wasDragged){if(this.panelOpen){this.closePanel();return}this.toggleCb?.()}}),this.gearEl.addEventListener(`click`,e=>{e.stopPropagation(),!this.wasDragged&&this.togglePanel()}),this.panelEl.querySelector(`[data-action=close]`).addEventListener(`click`,e=>{e.stopPropagation(),this.closePanel()}),this.recordBtn.addEventListener(`click`,e=>{e.stopPropagation(),this.toggleRecording()}),this.boundDocClick=e=>{this.panelOpen&&(e.composedPath().includes(this.host)||this.closePanel())},document.addEventListener(`click`,this.boundDocClick,{capture:!0}),this.boundDocKeyDown=e=>{e.key===`Escape`&&(this.isRecording?(this.stopRecording(),e.preventDefault(),e.stopPropagation()):this.panelOpen&&(this.closePanel(),e.preventDefault(),e.stopPropagation()))},document.addEventListener(`keydown`,this.boundDocKeyDown,{capture:!0})}destroy(){this.boundDocClick&&document.removeEventListener(`click`,this.boundDocClick,{capture:!0}),this.boundDocKeyDown&&document.removeEventListener(`keydown`,this.boundDocKeyDown,{capture:!0}),this.boundRecordKeyDown&&document.removeEventListener(`keydown`,this.boundRecordKeyDown,{capture:!0}),this.host&&(this.host.remove(),this.host=null,this.shadowRoot=null,this.toolbarEl=null,this.btnEl=null,this.gearEl=null,this.panelEl=null,this.kbdEl=null,this.recordBtn=null)}setActive(e){this.btnEl&&(e?this.btnEl.classList.add(`active`):this.btnEl.classList.remove(`active`))}setHighlightColor(e){this.host?.style.setProperty(`--grab-color`,e)}setCurrentHotkey(e){this.currentHotkey=e,this.updateHotkeyDisplay()}onToggle(e){this.toggleCb=e}onHotkeyChange(e){this.hotkeyChangeCb=e}onPointerDown(e){if(e.button!==0)return;this.isDragging=!0,this.wasDragged=!1,this.dragPointerId=e.pointerId,this.dragStartX=e.clientX,this.dragStartY=e.clientY;let t=this.toolbarEl.getBoundingClientRect();this.dragOffsetX=e.clientX-t.left-t.width/2,this.dragOffsetY=e.clientY-t.top-t.height/2,this.host.style.transition=`none`}onPointerMove(e){if(!this.isDragging)return;let t=e.clientX-this.dragStartX,n=e.clientY-this.dragStartY;!this.wasDragged&&Math.abs(t)<m&&Math.abs(n)<m||(this.wasDragged||(this.wasDragged=!0,this.toolbarEl.setPointerCapture(this.dragPointerId),this.toolbarEl.classList.add(`dragging`)),this.posX=b((e.clientX-this.dragOffsetX)/window.innerWidth*100,2,98),this.posY=b((e.clientY-this.dragOffsetY)/window.innerHeight*100,2,98),this.applyPosition())}onPointerUp(e){this.isDragging&&(this.isDragging=!1,this.toolbarEl.releasePointerCapture(e.pointerId),this.toolbarEl.classList.remove(`dragging`),this.wasDragged&&(this.host.style.transition=h,this.snapToEdge(),w(this.config.storageKey,this.posX,this.posY)))}snapToEdge(){let e=this.getEdgeFromPosition(),t=v();e===`right`?this.posX=100-t:e===`bottom`?this.posY=100-g:e===`top`?this.posY=g:this.posX=t,this.applyPosition(),this.applyOrientation(e),this.panelOpen&&this.positionPanel()}applyPosition(){this.host&&(this.host.style.left=`${this.posX}%`,this.host.style.top=`${this.posY}%`)}getEdgeFromPosition(){let e=Math.atan2(this.posY-50,this.posX-50)*180/Math.PI;return e>=-45&&e<45?`right`:e>=45&&e<135?`bottom`:e>=-135&&e<-45?`top`:`left`}applyOrientation(e){if(!this.toolbarEl)return;let t=e===`left`||e===`right`;this.toolbarEl.classList.toggle(`vertical`,t)}togglePanel(){this.panelOpen?this.closePanel():this.openPanel()}openPanel(){this.panelEl&&(this.panelOpen=!0,this.positionPanel(),this.panelEl.classList.add(`open`))}closePanel(){this.panelEl&&(this.panelOpen=!1,this.panelEl.classList.remove(`open`),this.isRecording&&this.stopRecording())}positionPanel(){this.panelEl&&(this.posX>50?(this.panelEl.style.right=`calc(100% + 8px)`,this.panelEl.style.left=`auto`):(this.panelEl.style.left=`calc(100% + 8px)`,this.panelEl.style.right=`auto`),this.posY>70?(this.panelEl.style.bottom=`-4px`,this.panelEl.style.top=`auto`):(this.panelEl.style.top=`-4px`,this.panelEl.style.bottom=`auto`))}toggleRecording(){this.isRecording?this.stopRecording():this.startRecording()}startRecording(){this.isRecording=!0,this.kbdEl.textContent=`Press keys…`,this.kbdEl.classList.add(`recording`),this.recordBtn.textContent=`Cancel`,this.boundRecordKeyDown=e=>{if(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),[`Alt`,`Control`,`Shift`,`Meta`].includes(e.key))return;let t=d(e);this.currentHotkey=t,this.stopRecording(),E(this.config.hotkeyStorageKey,t),this.hotkeyChangeCb?.(t)},document.addEventListener(`keydown`,this.boundRecordKeyDown,{capture:!0})}stopRecording(){this.isRecording=!1,this.boundRecordKeyDown&&=(document.removeEventListener(`keydown`,this.boundRecordKeyDown,{capture:!0}),null),this.updateHotkeyDisplay(),this.recordBtn&&(this.recordBtn.textContent=`Record`),this.kbdEl&&this.kbdEl.classList.remove(`recording`)}updateHotkeyDisplay(){this.kbdEl&&(this.kbdEl.textContent=this.currentHotkey||`None`)}},j=new Set([`header`,`nav`,`footer`,`aside`,`main`,`layout`,`sidebar`]),M=class{config;overlay=null;callbacks=new Set;stateListeners=new Set;_isActive=!1;prevCursor=``;handleMouseMove=null;handleClick=null;handleKeyDown=null;constructor(e){this.config=e}get isActive(){return this._isActive}activate(){this._isActive||(this._isActive=!0,this.stateListeners.forEach(e=>e(!0)),this.overlay=new l(this.config),this.overlay.mount(),this.prevCursor=document.body.style.cursor,document.body.style.cursor=`crosshair`,this.handleMouseMove=e=>{let t=document.elementFromPoint(e.clientX,e.clientY);if(!t||this.shouldIgnore(t)){this.overlay?.clearHighlight();return}let n=this.getVueComponent(t);this.overlay?.highlight(t,this.getComponentLabelFromInstance(t,n))},this.handleClick=e=>{e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation();let t=document.elementFromPoint(e.clientX,e.clientY);if(!t||this.shouldIgnore(t))return;let n=t.outerHTML;this.config.maxHtmlLength>0&&n.length>this.config.maxHtmlLength&&(n=n.slice(0,this.config.maxHtmlLength)+`<!-- truncated -->`);let r={element:t,html:n,componentStack:this.getComponentStack(t),selector:this.generateSelector(t)};this.callbacks.forEach(e=>e(r)),this.deactivate()},this.handleKeyDown=e=>{e.key===`Escape`&&(e.preventDefault(),this.deactivate())},document.addEventListener(`mousemove`,this.handleMouseMove,{capture:!0}),document.addEventListener(`click`,this.handleClick,{capture:!0}),document.addEventListener(`keydown`,this.handleKeyDown,{capture:!0}))}deactivate(){this._isActive&&(this._isActive=!1,this.stateListeners.forEach(e=>e(!1)),this.handleMouseMove&&document.removeEventListener(`mousemove`,this.handleMouseMove,{capture:!0}),this.handleClick&&document.removeEventListener(`click`,this.handleClick,{capture:!0}),this.handleKeyDown&&document.removeEventListener(`keydown`,this.handleKeyDown,{capture:!0}),this.handleMouseMove=null,this.handleClick=null,this.handleKeyDown=null,this.overlay?.destroy(),this.overlay=null,document.body.style.cursor=this.prevCursor)}onGrab(e){return this.callbacks.add(e),()=>this.callbacks.delete(e)}onStateChange(e){return this.stateListeners.add(e),()=>this.stateListeners.delete(e)}toggle(){this._isActive?this.deactivate():this.activate()}destroy(){this.deactivate(),this.callbacks.clear(),this.stateListeners.clear()}updateConfig(e){this.config=a(this.config,e)}shouldIgnore(e){if(e.closest(`#vue-grab-overlay-host, #vue-grab-fab-host`))return!0;let t=e.tagName.toLowerCase();if(this.config.filter.ignoreTags.includes(t))return!0;for(let t of this.config.filter.ignoreSelectors)try{if(e.matches(t))return!0}catch{}if(this.config.filter.skipCommonComponents){let t=this.getVueComponent(e);if(t){let e=t.type.name||t.type.__name||``;if(j.has(e.toLowerCase()))return!0}}return!1}getVueComponent(e){let t=e;for(;t;){let e=t.__vueParentComponent||t.__vue_app__?._instance;if(e)return e;t=t.parentElement}return null}getComponentLabelFromInstance(e,t){if(t){let e=t.type?.name||t.type?.__name;if(e)return`<${e}>`}return`<${e.tagName.toLowerCase()}>`}getComponentStack(e){let t=[],n=e;for(;n;){let e=n.__vueParentComponent||n.__vue_app__?._instance;if(e){let n=e.type?.name||e.type?.__name||`Anonymous`,r=e.type?.__file,i={name:n};r&&(i.filePath=r),t.push(i)}n=n.parentElement}return t}generateSelector(e){if(e.id)return`#${CSS.escape(e.id)}`;let t=[],n=e;for(;n&&n!==document.body&&n!==document.documentElement;){let e=n.tagName.toLowerCase();if(n.id){t.unshift(`#${CSS.escape(n.id)}`);break}if(n.className&&typeof n.className==`string`){let t=n.className.trim().split(/\s+/).filter(Boolean);t.length>0&&(e+=`.${t.slice(0,2).map(e=>CSS.escape(e)).join(`.`)}`)}t.unshift(e),n=n.parentElement}return t.join(` > `)}};function N(e){let t=new M(e),n=new f,i=null;if(e.floatingButton.enabled){let r=new A(e.floatingButton);i=r;let a=r.getCurrentHotkey()||`Alt+Shift+G`;n.register(a,()=>t.toggle()),r.setHighlightColor(e.highlightColor),r.setCurrentHotkey(a),r.onToggle(()=>t.toggle()),r.onHotkeyChange(e=>{n.destroy(),n.register(e,()=>t.toggle()),r.setCurrentHotkey(e)}),t.onStateChange(e=>r.setActive(e)),r.mount()}else n.register(r,()=>t.toggle());return{engine:t,hotkeys:n,fab:i,destroy(){t.destroy(),n.destroy(),i?.destroy()}}}function P(){let t=(0,e.inject)(o,{...i}),n=(0,e.ref)(!1),r=(0,e.ref)(null),a=N(t),{engine:s}=a,c=s.onGrab(e=>{r.value=e}),l=s.onStateChange(e=>{n.value=e});return(0,e.onUnmounted)(()=>{c(),l(),a.destroy()}),{config:t,isActive:(0,e.readonly)(n),lastResult:(0,e.readonly)(r),activate:()=>s.activate(),deactivate:()=>s.deactivate(),toggle:()=>s.toggle()}}function F(e={}){let t=N(a(i,e));return{activate:()=>t.engine.activate(),deactivate:()=>t.engine.deactivate(),onGrab:e=>t.engine.onGrab(e),destroy:()=>t.destroy()}}exports.FAB_HOST_ID=p,exports.FloatingButton=A,exports.VUE_GRAB_CONFIG_KEY=o,exports.createVueGrab=s,exports.init=F,exports.useGrab=P;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createVueGrab, VUE_GRAB_CONFIG_KEY } from './plugin';
|
|
2
|
+
export { useGrab } from './composables';
|
|
3
|
+
export { init } from './init';
|
|
4
|
+
export { FloatingButton, FAB_HOST_ID } from './floating-button';
|
|
5
|
+
export type { GrabConfig, GrabResult, ComponentInfo, GrabFilterConfig, FloatingButtonConfig, } from '@sakana-y/vue-grab-shared';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChE,YAAY,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { inject as e, onUnmounted as t, readonly as n, ref as r } from "vue";
|
|
2
|
+
//#region ../shared/dist/index.mjs
|
|
3
|
+
var i = "#4f46e5", a = "#ffffff", o = "Alt+Shift+G", s = {
|
|
4
|
+
highlightColor: i,
|
|
5
|
+
labelTextColor: a,
|
|
6
|
+
showTagHint: !0,
|
|
7
|
+
maxHtmlLength: 1e4,
|
|
8
|
+
filter: {
|
|
9
|
+
ignoreSelectors: [],
|
|
10
|
+
ignoreTags: [],
|
|
11
|
+
skipCommonComponents: !1
|
|
12
|
+
},
|
|
13
|
+
floatingButton: {
|
|
14
|
+
enabled: !1,
|
|
15
|
+
initialPosition: "top-center",
|
|
16
|
+
storageKey: "vue-grab-fab-pos",
|
|
17
|
+
hotkeyStorageKey: "vue-grab-hotkey"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function c(e, t) {
|
|
21
|
+
let { filter: n, floatingButton: r, ...i } = t;
|
|
22
|
+
return {
|
|
23
|
+
...e,
|
|
24
|
+
...i,
|
|
25
|
+
filter: {
|
|
26
|
+
...e.filter,
|
|
27
|
+
...n
|
|
28
|
+
},
|
|
29
|
+
floatingButton: {
|
|
30
|
+
...e.floatingButton,
|
|
31
|
+
...r
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/plugin.ts
|
|
37
|
+
var l = Symbol("vue-grab-config");
|
|
38
|
+
function u(e = {}) {
|
|
39
|
+
let t = c(s, e);
|
|
40
|
+
return { install(e) {
|
|
41
|
+
e.provide(l, t);
|
|
42
|
+
} };
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/overlay/index.ts
|
|
46
|
+
var d = "vue-grab-overlay-host", f = class {
|
|
47
|
+
host = null;
|
|
48
|
+
shadowRoot = null;
|
|
49
|
+
highlightBox = null;
|
|
50
|
+
labelEl = null;
|
|
51
|
+
config;
|
|
52
|
+
constructor(e) {
|
|
53
|
+
this.config = e;
|
|
54
|
+
}
|
|
55
|
+
mount() {
|
|
56
|
+
if (this.host) return;
|
|
57
|
+
this.host = document.createElement("div"), this.host.id = d, this.host.style.cssText = "position:fixed;top:0;left:0;width:0;height:0;z-index:2147483647;pointer-events:none;", document.body.appendChild(this.host), this.shadowRoot = this.host.attachShadow({ mode: "open" }), this.host.style.setProperty("--grab-color", this.config.highlightColor), this.host.style.setProperty("--grab-label-color", this.config.labelTextColor);
|
|
58
|
+
let e = document.createElement("style");
|
|
59
|
+
e.textContent = "\n .grab-highlight {\n position: fixed;\n pointer-events: none;\n border: 2px solid var(--grab-color);\n background: color-mix(in srgb, var(--grab-color) 12%, transparent);\n border-radius: 2px;\n transition: all 0.05s ease-out;\n display: none;\n box-sizing: border-box;\n }\n .grab-label {\n position: fixed;\n pointer-events: none;\n background: var(--grab-color);\n color: var(--grab-label-color);\n font-size: 11px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n padding: 2px 6px;\n border-radius: 2px;\n white-space: nowrap;\n display: none;\n line-height: 1.4;\n }\n ", this.shadowRoot.appendChild(e), this.highlightBox = document.createElement("div"), this.highlightBox.className = "grab-highlight", this.shadowRoot.appendChild(this.highlightBox), this.labelEl = document.createElement("div"), this.labelEl.className = "grab-label", this.shadowRoot.appendChild(this.labelEl);
|
|
60
|
+
}
|
|
61
|
+
highlight(e, t) {
|
|
62
|
+
if (!this.highlightBox || !this.labelEl) return;
|
|
63
|
+
let n = e.getBoundingClientRect();
|
|
64
|
+
this.highlightBox.style.top = `${n.top}px`, this.highlightBox.style.left = `${n.left}px`, this.highlightBox.style.width = `${n.width}px`, this.highlightBox.style.height = `${n.height}px`, this.highlightBox.style.display = "block", this.config.showTagHint && t ? (this.labelEl.textContent = t, n.top > 24 ? this.labelEl.style.top = `${n.top - 20 - 4}px` : this.labelEl.style.top = `${n.bottom + 4}px`, this.labelEl.style.left = `${n.left}px`, this.labelEl.style.display = "block") : this.labelEl.style.display = "none";
|
|
65
|
+
}
|
|
66
|
+
clearHighlight() {
|
|
67
|
+
this.highlightBox && (this.highlightBox.style.display = "none"), this.labelEl && (this.labelEl.style.display = "none");
|
|
68
|
+
}
|
|
69
|
+
destroy() {
|
|
70
|
+
this.host && (this.host.remove(), this.host = null, this.shadowRoot = null, this.highlightBox = null, this.labelEl = null);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/hotkeys/index.ts
|
|
75
|
+
function p(e) {
|
|
76
|
+
let t = e.split("+").map((e) => e.trim().toLowerCase());
|
|
77
|
+
return {
|
|
78
|
+
key: t[t.length - 1],
|
|
79
|
+
alt: t.includes("alt"),
|
|
80
|
+
ctrl: t.includes("ctrl") || t.includes("control"),
|
|
81
|
+
shift: t.includes("shift"),
|
|
82
|
+
meta: t.includes("meta") || t.includes("cmd") || t.includes("command")
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function m(e) {
|
|
86
|
+
let t = [];
|
|
87
|
+
e.ctrlKey && t.push("Ctrl"), e.altKey && t.push("Alt"), e.shiftKey && t.push("Shift"), e.metaKey && t.push("Meta");
|
|
88
|
+
let n = e.key.length === 1 ? e.key.toUpperCase() : e.key;
|
|
89
|
+
return t.push(n), t.join("+");
|
|
90
|
+
}
|
|
91
|
+
var h = class {
|
|
92
|
+
cleanups = [];
|
|
93
|
+
register(e, t) {
|
|
94
|
+
let n = p(e), r = (e) => {
|
|
95
|
+
e.key.toLowerCase() === n.key && e.altKey === n.alt && e.ctrlKey === n.ctrl && e.shiftKey === n.shift && e.metaKey === n.meta && (e.preventDefault(), t());
|
|
96
|
+
};
|
|
97
|
+
document.addEventListener("keydown", r, { capture: !0 }), this.cleanups.push(() => document.removeEventListener("keydown", r, { capture: !0 }));
|
|
98
|
+
}
|
|
99
|
+
destroy() {
|
|
100
|
+
this.cleanups.forEach((e) => e()), this.cleanups = [];
|
|
101
|
+
}
|
|
102
|
+
}, g = "vue-grab-fab-host", _ = 3, v = "left 0.3s ease, top 0.3s ease", y = 3, b = 5;
|
|
103
|
+
function x() {
|
|
104
|
+
return y * window.innerHeight / window.innerWidth;
|
|
105
|
+
}
|
|
106
|
+
var S = {
|
|
107
|
+
"bottom-right": {
|
|
108
|
+
x: 97,
|
|
109
|
+
y: 85
|
|
110
|
+
},
|
|
111
|
+
"bottom-left": {
|
|
112
|
+
x: 3,
|
|
113
|
+
y: 85
|
|
114
|
+
},
|
|
115
|
+
"top-right": {
|
|
116
|
+
x: 97,
|
|
117
|
+
y: 15
|
|
118
|
+
},
|
|
119
|
+
"top-left": {
|
|
120
|
+
x: 3,
|
|
121
|
+
y: 15
|
|
122
|
+
},
|
|
123
|
+
"top-center": {
|
|
124
|
+
x: 50,
|
|
125
|
+
y: 3
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
function C(e, t, n) {
|
|
129
|
+
return Math.min(Math.max(e, t), n);
|
|
130
|
+
}
|
|
131
|
+
function w(e, t) {
|
|
132
|
+
if (!e) return null;
|
|
133
|
+
try {
|
|
134
|
+
let n = localStorage.getItem(e);
|
|
135
|
+
return n ? t(n) : null;
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function T(e, t) {
|
|
141
|
+
if (e) try {
|
|
142
|
+
localStorage.setItem(e, t);
|
|
143
|
+
} catch {}
|
|
144
|
+
}
|
|
145
|
+
function E(e) {
|
|
146
|
+
return w(e, (e) => {
|
|
147
|
+
let { x: t, y: n } = JSON.parse(e);
|
|
148
|
+
return typeof t == "number" && typeof n == "number" ? {
|
|
149
|
+
x: t,
|
|
150
|
+
y: n
|
|
151
|
+
} : null;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
function D(e, t, n) {
|
|
155
|
+
T(e, JSON.stringify({
|
|
156
|
+
x: t,
|
|
157
|
+
y: n
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
function O(e) {
|
|
161
|
+
return w(e, (e) => typeof e == "string" ? e : null);
|
|
162
|
+
}
|
|
163
|
+
function k(e, t) {
|
|
164
|
+
T(e, t);
|
|
165
|
+
}
|
|
166
|
+
var A = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"22\" y1=\"12\" x2=\"18\" y2=\"12\"/><line x1=\"6\" y1=\"12\" x2=\"2\" y2=\"12\"/><line x1=\"12\" y1=\"6\" x2=\"12\" y2=\"2\"/><line x1=\"12\" y1=\"22\" x2=\"12\" y2=\"18\"/><line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"9\"/><line x1=\"9\" y1=\"12\" x2=\"15\" y2=\"12\"/></svg>", j = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.32 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\"/></svg>", M = "\n :host {\n all: initial;\n }\n .toolbar {\n display: inline-flex;\n align-items: center;\n height: 36px;\n padding: 0 4px;\n gap: 2px;\n border-radius: 8px;\n border: 1px solid rgba(255,255,255,0.1);\n background: rgba(30,30,30,0.85);\n backdrop-filter: blur(8px);\n -webkit-backdrop-filter: blur(8px);\n box-shadow: 0 2px 10px rgba(0,0,0,0.35);\n cursor: grab;\n user-select: none;\n touch-action: none;\n position: relative;\n }\n .toolbar.dragging {\n cursor: grabbing;\n box-shadow: 0 4px 16px rgba(0,0,0,0.5);\n }\n .toolbar-btn {\n width: 28px;\n height: 28px;\n border-radius: 6px;\n border: none;\n background: transparent;\n color: #a0a0a0;\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n transition: color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;\n }\n .toolbar-btn:hover {\n color: #e0e0e0;\n background: rgba(255,255,255,0.08);\n }\n .grab-btn.active {\n color: var(--grab-color, #4f46e5);\n box-shadow: inset 0 0 0 1.5px var(--grab-color, #4f46e5);\n background: color-mix(in srgb, var(--grab-color, #4f46e5) 12%, transparent);\n }\n .toolbar-divider {\n width: 1px;\n height: 18px;\n background: rgba(255,255,255,0.12);\n margin: 0 2px;\n }\n\n .panel {\n position: absolute;\n width: 240px;\n background: rgba(25,25,25,0.94);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border: 1px solid rgba(255,255,255,0.1);\n border-radius: 12px;\n color: #e0e0e0;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 13px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.4);\n padding: 14px;\n display: none;\n z-index: 1;\n }\n .panel.open {\n display: block;\n }\n .panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 12px;\n font-weight: 600;\n font-size: 13px;\n color: #fff;\n }\n .panel-close {\n background: none;\n border: none;\n color: #888;\n cursor: pointer;\n font-size: 16px;\n line-height: 1;\n padding: 0 2px;\n }\n .panel-close:hover {\n color: #fff;\n }\n .panel-label {\n font-size: 11px;\n color: #888;\n margin-bottom: 6px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n .hotkey-row {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n kbd {\n display: inline-flex;\n align-items: center;\n background: rgba(255,255,255,0.08);\n border: 1px solid rgba(255,255,255,0.15);\n border-radius: 6px;\n padding: 4px 10px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-size: 12px;\n color: #ddd;\n min-width: 80px;\n justify-content: center;\n flex: 1;\n }\n kbd.recording {\n border-color: var(--grab-color, #4f46e5);\n box-shadow: 0 0 0 1px var(--grab-color, #4f46e5);\n animation: pulse 1.5s ease-in-out infinite;\n }\n @keyframes pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.6; }\n }\n .record-btn {\n background: rgba(255,255,255,0.08);\n border: 1px solid rgba(255,255,255,0.15);\n border-radius: 6px;\n color: #ccc;\n padding: 4px 10px;\n font-size: 12px;\n cursor: pointer;\n white-space: nowrap;\n font-family: inherit;\n }\n .record-btn:hover {\n background: rgba(255,255,255,0.14);\n color: #fff;\n }\n .panel-hint {\n margin-top: 12px;\n font-size: 11px;\n color: #555;\n text-align: center;\n }\n .toolbar.vertical {\n flex-direction: column;\n height: auto;\n width: 36px;\n padding: 4px 0;\n }\n .toolbar.vertical .toolbar-divider {\n width: 18px;\n height: 1px;\n margin: 2px 0;\n }\n", N = class {
|
|
167
|
+
host = null;
|
|
168
|
+
shadowRoot = null;
|
|
169
|
+
toolbarEl = null;
|
|
170
|
+
btnEl = null;
|
|
171
|
+
gearEl = null;
|
|
172
|
+
panelEl = null;
|
|
173
|
+
kbdEl = null;
|
|
174
|
+
recordBtn = null;
|
|
175
|
+
config;
|
|
176
|
+
posX = 97;
|
|
177
|
+
posY = 85;
|
|
178
|
+
isDragging = !1;
|
|
179
|
+
wasDragged = !1;
|
|
180
|
+
dragPointerId = -1;
|
|
181
|
+
dragStartX = 0;
|
|
182
|
+
dragStartY = 0;
|
|
183
|
+
dragOffsetX = 0;
|
|
184
|
+
dragOffsetY = 0;
|
|
185
|
+
panelOpen = !1;
|
|
186
|
+
isRecording = !1;
|
|
187
|
+
currentHotkey = "";
|
|
188
|
+
toggleCb = null;
|
|
189
|
+
hotkeyChangeCb = null;
|
|
190
|
+
boundPointerDown = null;
|
|
191
|
+
boundPointerMove = null;
|
|
192
|
+
boundPointerUp = null;
|
|
193
|
+
boundDocClick = null;
|
|
194
|
+
boundDocKeyDown = null;
|
|
195
|
+
boundRecordKeyDown = null;
|
|
196
|
+
constructor(e) {
|
|
197
|
+
this.config = e;
|
|
198
|
+
let t = S[e.initialPosition] ?? S["bottom-right"], n = E(e.storageKey);
|
|
199
|
+
n ? (this.posX = n.x, this.posY = n.y) : (this.posX = t.x, this.posY = t.y, this.posX <= b ? this.posX = x() : this.posX >= 100 - b && (this.posX = 100 - x())), this.currentHotkey = O(e.hotkeyStorageKey) ?? "";
|
|
200
|
+
}
|
|
201
|
+
getCurrentHotkey() {
|
|
202
|
+
return this.currentHotkey;
|
|
203
|
+
}
|
|
204
|
+
mount() {
|
|
205
|
+
if (this.host) return;
|
|
206
|
+
this.host = document.createElement("div"), this.host.id = g, this.host.style.cssText = `position:fixed;z-index:2147483646;pointer-events:auto;transform:translate(-50%,-50%);transition:${v};`, this.applyPosition(), document.body.appendChild(this.host), this.shadowRoot = this.host.attachShadow({ mode: "open" });
|
|
207
|
+
let e = document.createElement("style");
|
|
208
|
+
e.textContent = M, this.shadowRoot.appendChild(e), this.toolbarEl = document.createElement("div"), this.toolbarEl.className = "toolbar", this.btnEl = document.createElement("div"), this.btnEl.className = "toolbar-btn grab-btn", this.btnEl.innerHTML = A, this.toolbarEl.appendChild(this.btnEl);
|
|
209
|
+
let t = document.createElement("div");
|
|
210
|
+
t.className = "toolbar-divider", this.toolbarEl.appendChild(t), this.gearEl = document.createElement("div"), this.gearEl.className = "toolbar-btn gear-btn", this.gearEl.innerHTML = j, this.toolbarEl.appendChild(this.gearEl), this.panelEl = document.createElement("div"), this.panelEl.className = "panel", this.panelEl.innerHTML = "\n <div class=\"panel-header\">\n <span>Settings</span>\n <button class=\"panel-close\" data-action=\"close\">×</button>\n </div>\n <div class=\"panel-label\">Hotkey</div>\n <div class=\"hotkey-row\">\n <kbd></kbd>\n <button class=\"record-btn\">Record</button>\n </div>\n <div class=\"panel-hint\">Drag toolbar to reposition</div>\n ", this.toolbarEl.appendChild(this.panelEl), this.shadowRoot.appendChild(this.toolbarEl), this.kbdEl = this.panelEl.querySelector("kbd"), this.recordBtn = this.panelEl.querySelector(".record-btn"), this.updateHotkeyDisplay(), this.applyOrientation(this.getEdgeFromPosition()), this.boundPointerDown = this.onPointerDown.bind(this), this.boundPointerMove = this.onPointerMove.bind(this), this.boundPointerUp = this.onPointerUp.bind(this), this.toolbarEl.addEventListener("pointerdown", this.boundPointerDown), this.toolbarEl.addEventListener("pointermove", this.boundPointerMove), this.toolbarEl.addEventListener("pointerup", this.boundPointerUp), this.btnEl.addEventListener("click", (e) => {
|
|
211
|
+
if (e.stopPropagation(), !this.wasDragged) {
|
|
212
|
+
if (this.panelOpen) {
|
|
213
|
+
this.closePanel();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
this.toggleCb?.();
|
|
217
|
+
}
|
|
218
|
+
}), this.gearEl.addEventListener("click", (e) => {
|
|
219
|
+
e.stopPropagation(), !this.wasDragged && this.togglePanel();
|
|
220
|
+
}), this.panelEl.querySelector("[data-action=close]").addEventListener("click", (e) => {
|
|
221
|
+
e.stopPropagation(), this.closePanel();
|
|
222
|
+
}), this.recordBtn.addEventListener("click", (e) => {
|
|
223
|
+
e.stopPropagation(), this.toggleRecording();
|
|
224
|
+
}), this.boundDocClick = (e) => {
|
|
225
|
+
this.panelOpen && (e.composedPath().includes(this.host) || this.closePanel());
|
|
226
|
+
}, document.addEventListener("click", this.boundDocClick, { capture: !0 }), this.boundDocKeyDown = (e) => {
|
|
227
|
+
e.key === "Escape" && (this.isRecording ? (this.stopRecording(), e.preventDefault(), e.stopPropagation()) : this.panelOpen && (this.closePanel(), e.preventDefault(), e.stopPropagation()));
|
|
228
|
+
}, document.addEventListener("keydown", this.boundDocKeyDown, { capture: !0 });
|
|
229
|
+
}
|
|
230
|
+
destroy() {
|
|
231
|
+
this.boundDocClick && document.removeEventListener("click", this.boundDocClick, { capture: !0 }), this.boundDocKeyDown && document.removeEventListener("keydown", this.boundDocKeyDown, { capture: !0 }), this.boundRecordKeyDown && document.removeEventListener("keydown", this.boundRecordKeyDown, { capture: !0 }), this.host && (this.host.remove(), this.host = null, this.shadowRoot = null, this.toolbarEl = null, this.btnEl = null, this.gearEl = null, this.panelEl = null, this.kbdEl = null, this.recordBtn = null);
|
|
232
|
+
}
|
|
233
|
+
setActive(e) {
|
|
234
|
+
this.btnEl && (e ? this.btnEl.classList.add("active") : this.btnEl.classList.remove("active"));
|
|
235
|
+
}
|
|
236
|
+
setHighlightColor(e) {
|
|
237
|
+
this.host?.style.setProperty("--grab-color", e);
|
|
238
|
+
}
|
|
239
|
+
setCurrentHotkey(e) {
|
|
240
|
+
this.currentHotkey = e, this.updateHotkeyDisplay();
|
|
241
|
+
}
|
|
242
|
+
onToggle(e) {
|
|
243
|
+
this.toggleCb = e;
|
|
244
|
+
}
|
|
245
|
+
onHotkeyChange(e) {
|
|
246
|
+
this.hotkeyChangeCb = e;
|
|
247
|
+
}
|
|
248
|
+
onPointerDown(e) {
|
|
249
|
+
if (e.button !== 0) return;
|
|
250
|
+
this.isDragging = !0, this.wasDragged = !1, this.dragPointerId = e.pointerId, this.dragStartX = e.clientX, this.dragStartY = e.clientY;
|
|
251
|
+
let t = this.toolbarEl.getBoundingClientRect();
|
|
252
|
+
this.dragOffsetX = e.clientX - t.left - t.width / 2, this.dragOffsetY = e.clientY - t.top - t.height / 2, this.host.style.transition = "none";
|
|
253
|
+
}
|
|
254
|
+
onPointerMove(e) {
|
|
255
|
+
if (!this.isDragging) return;
|
|
256
|
+
let t = e.clientX - this.dragStartX, n = e.clientY - this.dragStartY;
|
|
257
|
+
!this.wasDragged && Math.abs(t) < _ && Math.abs(n) < _ || (this.wasDragged || (this.wasDragged = !0, this.toolbarEl.setPointerCapture(this.dragPointerId), this.toolbarEl.classList.add("dragging")), this.posX = C((e.clientX - this.dragOffsetX) / window.innerWidth * 100, 2, 98), this.posY = C((e.clientY - this.dragOffsetY) / window.innerHeight * 100, 2, 98), this.applyPosition());
|
|
258
|
+
}
|
|
259
|
+
onPointerUp(e) {
|
|
260
|
+
this.isDragging && (this.isDragging = !1, this.toolbarEl.releasePointerCapture(e.pointerId), this.toolbarEl.classList.remove("dragging"), this.wasDragged && (this.host.style.transition = v, this.snapToEdge(), D(this.config.storageKey, this.posX, this.posY)));
|
|
261
|
+
}
|
|
262
|
+
snapToEdge() {
|
|
263
|
+
let e = this.getEdgeFromPosition(), t = x();
|
|
264
|
+
e === "right" ? this.posX = 100 - t : e === "bottom" ? this.posY = 100 - y : e === "top" ? this.posY = y : this.posX = t, this.applyPosition(), this.applyOrientation(e), this.panelOpen && this.positionPanel();
|
|
265
|
+
}
|
|
266
|
+
applyPosition() {
|
|
267
|
+
this.host && (this.host.style.left = `${this.posX}%`, this.host.style.top = `${this.posY}%`);
|
|
268
|
+
}
|
|
269
|
+
getEdgeFromPosition() {
|
|
270
|
+
let e = Math.atan2(this.posY - 50, this.posX - 50) * 180 / Math.PI;
|
|
271
|
+
return e >= -45 && e < 45 ? "right" : e >= 45 && e < 135 ? "bottom" : e >= -135 && e < -45 ? "top" : "left";
|
|
272
|
+
}
|
|
273
|
+
applyOrientation(e) {
|
|
274
|
+
if (!this.toolbarEl) return;
|
|
275
|
+
let t = e === "left" || e === "right";
|
|
276
|
+
this.toolbarEl.classList.toggle("vertical", t);
|
|
277
|
+
}
|
|
278
|
+
togglePanel() {
|
|
279
|
+
this.panelOpen ? this.closePanel() : this.openPanel();
|
|
280
|
+
}
|
|
281
|
+
openPanel() {
|
|
282
|
+
this.panelEl && (this.panelOpen = !0, this.positionPanel(), this.panelEl.classList.add("open"));
|
|
283
|
+
}
|
|
284
|
+
closePanel() {
|
|
285
|
+
this.panelEl && (this.panelOpen = !1, this.panelEl.classList.remove("open"), this.isRecording && this.stopRecording());
|
|
286
|
+
}
|
|
287
|
+
positionPanel() {
|
|
288
|
+
this.panelEl && (this.posX > 50 ? (this.panelEl.style.right = "calc(100% + 8px)", this.panelEl.style.left = "auto") : (this.panelEl.style.left = "calc(100% + 8px)", this.panelEl.style.right = "auto"), this.posY > 70 ? (this.panelEl.style.bottom = "-4px", this.panelEl.style.top = "auto") : (this.panelEl.style.top = "-4px", this.panelEl.style.bottom = "auto"));
|
|
289
|
+
}
|
|
290
|
+
toggleRecording() {
|
|
291
|
+
this.isRecording ? this.stopRecording() : this.startRecording();
|
|
292
|
+
}
|
|
293
|
+
startRecording() {
|
|
294
|
+
this.isRecording = !0, this.kbdEl.textContent = "Press keys…", this.kbdEl.classList.add("recording"), this.recordBtn.textContent = "Cancel", this.boundRecordKeyDown = (e) => {
|
|
295
|
+
if (e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), [
|
|
296
|
+
"Alt",
|
|
297
|
+
"Control",
|
|
298
|
+
"Shift",
|
|
299
|
+
"Meta"
|
|
300
|
+
].includes(e.key)) return;
|
|
301
|
+
let t = m(e);
|
|
302
|
+
this.currentHotkey = t, this.stopRecording(), k(this.config.hotkeyStorageKey, t), this.hotkeyChangeCb?.(t);
|
|
303
|
+
}, document.addEventListener("keydown", this.boundRecordKeyDown, { capture: !0 });
|
|
304
|
+
}
|
|
305
|
+
stopRecording() {
|
|
306
|
+
this.isRecording = !1, this.boundRecordKeyDown &&= (document.removeEventListener("keydown", this.boundRecordKeyDown, { capture: !0 }), null), this.updateHotkeyDisplay(), this.recordBtn && (this.recordBtn.textContent = "Record"), this.kbdEl && this.kbdEl.classList.remove("recording");
|
|
307
|
+
}
|
|
308
|
+
updateHotkeyDisplay() {
|
|
309
|
+
this.kbdEl && (this.kbdEl.textContent = this.currentHotkey || "None");
|
|
310
|
+
}
|
|
311
|
+
}, P = new Set([
|
|
312
|
+
"header",
|
|
313
|
+
"nav",
|
|
314
|
+
"footer",
|
|
315
|
+
"aside",
|
|
316
|
+
"main",
|
|
317
|
+
"layout",
|
|
318
|
+
"sidebar"
|
|
319
|
+
]), F = class {
|
|
320
|
+
config;
|
|
321
|
+
overlay = null;
|
|
322
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
323
|
+
stateListeners = /* @__PURE__ */ new Set();
|
|
324
|
+
_isActive = !1;
|
|
325
|
+
prevCursor = "";
|
|
326
|
+
handleMouseMove = null;
|
|
327
|
+
handleClick = null;
|
|
328
|
+
handleKeyDown = null;
|
|
329
|
+
constructor(e) {
|
|
330
|
+
this.config = e;
|
|
331
|
+
}
|
|
332
|
+
get isActive() {
|
|
333
|
+
return this._isActive;
|
|
334
|
+
}
|
|
335
|
+
activate() {
|
|
336
|
+
this._isActive || (this._isActive = !0, this.stateListeners.forEach((e) => e(!0)), this.overlay = new f(this.config), this.overlay.mount(), this.prevCursor = document.body.style.cursor, document.body.style.cursor = "crosshair", this.handleMouseMove = (e) => {
|
|
337
|
+
let t = document.elementFromPoint(e.clientX, e.clientY);
|
|
338
|
+
if (!t || this.shouldIgnore(t)) {
|
|
339
|
+
this.overlay?.clearHighlight();
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
let n = this.getVueComponent(t);
|
|
343
|
+
this.overlay?.highlight(t, this.getComponentLabelFromInstance(t, n));
|
|
344
|
+
}, this.handleClick = (e) => {
|
|
345
|
+
e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation();
|
|
346
|
+
let t = document.elementFromPoint(e.clientX, e.clientY);
|
|
347
|
+
if (!t || this.shouldIgnore(t)) return;
|
|
348
|
+
let n = t.outerHTML;
|
|
349
|
+
this.config.maxHtmlLength > 0 && n.length > this.config.maxHtmlLength && (n = n.slice(0, this.config.maxHtmlLength) + "<!-- truncated -->");
|
|
350
|
+
let r = {
|
|
351
|
+
element: t,
|
|
352
|
+
html: n,
|
|
353
|
+
componentStack: this.getComponentStack(t),
|
|
354
|
+
selector: this.generateSelector(t)
|
|
355
|
+
};
|
|
356
|
+
this.callbacks.forEach((e) => e(r)), this.deactivate();
|
|
357
|
+
}, this.handleKeyDown = (e) => {
|
|
358
|
+
e.key === "Escape" && (e.preventDefault(), this.deactivate());
|
|
359
|
+
}, document.addEventListener("mousemove", this.handleMouseMove, { capture: !0 }), document.addEventListener("click", this.handleClick, { capture: !0 }), document.addEventListener("keydown", this.handleKeyDown, { capture: !0 }));
|
|
360
|
+
}
|
|
361
|
+
deactivate() {
|
|
362
|
+
this._isActive && (this._isActive = !1, this.stateListeners.forEach((e) => e(!1)), this.handleMouseMove && document.removeEventListener("mousemove", this.handleMouseMove, { capture: !0 }), this.handleClick && document.removeEventListener("click", this.handleClick, { capture: !0 }), this.handleKeyDown && document.removeEventListener("keydown", this.handleKeyDown, { capture: !0 }), this.handleMouseMove = null, this.handleClick = null, this.handleKeyDown = null, this.overlay?.destroy(), this.overlay = null, document.body.style.cursor = this.prevCursor);
|
|
363
|
+
}
|
|
364
|
+
onGrab(e) {
|
|
365
|
+
return this.callbacks.add(e), () => this.callbacks.delete(e);
|
|
366
|
+
}
|
|
367
|
+
onStateChange(e) {
|
|
368
|
+
return this.stateListeners.add(e), () => this.stateListeners.delete(e);
|
|
369
|
+
}
|
|
370
|
+
toggle() {
|
|
371
|
+
this._isActive ? this.deactivate() : this.activate();
|
|
372
|
+
}
|
|
373
|
+
destroy() {
|
|
374
|
+
this.deactivate(), this.callbacks.clear(), this.stateListeners.clear();
|
|
375
|
+
}
|
|
376
|
+
updateConfig(e) {
|
|
377
|
+
this.config = c(this.config, e);
|
|
378
|
+
}
|
|
379
|
+
shouldIgnore(e) {
|
|
380
|
+
if (e.closest("#vue-grab-overlay-host, #vue-grab-fab-host")) return !0;
|
|
381
|
+
let t = e.tagName.toLowerCase();
|
|
382
|
+
if (this.config.filter.ignoreTags.includes(t)) return !0;
|
|
383
|
+
for (let t of this.config.filter.ignoreSelectors) try {
|
|
384
|
+
if (e.matches(t)) return !0;
|
|
385
|
+
} catch {}
|
|
386
|
+
if (this.config.filter.skipCommonComponents) {
|
|
387
|
+
let t = this.getVueComponent(e);
|
|
388
|
+
if (t) {
|
|
389
|
+
let e = t.type.name || t.type.__name || "";
|
|
390
|
+
if (P.has(e.toLowerCase())) return !0;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return !1;
|
|
394
|
+
}
|
|
395
|
+
getVueComponent(e) {
|
|
396
|
+
let t = e;
|
|
397
|
+
for (; t;) {
|
|
398
|
+
let e = t.__vueParentComponent || t.__vue_app__?._instance;
|
|
399
|
+
if (e) return e;
|
|
400
|
+
t = t.parentElement;
|
|
401
|
+
}
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
getComponentLabelFromInstance(e, t) {
|
|
405
|
+
if (t) {
|
|
406
|
+
let e = t.type?.name || t.type?.__name;
|
|
407
|
+
if (e) return `<${e}>`;
|
|
408
|
+
}
|
|
409
|
+
return `<${e.tagName.toLowerCase()}>`;
|
|
410
|
+
}
|
|
411
|
+
getComponentStack(e) {
|
|
412
|
+
let t = [], n = e;
|
|
413
|
+
for (; n;) {
|
|
414
|
+
let e = n.__vueParentComponent || n.__vue_app__?._instance;
|
|
415
|
+
if (e) {
|
|
416
|
+
let n = e.type?.name || e.type?.__name || "Anonymous", r = e.type?.__file, i = { name: n };
|
|
417
|
+
r && (i.filePath = r), t.push(i);
|
|
418
|
+
}
|
|
419
|
+
n = n.parentElement;
|
|
420
|
+
}
|
|
421
|
+
return t;
|
|
422
|
+
}
|
|
423
|
+
generateSelector(e) {
|
|
424
|
+
if (e.id) return `#${CSS.escape(e.id)}`;
|
|
425
|
+
let t = [], n = e;
|
|
426
|
+
for (; n && n !== document.body && n !== document.documentElement;) {
|
|
427
|
+
let e = n.tagName.toLowerCase();
|
|
428
|
+
if (n.id) {
|
|
429
|
+
t.unshift(`#${CSS.escape(n.id)}`);
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
if (n.className && typeof n.className == "string") {
|
|
433
|
+
let t = n.className.trim().split(/\s+/).filter(Boolean);
|
|
434
|
+
t.length > 0 && (e += `.${t.slice(0, 2).map((e) => CSS.escape(e)).join(".")}`);
|
|
435
|
+
}
|
|
436
|
+
t.unshift(e), n = n.parentElement;
|
|
437
|
+
}
|
|
438
|
+
return t.join(" > ");
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
//#endregion
|
|
442
|
+
//#region src/session.ts
|
|
443
|
+
function I(e) {
|
|
444
|
+
let t = new F(e), n = new h(), r = null;
|
|
445
|
+
if (e.floatingButton.enabled) {
|
|
446
|
+
let i = new N(e.floatingButton);
|
|
447
|
+
r = i;
|
|
448
|
+
let a = i.getCurrentHotkey() || "Alt+Shift+G";
|
|
449
|
+
n.register(a, () => t.toggle()), i.setHighlightColor(e.highlightColor), i.setCurrentHotkey(a), i.onToggle(() => t.toggle()), i.onHotkeyChange((e) => {
|
|
450
|
+
n.destroy(), n.register(e, () => t.toggle()), i.setCurrentHotkey(e);
|
|
451
|
+
}), t.onStateChange((e) => i.setActive(e)), i.mount();
|
|
452
|
+
} else n.register(o, () => t.toggle());
|
|
453
|
+
return {
|
|
454
|
+
engine: t,
|
|
455
|
+
hotkeys: n,
|
|
456
|
+
fab: r,
|
|
457
|
+
destroy() {
|
|
458
|
+
t.destroy(), n.destroy(), r?.destroy();
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
//#endregion
|
|
463
|
+
//#region src/composables/index.ts
|
|
464
|
+
function L() {
|
|
465
|
+
let i = e(l, { ...s }), a = r(!1), o = r(null), c = I(i), { engine: u } = c, d = u.onGrab((e) => {
|
|
466
|
+
o.value = e;
|
|
467
|
+
}), f = u.onStateChange((e) => {
|
|
468
|
+
a.value = e;
|
|
469
|
+
});
|
|
470
|
+
return t(() => {
|
|
471
|
+
d(), f(), c.destroy();
|
|
472
|
+
}), {
|
|
473
|
+
config: i,
|
|
474
|
+
isActive: n(a),
|
|
475
|
+
lastResult: n(o),
|
|
476
|
+
activate: () => u.activate(),
|
|
477
|
+
deactivate: () => u.deactivate(),
|
|
478
|
+
toggle: () => u.toggle()
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
//#endregion
|
|
482
|
+
//#region src/init.ts
|
|
483
|
+
function R(e = {}) {
|
|
484
|
+
let t = I(c(s, e));
|
|
485
|
+
return {
|
|
486
|
+
activate: () => t.engine.activate(),
|
|
487
|
+
deactivate: () => t.engine.deactivate(),
|
|
488
|
+
onGrab: (e) => t.engine.onGrab(e),
|
|
489
|
+
destroy: () => t.destroy()
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
//#endregion
|
|
493
|
+
export { g as FAB_HOST_ID, N as FloatingButton, l as VUE_GRAB_CONFIG_KEY, u as createVueGrab, R as init, L as useGrab };
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`vue`)):typeof define==`function`&&define.amd?define([`exports`,`vue`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.VueGrab={},e.Vue))})(this,function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var n=`#4f46e5`,r=`#ffffff`,i=`Alt+Shift+G`,a={highlightColor:n,labelTextColor:r,showTagHint:!0,maxHtmlLength:1e4,filter:{ignoreSelectors:[],ignoreTags:[],skipCommonComponents:!1},floatingButton:{enabled:!1,initialPosition:`top-center`,storageKey:`vue-grab-fab-pos`,hotkeyStorageKey:`vue-grab-hotkey`}};function o(e,t){let{filter:n,floatingButton:r,...i}=t;return{...e,...i,filter:{...e.filter,...n},floatingButton:{...e.floatingButton,...r}}}var s=Symbol(`vue-grab-config`);function c(e={}){let t=o(a,e);return{install(e){e.provide(s,t)}}}var l=`vue-grab-overlay-host`,u=class{host=null;shadowRoot=null;highlightBox=null;labelEl=null;config;constructor(e){this.config=e}mount(){if(this.host)return;this.host=document.createElement(`div`),this.host.id=l,this.host.style.cssText=`position:fixed;top:0;left:0;width:0;height:0;z-index:2147483647;pointer-events:none;`,document.body.appendChild(this.host),this.shadowRoot=this.host.attachShadow({mode:`open`}),this.host.style.setProperty(`--grab-color`,this.config.highlightColor),this.host.style.setProperty(`--grab-label-color`,this.config.labelTextColor);let e=document.createElement(`style`);e.textContent=`
|
|
2
|
+
.grab-highlight {
|
|
3
|
+
position: fixed;
|
|
4
|
+
pointer-events: none;
|
|
5
|
+
border: 2px solid var(--grab-color);
|
|
6
|
+
background: color-mix(in srgb, var(--grab-color) 12%, transparent);
|
|
7
|
+
border-radius: 2px;
|
|
8
|
+
transition: all 0.05s ease-out;
|
|
9
|
+
display: none;
|
|
10
|
+
box-sizing: border-box;
|
|
11
|
+
}
|
|
12
|
+
.grab-label {
|
|
13
|
+
position: fixed;
|
|
14
|
+
pointer-events: none;
|
|
15
|
+
background: var(--grab-color);
|
|
16
|
+
color: var(--grab-label-color);
|
|
17
|
+
font-size: 11px;
|
|
18
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
19
|
+
padding: 2px 6px;
|
|
20
|
+
border-radius: 2px;
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
display: none;
|
|
23
|
+
line-height: 1.4;
|
|
24
|
+
}
|
|
25
|
+
`,this.shadowRoot.appendChild(e),this.highlightBox=document.createElement(`div`),this.highlightBox.className=`grab-highlight`,this.shadowRoot.appendChild(this.highlightBox),this.labelEl=document.createElement(`div`),this.labelEl.className=`grab-label`,this.shadowRoot.appendChild(this.labelEl)}highlight(e,t){if(!this.highlightBox||!this.labelEl)return;let n=e.getBoundingClientRect();this.highlightBox.style.top=`${n.top}px`,this.highlightBox.style.left=`${n.left}px`,this.highlightBox.style.width=`${n.width}px`,this.highlightBox.style.height=`${n.height}px`,this.highlightBox.style.display=`block`,this.config.showTagHint&&t?(this.labelEl.textContent=t,n.top>24?this.labelEl.style.top=`${n.top-20-4}px`:this.labelEl.style.top=`${n.bottom+4}px`,this.labelEl.style.left=`${n.left}px`,this.labelEl.style.display=`block`):this.labelEl.style.display=`none`}clearHighlight(){this.highlightBox&&(this.highlightBox.style.display=`none`),this.labelEl&&(this.labelEl.style.display=`none`)}destroy(){this.host&&(this.host.remove(),this.host=null,this.shadowRoot=null,this.highlightBox=null,this.labelEl=null)}};function d(e){let t=e.split(`+`).map(e=>e.trim().toLowerCase());return{key:t[t.length-1],alt:t.includes(`alt`),ctrl:t.includes(`ctrl`)||t.includes(`control`),shift:t.includes(`shift`),meta:t.includes(`meta`)||t.includes(`cmd`)||t.includes(`command`)}}function f(e){let t=[];e.ctrlKey&&t.push(`Ctrl`),e.altKey&&t.push(`Alt`),e.shiftKey&&t.push(`Shift`),e.metaKey&&t.push(`Meta`);let n=e.key.length===1?e.key.toUpperCase():e.key;return t.push(n),t.join(`+`)}var p=class{cleanups=[];register(e,t){let n=d(e),r=e=>{e.key.toLowerCase()===n.key&&e.altKey===n.alt&&e.ctrlKey===n.ctrl&&e.shiftKey===n.shift&&e.metaKey===n.meta&&(e.preventDefault(),t())};document.addEventListener(`keydown`,r,{capture:!0}),this.cleanups.push(()=>document.removeEventListener(`keydown`,r,{capture:!0}))}destroy(){this.cleanups.forEach(e=>e()),this.cleanups=[]}},m=`vue-grab-fab-host`,h=3,g=`left 0.3s ease, top 0.3s ease`,_=3,v=5;function y(){return _*window.innerHeight/window.innerWidth}var b={"bottom-right":{x:97,y:85},"bottom-left":{x:3,y:85},"top-right":{x:97,y:15},"top-left":{x:3,y:15},"top-center":{x:50,y:3}};function x(e,t,n){return Math.min(Math.max(e,t),n)}function S(e,t){if(!e)return null;try{let n=localStorage.getItem(e);return n?t(n):null}catch{return null}}function C(e,t){if(e)try{localStorage.setItem(e,t)}catch{}}function w(e){return S(e,e=>{let{x:t,y:n}=JSON.parse(e);return typeof t==`number`&&typeof n==`number`?{x:t,y:n}:null})}function T(e,t,n){C(e,JSON.stringify({x:t,y:n}))}function E(e){return S(e,e=>typeof e==`string`?e:null)}function D(e,t){C(e,t)}var O=`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="22" y1="12" x2="18" y2="12"/><line x1="6" y1="12" x2="2" y2="12"/><line x1="12" y1="6" x2="12" y2="2"/><line x1="12" y1="22" x2="12" y2="18"/><line x1="12" y1="15" x2="12" y2="9"/><line x1="9" y1="12" x2="15" y2="12"/></svg>`,k=`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.32 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>`,A=`
|
|
26
|
+
:host {
|
|
27
|
+
all: initial;
|
|
28
|
+
}
|
|
29
|
+
.toolbar {
|
|
30
|
+
display: inline-flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
height: 36px;
|
|
33
|
+
padding: 0 4px;
|
|
34
|
+
gap: 2px;
|
|
35
|
+
border-radius: 8px;
|
|
36
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
37
|
+
background: rgba(30,30,30,0.85);
|
|
38
|
+
backdrop-filter: blur(8px);
|
|
39
|
+
-webkit-backdrop-filter: blur(8px);
|
|
40
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.35);
|
|
41
|
+
cursor: grab;
|
|
42
|
+
user-select: none;
|
|
43
|
+
touch-action: none;
|
|
44
|
+
position: relative;
|
|
45
|
+
}
|
|
46
|
+
.toolbar.dragging {
|
|
47
|
+
cursor: grabbing;
|
|
48
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.5);
|
|
49
|
+
}
|
|
50
|
+
.toolbar-btn {
|
|
51
|
+
width: 28px;
|
|
52
|
+
height: 28px;
|
|
53
|
+
border-radius: 6px;
|
|
54
|
+
border: none;
|
|
55
|
+
background: transparent;
|
|
56
|
+
color: #a0a0a0;
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
justify-content: center;
|
|
60
|
+
cursor: pointer;
|
|
61
|
+
transition: color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
|
|
62
|
+
}
|
|
63
|
+
.toolbar-btn:hover {
|
|
64
|
+
color: #e0e0e0;
|
|
65
|
+
background: rgba(255,255,255,0.08);
|
|
66
|
+
}
|
|
67
|
+
.grab-btn.active {
|
|
68
|
+
color: var(--grab-color, #4f46e5);
|
|
69
|
+
box-shadow: inset 0 0 0 1.5px var(--grab-color, #4f46e5);
|
|
70
|
+
background: color-mix(in srgb, var(--grab-color, #4f46e5) 12%, transparent);
|
|
71
|
+
}
|
|
72
|
+
.toolbar-divider {
|
|
73
|
+
width: 1px;
|
|
74
|
+
height: 18px;
|
|
75
|
+
background: rgba(255,255,255,0.12);
|
|
76
|
+
margin: 0 2px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.panel {
|
|
80
|
+
position: absolute;
|
|
81
|
+
width: 240px;
|
|
82
|
+
background: rgba(25,25,25,0.94);
|
|
83
|
+
backdrop-filter: blur(12px);
|
|
84
|
+
-webkit-backdrop-filter: blur(12px);
|
|
85
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
86
|
+
border-radius: 12px;
|
|
87
|
+
color: #e0e0e0;
|
|
88
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
89
|
+
font-size: 13px;
|
|
90
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
|
91
|
+
padding: 14px;
|
|
92
|
+
display: none;
|
|
93
|
+
z-index: 1;
|
|
94
|
+
}
|
|
95
|
+
.panel.open {
|
|
96
|
+
display: block;
|
|
97
|
+
}
|
|
98
|
+
.panel-header {
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
justify-content: space-between;
|
|
102
|
+
margin-bottom: 12px;
|
|
103
|
+
font-weight: 600;
|
|
104
|
+
font-size: 13px;
|
|
105
|
+
color: #fff;
|
|
106
|
+
}
|
|
107
|
+
.panel-close {
|
|
108
|
+
background: none;
|
|
109
|
+
border: none;
|
|
110
|
+
color: #888;
|
|
111
|
+
cursor: pointer;
|
|
112
|
+
font-size: 16px;
|
|
113
|
+
line-height: 1;
|
|
114
|
+
padding: 0 2px;
|
|
115
|
+
}
|
|
116
|
+
.panel-close:hover {
|
|
117
|
+
color: #fff;
|
|
118
|
+
}
|
|
119
|
+
.panel-label {
|
|
120
|
+
font-size: 11px;
|
|
121
|
+
color: #888;
|
|
122
|
+
margin-bottom: 6px;
|
|
123
|
+
text-transform: uppercase;
|
|
124
|
+
letter-spacing: 0.5px;
|
|
125
|
+
}
|
|
126
|
+
.hotkey-row {
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
gap: 8px;
|
|
130
|
+
}
|
|
131
|
+
kbd {
|
|
132
|
+
display: inline-flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
background: rgba(255,255,255,0.08);
|
|
135
|
+
border: 1px solid rgba(255,255,255,0.15);
|
|
136
|
+
border-radius: 6px;
|
|
137
|
+
padding: 4px 10px;
|
|
138
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
139
|
+
font-size: 12px;
|
|
140
|
+
color: #ddd;
|
|
141
|
+
min-width: 80px;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
flex: 1;
|
|
144
|
+
}
|
|
145
|
+
kbd.recording {
|
|
146
|
+
border-color: var(--grab-color, #4f46e5);
|
|
147
|
+
box-shadow: 0 0 0 1px var(--grab-color, #4f46e5);
|
|
148
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
149
|
+
}
|
|
150
|
+
@keyframes pulse {
|
|
151
|
+
0%, 100% { opacity: 1; }
|
|
152
|
+
50% { opacity: 0.6; }
|
|
153
|
+
}
|
|
154
|
+
.record-btn {
|
|
155
|
+
background: rgba(255,255,255,0.08);
|
|
156
|
+
border: 1px solid rgba(255,255,255,0.15);
|
|
157
|
+
border-radius: 6px;
|
|
158
|
+
color: #ccc;
|
|
159
|
+
padding: 4px 10px;
|
|
160
|
+
font-size: 12px;
|
|
161
|
+
cursor: pointer;
|
|
162
|
+
white-space: nowrap;
|
|
163
|
+
font-family: inherit;
|
|
164
|
+
}
|
|
165
|
+
.record-btn:hover {
|
|
166
|
+
background: rgba(255,255,255,0.14);
|
|
167
|
+
color: #fff;
|
|
168
|
+
}
|
|
169
|
+
.panel-hint {
|
|
170
|
+
margin-top: 12px;
|
|
171
|
+
font-size: 11px;
|
|
172
|
+
color: #555;
|
|
173
|
+
text-align: center;
|
|
174
|
+
}
|
|
175
|
+
.toolbar.vertical {
|
|
176
|
+
flex-direction: column;
|
|
177
|
+
height: auto;
|
|
178
|
+
width: 36px;
|
|
179
|
+
padding: 4px 0;
|
|
180
|
+
}
|
|
181
|
+
.toolbar.vertical .toolbar-divider {
|
|
182
|
+
width: 18px;
|
|
183
|
+
height: 1px;
|
|
184
|
+
margin: 2px 0;
|
|
185
|
+
}
|
|
186
|
+
`,j=class{host=null;shadowRoot=null;toolbarEl=null;btnEl=null;gearEl=null;panelEl=null;kbdEl=null;recordBtn=null;config;posX=97;posY=85;isDragging=!1;wasDragged=!1;dragPointerId=-1;dragStartX=0;dragStartY=0;dragOffsetX=0;dragOffsetY=0;panelOpen=!1;isRecording=!1;currentHotkey=``;toggleCb=null;hotkeyChangeCb=null;boundPointerDown=null;boundPointerMove=null;boundPointerUp=null;boundDocClick=null;boundDocKeyDown=null;boundRecordKeyDown=null;constructor(e){this.config=e;let t=b[e.initialPosition]??b[`bottom-right`],n=w(e.storageKey);n?(this.posX=n.x,this.posY=n.y):(this.posX=t.x,this.posY=t.y,this.posX<=v?this.posX=y():this.posX>=100-v&&(this.posX=100-y())),this.currentHotkey=E(e.hotkeyStorageKey)??``}getCurrentHotkey(){return this.currentHotkey}mount(){if(this.host)return;this.host=document.createElement(`div`),this.host.id=m,this.host.style.cssText=`position:fixed;z-index:2147483646;pointer-events:auto;transform:translate(-50%,-50%);transition:${g};`,this.applyPosition(),document.body.appendChild(this.host),this.shadowRoot=this.host.attachShadow({mode:`open`});let e=document.createElement(`style`);e.textContent=A,this.shadowRoot.appendChild(e),this.toolbarEl=document.createElement(`div`),this.toolbarEl.className=`toolbar`,this.btnEl=document.createElement(`div`),this.btnEl.className=`toolbar-btn grab-btn`,this.btnEl.innerHTML=O,this.toolbarEl.appendChild(this.btnEl);let t=document.createElement(`div`);t.className=`toolbar-divider`,this.toolbarEl.appendChild(t),this.gearEl=document.createElement(`div`),this.gearEl.className=`toolbar-btn gear-btn`,this.gearEl.innerHTML=k,this.toolbarEl.appendChild(this.gearEl),this.panelEl=document.createElement(`div`),this.panelEl.className=`panel`,this.panelEl.innerHTML=`
|
|
187
|
+
<div class="panel-header">
|
|
188
|
+
<span>Settings</span>
|
|
189
|
+
<button class="panel-close" data-action="close">×</button>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="panel-label">Hotkey</div>
|
|
192
|
+
<div class="hotkey-row">
|
|
193
|
+
<kbd></kbd>
|
|
194
|
+
<button class="record-btn">Record</button>
|
|
195
|
+
</div>
|
|
196
|
+
<div class="panel-hint">Drag toolbar to reposition</div>
|
|
197
|
+
`,this.toolbarEl.appendChild(this.panelEl),this.shadowRoot.appendChild(this.toolbarEl),this.kbdEl=this.panelEl.querySelector(`kbd`),this.recordBtn=this.panelEl.querySelector(`.record-btn`),this.updateHotkeyDisplay(),this.applyOrientation(this.getEdgeFromPosition()),this.boundPointerDown=this.onPointerDown.bind(this),this.boundPointerMove=this.onPointerMove.bind(this),this.boundPointerUp=this.onPointerUp.bind(this),this.toolbarEl.addEventListener(`pointerdown`,this.boundPointerDown),this.toolbarEl.addEventListener(`pointermove`,this.boundPointerMove),this.toolbarEl.addEventListener(`pointerup`,this.boundPointerUp),this.btnEl.addEventListener(`click`,e=>{if(e.stopPropagation(),!this.wasDragged){if(this.panelOpen){this.closePanel();return}this.toggleCb?.()}}),this.gearEl.addEventListener(`click`,e=>{e.stopPropagation(),!this.wasDragged&&this.togglePanel()}),this.panelEl.querySelector(`[data-action=close]`).addEventListener(`click`,e=>{e.stopPropagation(),this.closePanel()}),this.recordBtn.addEventListener(`click`,e=>{e.stopPropagation(),this.toggleRecording()}),this.boundDocClick=e=>{this.panelOpen&&(e.composedPath().includes(this.host)||this.closePanel())},document.addEventListener(`click`,this.boundDocClick,{capture:!0}),this.boundDocKeyDown=e=>{e.key===`Escape`&&(this.isRecording?(this.stopRecording(),e.preventDefault(),e.stopPropagation()):this.panelOpen&&(this.closePanel(),e.preventDefault(),e.stopPropagation()))},document.addEventListener(`keydown`,this.boundDocKeyDown,{capture:!0})}destroy(){this.boundDocClick&&document.removeEventListener(`click`,this.boundDocClick,{capture:!0}),this.boundDocKeyDown&&document.removeEventListener(`keydown`,this.boundDocKeyDown,{capture:!0}),this.boundRecordKeyDown&&document.removeEventListener(`keydown`,this.boundRecordKeyDown,{capture:!0}),this.host&&(this.host.remove(),this.host=null,this.shadowRoot=null,this.toolbarEl=null,this.btnEl=null,this.gearEl=null,this.panelEl=null,this.kbdEl=null,this.recordBtn=null)}setActive(e){this.btnEl&&(e?this.btnEl.classList.add(`active`):this.btnEl.classList.remove(`active`))}setHighlightColor(e){this.host?.style.setProperty(`--grab-color`,e)}setCurrentHotkey(e){this.currentHotkey=e,this.updateHotkeyDisplay()}onToggle(e){this.toggleCb=e}onHotkeyChange(e){this.hotkeyChangeCb=e}onPointerDown(e){if(e.button!==0)return;this.isDragging=!0,this.wasDragged=!1,this.dragPointerId=e.pointerId,this.dragStartX=e.clientX,this.dragStartY=e.clientY;let t=this.toolbarEl.getBoundingClientRect();this.dragOffsetX=e.clientX-t.left-t.width/2,this.dragOffsetY=e.clientY-t.top-t.height/2,this.host.style.transition=`none`}onPointerMove(e){if(!this.isDragging)return;let t=e.clientX-this.dragStartX,n=e.clientY-this.dragStartY;!this.wasDragged&&Math.abs(t)<h&&Math.abs(n)<h||(this.wasDragged||(this.wasDragged=!0,this.toolbarEl.setPointerCapture(this.dragPointerId),this.toolbarEl.classList.add(`dragging`)),this.posX=x((e.clientX-this.dragOffsetX)/window.innerWidth*100,2,98),this.posY=x((e.clientY-this.dragOffsetY)/window.innerHeight*100,2,98),this.applyPosition())}onPointerUp(e){this.isDragging&&(this.isDragging=!1,this.toolbarEl.releasePointerCapture(e.pointerId),this.toolbarEl.classList.remove(`dragging`),this.wasDragged&&(this.host.style.transition=g,this.snapToEdge(),T(this.config.storageKey,this.posX,this.posY)))}snapToEdge(){let e=this.getEdgeFromPosition(),t=y();e===`right`?this.posX=100-t:e===`bottom`?this.posY=100-_:e===`top`?this.posY=_:this.posX=t,this.applyPosition(),this.applyOrientation(e),this.panelOpen&&this.positionPanel()}applyPosition(){this.host&&(this.host.style.left=`${this.posX}%`,this.host.style.top=`${this.posY}%`)}getEdgeFromPosition(){let e=Math.atan2(this.posY-50,this.posX-50)*180/Math.PI;return e>=-45&&e<45?`right`:e>=45&&e<135?`bottom`:e>=-135&&e<-45?`top`:`left`}applyOrientation(e){if(!this.toolbarEl)return;let t=e===`left`||e===`right`;this.toolbarEl.classList.toggle(`vertical`,t)}togglePanel(){this.panelOpen?this.closePanel():this.openPanel()}openPanel(){this.panelEl&&(this.panelOpen=!0,this.positionPanel(),this.panelEl.classList.add(`open`))}closePanel(){this.panelEl&&(this.panelOpen=!1,this.panelEl.classList.remove(`open`),this.isRecording&&this.stopRecording())}positionPanel(){this.panelEl&&(this.posX>50?(this.panelEl.style.right=`calc(100% + 8px)`,this.panelEl.style.left=`auto`):(this.panelEl.style.left=`calc(100% + 8px)`,this.panelEl.style.right=`auto`),this.posY>70?(this.panelEl.style.bottom=`-4px`,this.panelEl.style.top=`auto`):(this.panelEl.style.top=`-4px`,this.panelEl.style.bottom=`auto`))}toggleRecording(){this.isRecording?this.stopRecording():this.startRecording()}startRecording(){this.isRecording=!0,this.kbdEl.textContent=`Press keys…`,this.kbdEl.classList.add(`recording`),this.recordBtn.textContent=`Cancel`,this.boundRecordKeyDown=e=>{if(e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),[`Alt`,`Control`,`Shift`,`Meta`].includes(e.key))return;let t=f(e);this.currentHotkey=t,this.stopRecording(),D(this.config.hotkeyStorageKey,t),this.hotkeyChangeCb?.(t)},document.addEventListener(`keydown`,this.boundRecordKeyDown,{capture:!0})}stopRecording(){this.isRecording=!1,this.boundRecordKeyDown&&=(document.removeEventListener(`keydown`,this.boundRecordKeyDown,{capture:!0}),null),this.updateHotkeyDisplay(),this.recordBtn&&(this.recordBtn.textContent=`Record`),this.kbdEl&&this.kbdEl.classList.remove(`recording`)}updateHotkeyDisplay(){this.kbdEl&&(this.kbdEl.textContent=this.currentHotkey||`None`)}},M=new Set([`header`,`nav`,`footer`,`aside`,`main`,`layout`,`sidebar`]),N=class{config;overlay=null;callbacks=new Set;stateListeners=new Set;_isActive=!1;prevCursor=``;handleMouseMove=null;handleClick=null;handleKeyDown=null;constructor(e){this.config=e}get isActive(){return this._isActive}activate(){this._isActive||(this._isActive=!0,this.stateListeners.forEach(e=>e(!0)),this.overlay=new u(this.config),this.overlay.mount(),this.prevCursor=document.body.style.cursor,document.body.style.cursor=`crosshair`,this.handleMouseMove=e=>{let t=document.elementFromPoint(e.clientX,e.clientY);if(!t||this.shouldIgnore(t)){this.overlay?.clearHighlight();return}let n=this.getVueComponent(t);this.overlay?.highlight(t,this.getComponentLabelFromInstance(t,n))},this.handleClick=e=>{e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation();let t=document.elementFromPoint(e.clientX,e.clientY);if(!t||this.shouldIgnore(t))return;let n=t.outerHTML;this.config.maxHtmlLength>0&&n.length>this.config.maxHtmlLength&&(n=n.slice(0,this.config.maxHtmlLength)+`<!-- truncated -->`);let r={element:t,html:n,componentStack:this.getComponentStack(t),selector:this.generateSelector(t)};this.callbacks.forEach(e=>e(r)),this.deactivate()},this.handleKeyDown=e=>{e.key===`Escape`&&(e.preventDefault(),this.deactivate())},document.addEventListener(`mousemove`,this.handleMouseMove,{capture:!0}),document.addEventListener(`click`,this.handleClick,{capture:!0}),document.addEventListener(`keydown`,this.handleKeyDown,{capture:!0}))}deactivate(){this._isActive&&(this._isActive=!1,this.stateListeners.forEach(e=>e(!1)),this.handleMouseMove&&document.removeEventListener(`mousemove`,this.handleMouseMove,{capture:!0}),this.handleClick&&document.removeEventListener(`click`,this.handleClick,{capture:!0}),this.handleKeyDown&&document.removeEventListener(`keydown`,this.handleKeyDown,{capture:!0}),this.handleMouseMove=null,this.handleClick=null,this.handleKeyDown=null,this.overlay?.destroy(),this.overlay=null,document.body.style.cursor=this.prevCursor)}onGrab(e){return this.callbacks.add(e),()=>this.callbacks.delete(e)}onStateChange(e){return this.stateListeners.add(e),()=>this.stateListeners.delete(e)}toggle(){this._isActive?this.deactivate():this.activate()}destroy(){this.deactivate(),this.callbacks.clear(),this.stateListeners.clear()}updateConfig(e){this.config=o(this.config,e)}shouldIgnore(e){if(e.closest(`#vue-grab-overlay-host, #vue-grab-fab-host`))return!0;let t=e.tagName.toLowerCase();if(this.config.filter.ignoreTags.includes(t))return!0;for(let t of this.config.filter.ignoreSelectors)try{if(e.matches(t))return!0}catch{}if(this.config.filter.skipCommonComponents){let t=this.getVueComponent(e);if(t){let e=t.type.name||t.type.__name||``;if(M.has(e.toLowerCase()))return!0}}return!1}getVueComponent(e){let t=e;for(;t;){let e=t.__vueParentComponent||t.__vue_app__?._instance;if(e)return e;t=t.parentElement}return null}getComponentLabelFromInstance(e,t){if(t){let e=t.type?.name||t.type?.__name;if(e)return`<${e}>`}return`<${e.tagName.toLowerCase()}>`}getComponentStack(e){let t=[],n=e;for(;n;){let e=n.__vueParentComponent||n.__vue_app__?._instance;if(e){let n=e.type?.name||e.type?.__name||`Anonymous`,r=e.type?.__file,i={name:n};r&&(i.filePath=r),t.push(i)}n=n.parentElement}return t}generateSelector(e){if(e.id)return`#${CSS.escape(e.id)}`;let t=[],n=e;for(;n&&n!==document.body&&n!==document.documentElement;){let e=n.tagName.toLowerCase();if(n.id){t.unshift(`#${CSS.escape(n.id)}`);break}if(n.className&&typeof n.className==`string`){let t=n.className.trim().split(/\s+/).filter(Boolean);t.length>0&&(e+=`.${t.slice(0,2).map(e=>CSS.escape(e)).join(`.`)}`)}t.unshift(e),n=n.parentElement}return t.join(` > `)}};function P(e){let t=new N(e),n=new p,r=null;if(e.floatingButton.enabled){let i=new j(e.floatingButton);r=i;let a=i.getCurrentHotkey()||`Alt+Shift+G`;n.register(a,()=>t.toggle()),i.setHighlightColor(e.highlightColor),i.setCurrentHotkey(a),i.onToggle(()=>t.toggle()),i.onHotkeyChange(e=>{n.destroy(),n.register(e,()=>t.toggle()),i.setCurrentHotkey(e)}),t.onStateChange(e=>i.setActive(e)),i.mount()}else n.register(i,()=>t.toggle());return{engine:t,hotkeys:n,fab:r,destroy(){t.destroy(),n.destroy(),r?.destroy()}}}function F(){let e=(0,t.inject)(s,{...a}),n=(0,t.ref)(!1),r=(0,t.ref)(null),i=P(e),{engine:o}=i,c=o.onGrab(e=>{r.value=e}),l=o.onStateChange(e=>{n.value=e});return(0,t.onUnmounted)(()=>{c(),l(),i.destroy()}),{config:e,isActive:(0,t.readonly)(n),lastResult:(0,t.readonly)(r),activate:()=>o.activate(),deactivate:()=>o.deactivate(),toggle:()=>o.toggle()}}function I(e={}){let t=P(o(a,e));return{activate:()=>t.engine.activate(),deactivate:()=>t.engine.deactivate(),onGrab:e=>t.engine.onGrab(e),destroy:()=>t.destroy()}}e.FAB_HOST_ID=m,e.FloatingButton=j,e.VUE_GRAB_CONFIG_KEY=s,e.createVueGrab=c,e.init=I,e.useGrab=F});
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GrabConfig, GrabResult } from '@sakana-y/vue-grab-shared';
|
|
2
|
+
/**
|
|
3
|
+
* Standalone initialization for non-Vue contexts (e.g., script tag usage).
|
|
4
|
+
*/
|
|
5
|
+
export declare function init(options?: Partial<GrabConfig>): {
|
|
6
|
+
activate: () => void;
|
|
7
|
+
deactivate: () => void;
|
|
8
|
+
onGrab: (cb: (result: GrabResult) => void) => () => void;
|
|
9
|
+
destroy: () => void;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAIxE;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,GAAE,OAAO,CAAC,UAAU,CAAM;;;iBAOrC,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI;;EAG5C"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { GrabConfig } from '@sakana-y/vue-grab-shared';
|
|
2
|
+
declare const OVERLAY_HOST_ID = "vue-grab-overlay-host";
|
|
3
|
+
export declare class GrabOverlay {
|
|
4
|
+
private host;
|
|
5
|
+
private shadowRoot;
|
|
6
|
+
private highlightBox;
|
|
7
|
+
private labelEl;
|
|
8
|
+
private config;
|
|
9
|
+
constructor(config: Pick<GrabConfig, "highlightColor" | "labelTextColor" | "showTagHint">);
|
|
10
|
+
mount(): void;
|
|
11
|
+
highlight(el: Element, label?: string): void;
|
|
12
|
+
clearHighlight(): void;
|
|
13
|
+
destroy(): void;
|
|
14
|
+
}
|
|
15
|
+
export { OVERLAY_HOST_ID };
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/overlay/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,QAAA,MAAM,eAAe,0BAA0B,CAAC;AAEhD,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAA4B;IACxC,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,MAAM,CAAwE;gBAE1E,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,gBAAgB,GAAG,gBAAgB,GAAG,aAAa,CAAC;IAIzF,KAAK,IAAI,IAAI;IAoDb,SAAS,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IA2B5C,cAAc,IAAI,IAAI;IAKtB,OAAO,IAAI,IAAI;CAShB;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { App, InjectionKey } from 'vue';
|
|
2
|
+
import { GrabConfig } from '@sakana-y/vue-grab-shared';
|
|
3
|
+
export declare const VUE_GRAB_CONFIG_KEY: InjectionKey<GrabConfig>;
|
|
4
|
+
export declare function createVueGrab(options?: Partial<GrabConfig>): {
|
|
5
|
+
install(app: App): void;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,KAAK,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAG5D,eAAO,MAAM,mBAAmB,EAAE,YAAY,CAAC,UAAU,CAA6B,CAAC;AAEvF,wBAAgB,aAAa,CAAC,OAAO,GAAE,OAAO,CAAC,UAAU,CAAM;iBAI9C,GAAG;EAInB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { GrabConfig } from '@sakana-y/vue-grab-shared';
|
|
2
|
+
import { GrabEngine } from './core';
|
|
3
|
+
import { HotkeyManager } from './hotkeys';
|
|
4
|
+
import { FloatingButton } from './floating-button';
|
|
5
|
+
export interface GrabSession {
|
|
6
|
+
engine: GrabEngine;
|
|
7
|
+
hotkeys: HotkeyManager;
|
|
8
|
+
fab: FloatingButton | null;
|
|
9
|
+
destroy: () => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates a GrabEngine + HotkeyManager pair with the default hotkey wired up.
|
|
13
|
+
* Optionally creates a FloatingButton when config.floatingButton.enabled is true.
|
|
14
|
+
* Shared setup used by both the Vue composable and the standalone `init()`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createGrabSession(config: GrabConfig): GrabSession;
|
|
17
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,aAAa,CAAC;IACvB,GAAG,EAAE,cAAc,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,CAkCjE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sakana-y/vue-grab",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.mjs",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"unpkg": "./dist/index.umd.js",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"require": "./dist/index.cjs",
|
|
17
|
+
"types": "./dist/index.d.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@sakana-y/vue-grab-shared": "0.0.1"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@playwright/test": "^1.50.0",
|
|
28
|
+
"@vitejs/plugin-vue": "^6.0.0",
|
|
29
|
+
"@vitest/browser": "^4.1.0",
|
|
30
|
+
"@vitest/browser-playwright": "^4.1.0",
|
|
31
|
+
"@vue/test-utils": "^2.4.6",
|
|
32
|
+
"typescript": "~5.9.3",
|
|
33
|
+
"vite": "^8.0.0",
|
|
34
|
+
"vite-plugin-dts": "^4.5.0",
|
|
35
|
+
"vitest": "^4.1.0",
|
|
36
|
+
"vue": "^3.5.13"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"vue": "^3.5.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "vite build",
|
|
43
|
+
"test": "vitest run",
|
|
44
|
+
"typecheck": "tsc --noEmit"
|
|
45
|
+
}
|
|
46
|
+
}
|