@dimina-kit/devtools 0.4.0-dev.20260706064107 → 0.4.0-dev.20260706125036

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/main/app/app.d.ts +4 -1
  2. package/dist/main/app/app.js +20 -0
  3. package/dist/main/index.bundle.js +144 -8
  4. package/dist/main/ipc/popover.js +10 -1
  5. package/dist/main/ipc/session.js +17 -1
  6. package/dist/main/services/notifications/renderer-notifier.d.ts +5 -1
  7. package/dist/main/services/notifications/renderer-notifier.js +6 -0
  8. package/dist/main/services/projects/local-provider.js +4 -0
  9. package/dist/main/services/projects/project-repository.d.ts +7 -1
  10. package/dist/main/services/projects/project-repository.js +24 -0
  11. package/dist/main/services/projects/types.d.ts +25 -1
  12. package/dist/main/services/render-inspect/index.js +4 -4
  13. package/dist/main/services/views/view-manager.d.ts +15 -1
  14. package/dist/main/services/views/view-manager.js +12 -4
  15. package/dist/main/services/workbench-context.js +5 -0
  16. package/dist/main/services/workspace/workspace-service.d.ts +5 -1
  17. package/dist/main/services/workspace/workspace-service.js +15 -4
  18. package/dist/native-host/render/render.js +42 -52
  19. package/dist/native-host/service/service.js +2 -2
  20. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  21. package/dist/preload/windows/simulator.cjs.map +1 -1
  22. package/dist/renderer/assets/index-BMqyDx71.js +49 -0
  23. package/dist/renderer/assets/{input-CTb_le7F.js → input-CDAEwpBO.js} +2 -2
  24. package/dist/renderer/assets/ipc-transport-B7eFOR68.css +1 -0
  25. package/dist/renderer/assets/{ipc-transport-Ck5Xa8Tu.js → ipc-transport-_G1k5V1p.js} +2 -2
  26. package/dist/renderer/assets/popover-DEdjK14m.js +2 -0
  27. package/dist/renderer/assets/{select-ozSk5Pt6.js → select-DpKPCh3y.js} +2 -2
  28. package/dist/renderer/assets/{settings-CNwGOI22.js → settings-M5q2elUr.js} +2 -2
  29. package/dist/renderer/assets/{settings-api-DMbMp1Eq.js → settings-api-CDlZoHiO.js} +2 -2
  30. package/dist/renderer/assets/{workbenchSettings-Cr4EHo5w.js → workbenchSettings-CT_H1WhV.js} +2 -2
  31. package/dist/renderer/entries/main/index.html +6 -6
  32. package/dist/renderer/entries/popover/index.html +5 -5
  33. package/dist/renderer/entries/settings/index.html +5 -5
  34. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  35. package/dist/shared/ipc-channels.d.ts +6 -0
  36. package/dist/shared/ipc-channels.js +6 -0
  37. package/dist/shared/ipc-schemas.d.ts +29 -0
  38. package/dist/shared/ipc-schemas.js +34 -0
  39. package/dist/shared/types.d.ts +28 -0
  40. package/package.json +6 -6
  41. package/dist/renderer/assets/index-BwzPwCmM.js +0 -49
  42. package/dist/renderer/assets/ipc-transport-BPWIV5hA.css +0 -1
  43. package/dist/renderer/assets/popover-B1Zu2UQu.js +0 -2
@@ -1,5 +1,5 @@
1
1
  import { BrowserWindow } from 'electron';
2
- import type { WorkbenchAppConfig } from '../../shared/types.js';
2
+ import type { LaunchConfig, WorkbenchAppConfig } from '../../shared/types.js';
3
3
  import type { SimulatorApiHandler } from '../services/simulator/custom-apis.js';
4
4
  import { type WorkbenchContext } from '../services/workbench-context.js';
5
5
  import { type AutomationServer } from '../services/automation/index.js';
@@ -18,6 +18,9 @@ export interface WorkbenchAppInstance {
18
18
  automationServer?: AutomationServer;
19
19
  updateManager?: UpdateManager;
20
20
  dispose: () => Promise<void>;
21
+ getLaunchConfigs: () => Promise<LaunchConfig[]>;
22
+ setLaunchConfigs: (configs: LaunchConfig[]) => Promise<void>;
23
+ setActiveLaunchConfig: (id: string | null) => Promise<void>;
21
24
  }
