@akccakcctw/vue-grab 1.0.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/core/api.d.ts +28 -0
  2. package/dist/core/api.js +105 -0
  3. package/dist/core/identifier.d.ts +2 -0
  4. package/dist/core/identifier.js +101 -0
  5. package/dist/core/overlay.d.ts +24 -0
  6. package/dist/core/overlay.js +509 -0
  7. package/dist/core/widget.d.ts +10 -0
  8. package/dist/core/widget.js +251 -0
  9. package/dist/index.d.ts +10 -0
  10. package/dist/index.js +7 -0
  11. package/dist/nuxt/module.d.ts +8 -0
  12. package/dist/nuxt/module.js +40 -0
  13. package/dist/nuxt/runtime/plugin.d.ts +2 -0
  14. package/dist/nuxt/runtime/plugin.js +14 -0
  15. package/dist/plugin.d.ts +11 -0
  16. package/dist/plugin.js +47 -0
  17. package/dist/vite.d.ts +8 -0
  18. package/dist/vite.js +198 -0
  19. package/package.json +33 -8
  20. package/.github/release-please-config.json +0 -9
  21. package/.github/release-please-manifest.json +0 -3
  22. package/.github/workflows/release.yml +0 -37
  23. package/AGENTS.md +0 -75
  24. package/README.md +0 -116
  25. package/akccakcctw-vue-grab-1.0.0.tgz +0 -0
  26. package/docs/SDD.md +0 -188
  27. package/src/__tests__/plugin.spec.ts +0 -60
  28. package/src/core/__tests__/api.spec.ts +0 -178
  29. package/src/core/__tests__/identifier.spec.ts +0 -126
  30. package/src/core/__tests__/overlay.spec.ts +0 -431
  31. package/src/core/__tests__/widget.spec.ts +0 -57
  32. package/src/core/api.ts +0 -144
  33. package/src/core/identifier.ts +0 -89
  34. package/src/core/overlay.ts +0 -348
  35. package/src/core/widget.ts +0 -289
  36. package/src/index.ts +0 -8
  37. package/src/nuxt/module.ts +0 -102
  38. package/src/nuxt/runtime/plugin.ts +0 -13
  39. package/src/plugin.ts +0 -48
  40. package/tsconfig.json +0 -44
  41. package/vitest.config.ts +0 -9
