@dimina-kit/devtools 0.3.1-dev.20260518114324 → 0.3.2-dev.20260522085907
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/README.md +33 -71
- package/dist/main/app/app.js +5 -0
- package/dist/main/app/bootstrap.js +2 -1
- package/dist/main/app/launch.d.ts +1 -1
- package/dist/main/app/launch.js +6 -7
- package/dist/main/index.bundle.js +306 -317
- package/dist/main/ipc/panels.js +3 -5
- package/dist/main/ipc/projects.d.ts +1 -1
- package/dist/main/ipc/projects.js +5 -5
- package/dist/main/ipc/settings.d.ts +1 -1
- package/dist/main/ipc/settings.js +14 -1
- package/dist/main/ipc/simulator.d.ts +1 -1
- package/dist/main/ipc/simulator.js +1 -10
- package/dist/main/services/automation/exec.d.ts +2 -0
- package/dist/main/services/automation/exec.js +7 -9
- package/dist/main/services/automation/handlers/app.js +37 -51
- package/dist/main/services/automation/handlers/element.js +27 -41
- package/dist/main/services/default-adapter.js +3 -3
- package/dist/main/services/mcp/index.d.ts +1 -0
- package/dist/main/services/mcp/index.js +1 -0
- package/dist/main/services/mcp/server.js +5 -0
- package/dist/main/services/mcp/status.d.ts +10 -0
- package/dist/main/services/mcp/status.js +14 -0
- package/dist/main/services/mcp/target-manager.js +2 -1
- package/dist/main/services/notifications/renderer-notifier.d.ts +3 -3
- package/dist/main/services/notifications/renderer-notifier.js +3 -6
- package/dist/main/services/projects/local-provider.js +3 -3
- package/dist/main/services/projects/project-repository.d.ts +0 -1
- package/dist/main/services/settings/index.js +2 -1
- package/dist/main/services/simulator-storage/index.js +47 -77
- package/dist/main/services/views/view-manager.d.ts +3 -7
- package/dist/main/services/views/view-manager.js +20 -27
- package/dist/main/services/window-service.d.ts +3 -3
- package/dist/main/services/workbench-context.d.ts +3 -6
- package/dist/main/services/workbench-context.js +1 -3
- package/dist/main/services/workspace/workspace-service.js +8 -24
- package/dist/main/utils/theme.d.ts +24 -0
- package/dist/main/utils/theme.js +38 -1
- package/dist/preload/index.d.ts +5 -2
- package/dist/preload/index.js +3 -2
- package/dist/preload/instrumentation/app-data.d.ts +32 -2
- package/dist/preload/instrumentation/app-data.js +187 -172
- package/dist/preload/instrumentation/index.d.ts +2 -2
- package/dist/preload/instrumentation/index.js +2 -2
- package/dist/preload/instrumentation/wxml.d.ts +16 -4
- package/dist/preload/instrumentation/wxml.js +188 -178
- package/dist/preload/miniapp-snapshot/host.d.ts +12 -0
- package/dist/preload/miniapp-snapshot/host.js +117 -0
- package/dist/preload/miniapp-snapshot/types.d.ts +62 -0
- package/dist/preload/miniapp-snapshot/types.js +15 -0
- package/dist/preload/runtime/bridge.js +5 -24
- package/dist/preload/runtime/custom-apis.d.ts +1 -1
- package/dist/preload/runtime/custom-apis.js +3 -7
- package/dist/preload/shared/expose.d.ts +15 -0
- package/dist/preload/shared/expose.js +33 -0
- package/dist/preload/shared/page-iframe.d.ts +3 -0
- package/dist/preload/shared/page-iframe.js +6 -0
- package/dist/preload/shared/types.d.ts +2 -0
- package/dist/preload/windows/simulator.js +400 -348
- package/dist/renderer/assets/constants-DI382mTP.js +2 -0
- package/dist/renderer/assets/index-D8VFYBh-.js +46 -0
- package/dist/renderer/assets/{input-DqVQNCx_.js → input-CoWcfa8u.js} +2 -2
- package/dist/renderer/assets/ipc-transport-Cd2xPrsa.js +9 -0
- package/dist/renderer/assets/popover-Nq5gT9DO.js +2 -0
- package/dist/renderer/assets/select-By5KH-UG.js +2 -0
- package/dist/renderer/assets/{settings-BFNfnYvU.js → settings-Dj_N70qR.js} +2 -2
- package/dist/renderer/assets/settings-api-Do0_Smq1.js +2 -0
- package/dist/renderer/assets/workbenchSettings-CazSLGYt.js +8 -0
- package/dist/renderer/entries/main/index.html +5 -4
- package/dist/renderer/entries/popover/index.html +5 -4
- package/dist/renderer/entries/settings/index.html +4 -4
- package/dist/renderer/entries/workbench-settings/index.html +4 -3
- package/dist/shared/ipc-channels.d.ts +4 -12
- package/dist/shared/ipc-channels.js +10 -14
- package/dist/shared/simulator-route.d.ts +72 -0
- package/dist/shared/simulator-route.js +169 -0
- package/dist/shared/types.d.ts +2 -10
- package/dist/simulator/assets/simulator-DJCozrU5.js +9 -0
- package/dist/simulator/simulator.html +6 -43
- package/package.json +4 -4
- package/dist/preload/instrumentation/disposable.d.ts +0 -11
- package/dist/preload/instrumentation/disposable.js +0 -15
- package/dist/renderer/assets/index-Czz_bp6b.js +0 -46
- package/dist/renderer/assets/ipc-transport-CCHrCtDr.js +0 -9
- package/dist/renderer/assets/popover-BRg4XI_w.js +0 -2
- package/dist/renderer/assets/select-CvILHHZU.js +0 -2
- package/dist/renderer/assets/settings-api-DOAgmBst.js +0 -2
- package/dist/renderer/assets/workbenchSettings-DjVMgMMH.js +0 -8
- package/dist/simulator/assets/simulator-DL6kOCq-.js +0 -9
package/dist/main/ipc/panels.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { webContents } from 'electron';
|
|
2
1
|
import { PanelChannel } from '../../shared/ipc-channels.js';
|
|
3
2
|
import { PanelEvalSchema, PanelSelectSchema, } from '../../shared/ipc-schemas.js';
|
|
4
3
|
import { validate } from '../utils/ipc-schema.js';
|
|
@@ -16,11 +15,10 @@ export function registerPanelsIpc(ctx) {
|
|
|
16
15
|
})
|
|
17
16
|
.handle(PanelChannel.Eval, async (_event, ...args) => {
|
|
18
17
|
const [expression] = validate(PanelChannel.Eval, PanelEvalSchema, args);
|
|
19
|
-
|
|
20
|
-
if (!ctx.workspace.hasActiveSession() || simWcId == null)
|
|
18
|
+
if (!ctx.workspace.hasActiveSession())
|
|
21
19
|
return undefined;
|
|
22
|
-
const sim =
|
|
23
|
-
if (!sim
|
|
20
|
+
const sim = ctx.views.getSimulatorWebContents();
|
|
21
|
+
if (!sim)
|
|
24
22
|
return undefined;
|
|
25
23
|
try {
|
|
26
24
|
return await sim.executeJavaScript(expression);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { WorkbenchContext } from '../services/workbench-context.js';
|
|
2
2
|
import type { WorkbenchModule } from '../services/module.js';
|
|
3
3
|
import type { Disposable } from '../utils/disposable.js';
|
|
4
|
-
type ProjectsIpcCtx = Pick<WorkbenchContext, 'workspace' | '
|
|
4
|
+
type ProjectsIpcCtx = Pick<WorkbenchContext, 'workspace' | 'windows' | 'senderPolicy' | 'projectsProvider' | 'projectTemplates' | 'customCreateProjectDialog'>;
|
|
5
5
|
export declare function registerProjectsIpc(ctx: ProjectsIpcCtx): Disposable;
|
|
6
6
|
export declare const projectsModule: WorkbenchModule;
|
|
7
7
|
export {};
|
|
@@ -13,7 +13,7 @@ export function registerProjectsIpc(ctx) {
|
|
|
13
13
|
return ctx.workspace.listProjects();
|
|
14
14
|
})
|
|
15
15
|
.handle(DialogChannel.OpenDirectory, async () => {
|
|
16
|
-
const result = await dialog.showOpenDialog(ctx.mainWindow, {
|
|
16
|
+
const result = await dialog.showOpenDialog(ctx.windows.mainWindow, {
|
|
17
17
|
properties: ['openDirectory'],
|
|
18
18
|
title: '选择小程序项目目录',
|
|
19
19
|
});
|
|
@@ -23,7 +23,7 @@ export function registerProjectsIpc(ctx) {
|
|
|
23
23
|
const [dirPath] = validate(ProjectsChannel.Add, ProjectsAddSchema, args);
|
|
24
24
|
const dirError = await ctx.workspace.validateProjectDir(dirPath);
|
|
25
25
|
if (dirError) {
|
|
26
|
-
await dialog.showMessageBox(ctx.mainWindow, {
|
|
26
|
+
await dialog.showMessageBox(ctx.windows.mainWindow, {
|
|
27
27
|
type: 'error',
|
|
28
28
|
title: '无法导入项目',
|
|
29
29
|
message: '该目录不是有效的小程序项目',
|
|
@@ -35,7 +35,7 @@ export function registerProjectsIpc(ctx) {
|
|
|
35
35
|
const duplicate = await ctx.workspace.hasProject(dirPath);
|
|
36
36
|
const project = await ctx.workspace.addProject(dirPath);
|
|
37
37
|
if (duplicate) {
|
|
38
|
-
await dialog.showMessageBox(ctx.mainWindow, {
|
|
38
|
+
await dialog.showMessageBox(ctx.windows.mainWindow, {
|
|
39
39
|
type: 'info',
|
|
40
40
|
title: '项目已存在',
|
|
41
41
|
message: '该项目已在列表中',
|
|
@@ -61,7 +61,7 @@ export function registerProjectsIpc(ctx) {
|
|
|
61
61
|
return null;
|
|
62
62
|
const sanitized = sanitizeTemplates(ctx.projectTemplates ?? []);
|
|
63
63
|
return await ctx.customCreateProjectDialog({
|
|
64
|
-
parentWindow: ctx.mainWindow,
|
|
64
|
+
parentWindow: ctx.windows.mainWindow,
|
|
65
65
|
templates: sanitized,
|
|
66
66
|
});
|
|
67
67
|
})
|
|
@@ -87,7 +87,7 @@ export function registerProjectsIpc(ctx) {
|
|
|
87
87
|
// non-empty target dir, missing template, remote backend reject,
|
|
88
88
|
// …) as a native dialog so users see *why* the create silently
|
|
89
89
|
// fizzled. The renderer's catch then just bails out.
|
|
90
|
-
await dialog.showMessageBox(ctx.mainWindow, {
|
|
90
|
+
await dialog.showMessageBox(ctx.windows.mainWindow, {
|
|
91
91
|
type: 'error',
|
|
92
92
|
title: '创建项目失败',
|
|
93
93
|
message: '无法创建项目',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { WorkbenchContext } from '../services/workbench-context.js';
|
|
2
2
|
import type { Disposable } from '../utils/disposable.js';
|
|
3
3
|
import type { WorkbenchModule } from '../services/module.js';
|
|
4
|
-
export declare function registerSettingsIpc(ctx: Pick<WorkbenchContext, '
|
|
4
|
+
export declare function registerSettingsIpc(ctx: Pick<WorkbenchContext, 'views' | 'notify' | 'workspace' | 'rendererDir' | 'senderPolicy' | 'windows'>): Disposable;
|
|
5
5
|
export declare const settingsModule: WorkbenchModule;
|
|
6
6
|
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -2,6 +2,8 @@ import { app } from 'electron';
|
|
|
2
2
|
import { openSettingsWindow } from '../app/launch.js';
|
|
3
3
|
import { loadWorkbenchSettings, saveWorkbenchSettings, applyTheme, } from '../services/settings/index.js';
|
|
4
4
|
import { WorkbenchSettingsChannel, SettingsChannel } from '../../shared/ipc-channels.js';
|
|
5
|
+
import { DEFAULT_CDP_PORT } from '../../shared/constants.js';
|
|
6
|
+
import { getMcpStatus } from '../services/mcp/status.js';
|
|
5
7
|
import { SettingsConfigChangedSchema, SettingsProjectSettingsChangedSchema, SettingsSetVisibleSchema, WorkbenchSettingsSaveSchema, WorkbenchSettingsSetThemeSchema, WorkbenchSettingsSetVisibleSchema, } from '../../shared/ipc-schemas.js';
|
|
6
8
|
import { validate } from '../utils/ipc-schema.js';
|
|
7
9
|
import { IpcRegistry } from '../utils/ipc-registry.js';
|
|
@@ -23,7 +25,7 @@ export function registerSettingsIpc(ctx) {
|
|
|
23
25
|
.handle(WorkbenchSettingsChannel.GetCdpStatus, () => {
|
|
24
26
|
const settings = loadWorkbenchSettings();
|
|
25
27
|
const switchValue = app.commandLine.getSwitchValue('remote-debugging-port');
|
|
26
|
-
const implicitDevDefault = !app.isPackaged && !settings.cdp.enabled && switchValue ===
|
|
28
|
+
const implicitDevDefault = !app.isPackaged && !settings.cdp.enabled && switchValue === String(DEFAULT_CDP_PORT);
|
|
27
29
|
return {
|
|
28
30
|
configured: settings.cdp.enabled,
|
|
29
31
|
port: settings.cdp.port,
|
|
@@ -31,6 +33,17 @@ export function registerSettingsIpc(ctx) {
|
|
|
31
33
|
activePort: switchValue ? parseInt(switchValue, 10) : null,
|
|
32
34
|
implicitDevDefault,
|
|
33
35
|
};
|
|
36
|
+
})
|
|
37
|
+
.handle(WorkbenchSettingsChannel.GetMcpStatus, () => {
|
|
38
|
+
const settings = loadWorkbenchSettings();
|
|
39
|
+
const runtime = getMcpStatus();
|
|
40
|
+
return {
|
|
41
|
+
configured: settings.mcp.enabled,
|
|
42
|
+
configuredPort: settings.mcp.port,
|
|
43
|
+
running: runtime.running,
|
|
44
|
+
activePort: runtime.port,
|
|
45
|
+
error: runtime.error,
|
|
46
|
+
};
|
|
34
47
|
})
|
|
35
48
|
.handle(WorkbenchSettingsChannel.SetVisible, async (_, ...args) => {
|
|
36
49
|
const [visible] = validate(WorkbenchSettingsChannel.SetVisible, WorkbenchSettingsSetVisibleSchema, args);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { WorkbenchContext } from '../services/workbench-context.js';
|
|
2
2
|
import type { Disposable } from '../utils/disposable.js';
|
|
3
|
-
export declare function registerSimulatorIpc(ctx: Pick<WorkbenchContext, 'views' | '
|
|
3
|
+
export declare function registerSimulatorIpc(ctx: Pick<WorkbenchContext, 'views' | 'notify' | 'senderPolicy'>): Disposable;
|
|
4
4
|
//# sourceMappingURL=simulator.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SimulatorChannel, SimulatorCustomApiChannel
|
|
1
|
+
import { SimulatorChannel, SimulatorCustomApiChannel } from '../../shared/ipc-channels.js';
|
|
2
2
|
import { SimulatorAttachSchema, SimulatorCustomApiInvokeSchema, SimulatorResizeSchema, SimulatorSetVisibleSchema, } from '../../shared/ipc-schemas.js';
|
|
3
3
|
import { simulatorApiRegistry } from '../services/simulator/custom-apis.js';
|
|
4
4
|
import { validate } from '../utils/ipc-schema.js';
|
|
@@ -26,15 +26,6 @@ export function registerSimulatorIpc(ctx) {
|
|
|
26
26
|
.handle(SimulatorCustomApiChannel.Invoke, (_, ...args) => {
|
|
27
27
|
const [name, params] = validate(SimulatorCustomApiChannel.Invoke, SimulatorCustomApiInvokeSchema, args);
|
|
28
28
|
return simulatorApiRegistry.invoke(name, params);
|
|
29
|
-
})
|
|
30
|
-
.handle(WorkbenchChannel.GetPanelConfig, () => {
|
|
31
|
-
return ctx.panels;
|
|
32
|
-
})
|
|
33
|
-
.handle(WorkbenchChannel.GetApiNamespaces, () => {
|
|
34
|
-
return ctx.apiNamespaces;
|
|
35
|
-
})
|
|
36
|
-
.on(WorkbenchChannel.Reset, () => {
|
|
37
|
-
ctx.notify.workbenchReset();
|
|
38
29
|
});
|
|
39
30
|
}
|
|
40
31
|
//# sourceMappingURL=simulator.js.map
|
|
@@ -4,6 +4,8 @@ export declare function getSimulator(ctx: WorkbenchContext): Electron.WebContent
|
|
|
4
4
|
export declare function evalInSim<T = unknown>(ctx: WorkbenchContext, expression: string): Promise<T>;
|
|
5
5
|
/** Build JS that locates an element by selector+index inside the active page iframe. */
|
|
6
6
|
export declare function buildElAccess(ref: ElementRef, varName?: string): string;
|
|
7
|
+
/** Run `body` inside the active page iframe with `varName` bound to the element at `ref`. */
|
|
8
|
+
export declare function evalInElement<T = unknown>(ctx: WorkbenchContext, ref: ElementRef, body: string, varName?: string): Promise<T>;
|
|
7
9
|
/** Wrap code to run inside the active page iframe (last iframe in the stack). */
|
|
8
10
|
export declare function inIframe(code: string): string;
|
|
9
11
|
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import { webContents } from 'electron';
|
|
2
1
|
// ── Helpers ───────────────────────────────────────────────────────────
|
|
3
2
|
export function getSimulator(ctx) {
|
|
4
|
-
|
|
5
|
-
if (!ctx.workspace.hasActiveSession() || simWcId == null)
|
|
3
|
+
if (!ctx.workspace.hasActiveSession())
|
|
6
4
|
return null;
|
|
7
|
-
|
|
8
|
-
if (!sim || sim.isDestroyed())
|
|
9
|
-
return null;
|
|
10
|
-
return sim;
|
|
5
|
+
return ctx.views.getSimulatorWebContents();
|
|
11
6
|
}
|
|
12
7
|
export async function evalInSim(ctx, expression) {
|
|
13
8
|
const sim = getSimulator(ctx);
|
|
@@ -17,8 +12,11 @@ export async function evalInSim(ctx, expression) {
|
|
|
17
12
|
}
|
|
18
13
|
/** Build JS that locates an element by selector+index inside the active page iframe. */
|
|
19
14
|
export function buildElAccess(ref, varName = 'el') {
|
|
20
|
-
const
|
|
21
|
-
|
|
15
|
+
return `const ${varName} = _doc.querySelectorAll(${JSON.stringify(ref.selector)})[${ref.index}]`;
|
|
16
|
+
}
|
|
17
|
+
/** Run `body` inside the active page iframe with `varName` bound to the element at `ref`. */
|
|
18
|
+
export function evalInElement(ctx, ref, body, varName = 'el') {
|
|
19
|
+
return evalInSim(ctx, inIframe(`${buildElAccess(ref, varName)}\n${body}`));
|
|
22
20
|
}
|
|
23
21
|
/** Wrap code to run inside the active page iframe (last iframe in the stack). */
|
|
24
22
|
export function inIframe(code) {
|
|
@@ -1,59 +1,36 @@
|
|
|
1
1
|
import { evalInSim, getSimulator, inIframe } from '../exec.js';
|
|
2
|
+
import { parseLocationRoute } from '../../../../shared/simulator-route.js';
|
|
2
3
|
export const appHandlers = {};
|
|
3
4
|
// -- App domain --
|
|
5
|
+
async function readRoute(ctx) {
|
|
6
|
+
const { search } = await evalInSim(ctx, `({ search: location.search })`);
|
|
7
|
+
return parseLocationRoute(search);
|
|
8
|
+
}
|
|
4
9
|
appHandlers['App.getCurrentPage'] = async (ctx) => {
|
|
5
|
-
const
|
|
6
|
-
const clean = hash.replace(/^#/, '');
|
|
7
|
-
const qIdx = clean.indexOf('?');
|
|
8
|
-
const pathPart = qIdx >= 0 ? clean.substring(0, qIdx) : clean;
|
|
9
|
-
const queryPart = qIdx >= 0 ? clean.substring(qIdx + 1) : '';
|
|
10
|
-
// New format: #appid|page1|page2 — last segment is current page
|
|
11
|
-
let pagePath;
|
|
12
|
-
if (pathPart.includes('|')) {
|
|
13
|
-
const parts = pathPart.split('|');
|
|
14
|
-
pagePath = parts[parts.length - 1] ?? '';
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
// Legacy format: #appid/pagePath
|
|
18
|
-
const slashIdx = pathPart.indexOf('/');
|
|
19
|
-
pagePath = slashIdx >= 0 ? pathPart.substring(slashIdx + 1) : pathPart;
|
|
20
|
-
}
|
|
21
|
-
const query = {};
|
|
22
|
-
if (queryPart) {
|
|
23
|
-
for (const pair of queryPart.split('&')) {
|
|
24
|
-
const [k, v] = pair.split('=');
|
|
25
|
-
if (k)
|
|
26
|
-
query[k] = decodeURIComponent(v || '');
|
|
27
|
-
}
|
|
28
|
-
}
|
|
10
|
+
const route = await readRoute(ctx);
|
|
29
11
|
const iframeCount = await evalInSim(ctx, `document.querySelectorAll('iframe').length`);
|
|
30
|
-
return {
|
|
12
|
+
return {
|
|
13
|
+
pageId: iframeCount,
|
|
14
|
+
path: route?.current.pagePath ?? '',
|
|
15
|
+
query: route?.current.query ?? {},
|
|
16
|
+
};
|
|
31
17
|
};
|
|
32
18
|
appHandlers['App.getPageStack'] = async (ctx) => {
|
|
33
|
-
const
|
|
34
|
-
const clean = hash.replace(/^#/, '').replace(/\?.*$/, '');
|
|
19
|
+
const route = await readRoute(ctx);
|
|
35
20
|
const pageStack = [];
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
// Legacy format: #appid/pagePath — single page
|
|
45
|
-
const slashIdx = clean.indexOf('/');
|
|
46
|
-
const currentPath = slashIdx >= 0 ? clean.substring(slashIdx + 1) : clean;
|
|
47
|
-
if (currentPath) {
|
|
48
|
-
pageStack.push({ pageId: 1, path: currentPath, query: {} });
|
|
49
|
-
}
|
|
21
|
+
if (!route)
|
|
22
|
+
return { pageStack };
|
|
23
|
+
// Mirrors upstream HashRouter.parseSearch: `[entry]` or `[entry, current]`.
|
|
24
|
+
pageStack.push({ pageId: 1, path: route.entry.pagePath, query: route.entry.query });
|
|
25
|
+
if (route.current.pagePath !== route.entry.pagePath) {
|
|
26
|
+
pageStack.push({ pageId: 2, path: route.current.pagePath, query: route.current.query });
|
|
50
27
|
}
|
|
51
28
|
return { pageStack };
|
|
52
29
|
};
|
|
53
30
|
appHandlers['App.callWxMethod'] = async (ctx, params) => {
|
|
54
31
|
const method = params.method;
|
|
55
32
|
const args = params.args || [];
|
|
56
|
-
// For navigation methods, handle specially via
|
|
33
|
+
// For navigation methods, handle specially via DOM click / wx.* on the iframe
|
|
57
34
|
if (['navigateTo', 'redirectTo', 'reLaunch', 'switchTab'].includes(method)) {
|
|
58
35
|
const opts = args[0];
|
|
59
36
|
const url = opts?.url;
|
|
@@ -61,20 +38,29 @@ appHandlers['App.callWxMethod'] = async (ctx, params) => {
|
|
|
61
38
|
const cleanUrl = url.startsWith('/') ? url : `/${url}`;
|
|
62
39
|
// Try clicking a matching DOM element first
|
|
63
40
|
const clicked = await evalInSim(ctx, inIframe(`
|
|
64
|
-
const
|
|
41
|
+
const path = ${JSON.stringify(cleanUrl)}
|
|
42
|
+
const el = Array.from(_doc.querySelectorAll('[data-path]'))
|
|
43
|
+
.find(e => e.getAttribute('data-path') === path)
|
|
65
44
|
if (el) { el.click(); return true }
|
|
66
45
|
return false
|
|
67
46
|
`)).catch(() => false);
|
|
68
47
|
if (!clicked) {
|
|
69
|
-
// Fallback:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
48
|
+
// Fallback: drive navigation via wx.* on the rendered page iframe.
|
|
49
|
+
// Upstream's query router doesn't react to URL mutation; the active
|
|
50
|
+
// page's iframe exposes wx.* with the live dimina runtime bindings.
|
|
51
|
+
const apiName = JSON.stringify(method);
|
|
52
|
+
const urlJson = JSON.stringify(cleanUrl);
|
|
53
|
+
await evalInSim(ctx, `(() => {
|
|
54
|
+
const iframes = Array.from(document.querySelectorAll('iframe'))
|
|
55
|
+
for (const f of iframes) {
|
|
56
|
+
try {
|
|
57
|
+
if (f.contentWindow && f.contentWindow.wx && typeof f.contentWindow.wx[${apiName}] === 'function') {
|
|
58
|
+
f.contentWindow.wx[${apiName}]({ url: ${urlJson} })
|
|
59
|
+
return
|
|
77
60
|
}
|
|
61
|
+
} catch (_) {}
|
|
62
|
+
}
|
|
63
|
+
})()`);
|
|
78
64
|
}
|
|
79
65
|
// Wait for navigation
|
|
80
66
|
await new Promise((r) => setTimeout(r, 2000));
|
|
@@ -1,112 +1,100 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { evalInElement } from '../exec.js';
|
|
2
2
|
import { getElementRef, registerElement } from '../registry.js';
|
|
3
3
|
export const elementHandlers = {};
|
|
4
4
|
// -- Element domain --
|
|
5
5
|
elementHandlers['Element.tap'] = async (ctx, params) => {
|
|
6
6
|
const ref = getElementRef(params);
|
|
7
|
-
await
|
|
8
|
-
${buildElAccess(ref)}
|
|
7
|
+
await evalInElement(ctx, ref, `
|
|
9
8
|
if (!el) throw new Error('Element not found')
|
|
10
9
|
el.click()
|
|
11
|
-
`)
|
|
10
|
+
`);
|
|
12
11
|
return {};
|
|
13
12
|
};
|
|
14
13
|
elementHandlers['Element.triggerEvent'] = async (ctx, params) => {
|
|
15
14
|
const ref = getElementRef(params);
|
|
16
15
|
const type = params.type;
|
|
17
16
|
const detail = params.detail || {};
|
|
18
|
-
await
|
|
19
|
-
${buildElAccess(ref)}
|
|
17
|
+
await evalInElement(ctx, ref, `
|
|
20
18
|
if (!el) throw new Error('Element not found')
|
|
21
19
|
el.dispatchEvent(new CustomEvent('${type}', { detail: ${JSON.stringify(detail)}, bubbles: true }))
|
|
22
|
-
`)
|
|
20
|
+
`);
|
|
23
21
|
return {};
|
|
24
22
|
};
|
|
25
23
|
elementHandlers['Element.touchstart'] = async (ctx, params) => {
|
|
26
24
|
const ref = getElementRef(params);
|
|
27
|
-
await
|
|
28
|
-
${buildElAccess(ref)}
|
|
25
|
+
await evalInElement(ctx, ref, `
|
|
29
26
|
if (el) el.dispatchEvent(new TouchEvent('touchstart', { bubbles: true }))
|
|
30
|
-
`)
|
|
27
|
+
`);
|
|
31
28
|
return {};
|
|
32
29
|
};
|
|
33
30
|
elementHandlers['Element.touchmove'] = async (ctx, params) => {
|
|
34
31
|
const ref = getElementRef(params);
|
|
35
|
-
await
|
|
36
|
-
${buildElAccess(ref)}
|
|
32
|
+
await evalInElement(ctx, ref, `
|
|
37
33
|
if (el) el.dispatchEvent(new TouchEvent('touchmove', { bubbles: true }))
|
|
38
|
-
`)
|
|
34
|
+
`);
|
|
39
35
|
return {};
|
|
40
36
|
};
|
|
41
37
|
elementHandlers['Element.touchend'] = async (ctx, params) => {
|
|
42
38
|
const ref = getElementRef(params);
|
|
43
|
-
await
|
|
44
|
-
${buildElAccess(ref)}
|
|
39
|
+
await evalInElement(ctx, ref, `
|
|
45
40
|
if (el) el.dispatchEvent(new TouchEvent('touchend', { bubbles: true }))
|
|
46
|
-
`)
|
|
41
|
+
`);
|
|
47
42
|
return {};
|
|
48
43
|
};
|
|
49
44
|
elementHandlers['Element.getDOMProperties'] = async (ctx, params) => {
|
|
50
45
|
const ref = getElementRef(params);
|
|
51
46
|
const names = params.names;
|
|
52
|
-
const properties = await
|
|
53
|
-
${buildElAccess(ref)}
|
|
47
|
+
const properties = await evalInElement(ctx, ref, `
|
|
54
48
|
if (!el) return ${JSON.stringify(names.map(() => null))}
|
|
55
49
|
return ${JSON.stringify(names)}.map(n => el[n])
|
|
56
|
-
`)
|
|
50
|
+
`);
|
|
57
51
|
return { properties };
|
|
58
52
|
};
|
|
59
53
|
elementHandlers['Element.getAttributes'] = async (ctx, params) => {
|
|
60
54
|
const ref = getElementRef(params);
|
|
61
55
|
const names = params.names;
|
|
62
|
-
const attributes = await
|
|
63
|
-
${buildElAccess(ref)}
|
|
56
|
+
const attributes = await evalInElement(ctx, ref, `
|
|
64
57
|
if (!el) return ${JSON.stringify(names.map(() => null))}
|
|
65
58
|
return ${JSON.stringify(names)}.map(n => el.getAttribute(n))
|
|
66
|
-
`)
|
|
59
|
+
`);
|
|
67
60
|
return { attributes };
|
|
68
61
|
};
|
|
69
62
|
elementHandlers['Element.getStyles'] = async (ctx, params) => {
|
|
70
63
|
const ref = getElementRef(params);
|
|
71
64
|
const names = params.names;
|
|
72
|
-
const styles = await
|
|
73
|
-
${buildElAccess(ref)}
|
|
65
|
+
const styles = await evalInElement(ctx, ref, `
|
|
74
66
|
if (!el) return ${JSON.stringify(names.map(() => ''))}
|
|
75
67
|
const cs = getComputedStyle(el)
|
|
76
68
|
return ${JSON.stringify(names)}.map(n => cs.getPropertyValue(n))
|
|
77
|
-
`)
|
|
69
|
+
`);
|
|
78
70
|
return { styles };
|
|
79
71
|
};
|
|
80
72
|
elementHandlers['Element.getWXML'] = async (ctx, params) => {
|
|
81
73
|
const ref = getElementRef(params);
|
|
82
74
|
const type = params.type || 'inner';
|
|
83
75
|
const prop = type === 'outer' ? 'outerHTML' : 'innerHTML';
|
|
84
|
-
const wxml = await
|
|
85
|
-
${buildElAccess(ref)}
|
|
76
|
+
const wxml = await evalInElement(ctx, ref, `
|
|
86
77
|
return el ? el.${prop} : ''
|
|
87
|
-
`)
|
|
78
|
+
`);
|
|
88
79
|
return { wxml };
|
|
89
80
|
};
|
|
90
81
|
elementHandlers['Element.getOffset'] = async (ctx, params) => {
|
|
91
82
|
const ref = getElementRef(params);
|
|
92
|
-
return
|
|
93
|
-
${buildElAccess(ref)}
|
|
83
|
+
return evalInElement(ctx, ref, `
|
|
94
84
|
if (!el) return { left: 0, top: 0 }
|
|
95
85
|
const r = el.getBoundingClientRect()
|
|
96
86
|
return { left: r.left, top: r.top }
|
|
97
|
-
`)
|
|
87
|
+
`);
|
|
98
88
|
};
|
|
99
89
|
elementHandlers['Element.getElement'] = async (ctx, params) => {
|
|
100
90
|
const parentRef = getElementRef(params);
|
|
101
91
|
const selector = params.selector;
|
|
102
|
-
const
|
|
103
|
-
const info = await evalInSim(ctx, inIframe(`
|
|
104
|
-
${buildElAccess(parentRef, 'parent')}
|
|
92
|
+
const info = await evalInElement(ctx, parentRef, `
|
|
105
93
|
if (!parent) return null
|
|
106
|
-
const child = parent.querySelector(
|
|
94
|
+
const child = parent.querySelector(${JSON.stringify(selector)})
|
|
107
95
|
if (!child) return null
|
|
108
96
|
return { tagName: child.tagName.toLowerCase() }
|
|
109
|
-
|
|
97
|
+
`, 'parent');
|
|
110
98
|
if (!info)
|
|
111
99
|
throw new Error(`Child element not found: ${selector}`);
|
|
112
100
|
const combinedSelector = `${parentRef.selector} ${selector}`;
|
|
@@ -116,14 +104,12 @@ elementHandlers['Element.getElement'] = async (ctx, params) => {
|
|
|
116
104
|
elementHandlers['Element.getElements'] = async (ctx, params) => {
|
|
117
105
|
const parentRef = getElementRef(params);
|
|
118
106
|
const selector = params.selector;
|
|
119
|
-
const
|
|
120
|
-
const items = await evalInSim(ctx, inIframe(`
|
|
121
|
-
${buildElAccess(parentRef, 'parent')}
|
|
107
|
+
const items = await evalInElement(ctx, parentRef, `
|
|
122
108
|
if (!parent) return []
|
|
123
|
-
return Array.from(parent.querySelectorAll(
|
|
109
|
+
return Array.from(parent.querySelectorAll(${JSON.stringify(selector)})).map(el => ({
|
|
124
110
|
tagName: el.tagName.toLowerCase(),
|
|
125
111
|
}))
|
|
126
|
-
|
|
112
|
+
`, 'parent');
|
|
127
113
|
const combinedSelector = `${parentRef.selector} ${selector}`;
|
|
128
114
|
return {
|
|
129
115
|
elements: items.map((item, i) => ({
|
|
@@ -4,12 +4,12 @@ import { simulatorDir } from '../utils/paths.js';
|
|
|
4
4
|
export const defaultAdapter = {
|
|
5
5
|
async openProject(opts) {
|
|
6
6
|
const diminaKit = await import('@dimina-kit/devkit');
|
|
7
|
-
const
|
|
8
|
-
return openProject({
|
|
7
|
+
const openProjectOpts = {
|
|
9
8
|
outputDir: path.join(app.getPath('userData'), 'dimina-fe-output'),
|
|
10
9
|
...opts,
|
|
11
10
|
simulatorDir,
|
|
12
|
-
}
|
|
11
|
+
};
|
|
12
|
+
return diminaKit.openProject(openProjectOpts);
|
|
13
13
|
},
|
|
14
14
|
};
|
|
15
15
|
//# sourceMappingURL=default-adapter.js.map
|
|
@@ -12,6 +12,7 @@ import { createServer } from 'http';
|
|
|
12
12
|
import { createRequire } from 'node:module';
|
|
13
13
|
import { toDisposable } from '../../utils/disposable.js';
|
|
14
14
|
import { connectTarget, setCdpPort } from './target-manager.js';
|
|
15
|
+
import { recordMcpFailed, recordMcpStarted, recordMcpStopped } from './status.js';
|
|
15
16
|
import { registerCommonTargetTools } from './tool-registry.js';
|
|
16
17
|
import { registerContextTools } from './tools/context-tools.js';
|
|
17
18
|
import { registerSimulatorTools } from './tools/simulator-tools.js';
|
|
@@ -65,17 +66,21 @@ export function startMcpServer(resolvedCdpPort, mcpPort) {
|
|
|
65
66
|
httpServer.on('error', (err) => {
|
|
66
67
|
if (err.code === 'EADDRINUSE') {
|
|
67
68
|
console.warn(`[MCP] Port ${mcpPort} already in use — MCP server not started`);
|
|
69
|
+
recordMcpFailed('port-in-use');
|
|
68
70
|
}
|
|
69
71
|
else {
|
|
70
72
|
console.error('[MCP] Server error:', err);
|
|
73
|
+
recordMcpFailed(err.message);
|
|
71
74
|
}
|
|
72
75
|
});
|
|
73
76
|
httpServer.listen(mcpPort, '127.0.0.1', () => {
|
|
74
77
|
console.log(`[MCP] SSE server listening on http://127.0.0.1:${mcpPort}/sse`);
|
|
78
|
+
recordMcpStarted(mcpPort);
|
|
75
79
|
});
|
|
76
80
|
return toDisposable(() => {
|
|
77
81
|
transports.clear();
|
|
78
82
|
httpServer.close();
|
|
83
|
+
recordMcpStopped();
|
|
79
84
|
});
|
|
80
85
|
}
|
|
81
86
|
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface McpRuntimeStatus {
|
|
2
|
+
running: boolean;
|
|
3
|
+
port: number | null;
|
|
4
|
+
error: string | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function getMcpStatus(): McpRuntimeStatus;
|
|
7
|
+
export declare function recordMcpStarted(port: number): void;
|
|
8
|
+
export declare function recordMcpFailed(error: string): void;
|
|
9
|
+
export declare function recordMcpStopped(): void;
|
|
10
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
let status = { running: false, port: null, error: null };
|
|
2
|
+
export function getMcpStatus() {
|
|
3
|
+
return { ...status };
|
|
4
|
+
}
|
|
5
|
+
export function recordMcpStarted(port) {
|
|
6
|
+
status = { running: true, port, error: null };
|
|
7
|
+
}
|
|
8
|
+
export function recordMcpFailed(error) {
|
|
9
|
+
status = { running: false, port: null, error };
|
|
10
|
+
}
|
|
11
|
+
export function recordMcpStopped() {
|
|
12
|
+
status = { running: false, port: null, error: null };
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
* - rolling buffers of console log and network request events
|
|
11
11
|
*/
|
|
12
12
|
import CDP from 'chrome-remote-interface';
|
|
13
|
+
import { DEFAULT_CDP_PORT } from '../../../shared/constants.js';
|
|
13
14
|
const SIMULATOR_URL_PATTERN = 'localhost:7788';
|
|
14
15
|
const MAX_BUFFER = 500;
|
|
15
16
|
const RECONNECT_INTERVAL_MS = 3000;
|
|
16
|
-
let cdpPort =
|
|
17
|
+
let cdpPort = DEFAULT_CDP_PORT;
|
|
17
18
|
const targets = {
|
|
18
19
|
simulator: { client: null, connected: false, timer: null, consoleLogs: [], networkRequests: [] },
|
|
19
20
|
workbench: { client: null, connected: false, timer: null, consoleLogs: [], networkRequests: [] },
|
|
@@ -52,8 +52,6 @@ export interface RendererNotifier {
|
|
|
52
52
|
popoverClosed(): void;
|
|
53
53
|
/** Ask the main renderer to relaunch the simulator with a new config. */
|
|
54
54
|
popoverRelaunch(config: CompileConfig): void;
|
|
55
|
-
/** Ask the main renderer to re-initialise the devtools panels. */
|
|
56
|
-
workbenchReset(): void;
|
|
57
55
|
/** Tell the main renderer the toolbar actions list has changed. */
|
|
58
56
|
toolbarActionsChanged(): void;
|
|
59
57
|
/** Initialise the currently shown compile popover overlay. */
|
|
@@ -69,7 +67,9 @@ export interface RendererNotifier {
|
|
|
69
67
|
* between the notifier module and workbench-context.
|
|
70
68
|
*/
|
|
71
69
|
export interface NotifierContext {
|
|
72
|
-
|
|
70
|
+
windows: {
|
|
71
|
+
readonly mainWindow: BrowserWindow;
|
|
72
|
+
};
|
|
73
73
|
views: {
|
|
74
74
|
getSettingsWebContents(): WebContents | null;
|
|
75
75
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ProjectChannel, WindowChannel, SettingsChannel, PopoverChannel,
|
|
1
|
+
import { ProjectChannel, WindowChannel, SettingsChannel, PopoverChannel, ToolbarChannel, WorkbenchSettingsChannel, } from '../../../shared/ipc-channels.js';
|
|
2
2
|
/** Safely resolve a WebContents, skipping destroyed / missing targets. */
|
|
3
3
|
function liveWebContents(wc) {
|
|
4
4
|
if (!wc)
|
|
@@ -13,9 +13,9 @@ function liveWebContents(wc) {
|
|
|
13
13
|
*/
|
|
14
14
|
export function createRendererNotifier(ctx) {
|
|
15
15
|
function sendToMain(channel, ...args) {
|
|
16
|
-
if (ctx.mainWindow.isDestroyed())
|
|
16
|
+
if (ctx.windows.mainWindow.isDestroyed())
|
|
17
17
|
return;
|
|
18
|
-
const wc = liveWebContents(ctx.mainWindow.webContents);
|
|
18
|
+
const wc = liveWebContents(ctx.windows.mainWindow.webContents);
|
|
19
19
|
if (!wc)
|
|
20
20
|
return;
|
|
21
21
|
wc.send(channel, ...args);
|
|
@@ -39,9 +39,6 @@ export function createRendererNotifier(ctx) {
|
|
|
39
39
|
popoverRelaunch(config) {
|
|
40
40
|
sendToMain(PopoverChannel.Relaunch, config);
|
|
41
41
|
},
|
|
42
|
-
workbenchReset() {
|
|
43
|
-
sendToMain(WorkbenchChannel.Reset);
|
|
44
|
-
},
|
|
45
42
|
toolbarActionsChanged() {
|
|
46
43
|
sendToMain(ToolbarChannel.ActionsChanged);
|
|
47
44
|
},
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* `<userData>/dimina-projects.json` and exposes the validation / compile-
|
|
4
4
|
* config helpers historically provided by `project-repository.ts`.
|
|
5
5
|
*
|
|
6
|
-
* The repository module is kept for thumbnail/paths/page helpers
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* The repository module is kept for thumbnail/paths/page helpers; the
|
|
7
|
+
* canonical list & validation surface for the workspace service is this
|
|
8
|
+
* provider.
|
|
9
9
|
*/
|
|
10
10
|
import * as repo from './project-repository.js';
|
|
11
11
|
import { loadThumbnail, saveThumbnailFromDataUrl } from './thumbnail.js';
|
|
@@ -12,7 +12,6 @@ export interface ProjectPages {
|
|
|
12
12
|
export interface ProjectSettings {
|
|
13
13
|
uploadWithSourceMap: boolean;
|
|
14
14
|
}
|
|
15
|
-
export type { CompileConfig } from '../../../shared/types.js';
|
|
16
15
|
export declare function listProjects(): Project[];
|
|
17
16
|
export declare function validateProjectDir(dirPath: string): string | null;
|
|
18
17
|
export declare function hasProject(dirPath: string): boolean;
|