22
25
  /**
23
26
  * Pre-ready bootstrap side effects (app name, CDP port, CSP suppression,
@@ -400,6 +400,26 @@ export async function createDevtoolsRuntime(config = {}) {
400
400
  registerTrustedWindow: (win) => context.registry.add(registerTrustedWindow(context, win)),
401
401
  registerSimulatorApi: (name, handler) => context.registry.add(toDisposable(context.simulatorApis.register(name, handler))),
402
402
  dispose: () => disposeContext(context),
403
+ async getLaunchConfigs() {
404
+ const projectPath = context.workspace.getProjectPath();
405
+ if (!projectPath)
406
+ return [];
407
+ return context.workspace.getLaunchConfigs(projectPath);
408
+ },
409
+ async setLaunchConfigs(configs) {
410
+ const projectPath = context.workspace.getProjectPath();
411
+ if (!projectPath)
412
+ return;
413
+ await context.workspace.saveLaunchConfigs(projectPath, configs);
414
+ context.notify.popoverUpdateLaunchConfigs(configs);
415
+ },
416
+ async setActiveLaunchConfig(id) {
417
+ const projectPath = context.workspace.getProjectPath();
418
+ if (!projectPath)
419
+ return;
420
+ await context.workspace.saveActiveLaunchConfigId(projectPath, id);
421
+ context.notify.popoverSwitchLaunchConfig(id);
422
+ },
403
423
  };
404
424
  // Built-in simulator APIs: devtools-supplied wx.* implementations that run
405
425
  // in the main process. Hosts can override any of these in their onSetup by
@@ -391,7 +391,11 @@ var ProjectChannel = {
391
391
  // contract (compileEvents), this one carries the line stream (compileLogs).
392
392
  CompileLog: "project:compileLog",
393
393
  CaptureThumbnail: "project:captureThumbnail",
394
- GetThumbnail: "project:getThumbnail"
394
+ GetThumbnail: "project:getThumbnail",
395
+ GetLaunchConfigs: "project:getLaunchConfigs",
396
+ SaveLaunchConfigs: "project:saveLaunchConfigs",
397
+ GetActiveLaunchConfigId: "project:getActiveLaunchConfigId",
398
+ SaveActiveLaunchConfigId: "project:saveActiveLaunchConfigId"
395
399
  };
396
400
  var SessionChannel = {
397
401
  RuntimeStatus: "session:runtimeStatus"
@@ -476,7 +480,9 @@ var PopoverChannel = {
476
480
  Hide: "popover:hide",
477
481
  Relaunch: "popover:relaunch",
478
482
  Closed: "popover:closed",
479
- Init: "popover:init"
483
+ Init: "popover:init",
484
+ SwitchLaunchConfig: "popover:switchLaunchConfig",
485
+ UpdateLaunchConfigs: "popover:updateLaunchConfigs"
480
486
  };
481
487
  var WindowChannel = {
482
488
  NavigateBack: "window:navigateBack"
@@ -1123,6 +1129,12 @@ function createRendererNotifier(ctx) {
1123
1129
  popoverRelaunch(config) {
1124
1130
  sendToMain(PopoverChannel.Relaunch, config);
1125
1131
  },
1132
+ popoverSwitchLaunchConfig(id) {
1133
+ sendToMain(PopoverChannel.SwitchLaunchConfig, id);
1134
+ },
1135
+ popoverUpdateLaunchConfigs(configs) {
1136
+ sendToMain(PopoverChannel.UpdateLaunchConfigs, configs);
1137
+ },
1126
1138
  hostToolbarHeightChanged(height) {
1127
1139
  sendToMain(ViewChannel.HostToolbarHeightChanged, height);
1128
1140
  },
@@ -3705,12 +3717,15 @@ function createViewManager(ctx) {
3705
3717
  function reapplyToolbarDependentOverlays() {
3706
3718
  overlayPanels.reapplyPresentOverlays();
3707
3719
  }
3708
- function disposeAll() {
3720
+ function disposeProjectViews() {
3709
3721
  nativeSimulator.detachSimulator();
3710
3722
  workbench.detachWorkbench();
3711
- hostToolbar.dispose();
3712
3723
  safeArea.dispose();
3713
3724
  }
3725
+ function disposeAll() {
3726
+ disposeProjectViews();
3727
+ hostToolbar.dispose();
3728
+ }
3714
3729
  return {
3715
3730
  attachNativeSimulator: nativeSimulator.attachNativeSimulator,
3716
3731
  softReloadNativeSimulator: nativeSimulator.softReloadNativeSimulator,
@@ -3721,6 +3736,7 @@ function createViewManager(ctx) {
3721
3736
  showPopover: overlayPanels.showPopover,
3722
3737
  hidePopover: overlayPanels.hidePopover,
3723
3738
  repositionAll: () => overlayPanels.reapplyPresentOverlays(),
3739
+ disposeProjectViews,
3724
3740
  disposeAll,
3725
3741
  getSimulatorWebContentsId: nativeSimulator.getSimulatorWebContentsId,
3726
3742
  getSimulatorWebContents: nativeSimulator.getSimulatorWebContents,
@@ -3960,6 +3976,30 @@ function getProjectSettings(projectPath) {
3960
3976
  return { uploadWithSourceMap: false };
3961
3977
  }
3962
3978
  }
3979
+ function getLaunchConfigs(dirPath) {
3980
+ const project = load().find((p) => p.path === dirPath);
3981
+ return project?.launchConfigs ?? [];
3982
+ }
3983
+ function saveLaunchConfigs(dirPath, configs) {
3984
+ const projects = load();
3985
+ const idx = projects.findIndex((p) => p.path === dirPath);
3986
+ if (idx >= 0) {
3987
+ projects[idx] = { ...projects[idx], launchConfigs: configs };
3988
+ save(projects);
3989
+ }
3990
+ }
3991
+ function getActiveLaunchConfigId(dirPath) {
3992
+ const project = load().find((p) => p.path === dirPath);
3993
+ return project?.activeLaunchConfigId ?? null;
3994
+ }
3995
+ function saveActiveLaunchConfigId(dirPath, id) {
3996
+ const projects = load();
3997
+ const idx = projects.findIndex((p) => p.path === dirPath);
3998
+ if (idx >= 0) {
3999
+ projects[idx] = { ...projects[idx], activeLaunchConfigId: id };
4000
+ save(projects);
4001
+ }
4002
+ }
3963
4003
  function updateProjectSettings(projectPath, patch) {
3964
4004
  if (!projectPath) return;
3965
4005
  const configPath = path10.join(projectPath, "project.config.json");
@@ -4210,7 +4250,7 @@ function createWorkspaceService(ctx) {
4210
4250
  await disposeSession();
4211
4251
  if (currentProjectPath !== "") lastClosedProjectPath = currentProjectPath;
4212
4252
  currentProjectPath = "";
4213
- ctx.views.disposeAll();
4253
+ ctx.views.disposeProjectViews();
4214
4254
  } finally {
4215
4255
  closing = false;
4216
4256
  release();
@@ -4265,7 +4305,15 @@ function createWorkspaceService(ctx) {
4265
4305
  // Per-project settings (uploadWithSourceMap etc.) live in the project's
4266
4306
  // own `project.config.json`, not the registry — keep direct repo calls.
4267
4307
  getProjectSettings: (projectPath) => getProjectSettings(projectPath),
4268
- updateProjectSettings: (projectPath, patch) => updateProjectSettings(projectPath, patch)
4308
+ updateProjectSettings: (projectPath, patch) => updateProjectSettings(projectPath, patch),
4309
+ getLaunchConfigs: async (p) => provider.getLaunchConfigs ? await provider.getLaunchConfigs(p) : [],
4310
+ saveLaunchConfigs: async (p, c) => {
4311
+ if (provider.saveLaunchConfigs) await provider.saveLaunchConfigs(p, c);
4312
+ },
4313
+ getActiveLaunchConfigId: async (p) => provider.getActiveLaunchConfigId ? await provider.getActiveLaunchConfigId(p) : null,
4314
+ saveActiveLaunchConfigId: async (p, id) => {
4315
+ if (provider.saveActiveLaunchConfigId) await provider.saveActiveLaunchConfigId(p, id);
4316
+ }
4269
4317
  };
4270
4318
  }
4271
4319
 
@@ -4311,7 +4359,11 @@ function createLocalProjectsProvider() {
4311
4359
  getCompileConfig: (dirPath) => getCompileConfig(dirPath),
4312
4360
  saveCompileConfig: (dirPath, cfg) => saveCompileConfig(dirPath, cfg),
4313
4361
  saveThumbnail: (dirPath, dataUrl) => saveThumbnailFromDataUrl(dirPath, dataUrl),
4314
- getThumbnail: (dirPath) => loadThumbnail(dirPath)
4362
+ getThumbnail: (dirPath) => loadThumbnail(dirPath),
4363
+ getLaunchConfigs: (dirPath) => getLaunchConfigs(dirPath),
4364
+ saveLaunchConfigs: (dirPath, configs) => saveLaunchConfigs(dirPath, configs),
4365
+ getActiveLaunchConfigId: (dirPath) => getActiveLaunchConfigId(dirPath),
4366
+ saveActiveLaunchConfigId: (dirPath, id) => saveActiveLaunchConfigId(dirPath, id)
4315
4367
  };
4316
4368
  }
4317
4369
 
@@ -4376,6 +4428,7 @@ function createWorkbenchContext(opts) {
4376
4428
  ctx.simulatorApis = createSimulatorApiRegistry();
4377
4429
  ctx.windows = createWindowService(opts.mainWindow);
4378
4430
  ctx.views = createViewManager(ctx);
4431
+ ctx.registry.add(() => ctx.views.disposeAll());
4379
4432
  ctx.notify = createRendererNotifier(ctx);
4380
4433
  ctx.openSettings = () => openSettingsWindow(ctx);
4381
4434
  ctx.projectsProvider = opts.projectsProvider ?? createLocalProjectsProvider();
@@ -4645,6 +4698,29 @@ var SettingsProjectSettingsChangedSchema = z3.tuple([
4645
4698
  ]);
4646
4699
  var ProjectCaptureThumbnailSchema = z3.tuple([AbsolutePath]);
4647
4700
  var ProjectGetThumbnailSchema = z3.tuple([AbsolutePath]);
4701
+ var ProjectGetLaunchConfigsSchema = z3.tuple([AbsolutePath]);
4702
+ var LaunchConfigShape = z3.looseObject({
4703
+ id: z3.string().min(1),
4704
+ name: z3.string(),
4705
+ startPage: z3.string().optional(),
4706
+ scene: z3.number().optional(),
4707
+ queryParams: z3.array(z3.looseObject({ key: z3.string(), value: z3.string() })).optional()
4708
+ });
4709
+ var ProjectSaveLaunchConfigsSchema = z3.tuple([
4710
+ AbsolutePath,
4711
+ z3.array(LaunchConfigShape)
4712
+ ]);
4713
+ var ProjectGetActiveLaunchConfigIdSchema = z3.tuple([AbsolutePath]);
4714
+ var ProjectSaveActiveLaunchConfigIdSchema = z3.tuple([
4715
+ AbsolutePath,
4716
+ z3.union([z3.string(), z3.null()])
4717
+ ]);
4718
+ var PopoverSwitchLaunchConfigSchema = z3.tuple([
4719
+ z3.union([z3.string(), z3.null()])
4720
+ ]);
4721
+ var PopoverUpdateLaunchConfigsSchema = z3.tuple([
4722
+ z3.array(LaunchConfigShape)
4723
+ ]);
4648
4724
 
4649
4725
  // src/main/ipc/simulator.ts
4650
4726
  function registerSimulatorIpc(ctx) {
@@ -4681,6 +4757,21 @@ function registerPopoverIpc(ctx) {
4681
4757
  const [newConfig] = validate(PopoverChannel.Relaunch, PopoverRelaunchSchema, args);
4682
4758
  ctx.views.hidePopover();
4683
4759
  ctx.notify.popoverRelaunch(newConfig);
4760
+ }).on(PopoverChannel.SwitchLaunchConfig, (_event, ...args) => {
4761
+ const [id] = validate(
4762
+ PopoverChannel.SwitchLaunchConfig,
4763
+ PopoverSwitchLaunchConfigSchema,
4764
+ args
4765
+ );
4766
+ ctx.views.hidePopover();
4767
+ ctx.notify.popoverSwitchLaunchConfig(id);
4768
+ }).on(PopoverChannel.UpdateLaunchConfigs, (_event, ...args) => {
4769
+ const [configs] = validate(
4770
+ PopoverChannel.UpdateLaunchConfigs,
4771
+ PopoverUpdateLaunchConfigsSchema,
4772
+ args
4773
+ );
4774
+ ctx.notify.popoverUpdateLaunchConfigs(configs);
4684
4775
  });
4685
4776
  }
4686
4777
  var popoverModule = {
@@ -4906,6 +4997,34 @@ function registerSessionIpc(ctx) {
4906
4997
  args
4907
4998
  );
4908
4999
  return ctx.workspace.getThumbnail(projectPath);
5000
+ }).handle(ProjectChannel.GetLaunchConfigs, (_, ...args) => {
5001
+ const [projectPath] = validate(
5002
+ ProjectChannel.GetLaunchConfigs,
5003
+ ProjectGetLaunchConfigsSchema,
5004
+ args
5005
+ );
5006
+ return ctx.workspace.getLaunchConfigs(projectPath);
5007
+ }).handle(ProjectChannel.SaveLaunchConfigs, (_, ...args) => {
5008
+ const [projectPath, configs] = validate(
5009
+ ProjectChannel.SaveLaunchConfigs,
5010
+ ProjectSaveLaunchConfigsSchema,
5011
+ args
5012
+ );
5013
+ return ctx.workspace.saveLaunchConfigs(projectPath, configs);
5014
+ }).handle(ProjectChannel.GetActiveLaunchConfigId, (_, ...args) => {
5015
+ const [projectPath] = validate(
5016
+ ProjectChannel.GetActiveLaunchConfigId,
5017
+ ProjectGetActiveLaunchConfigIdSchema,
5018
+ args
5019
+ );
5020
+ return ctx.workspace.getActiveLaunchConfigId(projectPath);
5021
+ }).handle(ProjectChannel.SaveActiveLaunchConfigId, (_, ...args) => {
5022
+ const [projectPath, id] = validate(
5023
+ ProjectChannel.SaveActiveLaunchConfigId,
5024
+ ProjectSaveActiveLaunchConfigIdSchema,
5025
+ args
5026
+ );
5027
+ return ctx.workspace.saveActiveLaunchConfigId(projectPath, id);
4909
5028
  });
4910
5029
  }
4911
5030
  var sessionModule = {
@@ -11444,7 +11563,24 @@ async function createDevtoolsRuntime(config = {}) {
11444
11563
  // teardown, so a single dispose leaves no dead entry behind.
11445
11564
  registerTrustedWindow: (win) => context.registry.add(registerTrustedWindow(context, win)),
11446
11565
  registerSimulatorApi: (name, handler) => context.registry.add(toDisposable8(context.simulatorApis.register(name, handler))),
11447
- dispose: () => disposeContext(context)
11566
+ dispose: () => disposeContext(context),
11567
+ async getLaunchConfigs() {
11568
+ const projectPath = context.workspace.getProjectPath();
11569
+ if (!projectPath) return [];
11570
+ return context.workspace.getLaunchConfigs(projectPath);
11571
+ },
11572
+ async setLaunchConfigs(configs) {
11573
+ const projectPath = context.workspace.getProjectPath();
11574
+ if (!projectPath) return;
11575
+ await context.workspace.saveLaunchConfigs(projectPath, configs);
11576
+ context.notify.popoverUpdateLaunchConfigs(configs);
11577
+ },
11578
+ async setActiveLaunchConfig(id) {
11579
+ const projectPath = context.workspace.getProjectPath();
11580
+ if (!projectPath) return;
11581
+ await context.workspace.saveActiveLaunchConfigId(projectPath, id);
11582
+ context.notify.popoverSwitchLaunchConfig(id);
11583
+ }
11448
11584
  };
11449
11585
  instance.registerSimulatorApi("login", async () => {
11450
11586
  return "hello";
@@ -1,5 +1,5 @@
1
1
  import { PopoverChannel } from '../../shared/ipc-channels.js';
2
- import { PopoverShowSchema, PopoverRelaunchSchema, } from '../../shared/ipc-schemas.js';
2
+ import { PopoverShowSchema, PopoverRelaunchSchema, PopoverSwitchLaunchConfigSchema, PopoverUpdateLaunchConfigsSchema, } from '../../shared/ipc-schemas.js';
3
3
  import { validate } from '../utils/ipc-schema.js';
4
4
  import { IpcRegistry } from '../utils/ipc-registry.js';
5
5
  export function registerPopoverIpc(ctx) {
@@ -15,6 +15,15 @@ export function registerPopoverIpc(ctx) {
15
15
  const [newConfig] = validate(PopoverChannel.Relaunch, PopoverRelaunchSchema, args);
16
16
  ctx.views.hidePopover();
17
17
  ctx.notify.popoverRelaunch(newConfig);
18
+ })
19
+ .on(PopoverChannel.SwitchLaunchConfig, (_event, ...args) => {
20
+ const [id] = validate(PopoverChannel.SwitchLaunchConfig, PopoverSwitchLaunchConfigSchema, args);
21
+ ctx.views.hidePopover();
22
+ ctx.notify.popoverSwitchLaunchConfig(id);
23
+ })
24
+ .on(PopoverChannel.UpdateLaunchConfigs, (_event, ...args) => {
25
+ const [configs] = validate(PopoverChannel.UpdateLaunchConfigs, PopoverUpdateLaunchConfigsSchema, args);
26
+ ctx.notify.popoverUpdateLaunchConfigs(configs);
18
27
  });
19
28
  }
20
29
  export const popoverModule = {
@@ -1,5 +1,5 @@
1
1
  import { ProjectChannel } from '../../shared/ipc-channels.js';
2
- import { ProjectCaptureThumbnailSchema, ProjectGetCompileConfigSchema, ProjectGetPagesSchema, ProjectGetThumbnailSchema, ProjectOpenSchema, ProjectSaveCompileConfigSchema, } from '../../shared/ipc-schemas.js';
2
+ import { ProjectCaptureThumbnailSchema, ProjectGetCompileConfigSchema, ProjectGetPagesSchema, ProjectGetThumbnailSchema, ProjectOpenSchema, ProjectSaveCompileConfigSchema, ProjectGetLaunchConfigsSchema, ProjectSaveLaunchConfigsSchema, ProjectGetActiveLaunchConfigIdSchema, ProjectSaveActiveLaunchConfigIdSchema, } from '../../shared/ipc-schemas.js';
3
3
  import { validate } from '../utils/ipc-schema.js';
4
4
  import { IpcRegistry } from '../utils/ipc-registry.js';
5
5
  export function registerSessionIpc(ctx) {
@@ -30,6 +30,22 @@ export function registerSessionIpc(ctx) {
30
30
  .handle(ProjectChannel.GetThumbnail, (_, ...args) => {
31
31
  const [projectPath] = validate(ProjectChannel.GetThumbnail, ProjectGetThumbnailSchema, args);
32
32
  return ctx.workspace.getThumbnail(projectPath);
33
+ })
34
+ .handle(ProjectChannel.GetLaunchConfigs, (_, ...args) => {
35
+ const [projectPath] = validate(ProjectChannel.GetLaunchConfigs, ProjectGetLaunchConfigsSchema, args);
36
+ return ctx.workspace.getLaunchConfigs(projectPath);
37
+ })
38
+ .handle(ProjectChannel.SaveLaunchConfigs, (_, ...args) => {
39
+ const [projectPath, configs] = validate(ProjectChannel.SaveLaunchConfigs, ProjectSaveLaunchConfigsSchema, args);
40
+ return ctx.workspace.saveLaunchConfigs(projectPath, configs);
41
+ })
42
+ .handle(ProjectChannel.GetActiveLaunchConfigId, (_, ...args) => {
43
+ const [projectPath] = validate(ProjectChannel.GetActiveLaunchConfigId, ProjectGetActiveLaunchConfigIdSchema, args);
44
+ return ctx.workspace.getActiveLaunchConfigId(projectPath);
45
+ })
46
+ .handle(ProjectChannel.SaveActiveLaunchConfigId, (_, ...args) => {
47
+ const [projectPath, id] = validate(ProjectChannel.SaveActiveLaunchConfigId, ProjectSaveActiveLaunchConfigIdSchema, args);
48
+ return ctx.workspace.saveActiveLaunchConfigId(projectPath, id);
33
49
  });
34
50
  }
35
51
  export const sessionModule = {
@@ -1,5 +1,5 @@
1
1
  import type { BrowserWindow, WebContents, WebContentsView } from 'electron';
2
- import type { CompileConfig } from '../../../shared/types.js';
2
+ import type { CompileConfig, LaunchConfig } from '../../../shared/types.js';
3
3
  import { type EditorOpenFilePayload } from '../../../shared/ipc-channels.js';
4
4
  import type { WorkbenchSettings } from '../settings/index.js';
5
5
  import type { ProjectSettings } from '../projects/project-repository.js';
@@ -98,6 +98,10 @@ export interface RendererNotifier {
98
98
  popoverClosed(): void;
99
99
  /** Ask the main renderer to relaunch the simulator with a new config. */
