@dimina-kit/devtools 0.4.0-dev.20260824071518 → 0.4.0-dev.20260828161241
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/dist/main/api.d.ts +1 -1
- package/dist/main/app/app.js +59 -2
- package/dist/main/index.bundle.js +986 -245
- package/dist/main/ipc/index.d.ts +2 -0
- package/dist/main/ipc/index.js +2 -0
- package/dist/main/ipc/popover.js +1 -1
- package/dist/main/ipc/project-create.d.ts +15 -0
- package/dist/main/ipc/project-create.js +26 -0
- package/dist/main/ipc/projects.d.ts +1 -1
- package/dist/main/ipc/projects.js +36 -1
- package/dist/main/ipc/settings.js +2 -1
- package/dist/main/ipc/simulator-module.d.ts +4 -3
- package/dist/main/ipc/simulator-module.js +4 -5
- package/dist/main/ipc/tooltip.js +1 -1
- package/dist/main/ipc/views.d.ts +15 -1
- package/dist/main/ipc/views.js +61 -4
- package/dist/main/runtime/miniapp-runtime.d.ts +87 -19
- package/dist/main/services/notifications/renderer-notifier.d.ts +30 -1
- package/dist/main/services/notifications/renderer-notifier.js +23 -1
- package/dist/main/services/projects/create-project-service.js +9 -2
- package/dist/main/services/projects/local-provider.js +6 -0
- package/dist/main/services/projects/project-repository.d.ts +29 -1
- package/dist/main/services/projects/project-repository.js +151 -17
- package/dist/main/services/projects/types.d.ts +13 -2
- package/dist/main/services/update/update-manager.d.ts +17 -3
- package/dist/main/services/update/update-manager.js +25 -6
- package/dist/main/services/views/host-dialog-port-channel.d.ts +18 -0
- package/dist/main/services/views/host-dialog-port-channel.js +15 -0
- package/dist/main/services/views/host-dialog-session-runtime.d.ts +10 -0
- package/dist/main/services/views/host-dialog-session-runtime.js +17 -0
- package/dist/main/services/views/host-dialog-view.d.ts +95 -0
- package/dist/main/services/views/host-dialog-view.js +147 -0
- package/dist/main/services/views/host-sidebar-port-channel.d.ts +18 -0
- package/dist/main/services/views/host-sidebar-port-channel.js +15 -0
- package/dist/main/services/views/host-sidebar-session-runtime.d.ts +10 -0
- package/dist/main/services/views/host-sidebar-session-runtime.js +17 -0
- package/dist/main/services/views/host-sidebar-view.d.ts +59 -0
- package/dist/main/services/views/host-sidebar-view.js +44 -0
- package/dist/main/services/views/host-slot-port-channel.d.ts +107 -0
- package/dist/main/services/views/host-slot-port-channel.js +261 -0
- package/dist/main/services/views/host-slot-session-runtime.d.ts +40 -0
- package/dist/main/services/views/host-slot-session-runtime.js +50 -0
- package/dist/main/services/views/host-slot-view.d.ts +109 -0
- package/dist/main/services/views/host-slot-view.js +147 -0
- package/dist/main/services/views/host-toolbar-port-channel.d.ts +6 -91
- package/dist/main/services/views/host-toolbar-port-channel.js +10 -243
- package/dist/main/services/views/host-toolbar-session-runtime.d.ts +5 -30
- package/dist/main/services/views/host-toolbar-session-runtime.js +9 -47
- package/dist/main/services/views/host-toolbar-view.d.ts +34 -7
- package/dist/main/services/views/host-toolbar-view.js +30 -224
- package/dist/main/services/views/managed-web-contents-view.d.ts +48 -0
- package/dist/main/services/views/managed-web-contents-view.js +123 -0
- package/dist/main/services/views/overlay-panels-view.d.ts +23 -0
- package/dist/main/services/views/overlay-panels-view.js +93 -4
- package/dist/main/services/views/placement-reconciler.d.ts +14 -2
- package/dist/main/services/views/placement-reconciler.js +27 -0
- package/dist/main/services/views/placement-test-driver.d.ts +1 -0
- package/dist/main/services/views/placement-test-driver.js +3 -0
- package/dist/main/services/views/view-manager-devtools-host-test-fixtures.js +3 -1
- package/dist/main/services/views/view-manager.d.ts +10 -78
- package/dist/main/services/views/view-manager.js +34 -3
- package/dist/main/services/workbench-context.d.ts +18 -4
- package/dist/main/services/workbench-context.js +1 -0
- package/dist/main/services/workspace/workspace-service.d.ts +2 -0
- package/dist/main/services/workspace/workspace-service.js +14 -0
- package/dist/main/utils/paths.d.ts +4 -0
- package/dist/main/utils/paths.js +4 -0
- package/dist/main/utils/sender-policy.js +10 -0
- package/dist/native-host/common/common.js +65 -62
- package/dist/native-host/render/render.js +2230 -2052
- package/dist/native-host/service/service.js +2 -2
- package/dist/preload/runtime/host-dialog-advertiser.d.ts +22 -0
- package/dist/preload/runtime/host-dialog-advertiser.js +37 -0
- package/dist/preload/runtime/host-dialog-port.d.ts +44 -0
- package/dist/preload/runtime/host-dialog-port.js +53 -0
- package/dist/preload/runtime/host-dialog-runtime.d.ts +28 -0
- package/dist/preload/runtime/host-dialog-runtime.js +36 -0
- package/dist/preload/runtime/host-sidebar-advertiser.d.ts +19 -0
- package/dist/preload/runtime/host-sidebar-advertiser.js +35 -0
- package/dist/preload/runtime/host-sidebar-port.d.ts +44 -0
- package/dist/preload/runtime/host-sidebar-port.js +53 -0
- package/dist/preload/runtime/host-sidebar-runtime.d.ts +28 -0
- package/dist/preload/runtime/host-sidebar-runtime.js +36 -0
- package/dist/preload/runtime/host-slot-port-bridge.d.ts +18 -0
- package/dist/preload/runtime/host-slot-port-bridge.js +112 -0
- package/dist/preload/runtime/host-toolbar-advertiser.d.ts +5 -13
- package/dist/preload/runtime/host-toolbar-advertiser.js +18 -38
- package/dist/preload/runtime/host-toolbar-port.d.ts +9 -0
- package/dist/preload/runtime/host-toolbar-port.js +11 -95
- package/dist/preload/runtime/wait-for-slot-root.d.ts +20 -0
- package/dist/preload/runtime/wait-for-slot-root.js +52 -0
- package/dist/preload/windows/host-dialog-runtime.cjs +353 -0
- package/dist/preload/windows/host-dialog-runtime.cjs.map +7 -0
- package/dist/preload/windows/host-dialog-runtime.d.ts +2 -0
- package/dist/preload/windows/host-dialog-runtime.js +13 -0
- package/dist/preload/windows/host-sidebar-runtime.cjs +354 -0
- package/dist/preload/windows/host-sidebar-runtime.cjs.map +7 -0
- package/dist/preload/windows/host-sidebar-runtime.d.ts +2 -0
- package/dist/preload/windows/host-sidebar-runtime.js +13 -0
- package/dist/preload/windows/host-toolbar-runtime.cjs +134 -51
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +3 -3
- package/dist/preload/windows/simulator.cjs.map +1 -1
- package/dist/renderer/assets/button-D5Q2wkXq.js +2 -0
- package/dist/renderer/assets/constants-D7DfVqMp.js +2 -0
- package/dist/renderer/assets/constants-D8zsejFq.js +2 -0
- package/dist/renderer/assets/createLucideIcon-Bqi8hpdi.js +2 -0
- package/dist/renderer/assets/dialog-e1TecTOY.js +46 -0
- package/dist/renderer/assets/dist-DKWdE1TK.js +2 -0
- package/dist/renderer/assets/hostSidebarDefault-B3yLfy8j.js +2 -0
- package/dist/renderer/assets/index-CsPD68N_.js +5 -0
- package/dist/renderer/assets/input-Ct3alvkq.js +2 -0
- package/dist/renderer/assets/ipc-channels-overlays-LsDzGAtV.js +2 -0
- package/dist/renderer/assets/jsx-runtime-2vXHouwr.css +1 -0
- package/dist/renderer/assets/jsx-runtime-DGLHmmOq.js +9 -0
- package/dist/renderer/assets/popover-Dix5cm_b.js +2 -0
- package/dist/renderer/assets/project-api-k7Oq9iaq.js +2 -0
- package/dist/renderer/assets/projectCreateDialog-Bbjz75bP.js +2 -0
- package/dist/renderer/assets/select-acGb0AmJ.js +2 -0
- package/dist/renderer/assets/settings-CQrTMr43.js +2 -0
- package/dist/renderer/assets/settings-api-CrUQ9rYV.js +2 -0
- package/dist/renderer/assets/settings-tab-bar-Cw1kVIf2.js +2 -0
- package/dist/renderer/assets/tooltip-B_-_YVbw.js +2 -0
- package/dist/renderer/assets/types-CsEKtFyr.js +2 -0
- package/dist/renderer/assets/updateDialog-NsF8223A.js +2 -0
- package/dist/renderer/assets/utils-kfQov6Pa.js +2 -0
- package/dist/renderer/assets/view-api-CLn3rcQE.js +2 -0
- package/dist/renderer/assets/{workbenchSettings-oq_EyfKf.js → workbenchSettings-CV4LVA9b.js} +3 -3
- package/dist/renderer/entries/host-sidebar-default/index.html +21 -0
- package/dist/renderer/entries/main/index.html +17 -8
- package/dist/renderer/entries/popover/index.html +10 -7
- package/dist/renderer/entries/project-create-dialog/index.html +27 -0
- package/dist/renderer/entries/settings/index.html +10 -7
- package/dist/renderer/entries/tooltip/index.html +5 -4
- package/dist/renderer/entries/update-dialog/index.html +25 -0
- package/dist/renderer/entries/workbench-settings/index.html +11 -7
- package/dist/service-host/preload.cjs +10 -0
- package/dist/shared/constants.d.ts +22 -0
- package/dist/shared/constants.js +22 -0
- package/dist/shared/ipc-channels-overlays.d.ts +156 -0
- package/dist/shared/ipc-channels-overlays.js +186 -0
- package/dist/shared/ipc-channels.d.ts +4 -77
- package/dist/shared/ipc-channels.js +4 -98
- package/dist/shared/ipc-schemas.d.ts +44 -0
- package/dist/shared/ipc-schemas.js +55 -0
- package/dist/shared/types.d.ts +71 -0
- package/dist/shared/view-ids.d.ts +7 -0
- package/dist/shared/view-ids.js +10 -0
- package/package.json +8 -8
- package/dist/renderer/assets/constants-Bdz0LHdy.js +0 -2
- package/dist/renderer/assets/index-CtznrmHR.js +0 -49
- package/dist/renderer/assets/input-Dmm6nob5.js +0 -2
- package/dist/renderer/assets/ipc-transport-BOg0gR9W.js +0 -9
- package/dist/renderer/assets/ipc-transport-DgQhZaE9.css +0 -1
- package/dist/renderer/assets/popover-Cd1FEGOX.js +0 -2
- package/dist/renderer/assets/select-BaYox7If.js +0 -2
- package/dist/renderer/assets/settings-BXvwuFaz.js +0 -2
- package/dist/renderer/assets/settings-api-Bo9GbYqs.js +0 -2
- package/dist/renderer/assets/settings-tab-bar-Beia6lMr.js +0 -2
- package/dist/renderer/assets/tooltip-DfQqK3Sk.js +0 -2
- package/dist/renderer/assets/view-api-nn69je7A.js +0 -2
|
@@ -14,6 +14,12 @@ export function createLocalProjectsProvider() {
|
|
|
14
14
|
listProjects: () => repo.listProjects(),
|
|
15
15
|
addProject: (dirPath) => repo.addProject(dirPath),
|
|
16
16
|
removeProject: (dirPath) => repo.removeProject(dirPath),
|
|
17
|
+
updateProject: (dirPath, patch) => {
|
|
18
|
+
const updated = repo.updateProject(dirPath, patch);
|
|
19
|
+
if (!updated)
|
|
20
|
+
throw new Error(`No such project: ${dirPath}`);
|
|
21
|
+
return updated;
|
|
22
|
+
},
|
|
17
23
|
validateProjectDir: (dirPath) => repo.validateProjectDir(dirPath),
|
|
18
24
|
updateLastOpened: (dirPath) => repo.updateLastOpened(dirPath),
|
|
19
25
|
getCompileConfig: (dirPath) => repo.getCompileConfig(dirPath),
|
|
@@ -1,9 +1,28 @@
|
|
|
1
|
-
import type { CompileConfig } from '../../../shared/types.js';
|
|
1
|
+
import type { CompileConfig, ProjectType } from '../../../shared/types.js';
|
|
2
|
+
export type { ProjectType };
|
|
2
3
|
export interface Project {
|
|
3
4
|
name: string;
|
|
4
5
|
path: string;
|
|
5
6
|
lastOpened?: string | null;
|
|
6
7
|
compileConfig?: CompileConfig;
|
|
8
|
+
/** Absent on projects added before mini-game support — treat as 'miniprogram'. */
|
|
9
|
+
type?: ProjectType;
|
|
10
|
+
/**
|
|
11
|
+
* User-supplied icon for the project card. Absent means the card falls back
|
|
12
|
+
* to the first character of `name`.
|
|
13
|
+
*/
|
|
14
|
+
iconUrl?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The subset of a project record the user may edit after import. `path` is the
|
|
18
|
+
* record's identity (it keys every other per-project store — compile config,
|
|
19
|
+
* thumbnail, watcher) and is deliberately not patchable: pointing an existing
|
|
20
|
+
* record at another directory is an import, not an edit.
|
|
21
|
+
*/
|
|
22
|
+
export interface ProjectPatch {
|
|
23
|
+
name?: string;
|
|
24
|
+
/** Empty string clears the icon and restores the name-initial fallback. */
|
|
25
|
+
iconUrl?: string;
|
|
7
26
|
}
|
|
8
27
|
export interface ProjectPages {
|
|
9
28
|
pages: string[];
|
|
@@ -17,6 +36,15 @@ export declare function validateProjectDir(dirPath: string): string | null;
|
|
|
17
36
|
export declare function hasProject(dirPath: string): boolean;
|
|
18
37
|
export declare function addProject(dirPath: string): Project;
|
|
19
38
|
export declare function removeProject(dirPath: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Apply a user edit to the record at `dirPath`. Returns the updated record, or
|
|
41
|
+
* `null` when no record matches — callers decide whether a missing project is
|
|
42
|
+
* an error (the IPC path) or a no-op.
|
|
43
|
+
*
|
|
44
|
+
* A name change is written through to the project's own config (see
|
|
45
|
+
* `writeProjectName`); the icon lives only in our list.
|
|
46
|
+
*/
|
|
47
|
+
export declare function updateProject(dirPath: string, patch: ProjectPatch): Project | null;
|
|
20
48
|
export declare function updateLastOpened(dirPath: string): void;
|
|
21
49
|
export declare function getCompileConfig(dirPath: string): CompileConfig;
|
|
22
50
|
export declare function getProjectPages(dirPath: string): ProjectPages;
|
|
@@ -4,6 +4,92 @@ import fs from 'fs';
|
|
|
4
4
|
import { DEFAULT_SCENE } from '../../../shared/constants.js';
|
|
5
5
|
import { createLogger } from '../../utils/logger.js';
|
|
6
6
|
const log = createLogger('projects');
|
|
7
|
+
/**
|
|
8
|
+
* Merge project.config.json + project.private.config.json (private wins),
|
|
9
|
+
* mirroring `dimina/fe/packages/compiler/src/env.js`'s `storeInfo`.
|
|
10
|
+
*/
|
|
11
|
+
function readProjectConfig(dirPath) {
|
|
12
|
+
let merged = {};
|
|
13
|
+
for (const fileName of ['project.config.json', 'project.private.config.json']) {
|
|
14
|
+
const configPath = path.join(dirPath, fileName);
|
|
15
|
+
if (!fs.existsSync(configPath))
|
|
16
|
+
continue;
|
|
17
|
+
try {
|
|
18
|
+
merged = { ...merged, ...JSON.parse(fs.readFileSync(configPath, 'utf-8')) };
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
log.warn(`Failed to parse ${configPath}`, err);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return merged;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The project's display name as the project itself carries it.
|
|
28
|
+
*
|
|
29
|
+
* `projectname` lives in the merged config with the private file winning, and
|
|
30
|
+
* is URL-encoded when it leaves ASCII — WeChat DevTools 36.6.0 writes
|
|
31
|
+
* `"projectname": "%E6%BD%AE%E7%8E%A9%E6%97%8F"` and its own project list only
|
|
32
|
+
* mirrors that value. ASCII names encode to themselves, so plain values that
|
|
33
|
+
* were written by hand round-trip unchanged.
|
|
34
|
+
*
|
|
35
|
+
* Returns null when the config carries no usable name, leaving the caller to
|
|
36
|
+
* fall back to the directory name.
|
|
37
|
+
*/
|
|
38
|
+
function readProjectName(dirPath) {
|
|
39
|
+
const raw = readProjectConfig(dirPath).projectname;
|
|
40
|
+
if (typeof raw !== 'string' || !raw.trim())
|
|
41
|
+
return null;
|
|
42
|
+
try {
|
|
43
|
+
return decodeURIComponent(raw);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// A literal '%' that isn't a valid escape — keep the author's text.
|
|
47
|
+
return raw;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Persist the display name into `project.private.config.json`, the per-
|
|
52
|
+
* developer half of the project config (WeChat DevTools writes the name there
|
|
53
|
+
* too, leaving the shared `project.config.json` alone).
|
|
54
|
+
*
|
|
55
|
+
* This is what makes the config — not our list file — the single owner of the
|
|
56
|
+
* name: `addProject` re-reads it, so re-importing a directory restores the
|
|
57
|
+
* user's rename instead of resetting it.
|
|
58
|
+
*/
|
|
59
|
+
function writeProjectName(dirPath, name) {
|
|
60
|
+
const configPath = path.join(dirPath, 'project.private.config.json');
|
|
61
|
+
let config = {};
|
|
62
|
+
if (fs.existsSync(configPath)) {
|
|
63
|
+
try {
|
|
64
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Refuse rather than overwrite: this file also holds the user's IDE
|
|
68
|
+
// settings and compile conditions, which a blind rewrite would drop.
|
|
69
|
+
throw new Error(`无法重命名:${configPath} 不是合法 JSON`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
fs.writeFileSync(configPath, JSON.stringify({ ...config, projectname: encodeURIComponent(name) }, null, 2));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Mirrors `dimina/fe/packages/compiler/src/env.js`'s `detectRuntimeType` —
|
|
76
|
+
* an explicit `compileType` wins, else a mini-game is inferred from
|
|
77
|
+
* game.json + game.js/.ts with no app.json present.
|
|
78
|
+
*/
|
|
79
|
+
function detectRuntimeType(dirPath) {
|
|
80
|
+
const compileType = readProjectConfig(dirPath).compileType;
|
|
81
|
+
if (compileType === 'game')
|
|
82
|
+
return 'minigame';
|
|
83
|
+
if (compileType === 'miniprogram')
|
|
84
|
+
return 'miniprogram';
|
|
85
|
+
const hasMiniProgramConfig = fs.existsSync(path.join(dirPath, 'app.json'));
|
|
86
|
+
const hasMiniGameConfig = fs.existsSync(path.join(dirPath, 'game.json'));
|
|
87
|
+
const hasMiniGameEntry = ['game.js', 'game.ts']
|
|
88
|
+
.some((fileName) => fs.existsSync(path.join(dirPath, fileName)));
|
|
89
|
+
if (!hasMiniProgramConfig && hasMiniGameConfig && hasMiniGameEntry)
|
|
90
|
+
return 'minigame';
|
|
91
|
+
return 'miniprogram';
|
|
92
|
+
}
|
|
7
93
|
function getProjectsFile() {
|
|
8
94
|
return path.join(app.getPath('userData'), 'dimina-projects.json');
|
|
9
95
|
}
|
|
@@ -28,6 +114,12 @@ export function validateProjectDir(dirPath) {
|
|
|
28
114
|
if (!fs.existsSync(dirPath)) {
|
|
29
115
|
return `小程序目录不存在:${dirPath}`;
|
|
30
116
|
}
|
|
117
|
+
if (detectRuntimeType(dirPath) === 'minigame') {
|
|
118
|
+
if (!fs.existsSync(path.join(dirPath, 'game.json'))) {
|
|
119
|
+
return '该目录缺少 game.json,请选择包含小游戏源码的目录';
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
31
123
|
if (!fs.existsSync(path.join(dirPath, 'app.json'))) {
|
|
32
124
|
const configPath = path.join(dirPath, 'project.config.json');
|
|
33
125
|
if (fs.existsSync(configPath)) {
|
|
@@ -51,22 +143,14 @@ export function hasProject(dirPath) {
|
|
|
51
143
|
}
|
|
52
144
|
export function addProject(dirPath) {
|
|
53
145
|
const projects = load();
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (cfg.projectname)
|
|
60
|
-
name = cfg.projectname;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
64
|
-
log.warn('Failed to read project name from config', err);
|
|
65
|
-
}
|
|
66
|
-
const project = { name, path: dirPath, lastOpened: null };
|
|
146
|
+
// The config is the name's owner, so re-adding a directory picks up a rename
|
|
147
|
+
// the user made here rather than reverting it.
|
|
148
|
+
const name = readProjectName(dirPath) ?? path.basename(dirPath);
|
|
149
|
+
const type = detectRuntimeType(dirPath);
|
|
150
|
+
const project = { name, path: dirPath, lastOpened: null, type };
|
|
67
151
|
const idx = projects.findIndex((p) => p.path === dirPath);
|
|
68
152
|
if (idx >= 0) {
|
|
69
|
-
projects[idx] = { ...projects[idx], name };
|
|
153
|
+
projects[idx] = { ...projects[idx], name, type };
|
|
70
154
|
}
|
|
71
155
|
else {
|
|
72
156
|
projects.unshift(project);
|
|
@@ -77,6 +161,43 @@ export function addProject(dirPath) {
|
|
|
77
161
|
export function removeProject(dirPath) {
|
|
78
162
|
save(load().filter((p) => p.path !== dirPath));
|
|
79
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Apply a user edit to the record at `dirPath`. Returns the updated record, or
|
|
166
|
+
* `null` when no record matches — callers decide whether a missing project is
|
|
167
|
+
* an error (the IPC path) or a no-op.
|
|
168
|
+
*
|
|
169
|
+
* A name change is written through to the project's own config (see
|
|
170
|
+
* `writeProjectName`); the icon lives only in our list.
|
|
171
|
+
*/
|
|
172
|
+
export function updateProject(dirPath, patch) {
|
|
173
|
+
const projects = load();
|
|
174
|
+
const idx = projects.findIndex((p) => p.path === dirPath);
|
|
175
|
+
if (idx < 0)
|
|
176
|
+
return null;
|
|
177
|
+
const next = { ...projects[idx] };
|
|
178
|
+
if (patch.name !== undefined) {
|
|
179
|
+
const name = patch.name.trim();
|
|
180
|
+
if (!name)
|
|
181
|
+
throw new Error('Project name cannot be empty');
|
|
182
|
+
next.name = name;
|
|
183
|
+
}
|
|
184
|
+
if (patch.iconUrl !== undefined) {
|
|
185
|
+
const iconUrl = patch.iconUrl.trim();
|
|
186
|
+
if (iconUrl)
|
|
187
|
+
next.iconUrl = iconUrl;
|
|
188
|
+
else
|
|
189
|
+
delete next.iconUrl;
|
|
190
|
+
}
|
|
191
|
+
// The config write comes first and is allowed to throw: a rename that only
|
|
192
|
+
// reached our list would look applied until the next re-import silently
|
|
193
|
+
// reverted it. The icon has no counterpart in the project config — it is
|
|
194
|
+
// ours alone — so it never touches the project directory.
|
|
195
|
+
if (next.name !== projects[idx].name)
|
|
196
|
+
writeProjectName(dirPath, next.name);
|
|
197
|
+
projects[idx] = next;
|
|
198
|
+
save(projects);
|
|
199
|
+
return next;
|
|
200
|
+
}
|
|
80
201
|
export function updateLastOpened(dirPath) {
|
|
81
202
|
const projects = load();
|
|
82
203
|
const idx = projects.findIndex((p) => p.path === dirPath);
|
|
@@ -88,13 +209,26 @@ export function updateLastOpened(dirPath) {
|
|
|
88
209
|
export function getCompileConfig(dirPath) {
|
|
89
210
|
const projects = load();
|
|
90
211
|
const project = projects.find((p) => p.path === dirPath);
|
|
91
|
-
|
|
92
|
-
|
|
212
|
+
if (project?.compileConfig)
|
|
213
|
+
return project.compileConfig;
|
|
214
|
+
// Default startPage to the project's real entry page (mirrors
|
|
215
|
+
// getProjectPages: 'game' for mini-games, app.json's entryPagePath for
|
|
216
|
+
// mini-programs) rather than a mini-program-only literal — the simulator
|
|
217
|
+
// URL builders' own 'pages/index/index' fallback only fires when this is
|
|
218
|
+
// still empty (unreadable/malformed manifest).
|
|
219
|
+
return {
|
|
220
|
+
startPage: getProjectPages(dirPath).entryPagePath,
|
|
93
221
|
scene: DEFAULT_SCENE,
|
|
94
222
|
queryParams: [],
|
|
95
|
-
}
|
|
223
|
+
};
|
|
96
224
|
}
|
|
97
225
|
export function getProjectPages(dirPath) {
|
|
226
|
+
if (detectRuntimeType(dirPath) === 'minigame') {
|
|
227
|
+
// Mini-games have no page router — the compiler always emits a single
|
|
228
|
+
// synthetic 'game' entry (dimina/fe/packages/compiler/src/env.js
|
|
229
|
+
// storeAppConfig), mirrored here rather than reading a nonexistent app.json.
|
|
230
|
+
return { pages: ['game'], entryPagePath: 'game' };
|
|
231
|
+
}
|
|
98
232
|
const appJsonPath = path.join(dirPath, 'app.json');
|
|
99
233
|
try {
|
|
100
234
|
const appJson = JSON.parse(fs.readFileSync(appJsonPath, 'utf-8'));
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* template catalog, and the "新建项目" dialog.
|
|
6
6
|
*/
|
|
7
7
|
import type { CompileConfig } from '../../../shared/types.js';
|
|
8
|
-
import type { Project } from './project-repository.js';
|
|
9
|
-
export type { Project };
|
|
8
|
+
import type { Project, ProjectPatch } from './project-repository.js';
|
|
9
|
+
export type { Project, ProjectPatch };
|
|
10
10
|
export type { ProjectTemplate, CreateProjectInput } from '../../../shared/types.js';
|
|
11
11
|
/**
|
|
12
12
|
* Default compile config returned by WorkspaceService when the injected
|
|
@@ -40,6 +40,17 @@ export interface ProjectsProvider {
|
|
|
40
40
|
validateProjectDir?(dirPath: string): string | null | Promise<string | null>;
|
|
41
41
|
addProject(dirPath: string): Project | Promise<Project>;
|
|
42
42
|
removeProject(dirPath: string): void | Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Apply a user edit (name / icon) to an existing record. `dirPath` is the
|
|
45
|
+
* record's identity, so it is never part of the patch — see `ProjectPatch`.
|
|
46
|
+
*
|
|
47
|
+
* Default when omitted: throws, and the renderer shows that error in the
|
|
48
|
+
* edit dialog. A silent no-op would report success (dialog closes, list
|
|
49
|
+
* reloads) while discarding the user's input, since the caller has no way
|
|
50
|
+
* to tell "saved" apart from "provider doesn't support this". Implement
|
|
51
|
+
* this if your UI exposes the project-edit dialog.
|
|
52
|
+
*/
|
|
53
|
+
updateProject?(dirPath: string, patch: ProjectPatch): Project | Promise<Project>;
|
|
43
54
|
/**
|
|
44
55
|
* Record that the user just opened a project (drives "recent" ordering).
|
|
45
56
|
*
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { BrowserWindow } from 'electron';
|
|
2
1
|
import type { UpdateChecker, UpdateInfo } from '../../../shared/types.js';
|
|
3
2
|
import { type SenderPolicy } from '../../utils/ipc-registry.js';
|
|
4
3
|
export interface UpdateManagerOptions {
|
|
5
4
|
checker: UpdateChecker;
|
|
6
|
-
|
|
5
|
+
/** Show the update-available overlay panel, fed the discovered update's info. */
|
|
6
|
+
showUpdatePanel: (info: UpdateInfo) => void;
|
|
7
|
+
/** Forward a download-progress tick into the (already-shown) update overlay. */
|
|
8
|
+
notifyDownloadProgress: (percent: number) => void;
|
|
9
|
+
/** Hide the update overlay panel — the renderer's close (any path) forwards here. */
|
|
10
|
+
hideUpdatePanel: () => void;
|
|
7
11
|
/** Check interval in milliseconds. Default: 1 hour */
|
|
8
12
|
checkInterval?: number;
|
|
9
13
|
/** Delay before the first check after startup in ms. Default: 5000 */
|
|
@@ -20,7 +24,9 @@ export interface UpdateManagerOptions {
|
|
|
20
24
|
}
|
|
21
25
|
export declare class UpdateManager {
|
|
22
26
|
private checker;
|
|
23
|
-
private
|
|
27
|
+
private showUpdatePanel;
|
|
28
|
+
private notifyDownloadProgress;
|
|
29
|
+
private hideUpdatePanel;
|
|
24
30
|
private getCurrentVersion;
|
|
25
31
|
private latestUpdate;
|
|
26
32
|
private downloadedPath;
|
|
@@ -37,6 +43,14 @@ export declare class UpdateManager {
|
|
|
37
43
|
private timer;
|
|
38
44
|
private initialTimer;
|
|
39
45
|
private ipc;
|
|
46
|
+
/**
|
|
47
|
+
* Set by `dispose()`. Clearing the timers only stops FUTURE ticks — an
|
|
48
|
+
* in-flight `checkAndNotify()` invocation (already past its `await
|
|
49
|
+
* this.check()`) has no timer left to cancel it, and would otherwise call
|
|
50
|
+
* `showUpdatePanel` after dispose has torn down the overlay panel that
|
|
51
|
+
* owns (e.g. `views.disposeAll()` destroying its WebContentsView).
|
|
52
|
+
*/
|
|
53
|
+
private disposed;
|
|
40
54
|
constructor(opts: UpdateManagerOptions);
|
|
41
55
|
private registerIpc;
|
|
42
56
|
check(): Promise<{
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { app, shell } from 'electron';
|
|
2
|
-
import { UpdateChannel } from '../../../shared/ipc-channels.js';
|
|
2
|
+
import { UpdateChannel } from '../../../shared/ipc-channels-overlays.js';
|
|
3
3
|
import { IpcRegistry } from '../../utils/ipc-registry.js';
|
|
4
4
|
export class UpdateManager {
|
|
5
5
|
checker;
|
|
6
|
-
|
|
6
|
+
showUpdatePanel;
|
|
7
|
+
notifyDownloadProgress;
|
|
8
|
+
hideUpdatePanel;
|
|
7
9
|
getCurrentVersion;
|
|
8
10
|
latestUpdate = null;
|
|
9
11
|
downloadedPath = null;
|
|
@@ -20,9 +22,19 @@ export class UpdateManager {
|
|
|
20
22
|
timer = null;
|
|
21
23
|
initialTimer = null;
|
|
22
24
|
ipc;
|
|
25
|
+
/**
|
|
26
|
+
* Set by `dispose()`. Clearing the timers only stops FUTURE ticks — an
|
|
27
|
+
* in-flight `checkAndNotify()` invocation (already past its `await
|
|
28
|
+
* this.check()`) has no timer left to cancel it, and would otherwise call
|
|
29
|
+
* `showUpdatePanel` after dispose has torn down the overlay panel that
|
|
30
|
+
* owns (e.g. `views.disposeAll()` destroying its WebContentsView).
|
|
31
|
+
*/
|
|
32
|
+
disposed = false;
|
|
23
33
|
constructor(opts) {
|
|
24
34
|
this.checker = opts.checker;
|
|
25
|
-
this.
|
|
35
|
+
this.showUpdatePanel = opts.showUpdatePanel;
|
|
36
|
+
this.notifyDownloadProgress = opts.notifyDownloadProgress;
|
|
37
|
+
this.hideUpdatePanel = opts.hideUpdatePanel;
|
|
26
38
|
this.getCurrentVersion = opts.getCurrentVersion ?? (() => app.getVersion());
|
|
27
39
|
this.ipc = new IpcRegistry(opts.senderPolicy);
|
|
28
40
|
this.registerIpc();
|
|
@@ -32,7 +44,8 @@ export class UpdateManager {
|
|
|
32
44
|
this.ipc
|
|
33
45
|
.handle(UpdateChannel.Check, async () => this.check())
|
|
34
46
|
.handle(UpdateChannel.Download, async () => this.download())
|
|
35
|
-
.handle(UpdateChannel.Install, async () => this.install())
|
|
47
|
+
.handle(UpdateChannel.Install, async () => this.install())
|
|
48
|
+
.on(UpdateChannel.Close, () => this.hideUpdatePanel());
|
|
36
49
|
}
|
|
37
50
|
async check() {
|
|
38
51
|
try {
|
|
@@ -69,7 +82,7 @@ export class UpdateManager {
|
|
|
69
82
|
}
|
|
70
83
|
try {
|
|
71
84
|
const filePath = await this.checker.downloadUpdate(this.latestUpdate, (percent) => {
|
|
72
|
-
this.
|
|
85
|
+
this.notifyDownloadProgress(percent);
|
|
73
86
|
});
|
|
74
87
|
this.downloadedPath = filePath;
|
|
75
88
|
return { success: true, filePath };
|
|
@@ -104,6 +117,7 @@ export class UpdateManager {
|
|
|
104
117
|
* the timer clears + handler removals are scheduled synchronously.
|
|
105
118
|
*/
|
|
106
119
|
dispose() {
|
|
120
|
+
this.disposed = true;
|
|
107
121
|
if (this.timer) {
|
|
108
122
|
clearInterval(this.timer);
|
|
109
123
|
this.timer = null;
|
|
@@ -120,12 +134,17 @@ export class UpdateManager {
|
|
|
120
134
|
}
|
|
121
135
|
async checkAndNotify() {
|
|
122
136
|
const result = await this.check();
|
|
137
|
+
// dispose() only cancels the timers that SCHEDULE future ticks — a tick
|
|
138
|
+
// already in flight (past this await) has nothing left to cancel it, and
|
|
139
|
+
// must not call into a torn-down panel/registry after the fact.
|
|
140
|
+
if (this.disposed)
|
|
141
|
+
return;
|
|
123
142
|
if (!result.hasUpdate || !result.info)
|
|
124
143
|
return;
|
|
125
144
|
if (this.hasNotified)
|
|
126
145
|
return;
|
|
127
146
|
this.hasNotified = true;
|
|
128
|
-
this.
|
|
147
|
+
this.showUpdatePanel(result.info);
|
|
129
148
|
}
|
|
130
149
|
}
|
|
131
150
|
//# sourceMappingURL=update-manager.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dialog-specific instantiation of `createHostSlotPortChannel` (see that
|
|
3
|
+
* module for the full per-load handshake / navigation-invalidation
|
|
4
|
+
* contract).
|
|
5
|
+
*/
|
|
6
|
+
import type { WebContents } from 'electron';
|
|
7
|
+
import { type HostSlotMessageSubscription, type HostSlotPortChannel } from './host-slot-port-channel.js';
|
|
8
|
+
export type HostDialogMessageSubscription = HostSlotMessageSubscription;
|
|
9
|
+
export type HostDialogPortChannel = HostSlotPortChannel;
|
|
10
|
+
export declare function createHostDialogPortChannel(opts: {
|
|
11
|
+
/**
|
|
12
|
+
* Is `wc` still the manager's CURRENT live dialog webContents? Guards a
|
|
13
|
+
* stale wc's late `did-finish-load` from hijacking the channel after a
|
|
14
|
+
* rebuild swapped the view out underneath it.
|
|
15
|
+
*/
|
|
16
|
+
isCurrent: (wc: WebContents) => boolean;
|
|
17
|
+
}): HostDialogPortChannel;
|
|
18
|
+
//# sourceMappingURL=host-dialog-port-channel.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dialog-specific instantiation of `createHostSlotPortChannel` (see that
|
|
3
|
+
* module for the full per-load handshake / navigation-invalidation
|
|
4
|
+
* contract).
|
|
5
|
+
*/
|
|
6
|
+
import { ViewChannel } from '../../../shared/ipc-channels-overlays.js';
|
|
7
|
+
import { createHostSlotPortChannel, } from './host-slot-port-channel.js';
|
|
8
|
+
export function createHostDialogPortChannel(opts) {
|
|
9
|
+
return createHostSlotPortChannel({
|
|
10
|
+
isCurrent: opts.isCurrent,
|
|
11
|
+
channel: ViewChannel.HostDialogPort,
|
|
12
|
+
logPrefix: '[host-dialog]',
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=host-dialog-port-channel.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dialog-specific instantiation of `createHostSlotSessionRuntime` (see that
|
|
3
|
+
* module for the full ref-counting contract). Kept as a thin, independently
|
|
4
|
+
* named wrapper — same rationale as `host-toolbar-session-runtime.ts` — so
|
|
5
|
+
* a dialog-focused test can `vi.resetModules()` this module's state without
|
|
6
|
+
* touching the toolbar/sidebar's independent ref-counts.
|
|
7
|
+
*/
|
|
8
|
+
export declare function acquireHostDialogSessionRuntime(): void;
|
|
9
|
+
export declare function releaseHostDialogSessionRuntime(): void;
|
|
10
|
+
//# sourceMappingURL=host-dialog-session-runtime.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dialog-specific instantiation of `createHostSlotSessionRuntime` (see that
|
|
3
|
+
* module for the full ref-counting contract). Kept as a thin, independently
|
|
4
|
+
* named wrapper — same rationale as `host-toolbar-session-runtime.ts` — so
|
|
5
|
+
* a dialog-focused test can `vi.resetModules()` this module's state without
|
|
6
|
+
* touching the toolbar/sidebar's independent ref-counts.
|
|
7
|
+
*/
|
|
8
|
+
import { createHostSlotSessionRuntime } from './host-slot-session-runtime.js';
|
|
9
|
+
import { hostDialogRuntimePreloadPath } from '../../utils/paths.js';
|
|
10
|
+
const runtime = createHostSlotSessionRuntime(hostDialogRuntimePreloadPath);
|
|
11
|
+
export function acquireHostDialogSessionRuntime() {
|
|
12
|
+
runtime.acquire();
|
|
13
|
+
}
|
|
14
|
+
export function releaseHostDialogSessionRuntime() {
|
|
15
|
+
runtime.release();
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=host-dialog-session-runtime.js.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { WebContents } from 'electron';
|
|
2
|
+
import type { PlacementReconciler } from './placement-reconciler.js';
|
|
3
|
+
import type { ViewManagerContext } from './view-manager.js';
|
|
4
|
+
import type { HostDialogMessageSubscription } from './host-dialog-port-channel.js';
|
|
5
|
+
export type HostDialogAxis = 'block' | 'inline';
|
|
6
|
+
/**
|
|
7
|
+
* The control object the downstream host uses to own the dialog
|
|
8
|
+
* WebContentsView. Unlike `HostToolbarControl` / `HostSidebarControl`
|
|
9
|
+
* (persistent strips), the dialog is a by-demand overlay: no extent-mode
|
|
10
|
+
* setter (it always self-sizes from its own reverse advertiser, with a
|
|
11
|
+
* conservative default while unmeasured — see below) and an explicit
|
|
12
|
+
* `show()`/`hide()`/`isVisible()` surface instead of always being present
|
|
13
|
+
* once loaded.
|
|
14
|
+
*/
|
|
15
|
+
export interface HostDialogControl {
|
|
16
|
+
/** Load a URL into the dialog view (lazy-creates the view). */
|
|
17
|
+
loadURL(url: string): Promise<void>;
|
|
18
|
+
/** Load a local file into the dialog view (lazy-creates the view). */
|
|
19
|
+
loadFile(path: string): Promise<void>;
|
|
20
|
+
/** The dialog view's live WebContents, or null if not yet created/destroyed. */
|
|
21
|
+
readonly webContents: WebContents | null;
|
|
22
|
+
/** Same contract as `HostToolbarControl.setPreloadPath`. */
|
|
23
|
+
setPreloadPath(path: string | null): void;
|
|
24
|
+
/**
|
|
25
|
+
* Register a host-side handler for messages the dialog PAGE sends via
|
|
26
|
+
* `window.diminaHostDialog.send(channel, payload)`. Same contract as
|
|
27
|
+
* `HostToolbarControl.onMessage`.
|
|
28
|
+
*/
|
|
29
|
+
onMessage(channel: string, handler: (payload: unknown) => void): HostDialogMessageSubscription;
|
|
30
|
+
/** Same contract as `HostToolbarControl.onReady`. */
|
|
31
|
+
onReady(handler: () => void): HostDialogMessageSubscription;
|
|
32
|
+
/** Same contract as `HostToolbarControl.send`. */
|
|
33
|
+
send(channel: string, payload: unknown): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Show the dialog, centered in the main window using its self-advertised
|
|
36
|
+
* size on both axes (a conservative default for any axis not yet
|
|
37
|
+
* measured). Creates the view's placement if needed; does not (re)load
|
|
38
|
+
* content — the host must `loadURL`/`loadFile` first. Idempotent while
|
|
39
|
+
* already visible (re-centers with the latest measured extent instead of
|
|
40
|
+
* no-op, in case a resize happened while hidden).
|
|
41
|
+
*/
|
|
42
|
+
show(): void;
|
|
43
|
+
/** Hide the dialog (kept alive, content preserved) without destroying the view. */
|
|
44
|
+
hide(): void;
|
|
45
|
+
/** Whether the dialog is currently shown. */
|
|
46
|
+
isVisible(): boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The host-dialog slice of `ViewManager`'s public surface, split into its
|
|
50
|
+
* own file for the same file-length reason as sidebar's
|
|
51
|
+
* `HostSidebarViewManagerMembers` — a by-demand, dual-axis, main-centered
|
|
52
|
+
* overlay rather than a persistent strip.
|
|
53
|
+
*/
|
|
54
|
+
export interface HostDialogViewManagerMembers {
|
|
55
|
+
/** Return the webContents ID of the host-dialog overlay if alive, else null. */
|
|
56
|
+
getHostDialogWebContentsId(): number | null;
|
|
57
|
+
/** Reverse size-advertiser sink for either axis; re-centers while visible. */
|
|
58
|
+
reportHostDialogMeasuredExtent(axis: HostDialogAxis, extent: number): void;
|
|
59
|
+
/** Host-facing control surface for the dialog WebContentsView. */
|
|
60
|
+
readonly hostDialog: HostDialogControl;
|
|
61
|
+
}
|
|
62
|
+
export interface HostDialogView {
|
|
63
|
+
readonly control: HostDialogControl;
|
|
64
|
+
/**
|
|
65
|
+
* Reverse size-advertiser sink for either axis. While visible, immediately
|
|
66
|
+
* re-centers with the newly measured extent; while hidden, just retains it
|
|
67
|
+
* for the next `show()`. Non-finite / non-positive reports are dropped —
|
|
68
|
+
* same fail-closed posture as the base slots' extent validation.
|
|
69
|
+
*/
|
|
70
|
+
reportMeasuredExtent(axis: HostDialogAxis, extent: number): void;
|
|
71
|
+
getHostDialogWebContentsId(): number | null;
|
|
72
|
+
/**
|
|
73
|
+
* Re-center in the main window's CURRENT content rect, e.g. after a
|
|
74
|
+
* main-window resize. No-op while hidden — the next `show()` computes
|
|
75
|
+
* fresh bounds anyway, so there is nothing to reapply.
|
|
76
|
+
*/
|
|
77
|
+
reposition(): void;
|
|
78
|
+
dispose(): void;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Host-dialog factory — its PLACEMENT is deliberately NOT built on
|
|
82
|
+
* `createHostSlotView`. That factory models a persistent, single-axis,
|
|
83
|
+
* renderer-anchored strip (`setBaseDesired`); the dialog is a by-demand,
|
|
84
|
+
* dual-axis, main-centered overlay (`setOverlayDesired` /
|
|
85
|
+
* `deleteOverlayDesired`), closer in shape to the settings/popover/tooltip
|
|
86
|
+
* overlays in `overlay-panels-view.ts` than to the toolbar/sidebar. It still
|
|
87
|
+
* doesn't reuse `createOverlayPanel` from that file: those panels load a
|
|
88
|
+
* FIXED renderer-dir entry and push typed data, whereas the dialog loads
|
|
89
|
+
* ARBITRARY host content and needs the toolbar/sidebar's `loadURL`/`loadFile`
|
|
90
|
+
* + gated port-channel surface. The underlying WebContentsView lazy-create /
|
|
91
|
+
* liveness / load / teardown lifecycle IS shared with the base slots though
|
|
92
|
+
* — see `managed-web-contents-view.ts`.
|
|
93
|
+
*/
|
|
94
|
+
export declare function createHostDialogView(ctx: ViewManagerContext, reconciler: PlacementReconciler): HostDialogView;
|
|
95
|
+
//# sourceMappingURL=host-dialog-view.d.ts.map
|