@@ -0,0 +1,28 @@
1
+ import type { OverlayOptions } from './overlay.js';
2
+ export interface ComponentInfo {
3
+ name: string;
4
+ file: string;
5
+ props: Record<string, any>;
6
+ data: Record<string, any>;
7
+ element: HTMLElement;
8
+ line?: number;
9
+ column?: number;
10
+ vnode?: any;
11
+ }
12
+ export type VueGrabOptions = OverlayOptions;
13
+ export interface VueGrabAPI {
14
+ activate(): void;
15
+ deactivate(): void;
16
+ readonly isActive: boolean;
17
+ grabAt(x: number, y: number): ComponentInfo | null;
18
+ grabFromSelector(selector: string): ComponentInfo | null;
19
+ grabFromElement(element: Element): ComponentInfo | null;
20
+ highlight(selector: string): void;
21
+ enable(): void;
22
+ disable(): void;
23
+ getComponentDetails(selectorOrElement: string | Element): ComponentInfo | null;
24
+ setOverlayStyle(style: Record<string, string>): void;
25
+ setDomFileResolver(resolver: VueGrabOptions['domFileResolver']): void;
26
+ }
27
+ export declare function createVueGrabAPI(targetWindow: Window, options?: VueGrabOptions): VueGrabAPI;
28
+ export declare function installVueGrab(targetWindow: Window, options?: VueGrabOptions): VueGrabAPI;
@@ -0,0 +1,105 @@
1
+ import { extractMetadata, identifyComponent } from './identifier.js';
2
+ import { createOverlayController } from './overlay.js';
3
+ import { createToggleWidget } from './widget.js';
4
+ function getComponentInfo(el, resolver) {
5
+ if (!el)
6
+ return null;
7
+ const instance = identifyComponent(el);
8
+ const metadata = extractMetadata(instance, el);
9
+ if (!metadata)
10
+ return null;
11
+ const fallback = !instance && resolver ? resolver(el) : null;
12
+ if (fallback?.file)
13
+ metadata.file = fallback.file;
14
+ if (typeof fallback?.line === 'number')
15
+ metadata.line = fallback.line;
16
+ if (typeof fallback?.column === 'number')
17
+ metadata.column = fallback.column;
18
+ return {
19
+ ...metadata,
20
+ element: el
21
+ };
22
+ }
23
+ export function createVueGrabAPI(targetWindow, options = {}) {
24
+ let active = false;
25
+ let domFileResolver = options.domFileResolver;
26
+ const overlay = createOverlayController(targetWindow, {
27
+ ...options,
28
+ onAfterCopy: () => {
29
+ api.deactivate();
30
+ }
31
+ });
32
+ const widget = createToggleWidget(targetWindow, {
33
+ onToggle(nextActive) {
34
+ if (nextActive) {
35
+ api.activate();
36
+ }
37
+ else {
38
+ api.deactivate();
39
+ }
40
+ }
41
+ });
42
+ const api = {
43
+ activate() {
44
+ active = true;
45
+ overlay.start();
46
+ widget.setActive(true);
47
+ },
48
+ deactivate() {
49
+ active = false;
50
+ overlay.stop();
51
+ widget.setActive(false);
52
+ },
53
+ get isActive() {
54
+ return active;
55
+ },
56
+ grabAt(x, y) {
57
+ if (typeof targetWindow.document.elementFromPoint !== 'function')
58
+ return null;
59
+ const el = targetWindow.document.elementFromPoint(x, y);
60
+ return getComponentInfo(el, domFileResolver);
61
+ },
62
+ grabFromSelector(selector) {
63
+ const el = targetWindow.document.querySelector(selector);
64
+ return getComponentInfo(el, domFileResolver);
65
+ },
66
+ grabFromElement(element) {
67
+ return getComponentInfo(element, domFileResolver);
68
+ },
69
+ highlight(selector) {
70
+ const el = targetWindow.document.querySelector(selector);
71
+ overlay.highlight(el);
72
+ },
73
+ enable() {
74
+ this.activate();
75
+ },
76
+ disable() {
77
+ this.deactivate();
78
+ },
79
+ getComponentDetails(selectorOrElement) {
80
+ if (typeof selectorOrElement === 'string') {
81
+ const el = targetWindow.document.querySelector(selectorOrElement);
82
+ return getComponentInfo(el, domFileResolver);
83
+ }
84
+ return getComponentInfo(selectorOrElement, domFileResolver);
85
+ },
86
+ setOverlayStyle(style) {
87
+ overlay.setStyle(style);
88
+ },
89
+ setDomFileResolver(resolver) {
90
+ domFileResolver = resolver;
91
+ overlay.setDomFileResolver(domFileResolver);
92
+ }
93
+ };
94
+ widget.mount();
95
+ widget.setActive(active);
96
+ return api;
97
+ }
98
+ export function installVueGrab(targetWindow, options = {}) {
99
+ const existing = targetWindow.__VUE_GRAB__;
100
+ if (existing)
101
+ return existing;
102
+ const api = createVueGrabAPI(targetWindow, options);
103
+ targetWindow.__VUE_GRAB__ = api;
104
+ return api;
105
+ }
@@ -0,0 +1,2 @@
1
+ export declare function identifyComponent(el: HTMLElement | null): any;
2
+ export declare function extractMetadata(instance: any, el?: HTMLElement | null): Record<string, any> | null;
@@ -0,0 +1,101 @@
1
+ export function identifyComponent(el) {
2
+ let curr = el;
3
+ while (curr) {
4
+ // Vue 3 stores the internal component instance on the DOM element
5
+ // under specific keys depending on the version/environment.
6
+ const currAny = curr;
7
+ const instance = currAny.__vueParentComponent ||
8
+ currAny.__vnode?.component ||
9
+ currAny.__vnode?.ctx ||
10
+ currAny.__vue__;
11
+ if (instance)
12
+ return instance;
13
+ curr = curr.parentElement;
14
+ }
15
+ return null;
16
+ }
17
+ function getElementLoc(el) {
18
+ if (!el || typeof el.getAttribute !== 'function')
19
+ return null;
20
+ const locAttr = el.getAttribute('data-vue-grab-loc');
21
+ if (!locAttr)
22
+ return null;
23
+ const [lineRaw, columnRaw] = locAttr.split(':');
24
+ const line = Number(lineRaw);
25
+ const column = Number(columnRaw);
26
+ if (!Number.isFinite(line) || !Number.isFinite(column))
27
+ return null;
28
+ return { line, column };
29
+ }
30
+ export function extractMetadata(instance, el) {
31
+ if (!instance && !el)
32
+ return null;
33
+ const resolveType = (start) => {
34
+ let curr = start;
35
+ while (curr) {
36
+ const t = curr.type || curr.$options || curr.type?.__vccOpts || {};
37
+ const file = t.__file || t.__vccOpts?.__file;
38
+ if (file) {
39
+ return { type: t, instance: curr };
40
+ }
41
+ curr = curr.parent;
42
+ }
43
+ return { type: start.type || start.$options || start.type?.__vccOpts || {}, instance: start };
44
+ };
45
+ const resolved = instance ? resolveType(instance) : { type: {}, instance: null };
46
+ const type = resolved.type || {};
47
+ const props = instance?.props || instance?.$props || {};
48
+ const data = (instance?.data && Object.keys(instance.data).length > 0
49
+ ? instance.data
50
+ : instance?.setupState) ||
51
+ instance?.$data ||
52
+ {};
53
+ const vnode = instance?.vnode || instance?.$vnode;
54
+ const domLoc = getElementLoc(el);
55
+ const loc = domLoc ||
56
+ vnode?.loc?.start ||
57
+ resolved.instance?.vnode?.loc?.start ||
58
+ resolved.instance?.parent?.vnode?.loc?.start;
59
+ const metadata = instance
60
+ ? {
61
+ name: type.name || type.__name || type.__vccOpts?.name || 'AnonymousComponent',
62
+ file: type.__file || type.__vccOpts?.__file || 'unknown',
63
+ props,
64
+ data
65
+ }
66
+ : {
67
+ name: el?.tagName ? `<${el.tagName.toLowerCase()}>` : 'unknown',
68
+ file: 'unknown',
69
+ props: {},
70
+ data: {},
71
+ element: el || null
72
+ };
73
+ if (typeof loc?.line === 'number') {
74
+ metadata.line = loc.line;
75
+ }
76
+ else if (typeof type.__line === 'number') {
77
+ metadata.line = type.__line;
78
+ }
79
+ else if (typeof type.line === 'number') {
80
+ metadata.line = type.line;
81
+ }
82
+ else if (typeof type.__vccOpts?.__line === 'number') {
83
+ metadata.line = type.__vccOpts.__line;
84
+ }
85
+ if (typeof loc?.column === 'number') {
86
+ metadata.column = loc.column;
87
+ }
88
+ else if (typeof type.__column === 'number') {
89
+ metadata.column = type.__column;
90
+ }
91
+ else if (typeof type.column === 'number') {
92
+ metadata.column = type.column;
93
+ }
94
+ else if (typeof type.__vccOpts?.__column === 'number') {
95
+ metadata.column = type.__vccOpts.__column;
96
+ }
97
+ if (vnode) {
98
+ metadata.vnode = vnode;
99
+ }
100
+ return metadata;
101
+ }
@@ -0,0 +1,24 @@
1
+ type OverlayController = {
2
+ start: () => void;
3
+ stop: () => void;
4
+ isActive: () => boolean;
5
+ highlight: (el: HTMLElement | null) => void;
6
+ clear: () => void;
7
+ setStyle: (style: OverlayStyle) => void;
8
+ setDomFileResolver: (resolver: OverlayOptions['domFileResolver']) => void;
9
+ };
10
+ export type OverlayStyle = Record<string, string>;
11
+ export type OverlayOptions = {
12
+ overlayStyle?: OverlayStyle;
13
+ onCopy?: (payload: string) => void;
14
+ onAfterCopy?: () => void;
15
+ copyOnClick?: boolean;
16
+ rootDir?: string;
17
+ domFileResolver?: (el: HTMLElement) => {
18
+ file?: string;
19
+ line?: number;
20
+ column?: number;
21
+ } | null;
22
+ };
23
+ export declare function createOverlayController(targetWindow: Window, options?: OverlayOptions): OverlayController;
24
+ export {};