100
100
  popoverRelaunch(config: CompileConfig): void;
101
+ /** Ask the main renderer to switch to a launch config (or null for normal). */
102
+ popoverSwitchLaunchConfig(id: string | null): void;
103
+ /** Ask the main renderer to update its launch configs list. */
104
+ popoverUpdateLaunchConfigs(configs: LaunchConfig[]): void;
101
105
  /**
102
106
  * Push the reserved host-toolbar height to the main renderer so its toolbar
103
107
  * placeholder div resizes (closes the host-toolbar dynamic-height loop).
@@ -39,6 +39,12 @@ export function createRendererNotifier(ctx) {
39
39
  popoverRelaunch(config) {
40
40
  sendToMain(PopoverChannel.Relaunch, config);
41
41
  },
42
+ popoverSwitchLaunchConfig(id) {
43
+ sendToMain(PopoverChannel.SwitchLaunchConfig, id);
44
+ },
45
+ popoverUpdateLaunchConfigs(configs) {
46
+ sendToMain(PopoverChannel.UpdateLaunchConfigs, configs);
47
+ },
42
48
  hostToolbarHeightChanged(height) {
43
49
  sendToMain(ViewChannel.HostToolbarHeightChanged, height);
44
50
  },
@@ -20,6 +20,10 @@ export function createLocalProjectsProvider() {
20
20
  saveCompileConfig: (dirPath, cfg) => repo.saveCompileConfig(dirPath, cfg),
21
21
  saveThumbnail: (dirPath, dataUrl) => saveThumbnailFromDataUrl(dirPath, dataUrl),
22
22
  getThumbnail: (dirPath) => loadThumbnail(dirPath),
23
+ getLaunchConfigs: (dirPath) => repo.getLaunchConfigs(dirPath),
24
+ saveLaunchConfigs: (dirPath, configs) => repo.saveLaunchConfigs(dirPath, configs),
25
+ getActiveLaunchConfigId: (dirPath) => repo.getActiveLaunchConfigId(dirPath),
26
+ saveActiveLaunchConfigId: (dirPath, id) => repo.saveActiveLaunchConfigId(dirPath, id),
23
27
  };
24
28
  }
25
29
  //# sourceMappingURL=local-provider.js.map
@@ -1,9 +1,11 @@
1
- import type { CompileConfig } from '../../../shared/types.js';
1
+ import type { CompileConfig, LaunchConfig } from '../../../shared/types.js';
2
2
  export interface Project {
3
3
  name: string;
4
4
  path: string;
5
5
  lastOpened?: string | null;
6
6
  compileConfig?: CompileConfig;
7
+ launchConfigs?: LaunchConfig[];
8
+ activeLaunchConfigId?: string | null;
7
9
  }
8
10
  export interface ProjectPages {
9
11
  pages: string[];
@@ -23,6 +25,10 @@ export declare function getProjectPages(dirPath: string): ProjectPages;
23
25
  export declare function saveCompileConfig(dirPath: string, config: CompileConfig): void;
24
26
  /** Read a subset of `project.config.json` exposed to the settings panel. */
