@aipanel/core 1.2.19 → 1.2.21
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/es/common/constants.d.ts +12 -1
- package/es/common/constants.mjs +5 -0
- package/es/common/inspector/index.d.ts +6 -0
- package/es/common/inspector/index.mjs +14 -0
- package/es/common/inspector/react.d.ts +8 -0
- package/es/common/inspector/react.mjs +105 -0
- package/es/common/inspector/types.d.ts +35 -0
- package/es/common/inspector/types.mjs +12 -0
- package/es/common/inspector/vue.d.ts +20 -0
- package/es/common/inspector/vue.mjs +120 -0
- package/es/common/types.d.ts +12 -0
- package/es/index.d.ts +1 -0
- package/es/index.mjs +1 -0
- package/lib/common/constants.cjs +6 -0
- package/lib/common/constants.d.ts +12 -1
- package/lib/common/inspector/index.cjs +40 -0
- package/lib/common/inspector/index.d.ts +6 -0
- package/lib/common/inspector/react.cjs +126 -0
- package/lib/common/inspector/react.d.ts +8 -0
- package/lib/common/inspector/types.cjs +35 -0
- package/lib/common/inspector/types.d.ts +35 -0
- package/lib/common/inspector/vue.cjs +141 -0
- package/lib/common/inspector/vue.d.ts +20 -0
- package/lib/common/types.d.ts +12 -0
- package/lib/index.cjs +2 -0
- package/lib/index.d.ts +1 -0
- package/package.json +2 -1
package/es/common/constants.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export declare const HOST_EVENTS_API_PATH = "/__aipanel_host_events__";
|
|
|
45
45
|
export declare const CONTEXT_UPDATE_INTERVAL = 500;
|
|
46
46
|
/** 服务器同步间隔(毫秒) */
|
|
47
47
|
export declare const SERVER_SYNC_INTERVAL = 2000;
|
|
48
|
-
/**
|
|
48
|
+
/** 框架 Inspector 检查间隔(毫秒) */
|
|
49
49
|
export declare const INSPECTOR_CHECK_INTERVAL = 500;
|
|
50
50
|
/** 自动打开延迟(毫秒) */
|
|
51
51
|
export declare const AUTO_OPEN_DELAY = 1000;
|
|
@@ -185,3 +185,14 @@ export declare const SSE_EVENT_TYPES: {
|
|
|
185
185
|
readonly CLEAR_ELEMENTS: "CLEAR_ELEMENTS";
|
|
186
186
|
};
|
|
187
187
|
export type SSEEventType = (typeof SSE_EVENT_TYPES)[keyof typeof SSE_EVENT_TYPES];
|
|
188
|
+
/** ==================== 元素选择器(click-to-source) ==================== */
|
|
189
|
+
/**
|
|
190
|
+
* 元素选择器适配器标识:服务端注入的构建期插件与客户端运行时适配器共用同一标识,
|
|
191
|
+
* 已登记 Vue / React;新增框架在此登记,两侧按同一 id 对齐。
|
|
192
|
+
*/
|
|
193
|
+
export declare const INSPECTOR_ADAPTER_IDS: {
|
|
194
|
+
readonly vue: "vue";
|
|
195
|
+
readonly react: "react";
|
|
196
|
+
};
|
|
197
|
+
/** 元素选择器适配器标识类型 */
|
|
198
|
+
export type InspectorAdapterId = (typeof INSPECTOR_ADAPTER_IDS)[keyof typeof INSPECTOR_ADAPTER_IDS];
|
package/es/common/constants.mjs
CHANGED
|
@@ -106,6 +106,10 @@ const SSE_EVENT_TYPES = {
|
|
|
106
106
|
SESSION_EVENT: "SESSION_EVENT",
|
|
107
107
|
CLEAR_ELEMENTS: "CLEAR_ELEMENTS"
|
|
108
108
|
};
|
|
109
|
+
const INSPECTOR_ADAPTER_IDS = {
|
|
110
|
+
vue: "vue",
|
|
111
|
+
react: "react"
|
|
112
|
+
};
|
|
109
113
|
export {
|
|
110
114
|
AIPANEL_CACHE_DIR,
|
|
111
115
|
AUTO_OPEN_DELAY,
|
|
@@ -124,6 +128,7 @@ export {
|
|
|
124
128
|
EXT_MSG,
|
|
125
129
|
HOST_EVENTS_API_PATH,
|
|
126
130
|
INIT_MARKER,
|
|
131
|
+
INSPECTOR_ADAPTER_IDS,
|
|
127
132
|
INSPECTOR_CHECK_INTERVAL,
|
|
128
133
|
LOGS_API_PATH,
|
|
129
134
|
LOG_PREFIX,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InspectorAdapter } from "./types";
|
|
2
|
+
export * from "./types";
|
|
3
|
+
/** 全部已登记适配器:用于静态元数据(忽略标记、源码位置解析) */
|
|
4
|
+
export declare function listInspectorAdapters(): readonly InspectorAdapter[];
|
|
5
|
+
/** 当前可用的适配器(对应框架的 inspector 运行时已注入页面);无可用返回 null */
|
|
6
|
+
export declare function resolveInspectorAdapter(): InspectorAdapter | null;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { reactInspectorAdapter } from "./react.mjs";
|
|
2
|
+
import { vueInspectorAdapter } from "./vue.mjs";
|
|
3
|
+
export * from "./types.mjs";
|
|
4
|
+
const adapters = [vueInspectorAdapter, reactInspectorAdapter];
|
|
5
|
+
function listInspectorAdapters() {
|
|
6
|
+
return adapters;
|
|
7
|
+
}
|
|
8
|
+
function resolveInspectorAdapter() {
|
|
9
|
+
return adapters.find((adapter) => adapter.isAvailable()) ?? null;
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
listInspectorAdapters,
|
|
13
|
+
resolveInspectorAdapter
|
|
14
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type InspectorAdapter } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* React 适配器:构建期由 vite 集成(@code-inspector/core transformCode)注入
|
|
4
|
+
* data-insp-path 源码坐标标记,运行时按「注入标记 → react-dev-inspector 兼容属性 →
|
|
5
|
+
* fiber._debugSource」依次解析;React 无底层 inspector 运行时,
|
|
6
|
+
* 点击接管由适配器自装的 document 捕获监听实现。
|
|
7
|
+
*/
|
|
8
|
+
export declare const reactInspectorAdapter: InspectorAdapter;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { INSPECTOR_ADAPTER_IDS } from "../constants.mjs";
|
|
2
|
+
import {
|
|
3
|
+
parseSourceLocation
|
|
4
|
+
} from "./types.mjs";
|
|
5
|
+
const SOURCE_LOCATION_ATTRIBUTE = "data-insp-path";
|
|
6
|
+
const RDI_PATH_ATTRIBUTE = "data-inspector-relative-path";
|
|
7
|
+
const RDI_LINE_ATTRIBUTE = "data-inspector-line";
|
|
8
|
+
const RDI_COLUMN_ATTRIBUTE = "data-inspector-column";
|
|
9
|
+
const FIBER_KEY_PREFIX = "__reactFiber$";
|
|
10
|
+
const CONTAINER_KEY_PREFIX = "__reactContainer$";
|
|
11
|
+
const FIBER_WALK_LIMIT = 50;
|
|
12
|
+
const DETECT_SCAN_LIMIT = 200;
|
|
13
|
+
let clickHandler = null;
|
|
14
|
+
let listenerInstalled = false;
|
|
15
|
+
let enabled = false;
|
|
16
|
+
function hasFiberKey(element) {
|
|
17
|
+
const keys = Object.keys(element);
|
|
18
|
+
return keys.some(
|
|
19
|
+
(key) => key.startsWith(FIBER_KEY_PREFIX) || key.startsWith(CONTAINER_KEY_PREFIX)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
function detectReactRuntime() {
|
|
23
|
+
if (typeof document === "undefined") return false;
|
|
24
|
+
const roots = document.querySelectorAll("#root, #__next, [data-reactroot]");
|
|
25
|
+
for (const element of [document.body, ...roots]) {
|
|
26
|
+
if (element && hasFiberKey(element)) return true;
|
|
27
|
+
}
|
|
28
|
+
const descendants = document.body?.querySelectorAll("*") ?? [];
|
|
29
|
+
const limit = Math.min(descendants.length, DETECT_SCAN_LIMIT);
|
|
30
|
+
for (let index = 0; index < limit; index += 1) {
|
|
31
|
+
if (hasFiberKey(descendants[index])) return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
function resolveFromAttributes(element) {
|
|
36
|
+
const marker = element.getAttribute(SOURCE_LOCATION_ATTRIBUTE);
|
|
37
|
+
if (marker) {
|
|
38
|
+
const location = parseSourceLocation(marker.replace(/:[^:]*$/, ""));
|
|
39
|
+
if (location) return location;
|
|
40
|
+
}
|
|
41
|
+
const rdiPath = element.getAttribute(RDI_PATH_ATTRIBUTE);
|
|
42
|
+
if (rdiPath) {
|
|
43
|
+
return {
|
|
44
|
+
file: rdiPath,
|
|
45
|
+
line: Number.parseInt(element.getAttribute(RDI_LINE_ATTRIBUTE) ?? "", 10) || null,
|
|
46
|
+
column: Number.parseInt(element.getAttribute(RDI_COLUMN_ATTRIBUTE) ?? "", 10) || null
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function resolveFromFiber(element) {
|
|
52
|
+
const fiberKey = Object.keys(element).find((key) => key.startsWith(FIBER_KEY_PREFIX));
|
|
53
|
+
let fiber = fiberKey ? element[fiberKey] : null;
|
|
54
|
+
for (let depth = 0; fiber && depth < FIBER_WALK_LIMIT; depth += 1) {
|
|
55
|
+
const source = fiber._debugSource;
|
|
56
|
+
if (source?.fileName) {
|
|
57
|
+
return {
|
|
58
|
+
file: source.fileName,
|
|
59
|
+
line: source.lineNumber ?? null,
|
|
60
|
+
// React 的 columnNumber 为 0 基,统一转 1 基(与编辑器跳转一致)
|
|
61
|
+
column: source.columnNumber != null ? source.columnNumber + 1 : null
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
fiber = fiber.return;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
function handleDocumentClick(event) {
|
|
69
|
+
if (!enabled) return;
|
|
70
|
+
const element = event.target instanceof Element ? event.target : null;
|
|
71
|
+
if (clickHandler?.(element, event)) {
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
event.stopPropagation();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const reactInspectorAdapter = {
|
|
77
|
+
id: INSPECTOR_ADAPTER_IDS.react,
|
|
78
|
+
label: "React Inspector",
|
|
79
|
+
ignoreSelectors: [],
|
|
80
|
+
ignoreAttributes: [],
|
|
81
|
+
isAvailable() {
|
|
82
|
+
return detectReactRuntime();
|
|
83
|
+
},
|
|
84
|
+
resolveSourceLocation(element) {
|
|
85
|
+
let current = element;
|
|
86
|
+
while (current) {
|
|
87
|
+
const location = resolveFromAttributes(current) ?? resolveFromFiber(current);
|
|
88
|
+
if (location?.file) return location;
|
|
89
|
+
current = current.parentElement;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
},
|
|
93
|
+
onElementClick(handler) {
|
|
94
|
+
clickHandler = handler;
|
|
95
|
+
if (listenerInstalled) return;
|
|
96
|
+
document.addEventListener("click", handleDocumentClick, true);
|
|
97
|
+
listenerInstalled = true;
|
|
98
|
+
},
|
|
99
|
+
setEnabled(next) {
|
|
100
|
+
enabled = next;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
export {
|
|
104
|
+
reactInspectorAdapter
|
|
105
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { InspectorAdapterId } from "../constants";
|
|
2
|
+
/** 元素对应的源码位置(跨框架统一形态;未解析到的字段为 null) */
|
|
3
|
+
export interface InspectorSourceLocation {
|
|
4
|
+
file: string | null;
|
|
5
|
+
line: number | null;
|
|
6
|
+
column: number | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 元素点击处理器。
|
|
10
|
+
* 返回 true 表示宿主已接管:适配器抑制底层 inspector 的默认行为(如打开编辑器)。
|
|
11
|
+
*/
|
|
12
|
+
export type InspectorElementClickHandler = (element: Element | null, event: MouseEvent) => boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 框架侧「点击元素 → 源码位置」适配器契约。
|
|
15
|
+
* 宿主(挂件 / 扩展)只依赖此接口,不感知 Vue、React 等框架差异。
|
|
16
|
+
*/
|
|
17
|
+
export interface InspectorAdapter {
|
|
18
|
+
readonly id: InspectorAdapterId;
|
|
19
|
+
/** 用户可见名称(提示文案用) */
|
|
20
|
+
readonly label: string;
|
|
21
|
+
/** 运行时是否可用(对应框架的 inspector 已注入页面) */
|
|
22
|
+
isAvailable(): boolean;
|
|
23
|
+
/** 适配器自身覆盖层的选择器:宿主需忽略,不参与选择 */
|
|
24
|
+
readonly ignoreSelectors: readonly string[];
|
|
25
|
+
/** 适配器自带的忽略标记属性:宿主需忽略,不参与选择 */
|
|
26
|
+
readonly ignoreAttributes: readonly string[];
|
|
27
|
+
/** 元素 → 源码位置;解析不到返回 null */
|
|
28
|
+
resolveSourceLocation(element: Element): InspectorSourceLocation | null;
|
|
29
|
+
/** 注册元素点击处理器(幂等:同一底层 inspector 只安装一次) */
|
|
30
|
+
onElementClick(handler: InspectorElementClickHandler): void;
|
|
31
|
+
/** 同步底层 inspector 的启用状态 */
|
|
32
|
+
setEnabled(enabled: boolean): void;
|
|
33
|
+
}
|
|
34
|
+
/** 解析 "文件:行:列" 形式的源码坐标(各框架适配器共用的标记格式) */
|
|
35
|
+
export declare function parseSourceLocation(value: string): InspectorSourceLocation | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function parseSourceLocation(value) {
|
|
2
|
+
const match = /^(.+):([\d]+):([\d]+)$/.exec(value);
|
|
3
|
+
if (!match) return null;
|
|
4
|
+
return {
|
|
5
|
+
file: match[1],
|
|
6
|
+
line: Number.parseInt(match[2], 10),
|
|
7
|
+
column: Number.parseInt(match[3], 10)
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
parseSourceLocation
|
|
12
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type InspectorAdapter } from "./types";
|
|
2
|
+
/** unplugin-vue-inspector 暴露到页面的运行时控制对象 */
|
|
3
|
+
interface VueInspectorRuntime {
|
|
4
|
+
handleClick: (e: MouseEvent) => void;
|
|
5
|
+
enable: () => void;
|
|
6
|
+
disable: () => void;
|
|
7
|
+
/** AIPanel 已接管 handleClick 的标记(幂等安装) */
|
|
8
|
+
__aipanel_hooked?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare global {
|
|
11
|
+
interface Window {
|
|
12
|
+
__VUE_INSPECTOR__?: VueInspectorRuntime;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Vue 适配器:复用 unplugin-vue-inspector 注入的运行时与源码坐标标记,
|
|
17
|
+
* 在宿主选择模式下接管点击、解析元素源码位置。
|
|
18
|
+
*/
|
|
19
|
+
export declare const vueInspectorAdapter: InspectorAdapter;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { INSPECTOR_ADAPTER_IDS } from "../constants.mjs";
|
|
2
|
+
import {
|
|
3
|
+
parseSourceLocation
|
|
4
|
+
} from "./types.mjs";
|
|
5
|
+
const SOURCE_LOCATION_ATTRIBUTE = "data-v-inspector";
|
|
6
|
+
const VNODE_SOURCE_KEY = "__v_inspector";
|
|
7
|
+
const IGNORE_ATTRIBUTE = "data-v-inspector-ignore";
|
|
8
|
+
const OVERLAY_SELECTOR = "#vue-inspector-container";
|
|
9
|
+
function getRuntime() {
|
|
10
|
+
return typeof window === "undefined" ? void 0 : window.__VUE_INSPECTOR__;
|
|
11
|
+
}
|
|
12
|
+
function isAdapterOwned(element) {
|
|
13
|
+
return element.hasAttribute(IGNORE_ATTRIBUTE) || Boolean(element.closest(OVERLAY_SELECTOR));
|
|
14
|
+
}
|
|
15
|
+
function getMarkerData(element) {
|
|
16
|
+
const vnodeData = element.__vnode?.props?.[VNODE_SOURCE_KEY];
|
|
17
|
+
if (vnodeData) return vnodeData;
|
|
18
|
+
const ctxVNode = element.__vnode?.ctx?.vnode;
|
|
19
|
+
if (ctxVNode?.el === element) {
|
|
20
|
+
const ctxData = ctxVNode.props?.[VNODE_SOURCE_KEY];
|
|
21
|
+
if (ctxData) return ctxData;
|
|
22
|
+
}
|
|
23
|
+
const vueInstance = element.__vueParentComponent;
|
|
24
|
+
let currentParent = vueInstance?.parent;
|
|
25
|
+
while (currentParent) {
|
|
26
|
+
if (currentParent.vnode?.el === element) {
|
|
27
|
+
const parentData = currentParent.vnode.props?.[VNODE_SOURCE_KEY];
|
|
28
|
+
if (parentData) return parentData;
|
|
29
|
+
}
|
|
30
|
+
currentParent = currentParent.parent;
|
|
31
|
+
}
|
|
32
|
+
return element.getAttribute(SOURCE_LOCATION_ATTRIBUTE) ?? void 0;
|
|
33
|
+
}
|
|
34
|
+
function resolveFromMarker(element) {
|
|
35
|
+
const data = getMarkerData(element);
|
|
36
|
+
return data ? parseSourceLocation(data) : null;
|
|
37
|
+
}
|
|
38
|
+
function resolveFromVueInstance(element) {
|
|
39
|
+
const vue3Instance = element.__vueParentComponent;
|
|
40
|
+
if (vue3Instance) {
|
|
41
|
+
let current = vue3Instance;
|
|
42
|
+
while (current) {
|
|
43
|
+
const file = current.type?.__file || current.vnode?.type?.__file;
|
|
44
|
+
if (file) {
|
|
45
|
+
return { file, line: null, column: null };
|
|
46
|
+
}
|
|
47
|
+
current = current.parent;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const vue2Instance = element.__vue__;
|
|
51
|
+
if (vue2Instance) {
|
|
52
|
+
let current = vue2Instance;
|
|
53
|
+
while (current) {
|
|
54
|
+
const file = current.$options?.__file;
|
|
55
|
+
if (file) {
|
|
56
|
+
return { file, line: null, column: null };
|
|
57
|
+
}
|
|
58
|
+
current = current.$parent;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function mergeSourceLocations(markerLocation, instanceLocation) {
|
|
64
|
+
if (!markerLocation?.file && !instanceLocation?.file) return null;
|
|
65
|
+
if (markerLocation?.file && instanceLocation?.file) {
|
|
66
|
+
const isNodeModules = (path) => path.includes("node_modules");
|
|
67
|
+
if (!isNodeModules(markerLocation.file)) return markerLocation;
|
|
68
|
+
if (!isNodeModules(instanceLocation.file)) return instanceLocation;
|
|
69
|
+
return markerLocation;
|
|
70
|
+
}
|
|
71
|
+
return markerLocation?.file ? markerLocation : instanceLocation;
|
|
72
|
+
}
|
|
73
|
+
let clickHandler = null;
|
|
74
|
+
const vueInspectorAdapter = {
|
|
75
|
+
id: INSPECTOR_ADAPTER_IDS.vue,
|
|
76
|
+
label: "Vue Inspector",
|
|
77
|
+
ignoreSelectors: [OVERLAY_SELECTOR],
|
|
78
|
+
ignoreAttributes: [IGNORE_ATTRIBUTE],
|
|
79
|
+
isAvailable() {
|
|
80
|
+
return Boolean(getRuntime());
|
|
81
|
+
},
|
|
82
|
+
resolveSourceLocation(element) {
|
|
83
|
+
let current = element;
|
|
84
|
+
let markerLocation = null;
|
|
85
|
+
let instanceLocation = null;
|
|
86
|
+
while (current && !(markerLocation && instanceLocation)) {
|
|
87
|
+
if (!isAdapterOwned(current)) {
|
|
88
|
+
markerLocation ?? (markerLocation = resolveFromMarker(current));
|
|
89
|
+
instanceLocation ?? (instanceLocation = resolveFromVueInstance(current));
|
|
90
|
+
}
|
|
91
|
+
current = current.parentElement;
|
|
92
|
+
}
|
|
93
|
+
return mergeSourceLocations(markerLocation, instanceLocation);
|
|
94
|
+
},
|
|
95
|
+
onElementClick(handler) {
|
|
96
|
+
clickHandler = handler;
|
|
97
|
+
const runtime = getRuntime();
|
|
98
|
+
if (!runtime || runtime.__aipanel_hooked) return;
|
|
99
|
+
const originalHandleClick = runtime.handleClick.bind(runtime);
|
|
100
|
+
runtime.handleClick = (event) => {
|
|
101
|
+
const element = event.target instanceof Element ? event.target : null;
|
|
102
|
+
if (clickHandler?.(element, event)) {
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
event.stopPropagation();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
originalHandleClick(event);
|
|
108
|
+
};
|
|
109
|
+
runtime.__aipanel_hooked = true;
|
|
110
|
+
},
|
|
111
|
+
setEnabled(enabled) {
|
|
112
|
+
const runtime = getRuntime();
|
|
113
|
+
if (!runtime) return;
|
|
114
|
+
if (enabled) runtime.enable();
|
|
115
|
+
else runtime.disable();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
export {
|
|
119
|
+
vueInspectorAdapter
|
|
120
|
+
};
|
package/es/common/types.d.ts
CHANGED
|
@@ -113,6 +113,18 @@ export interface PageContext {
|
|
|
113
113
|
* 服务启动任务状态
|
|
114
114
|
*/
|
|
115
115
|
export type ServiceStartupTask = "checking_provider" | "allocating_port" | "preparing_runtime" | "starting_web" | "waiting_web_ready" | "starting_proxy" | "warming_up_chrome" | "creating_session" | "provider_not_installed" | "web_start_timeout" | "proxy_start_failed" | "session_creation_failed" | "chrome_mcp_failed" | "ready";
|
|
116
|
+
/**
|
|
117
|
+
* 服务启动任务状态载荷
|
|
118
|
+
* currentTask / SSE TASK_UPDATE / STATUS_SYNC 共用同一形态(单一来源)
|
|
119
|
+
*/
|
|
120
|
+
export interface ServiceTaskState {
|
|
121
|
+
/** 当前任务 */
|
|
122
|
+
task: ServiceStartupTask;
|
|
123
|
+
/** 失败原因类型(如 ChromeMcpWarmupErrorType) */
|
|
124
|
+
errorType?: string;
|
|
125
|
+
/** 失败原因文案 */
|
|
126
|
+
errorMessage?: string;
|
|
127
|
+
}
|
|
116
128
|
/**
|
|
117
129
|
* 服务启动任务状态映射
|
|
118
130
|
*/
|
package/es/index.d.ts
CHANGED
package/es/index.mjs
CHANGED
package/lib/common/constants.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(constants_exports, {
|
|
|
34
34
|
EXT_MSG: () => EXT_MSG,
|
|
35
35
|
HOST_EVENTS_API_PATH: () => HOST_EVENTS_API_PATH,
|
|
36
36
|
INIT_MARKER: () => INIT_MARKER,
|
|
37
|
+
INSPECTOR_ADAPTER_IDS: () => INSPECTOR_ADAPTER_IDS,
|
|
37
38
|
INSPECTOR_CHECK_INTERVAL: () => INSPECTOR_CHECK_INTERVAL,
|
|
38
39
|
LOGS_API_PATH: () => LOGS_API_PATH,
|
|
39
40
|
LOG_PREFIX: () => LOG_PREFIX,
|
|
@@ -174,6 +175,10 @@ const SSE_EVENT_TYPES = {
|
|
|
174
175
|
SESSION_EVENT: "SESSION_EVENT",
|
|
175
176
|
CLEAR_ELEMENTS: "CLEAR_ELEMENTS"
|
|
176
177
|
};
|
|
178
|
+
const INSPECTOR_ADAPTER_IDS = {
|
|
179
|
+
vue: "vue",
|
|
180
|
+
react: "react"
|
|
181
|
+
};
|
|
177
182
|
// Annotate the CommonJS export names for ESM import in node:
|
|
178
183
|
0 && (module.exports = {
|
|
179
184
|
AIPANEL_CACHE_DIR,
|
|
@@ -193,6 +198,7 @@ const SSE_EVENT_TYPES = {
|
|
|
193
198
|
EXT_MSG,
|
|
194
199
|
HOST_EVENTS_API_PATH,
|
|
195
200
|
INIT_MARKER,
|
|
201
|
+
INSPECTOR_ADAPTER_IDS,
|
|
196
202
|
INSPECTOR_CHECK_INTERVAL,
|
|
197
203
|
LOGS_API_PATH,
|
|
198
204
|
LOG_PREFIX,
|
|
@@ -45,7 +45,7 @@ export declare const HOST_EVENTS_API_PATH = "/__aipanel_host_events__";
|
|
|
45
45
|
export declare const CONTEXT_UPDATE_INTERVAL = 500;
|
|
46
46
|
/** 服务器同步间隔(毫秒) */
|
|
47
47
|
export declare const SERVER_SYNC_INTERVAL = 2000;
|
|
48
|
-
/**
|
|
48
|
+
/** 框架 Inspector 检查间隔(毫秒) */
|
|
49
49
|
export declare const INSPECTOR_CHECK_INTERVAL = 500;
|
|
50
50
|
/** 自动打开延迟(毫秒) */
|
|
51
51
|
export declare const AUTO_OPEN_DELAY = 1000;
|
|
@@ -185,3 +185,14 @@ export declare const SSE_EVENT_TYPES: {
|
|
|
185
185
|
readonly CLEAR_ELEMENTS: "CLEAR_ELEMENTS";
|
|
186
186
|
};
|
|
187
187
|
export type SSEEventType = (typeof SSE_EVENT_TYPES)[keyof typeof SSE_EVENT_TYPES];
|
|
188
|
+
/** ==================== 元素选择器(click-to-source) ==================== */
|
|
189
|
+
/**
|
|
190
|
+
* 元素选择器适配器标识:服务端注入的构建期插件与客户端运行时适配器共用同一标识,
|
|
191
|
+
* 已登记 Vue / React;新增框架在此登记,两侧按同一 id 对齐。
|
|
192
|
+
*/
|
|
193
|
+
export declare const INSPECTOR_ADAPTER_IDS: {
|
|
194
|
+
readonly vue: "vue";
|
|
195
|
+
readonly react: "react";
|
|
196
|
+
};
|
|
197
|
+
/** 元素选择器适配器标识类型 */
|
|
198
|
+
export type InspectorAdapterId = (typeof INSPECTOR_ADAPTER_IDS)[keyof typeof INSPECTOR_ADAPTER_IDS];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var inspector_exports = {};
|
|
20
|
+
__export(inspector_exports, {
|
|
21
|
+
listInspectorAdapters: () => listInspectorAdapters,
|
|
22
|
+
resolveInspectorAdapter: () => resolveInspectorAdapter
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(inspector_exports);
|
|
25
|
+
var import_react = require("./react.cjs");
|
|
26
|
+
var import_vue = require("./vue.cjs");
|
|
27
|
+
__reExport(inspector_exports, require("./types.cjs"), module.exports);
|
|
28
|
+
const adapters = [import_vue.vueInspectorAdapter, import_react.reactInspectorAdapter];
|
|
29
|
+
function listInspectorAdapters() {
|
|
30
|
+
return adapters;
|
|
31
|
+
}
|
|
32
|
+
function resolveInspectorAdapter() {
|
|
33
|
+
return adapters.find((adapter) => adapter.isAvailable()) ?? null;
|
|
34
|
+
}
|
|
35
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
36
|
+
0 && (module.exports = {
|
|
37
|
+
listInspectorAdapters,
|
|
38
|
+
resolveInspectorAdapter,
|
|
39
|
+
...require("./types.cjs")
|
|
40
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InspectorAdapter } from "./types";
|
|
2
|
+
export * from "./types";
|
|
3
|
+
/** 全部已登记适配器:用于静态元数据(忽略标记、源码位置解析) */
|
|
4
|
+
export declare function listInspectorAdapters(): readonly InspectorAdapter[];
|
|
5
|
+
/** 当前可用的适配器(对应框架的 inspector 运行时已注入页面);无可用返回 null */
|
|
6
|
+
export declare function resolveInspectorAdapter(): InspectorAdapter | null;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var react_exports = {};
|
|
19
|
+
__export(react_exports, {
|
|
20
|
+
reactInspectorAdapter: () => reactInspectorAdapter
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(react_exports);
|
|
23
|
+
var import_constants = require("../constants.cjs");
|
|
24
|
+
var import_types = require("./types.cjs");
|
|
25
|
+
const SOURCE_LOCATION_ATTRIBUTE = "data-insp-path";
|
|
26
|
+
const RDI_PATH_ATTRIBUTE = "data-inspector-relative-path";
|
|
27
|
+
const RDI_LINE_ATTRIBUTE = "data-inspector-line";
|
|
28
|
+
const RDI_COLUMN_ATTRIBUTE = "data-inspector-column";
|
|
29
|
+
const FIBER_KEY_PREFIX = "__reactFiber$";
|
|
30
|
+
const CONTAINER_KEY_PREFIX = "__reactContainer$";
|
|
31
|
+
const FIBER_WALK_LIMIT = 50;
|
|
32
|
+
const DETECT_SCAN_LIMIT = 200;
|
|
33
|
+
let clickHandler = null;
|
|
34
|
+
let listenerInstalled = false;
|
|
35
|
+
let enabled = false;
|
|
36
|
+
function hasFiberKey(element) {
|
|
37
|
+
const keys = Object.keys(element);
|
|
38
|
+
return keys.some(
|
|
39
|
+
(key) => key.startsWith(FIBER_KEY_PREFIX) || key.startsWith(CONTAINER_KEY_PREFIX)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
function detectReactRuntime() {
|
|
43
|
+
if (typeof document === "undefined") return false;
|
|
44
|
+
const roots = document.querySelectorAll("#root, #__next, [data-reactroot]");
|
|
45
|
+
for (const element of [document.body, ...roots]) {
|
|
46
|
+
if (element && hasFiberKey(element)) return true;
|
|
47
|
+
}
|
|
48
|
+
const descendants = document.body?.querySelectorAll("*") ?? [];
|
|
49
|
+
const limit = Math.min(descendants.length, DETECT_SCAN_LIMIT);
|
|
50
|
+
for (let index = 0; index < limit; index += 1) {
|
|
51
|
+
if (hasFiberKey(descendants[index])) return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
function resolveFromAttributes(element) {
|
|
56
|
+
const marker = element.getAttribute(SOURCE_LOCATION_ATTRIBUTE);
|
|
57
|
+
if (marker) {
|
|
58
|
+
const location = (0, import_types.parseSourceLocation)(marker.replace(/:[^:]*$/, ""));
|
|
59
|
+
if (location) return location;
|
|
60
|
+
}
|
|
61
|
+
const rdiPath = element.getAttribute(RDI_PATH_ATTRIBUTE);
|
|
62
|
+
if (rdiPath) {
|
|
63
|
+
return {
|
|
64
|
+
file: rdiPath,
|
|
65
|
+
line: Number.parseInt(element.getAttribute(RDI_LINE_ATTRIBUTE) ?? "", 10) || null,
|
|
66
|
+
column: Number.parseInt(element.getAttribute(RDI_COLUMN_ATTRIBUTE) ?? "", 10) || null
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
function resolveFromFiber(element) {
|
|
72
|
+
const fiberKey = Object.keys(element).find((key) => key.startsWith(FIBER_KEY_PREFIX));
|
|
73
|
+
let fiber = fiberKey ? element[fiberKey] : null;
|
|
74
|
+
for (let depth = 0; fiber && depth < FIBER_WALK_LIMIT; depth += 1) {
|
|
75
|
+
const source = fiber._debugSource;
|
|
76
|
+
if (source?.fileName) {
|
|
77
|
+
return {
|
|
78
|
+
file: source.fileName,
|
|
79
|
+
line: source.lineNumber ?? null,
|
|
80
|
+
// React 的 columnNumber 为 0 基,统一转 1 基(与编辑器跳转一致)
|
|
81
|
+
column: source.columnNumber != null ? source.columnNumber + 1 : null
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
fiber = fiber.return;
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
function handleDocumentClick(event) {
|
|
89
|
+
if (!enabled) return;
|
|
90
|
+
const element = event.target instanceof Element ? event.target : null;
|
|
91
|
+
if (clickHandler?.(element, event)) {
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
event.stopPropagation();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const reactInspectorAdapter = {
|
|
97
|
+
id: import_constants.INSPECTOR_ADAPTER_IDS.react,
|
|
98
|
+
label: "React Inspector",
|
|
99
|
+
ignoreSelectors: [],
|
|
100
|
+
ignoreAttributes: [],
|
|
101
|
+
isAvailable() {
|
|
102
|
+
return detectReactRuntime();
|
|
103
|
+
},
|
|
104
|
+
resolveSourceLocation(element) {
|
|
105
|
+
let current = element;
|
|
106
|
+
while (current) {
|
|
107
|
+
const location = resolveFromAttributes(current) ?? resolveFromFiber(current);
|
|
108
|
+
if (location?.file) return location;
|
|
109
|
+
current = current.parentElement;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
},
|
|
113
|
+
onElementClick(handler) {
|
|
114
|
+
clickHandler = handler;
|
|
115
|
+
if (listenerInstalled) return;
|
|
116
|
+
document.addEventListener("click", handleDocumentClick, true);
|
|
117
|
+
listenerInstalled = true;
|
|
118
|
+
},
|
|
119
|
+
setEnabled(next) {
|
|
120
|
+
enabled = next;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
124
|
+
0 && (module.exports = {
|
|
125
|
+
reactInspectorAdapter
|
|
126
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type InspectorAdapter } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* React 适配器:构建期由 vite 集成(@code-inspector/core transformCode)注入
|
|
4
|
+
* data-insp-path 源码坐标标记,运行时按「注入标记 → react-dev-inspector 兼容属性 →
|
|
5
|
+
* fiber._debugSource」依次解析;React 无底层 inspector 运行时,
|
|
6
|
+
* 点击接管由适配器自装的 document 捕获监听实现。
|
|
7
|
+
*/
|
|
8
|
+
export declare const reactInspectorAdapter: InspectorAdapter;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var types_exports = {};
|
|
19
|
+
__export(types_exports, {
|
|
20
|
+
parseSourceLocation: () => parseSourceLocation
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(types_exports);
|
|
23
|
+
function parseSourceLocation(value) {
|
|
24
|
+
const match = /^(.+):([\d]+):([\d]+)$/.exec(value);
|
|
25
|
+
if (!match) return null;
|
|
26
|
+
return {
|
|
27
|
+
file: match[1],
|
|
28
|
+
line: Number.parseInt(match[2], 10),
|
|
29
|
+
column: Number.parseInt(match[3], 10)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
33
|
+
0 && (module.exports = {
|
|
34
|
+
parseSourceLocation
|
|
35
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { InspectorAdapterId } from "../constants";
|
|
2
|
+
/** 元素对应的源码位置(跨框架统一形态;未解析到的字段为 null) */
|
|
3
|
+
export interface InspectorSourceLocation {
|
|
4
|
+
file: string | null;
|
|
5
|
+
line: number | null;
|
|
6
|
+
column: number | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 元素点击处理器。
|
|
10
|
+
* 返回 true 表示宿主已接管:适配器抑制底层 inspector 的默认行为(如打开编辑器)。
|
|
11
|
+
*/
|
|
12
|
+
export type InspectorElementClickHandler = (element: Element | null, event: MouseEvent) => boolean;
|
|
13
|
+
/**
|
|
14
|
+
* 框架侧「点击元素 → 源码位置」适配器契约。
|
|
15
|
+
* 宿主(挂件 / 扩展)只依赖此接口,不感知 Vue、React 等框架差异。
|
|
16
|
+
*/
|
|
17
|
+
export interface InspectorAdapter {
|
|
18
|
+
readonly id: InspectorAdapterId;
|
|
19
|
+
/** 用户可见名称(提示文案用) */
|
|
20
|
+
readonly label: string;
|
|
21
|
+
/** 运行时是否可用(对应框架的 inspector 已注入页面) */
|
|
22
|
+
isAvailable(): boolean;
|
|
23
|
+
/** 适配器自身覆盖层的选择器:宿主需忽略,不参与选择 */
|
|
24
|
+
readonly ignoreSelectors: readonly string[];
|
|
25
|
+
/** 适配器自带的忽略标记属性:宿主需忽略,不参与选择 */
|
|
26
|
+
readonly ignoreAttributes: readonly string[];
|
|
27
|
+
/** 元素 → 源码位置;解析不到返回 null */
|
|
28
|
+
resolveSourceLocation(element: Element): InspectorSourceLocation | null;
|
|
29
|
+
/** 注册元素点击处理器(幂等:同一底层 inspector 只安装一次) */
|
|
30
|
+
onElementClick(handler: InspectorElementClickHandler): void;
|
|
31
|
+
/** 同步底层 inspector 的启用状态 */
|
|
32
|
+
setEnabled(enabled: boolean): void;
|
|
33
|
+
}
|
|
34
|
+
/** 解析 "文件:行:列" 形式的源码坐标(各框架适配器共用的标记格式) */
|
|
35
|
+
export declare function parseSourceLocation(value: string): InspectorSourceLocation | null;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var vue_exports = {};
|
|
19
|
+
__export(vue_exports, {
|
|
20
|
+
vueInspectorAdapter: () => vueInspectorAdapter
|
|
21
|
+
});
|
|
22
|
+
module.exports = __toCommonJS(vue_exports);
|
|
23
|
+
var import_constants = require("../constants.cjs");
|
|
24
|
+
var import_types = require("./types.cjs");
|
|
25
|
+
const SOURCE_LOCATION_ATTRIBUTE = "data-v-inspector";
|
|
26
|
+
const VNODE_SOURCE_KEY = "__v_inspector";
|
|
27
|
+
const IGNORE_ATTRIBUTE = "data-v-inspector-ignore";
|
|
28
|
+
const OVERLAY_SELECTOR = "#vue-inspector-container";
|
|
29
|
+
function getRuntime() {
|
|
30
|
+
return typeof window === "undefined" ? void 0 : window.__VUE_INSPECTOR__;
|
|
31
|
+
}
|
|
32
|
+
function isAdapterOwned(element) {
|
|
33
|
+
return element.hasAttribute(IGNORE_ATTRIBUTE) || Boolean(element.closest(OVERLAY_SELECTOR));
|
|
34
|
+
}
|
|
35
|
+
function getMarkerData(element) {
|
|
36
|
+
const vnodeData = element.__vnode?.props?.[VNODE_SOURCE_KEY];
|
|
37
|
+
if (vnodeData) return vnodeData;
|
|
38
|
+
const ctxVNode = element.__vnode?.ctx?.vnode;
|
|
39
|
+
if (ctxVNode?.el === element) {
|
|
40
|
+
const ctxData = ctxVNode.props?.[VNODE_SOURCE_KEY];
|
|
41
|
+
if (ctxData) return ctxData;
|
|
42
|
+
}
|
|
43
|
+
const vueInstance = element.__vueParentComponent;
|
|
44
|
+
let currentParent = vueInstance?.parent;
|
|
45
|
+
while (currentParent) {
|
|
46
|
+
if (currentParent.vnode?.el === element) {
|
|
47
|
+
const parentData = currentParent.vnode.props?.[VNODE_SOURCE_KEY];
|
|
48
|
+
if (parentData) return parentData;
|
|
49
|
+
}
|
|
50
|
+
currentParent = currentParent.parent;
|
|
51
|
+
}
|
|
52
|
+
return element.getAttribute(SOURCE_LOCATION_ATTRIBUTE) ?? void 0;
|
|
53
|
+
}
|
|
54
|
+
function resolveFromMarker(element) {
|
|
55
|
+
const data = getMarkerData(element);
|
|
56
|
+
return data ? (0, import_types.parseSourceLocation)(data) : null;
|
|
57
|
+
}
|
|
58
|
+
function resolveFromVueInstance(element) {
|
|
59
|
+
const vue3Instance = element.__vueParentComponent;
|
|
60
|
+
if (vue3Instance) {
|
|
61
|
+
let current = vue3Instance;
|
|
62
|
+
while (current) {
|
|
63
|
+
const file = current.type?.__file || current.vnode?.type?.__file;
|
|
64
|
+
if (file) {
|
|
65
|
+
return { file, line: null, column: null };
|
|
66
|
+
}
|
|
67
|
+
current = current.parent;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const vue2Instance = element.__vue__;
|
|
71
|
+
if (vue2Instance) {
|
|
72
|
+
let current = vue2Instance;
|
|
73
|
+
while (current) {
|
|
74
|
+
const file = current.$options?.__file;
|
|
75
|
+
if (file) {
|
|
76
|
+
return { file, line: null, column: null };
|
|
77
|
+
}
|
|
78
|
+
current = current.$parent;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
function mergeSourceLocations(markerLocation, instanceLocation) {
|
|
84
|
+
if (!markerLocation?.file && !instanceLocation?.file) return null;
|
|
85
|
+
if (markerLocation?.file && instanceLocation?.file) {
|
|
86
|
+
const isNodeModules = (path) => path.includes("node_modules");
|
|
87
|
+
if (!isNodeModules(markerLocation.file)) return markerLocation;
|
|
88
|
+
if (!isNodeModules(instanceLocation.file)) return instanceLocation;
|
|
89
|
+
return markerLocation;
|
|
90
|
+
}
|
|
91
|
+
return markerLocation?.file ? markerLocation : instanceLocation;
|
|
92
|
+
}
|
|
93
|
+
let clickHandler = null;
|
|
94
|
+
const vueInspectorAdapter = {
|
|
95
|
+
id: import_constants.INSPECTOR_ADAPTER_IDS.vue,
|
|
96
|
+
label: "Vue Inspector",
|
|
97
|
+
ignoreSelectors: [OVERLAY_SELECTOR],
|
|
98
|
+
ignoreAttributes: [IGNORE_ATTRIBUTE],
|
|
99
|
+
isAvailable() {
|
|
100
|
+
return Boolean(getRuntime());
|
|
101
|
+
},
|
|
102
|
+
resolveSourceLocation(element) {
|
|
103
|
+
let current = element;
|
|
104
|
+
let markerLocation = null;
|
|
105
|
+
let instanceLocation = null;
|
|
106
|
+
while (current && !(markerLocation && instanceLocation)) {
|
|
107
|
+
if (!isAdapterOwned(current)) {
|
|
108
|
+
markerLocation ?? (markerLocation = resolveFromMarker(current));
|
|
109
|
+
instanceLocation ?? (instanceLocation = resolveFromVueInstance(current));
|
|
110
|
+
}
|
|
111
|
+
current = current.parentElement;
|
|
112
|
+
}
|
|
113
|
+
return mergeSourceLocations(markerLocation, instanceLocation);
|
|
114
|
+
},
|
|
115
|
+
onElementClick(handler) {
|
|
116
|
+
clickHandler = handler;
|
|
117
|
+
const runtime = getRuntime();
|
|
118
|
+
if (!runtime || runtime.__aipanel_hooked) return;
|
|
119
|
+
const originalHandleClick = runtime.handleClick.bind(runtime);
|
|
120
|
+
runtime.handleClick = (event) => {
|
|
121
|
+
const element = event.target instanceof Element ? event.target : null;
|
|
122
|
+
if (clickHandler?.(element, event)) {
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
event.stopPropagation();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
originalHandleClick(event);
|
|
128
|
+
};
|
|
129
|
+
runtime.__aipanel_hooked = true;
|
|
130
|
+
},
|
|
131
|
+
setEnabled(enabled) {
|
|
132
|
+
const runtime = getRuntime();
|
|
133
|
+
if (!runtime) return;
|
|
134
|
+
if (enabled) runtime.enable();
|
|
135
|
+
else runtime.disable();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
139
|
+
0 && (module.exports = {
|
|
140
|
+
vueInspectorAdapter
|
|
141
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type InspectorAdapter } from "./types";
|
|
2
|
+
/** unplugin-vue-inspector 暴露到页面的运行时控制对象 */
|
|
3
|
+
interface VueInspectorRuntime {
|
|
4
|
+
handleClick: (e: MouseEvent) => void;
|
|
5
|
+
enable: () => void;
|
|
6
|
+
disable: () => void;
|
|
7
|
+
/** AIPanel 已接管 handleClick 的标记(幂等安装) */
|
|
8
|
+
__aipanel_hooked?: boolean;
|
|
9
|
+
}
|
|
10
|
+
declare global {
|
|
11
|
+
interface Window {
|
|
12
|
+
__VUE_INSPECTOR__?: VueInspectorRuntime;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Vue 适配器:复用 unplugin-vue-inspector 注入的运行时与源码坐标标记,
|
|
17
|
+
* 在宿主选择模式下接管点击、解析元素源码位置。
|
|
18
|
+
*/
|
|
19
|
+
export declare const vueInspectorAdapter: InspectorAdapter;
|
|
20
|
+
export {};
|
package/lib/common/types.d.ts
CHANGED
|
@@ -113,6 +113,18 @@ export interface PageContext {
|
|
|
113
113
|
* 服务启动任务状态
|
|
114
114
|
*/
|
|
115
115
|
export type ServiceStartupTask = "checking_provider" | "allocating_port" | "preparing_runtime" | "starting_web" | "waiting_web_ready" | "starting_proxy" | "warming_up_chrome" | "creating_session" | "provider_not_installed" | "web_start_timeout" | "proxy_start_failed" | "session_creation_failed" | "chrome_mcp_failed" | "ready";
|
|
116
|
+
/**
|
|
117
|
+
* 服务启动任务状态载荷
|
|
118
|
+
* currentTask / SSE TASK_UPDATE / STATUS_SYNC 共用同一形态(单一来源)
|
|
119
|
+
*/
|
|
120
|
+
export interface ServiceTaskState {
|
|
121
|
+
/** 当前任务 */
|
|
122
|
+
task: ServiceStartupTask;
|
|
123
|
+
/** 失败原因类型(如 ChromeMcpWarmupErrorType) */
|
|
124
|
+
errorType?: string;
|
|
125
|
+
/** 失败原因文案 */
|
|
126
|
+
errorMessage?: string;
|
|
127
|
+
}
|
|
116
128
|
/**
|
|
117
129
|
* 服务启动任务状态映射
|
|
118
130
|
*/
|
package/lib/index.cjs
CHANGED
|
@@ -15,6 +15,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
15
15
|
var lib_exports = {};
|
|
16
16
|
module.exports = __toCommonJS(lib_exports);
|
|
17
17
|
__reExport(lib_exports, require("./common/constants.cjs"), module.exports);
|
|
18
|
+
__reExport(lib_exports, require("./common/inspector/index.cjs"), module.exports);
|
|
18
19
|
__reExport(lib_exports, require("./common/logger-core.cjs"), module.exports);
|
|
19
20
|
__reExport(lib_exports, require("./common/options.cjs"), module.exports);
|
|
20
21
|
__reExport(lib_exports, require("./common/provider.cjs"), module.exports);
|
|
@@ -23,6 +24,7 @@ __reExport(lib_exports, require("./common/utils.cjs"), module.exports);
|
|
|
23
24
|
// Annotate the CommonJS export names for ESM import in node:
|
|
24
25
|
0 && (module.exports = {
|
|
25
26
|
...require("./common/constants.cjs"),
|
|
27
|
+
...require("./common/inspector/index.cjs"),
|
|
26
28
|
...require("./common/logger-core.cjs"),
|
|
27
29
|
...require("./common/options.cjs"),
|
|
28
30
|
...require("./common/provider.cjs"),
|
package/lib/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aipanel/core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "lib/index.cjs",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "pagoda-cli build",
|
|
39
39
|
"test": "vitest run",
|
|
40
|
+
"test:coverage": "vitest run --coverage",
|
|
40
41
|
"typecheck": "tsc -p tsconfig.declaration.json --noEmit"
|
|
41
42
|
}
|
|
42
43
|
}
|