25
27
  export declare function getProjectSettings(projectPath: string): ProjectSettings;
28
+ export declare function getLaunchConfigs(dirPath: string): LaunchConfig[];
29
+ export declare function saveLaunchConfigs(dirPath: string, configs: LaunchConfig[]): void;
30
+ export declare function getActiveLaunchConfigId(dirPath: string): string | null;
31
+ export declare function saveActiveLaunchConfigId(dirPath: string, id: string | null): void;
26
32
  /** Persist a partial patch into the `setting` block of `project.config.json`. */
27
33
  export declare function updateProjectSettings(projectPath: string, patch: Partial<ProjectSettings>): void;
28
34
  //# sourceMappingURL=project-repository.d.ts.map
@@ -132,6 +132,30 @@ export function getProjectSettings(projectPath) {
132
132
  return { uploadWithSourceMap: false };
133
133
  }
134
134
  }
135
+ export function getLaunchConfigs(dirPath) {
136
+ const project = load().find((p) => p.path === dirPath);
137
+ return project?.launchConfigs ?? [];
138
+ }
139
+ export function saveLaunchConfigs(dirPath, configs) {
140
+ const projects = load();
141
+ const idx = projects.findIndex((p) => p.path === dirPath);
142
+ if (idx >= 0) {
143
+ projects[idx] = { ...projects[idx], launchConfigs: configs };
144
+ save(projects);
145
+ }
146
+ }
147
+ export function getActiveLaunchConfigId(dirPath) {
148
+ const project = load().find((p) => p.path === dirPath);
149
+ return project?.activeLaunchConfigId ?? null;
150
+ }
151
+ export function saveActiveLaunchConfigId(dirPath, id) {
152
+ const projects = load();
153
+ const idx = projects.findIndex((p) => p.path === dirPath);
154
+ if (idx >= 0) {
155
+ projects[idx] = { ...projects[idx], activeLaunchConfigId: id };
156
+ save(projects);
157
+ }
158
+ }
135
159
  /** Persist a partial patch into the `setting` block of `project.config.json`. */
136
160
  export function updateProjectSettings(projectPath, patch) {
137
161
  if (!projectPath)
@@ -4,7 +4,7 @@
4
4
  * `WorkbenchAppConfig` to fully take over the project source-of-truth, the
5
5
  * template catalog, and the "新建项目" dialog.
6
6
  */
7
- import type { CompileConfig } from '../../../shared/types.js';
7
+ import type { CompileConfig, LaunchConfig } from '../../../shared/types.js';
8
8
  import type { Project } from './project-repository.js';
9
9
  export type { Project };
10
10
  export type { ProjectTemplate, CreateProjectInput } from '../../../shared/types.js';
@@ -81,6 +81,30 @@ export interface ProjectsProvider {
81
81
  * Default when omitted: returns `null`.
82
82
  */
83
83
  getThumbnail?(dirPath: string): string | null | Promise<string | null>;
84
+ /**
85
+ * Read saved launch configs for a project.
86
+ *
87
+ * Default when omitted: returns `[]`.
88
+ */
89
+ getLaunchConfigs?(dirPath: string): LaunchConfig[] | Promise<LaunchConfig[]>;
90
+ /**
91
+ * Persist the full list of launch configs for a project.
92
+ *
93
+ * Default when omitted: silently no-ops.
94
+ */
95
+ saveLaunchConfigs?(dirPath: string, configs: LaunchConfig[]): void | Promise<void>;
96
+ /**
97
+ * Read the active launch config id for a project.
98
+ *
99
+ * Default when omitted: returns `null` (normal mode).
100
+ */
101
+ getActiveLaunchConfigId?(dirPath: string): string | null | Promise<string | null>;
102
+ /**
103
+ * Persist the active launch config id for a project.
104
+ *
105
+ * Default when omitted: silently no-ops.
106
+ */
107
+ saveActiveLaunchConfigId?(dirPath: string, id: string | null): void | Promise<void>;
84
108
  }
85
109
  /** Built-in template policy. */
86
110
  export type BuiltinTemplatesMode = 'all' | 'none' | readonly string[];
@@ -91,7 +91,7 @@ export function createRenderInspector(options = {}) {
91
91
  if (!(await ensureInjected(wc)))
92
92
  return null;
93
93
  try {
94
- const result = await wc.executeJavaScript('window.__diminaRenderInspect ? window.__diminaRenderInspect.getWxml() : null');
94
+ const result = (await wc.executeJavaScript('window.__diminaRenderInspect ? window.__diminaRenderInspect.getWxml() : null'));
95
95
  return result ?? null;
96
96
  }
97
97
  catch {
@@ -105,7 +105,7 @@ export function createRenderInspector(options = {}) {
105
105
  return null;
106
106
  let inspection;
107
107
  try {
108
- const result = await wc.executeJavaScript(`window.__diminaRenderInspect ? window.__diminaRenderInspect.highlightElement(${JSON.stringify(sid)}) : null`);
108
+ const result = (await wc.executeJavaScript(`window.__diminaRenderInspect ? window.__diminaRenderInspect.highlightElement(${JSON.stringify(sid)}) : null`));
109
109
  inspection = result ?? null;
110
110
  }
111
111
  catch {
@@ -149,11 +149,11 @@ export function createRenderInspector(options = {}) {
149
149
  return;
150
150
  const expression = `window.__diminaRenderInspect && window.__diminaRenderInspect.elementFor(${JSON.stringify(sid)})`;
151
151
  try {
152
- const evaluated = await wc.debugger.sendCommand('Runtime.evaluate', {
152
+ const evaluated = (await wc.debugger.sendCommand('Runtime.evaluate', {
153
153
  expression,
154
154
  returnByValue: false,
155
155
  objectGroup: HOVER_OBJECT_GROUP,
156
- });
156
+ }));
157
157
  const objectId = evaluated?.result?.objectId;
158
158
  if (!objectId)
159
159
  return;
@@ -121,7 +121,21 @@ export interface ViewManager {
121
121
  hidePopover(): void;
122
122
  /** Re-apply layout for every currently visible overlay (on window resize). */
123
123
  repositionAll(): void;
124
- /** Destroy all overlay webContents and null out the cached views. */
124
+ /**
125
+ * Destroy the PROJECT-scoped views: the simulator (with its devtools host
126
+ * and settings/popover overlays), the embedded workbench editor, and the
127
+ * per-guest safe-area sessions. Deliberately does NOT touch the host
128
+ * toolbar — its webContents lifecycle belongs to the HOST, so it survives
129
+ * closing a project. This is what `workspace.closeProject()` calls.
130
+ */
131
+ disposeProjectViews(): void;
132
+ /**
133
+ * Destroy ALL managed views: everything `disposeProjectViews()` covers PLUS
134
+ * the host toolbar (its view, port channel, and the ref-counted
135
+ * session-runtime preload registration). App/window teardown only — the
136
+ * context's DisposableRegistry runs this so the toolbar's session-level
137
+ * resources are released exactly once, at the end of the manager's life.
138
+ */
125
139
  disposeAll(): void;
126
140
  /** Return the webContents ID of the currently attached simulator. */
127
141
  getSimulatorWebContentsId(): number | null;
@@ -45,17 +45,24 @@ export function createViewManager(ctx) {
45
45
  function reapplyToolbarDependentOverlays() {
46
46
  overlayPanels.reapplyPresentOverlays();
47
47
  }
48
- function disposeAll() {
48
+ function disposeProjectViews() {
49
49
  // Aggregate simulator detach first (native simulator + devtools host +
50
- // settings/popover), then the workbench, the host toolbar, and the
51
- // per-guest safe-area sessions.
50
+ // settings/popover), then the workbench, then the per-guest safe-area
51
+ // sessions. The host toolbar is exempt: it is HOST-scoped (the host loads
52
+ // and drives it; the height-replay machinery exists precisely for the
53
+ // close-project → reopen flow), so a project's teardown must not kill it.
52
54
  nativeSimulator.detachSimulator();
53
55
  // Embedded workbench editor view (no-op when the host never opted in;
54
56
  // also removes the devtools-theme sync listener).
55
57
  workbench.detachWorkbench();
56
- hostToolbar.dispose();
57
58
  safeArea.dispose();
58
59
  }
60
+ function disposeAll() {
61
+ disposeProjectViews();
62
+ // Host-scoped teardown: the toolbar view, its port channel, and the
63
+ // ref-counted session-runtime preload registration.
64
+ hostToolbar.dispose();
65
+ }
59
66
  return {
60
67
  attachNativeSimulator: nativeSimulator.attachNativeSimulator,
61
68
  softReloadNativeSimulator: nativeSimulator.softReloadNativeSimulator,
@@ -66,6 +73,7 @@ export function createViewManager(ctx) {
66
73
  showPopover: overlayPanels.showPopover,
67
74
  hidePopover: overlayPanels.hidePopover,
68
75
  repositionAll: () => overlayPanels.reapplyPresentOverlays(),
76
+ disposeProjectViews,
69
77
  disposeAll,
70
78
  getSimulatorWebContentsId: nativeSimulator.getSimulatorWebContentsId,
71
79
  getSimulatorWebContents: nativeSimulator.getSimulatorWebContents,
@@ -30,6 +30,11 @@ export function createWorkbenchContext(opts) {
30
30
  ctx.simulatorApis = createSimulatorApiRegistry();
31
31
  ctx.windows = createWindowService(opts.mainWindow);
32
32
  ctx.views = createViewManager(ctx);
33
+ // Full view teardown belongs to the CONTEXT's life, not a project's:
34
+ // closeProject only disposes project-scoped views, so this registration is
35
+ // the one place that releases the HOST-scoped toolbar (its view and the
36
+ // ref-counted session-runtime preload) when the app/context winds down.
37
+ ctx.registry.add(() => ctx.views.disposeAll());
33
38
  ctx.notify = createRendererNotifier(ctx);
34
39
  // Lazy closure (not a bound snapshot): reads ctx.windows/notify/rendererDir
35
40
  // at call time through the live context, which structurally satisfies the
@@ -1,4 +1,4 @@
1
- import type { AppInfo, CompileConfig, ProjectSession } from '../../../shared/types.js';
1
+ import type { AppInfo, CompileConfig, LaunchConfig, ProjectSession } from '../../../shared/types.js';
2
2
  import type { WorkbenchContext } from '../workbench-context.js';
3
3
  import type { Project, ProjectPages, ProjectSettings } from '../projects/project-repository.js';
4
4
  /**
@@ -57,6 +57,10 @@ export interface WorkspaceService {
57
57
  saveCompileConfig(projectPath: string, config: CompileConfig): Promise<void>;
58
58
  getProjectSettings(projectPath: string): ProjectSettings;
59
59
  updateProjectSettings(projectPath: string, patch: Partial<ProjectSettings>): void;
60
+ getLaunchConfigs(projectPath: string): Promise<LaunchConfig[]>;
61
+ saveLaunchConfigs(projectPath: string, configs: LaunchConfig[]): Promise<void>;
62
+ getActiveLaunchConfigId(projectPath: string): Promise<string | null>;
63
+ saveActiveLaunchConfigId(projectPath: string, id: string | null): Promise<void>;
60
64
  }
61
65
  /** Build a workspace service bound to the given workbench context. */
62
66
  export declare function createWorkspaceService(ctx: WorkbenchContext): WorkspaceService;
@@ -273,9 +273,9 @@ export function createWorkspaceService(ctx) {
273
273
  },
274
274
  async closeProject() {
275
275
  // Claim a request seq and take ownership immediately (close has no veto),
276
- // then serialize against a concurrent openProject: disposeAll() must not
277
- // run while an open is mid-teardown/commit (it would tear down the views
278
- // the open is building).
276
+ // then serialize against a concurrent openProject: disposeProjectViews()
277
+ // must not run while an open is mid-teardown/commit (it would tear down
278
+ // the views the open is building).
279
279
  const mySeq = opLock.nextSeq();
280
280
  opLock.takeOwnership(mySeq);
281
281
  const release = await opLock.acquire();
@@ -300,7 +300,10 @@ export function createWorkspaceService(ctx) {
300
300
  if (currentProjectPath !== '')
301
301
  lastClosedProjectPath = currentProjectPath;
302
302
  currentProjectPath = '';
303
- ctx.views.disposeAll();
303
+ // Project-scoped views only. The host toolbar is HOST-scoped and must
304
+ // survive closing a project — its full teardown (disposeAll) runs from
305
+ // the context registry at app teardown instead.
306
+ ctx.views.disposeProjectViews();
304
307
  }
305
308
  finally {
306
309
  closing = false;
@@ -383,6 +386,14 @@ export function createWorkspaceService(ctx) {
383
386
  // own `project.config.json`, not the registry — keep direct repo calls.
384
387
  getProjectSettings: (projectPath) => repo.getProjectSettings(projectPath),
385
388
  updateProjectSettings: (projectPath, patch) => repo.updateProjectSettings(projectPath, patch),
389
+ getLaunchConfigs: async (p) => (provider.getLaunchConfigs ? await provider.getLaunchConfigs(p) : []),
390
+ saveLaunchConfigs: async (p, c) => { if (provider.saveLaunchConfigs)
391
+ await provider.saveLaunchConfigs(p, c); },
392
+ getActiveLaunchConfigId: async (p) => provider.getActiveLaunchConfigId ? await provider.getActiveLaunchConfigId(p) : null,
393
+ saveActiveLaunchConfigId: async (p, id) => {
394
+ if (provider.saveActiveLaunchConfigId)
395
+ await provider.saveActiveLaunchConfigId(p, id);
396
+ },
386
397
  };
387
398
  }
388
399
  //# sourceMappingURL=workspace-service.js.map