@dimina-kit/devtools 0.3.1-dev.20260518054451 → 0.3.1-dev.20260518114324

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 (77) hide show
  1. package/README.md +120 -0
  2. package/dist/main/app/app.js +8 -0
  3. package/dist/main/index.bundle.js +322 -85
  4. package/dist/main/ipc/projects.d.ts +3 -1
  5. package/dist/main/ipc/projects.js +88 -4
  6. package/dist/main/ipc/settings.js +8 -6
  7. package/dist/main/services/projects/builtin-templates.d.ts +3 -0
  8. package/dist/main/services/projects/builtin-templates.js +24 -0
  9. package/dist/main/services/projects/create-project-service.d.ts +7 -0
  10. package/dist/main/services/projects/create-project-service.js +84 -0
  11. package/dist/main/services/projects/index.d.ts +7 -0
  12. package/dist/main/services/projects/index.js +9 -0
  13. package/dist/main/services/projects/local-provider.d.ts +3 -0
  14. package/dist/main/services/projects/local-provider.js +25 -0
  15. package/dist/main/services/projects/templates.d.ts +21 -0
  16. package/dist/main/services/projects/templates.js +39 -0
  17. package/dist/main/services/projects/thumbnail.d.ts +6 -2
  18. package/dist/main/services/projects/thumbnail.js +13 -8
  19. package/dist/main/services/projects/types.d.ts +118 -0
  20. package/dist/main/services/projects/types.js +19 -0
  21. package/dist/main/services/settings/index.d.ts +7 -0
  22. package/dist/main/services/settings/index.js +4 -0
  23. package/dist/main/services/workbench-context.d.ts +35 -0
  24. package/dist/main/services/workbench-context.js +9 -0
  25. package/dist/main/services/workspace/workspace-service.d.ts +8 -8
  26. package/dist/main/services/workspace/workspace-service.js +65 -13
  27. package/dist/main/windows/main-window/create.js +1 -1
  28. package/dist/renderer/assets/index-Czz_bp6b.js +46 -0
  29. package/dist/renderer/assets/{input-AH9UDYmj.js → input-DqVQNCx_.js} +2 -2
  30. package/dist/renderer/assets/{ipc-transport-DNgBM6Qz.js → ipc-transport-CCHrCtDr.js} +2 -2
  31. package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +1 -0
  32. package/dist/renderer/assets/{popover-D1hoVYiY.js → popover-BRg4XI_w.js} +2 -2
  33. package/dist/renderer/assets/{select-BgDHRj6r.js → select-CvILHHZU.js} +2 -2
  34. package/dist/renderer/assets/{settings-m-tcmZy_.js → settings-BFNfnYvU.js} +2 -2
  35. package/dist/renderer/assets/{settings-api-DBKlNKV3.js → settings-api-DOAgmBst.js} +2 -2
  36. package/dist/renderer/assets/{workbenchSettings-CGacu-pI.js → workbenchSettings-DjVMgMMH.js} +2 -2
  37. package/dist/renderer/entries/main/index.html +5 -5
  38. package/dist/renderer/entries/popover/index.html +5 -5
  39. package/dist/renderer/entries/settings/index.html +5 -5
  40. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  41. package/dist/shared/ipc-channels.d.ts +14 -1
  42. package/dist/shared/ipc-channels.js +8 -0
  43. package/dist/shared/ipc-schemas.d.ts +1 -0
  44. package/dist/shared/ipc-schemas.js +1 -0
  45. package/dist/shared/types.d.ts +80 -0
  46. package/package.json +7 -2
  47. package/templates/blank/app.js +6 -0
  48. package/templates/blank/app.json +11 -0
  49. package/templates/blank/app.wxss +5 -0
  50. package/templates/blank/pages/index/index.js +6 -0
  51. package/templates/blank/pages/index/index.json +3 -0
  52. package/templates/blank/pages/index/index.wxml +3 -0
  53. package/templates/blank/pages/index/index.wxss +11 -0
  54. package/templates/blank/project.config.json +13 -0
  55. package/templates/blank/sitemap.json +9 -0
  56. package/templates/taro-todo/app.js +1 -0
  57. package/templates/taro-todo/app.json +1 -0
  58. package/templates/taro-todo/app.wxss +1 -0
  59. package/templates/taro-todo/base.wxml +851 -0
  60. package/templates/taro-todo/common.js +1 -0
  61. package/templates/taro-todo/comp.js +1 -0
  62. package/templates/taro-todo/comp.json +1 -0
  63. package/templates/taro-todo/comp.wxml +2 -0
  64. package/templates/taro-todo/custom-wrapper.js +1 -0
  65. package/templates/taro-todo/custom-wrapper.json +1 -0
  66. package/templates/taro-todo/custom-wrapper.wxml +4 -0
  67. package/templates/taro-todo/pages/index/index.js +2 -0
  68. package/templates/taro-todo/pages/index/index.json +1 -0
  69. package/templates/taro-todo/pages/index/index.wxml +2 -0
  70. package/templates/taro-todo/pages/index/index.wxss +1 -0
  71. package/templates/taro-todo/project.config.json +44 -0
  72. package/templates/taro-todo/runtime.js +1 -0
  73. package/templates/taro-todo/taro.js +2 -0
  74. package/templates/taro-todo/utils.wxs +39 -0
  75. package/templates/taro-todo/vendors.js +2 -0
  76. package/dist/renderer/assets/index-BFdItKBK.js +0 -46
  77. package/dist/renderer/assets/ipc-transport-Dvmd5JZj.css +0 -1
@@ -17,7 +17,8 @@ var DEFAULTS = {
17
17
  compile: {
18
18
  watch: true
19
19
  },
20
- theme: "system"
20
+ theme: "system",
21
+ lastCreateBaseDir: null
21
22
  };
22
23
  function getSettingsFile() {
23
24
  return path.join(app.getPath("userData"), "dimina-workbench-settings.json");
@@ -38,7 +39,8 @@ function loadWorkbenchSettings() {
38
39
  compile: {
39
40
  watch: data.compile?.watch ?? DEFAULTS.compile.watch
40
41
  },
41
- theme
42
+ theme,
43
+ lastCreateBaseDir: typeof data.lastCreateBaseDir === "string" ? data.lastCreateBaseDir : DEFAULTS.lastCreateBaseDir
42
44
  };
43
45
  } catch {
44
46
  return {
@@ -73,9 +75,9 @@ function setupCdpPort() {
73
75
  }
74
76
 
75
77
  // src/main/app/app.ts
76
- import { app as app12, BrowserWindow as BrowserWindow3, nativeImage } from "electron";
77
- import fs6 from "fs";
78
- import path9 from "path";
78
+ import { app as app13, BrowserWindow as BrowserWindow3, nativeImage } from "electron";
79
+ import fs7 from "fs";
80
+ import path12 from "path";
79
81
 
80
82
  // src/main/utils/paths.ts
81
83
  import fs2 from "fs";
@@ -200,7 +202,7 @@ function configureSimulatorSession(simulatorPreloadPath) {
200
202
  headers["access-control-allow-headers"] = ["*"];
201
203
  headers["access-control-allow-methods"] = ["*"];
202
204
  headers["Cross-Origin-Opener-Policy"] = ["same-origin"];
203
- headers["Cross-Origin-Embedder-Policy"] = ["require-corp"];
205
+ headers["Cross-Origin-Embedder-Policy"] = ["credentialless"];
204
206
  callback({ responseHeaders: headers });
205
207
  });
206
208
  }
@@ -448,7 +450,15 @@ var ProjectChannel = {
448
450
  var ProjectsChannel = {
449
451
  List: "projects:list",
450
452
  Add: "projects:add",
451
- Remove: "projects:remove"
453
+ Remove: "projects:remove",
454
+ /** Phase 3: merged + sanitized template catalog for the create-project dialog. */
455
+ ListTemplates: "projects:listTemplates",
456
+ /** Phase 3: host-supplied create-project dialog hook (returns input or null). */
457
+ OpenCreateDialog: "projects:openCreateDialog",
458
+ /** Phase 3: server-side scaffold + register (delegates to create-project-service). */
459
+ Create: "projects:create",
460
+ /** Default values used to pre-fill the create-project dialog (baseDir). */
461
+ GetCreateDefaults: "projects:getCreateDefaults"
452
462
  };
453
463
  var DialogChannel = {
454
464
  OpenDirectory: "dialog:openDirectory"
@@ -960,9 +970,6 @@ function validateProjectDir(dirPath) {
960
970
  }
961
971
  return null;
962
972
  }
963
- function hasProject(dirPath) {
964
- return load().some((p) => p.path === dirPath);
965
- }
966
973
  function addProject(dirPath) {
967
974
  const projects = load();
968
975
  let name = path7.basename(dirPath);
@@ -1059,37 +1066,12 @@ function updateProjectSettings(projectPath, patch) {
1059
1066
  );
1060
1067
  }
1061
1068
 
1062
- // src/main/services/projects/thumbnail.ts
1063
- import { createHash } from "crypto";
1064
- import fs4 from "fs";
1065
- import path8 from "path";
1066
- import { app as app7 } from "electron";
1067
- function getThumbnailDir() {
1068
- return path8.join(app7.getPath("userData"), "thumbnails");
1069
- }
1070
- function hashProjectPath(projectPath) {
1071
- return createHash("sha256").update(projectPath).digest("hex").slice(0, 16);
1072
- }
1073
- function getThumbnailPath(projectPath) {
1074
- return path8.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
1075
- }
1076
- function saveThumbnail(projectPath, image) {
1077
- const png = image.toPNG();
1078
- const dir = getThumbnailDir();
1079
- fs4.mkdirSync(dir, { recursive: true });
1080
- const filePath = getThumbnailPath(projectPath);
1081
- fs4.writeFileSync(filePath, png);
1082
- return `data:image/png;base64,${png.toString("base64")}`;
1083
- }
1084
- function loadThumbnail(projectPath) {
1085
- const filePath = getThumbnailPath(projectPath);
1086
- try {
1087
- const buf = fs4.readFileSync(filePath);
1088
- return `data:image/png;base64,${buf.toString("base64")}`;
1089
- } catch {
1090
- return null;
1091
- }
1092
- }
1069
+ // src/main/services/projects/types.ts
1070
+ var DEFAULT_COMPILE_CONFIG = {
1071
+ startPage: "",
1072
+ scene: DEFAULT_SCENE,
1073
+ queryParams: []
1074
+ };
1093
1075
 
1094
1076
  // src/main/services/workspace/workspace-service.ts
1095
1077
  function createWorkspaceService(ctx) {
@@ -1129,17 +1111,31 @@ function createWorkspaceService(ctx) {
1129
1111
  );
1130
1112
  }
1131
1113
  }
1132
- return {
1114
+ const provider = ctx.projectsProvider ?? {
1133
1115
  listProjects: () => listProjects(),
1134
- addProject: (dirPath) => addProject(dirPath),
1135
- removeProject: (dirPath) => removeProject(dirPath),
1136
- hasProject: (dirPath) => hasProject(dirPath),
1137
- validateProjectDir: (dirPath) => validateProjectDir(dirPath),
1116
+ addProject: (p) => addProject(p),
1117
+ removeProject: (p) => removeProject(p),
1118
+ validateProjectDir: (p) => validateProjectDir(p),
1119
+ updateLastOpened: (p) => updateLastOpened(p),
1120
+ getCompileConfig: (p) => getCompileConfig(p),
1121
+ saveCompileConfig: (p, c) => saveCompileConfig(p, c)
1122
+ };
1123
+ return {
1124
+ listProjects: async () => provider.listProjects(),
1125
+ addProject: async (dirPath) => provider.addProject(dirPath),
1126
+ removeProject: async (dirPath) => {
1127
+ await provider.removeProject(dirPath);
1128
+ },
1129
+ hasProject: async (dirPath) => {
1130
+ const projects = await provider.listProjects();
1131
+ return projects.some((p) => p.path === dirPath);
1132
+ },
1133
+ validateProjectDir: async (dirPath) => provider.validateProjectDir ? provider.validateProjectDir(dirPath) : null,
1138
1134
  async openProject(projectPath) {
1139
1135
  clearSimulatorServicewechatReferer();
1140
1136
  await disposeSession();
1141
1137
  currentProjectPath = "";
1142
- const dirError = validateProjectDir(projectPath);
1138
+ const dirError = provider.validateProjectDir ? await provider.validateProjectDir(projectPath) : null;
1143
1139
  if (dirError) {
1144
1140
  sendStatus("error", dirError);
1145
1141
  return { success: false, error: dirError };
@@ -1162,7 +1158,9 @@ function createWorkspaceService(ctx) {
1162
1158
  }
1163
1159
  currentSession = session2;
1164
1160
  currentProjectPath = projectPath;
1165
- bestEffort("updateLastOpened", () => updateLastOpened(projectPath));
1161
+ bestEffort("updateLastOpened", () => {
1162
+ if (provider.updateLastOpened) provider.updateLastOpened(projectPath);
1163
+ });
1166
1164
  bestEffort("sendStatus", () => sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210"));
1167
1165
  bestEffort("applyReferer", () => applyRefererFromSession(session2));
1168
1166
  return {
@@ -1187,22 +1185,127 @@ function createWorkspaceService(ctx) {
1187
1185
  if (!wc || wc.isDestroyed()) return null;
1188
1186
  try {
1189
1187
  const image = await wc.capturePage();
1190
- return saveThumbnail(projectPath, image);
1188
+ const dataUrl = `data:image/png;base64,${image.toPNG().toString("base64")}`;
1189
+ if (provider.saveThumbnail) {
1190
+ await provider.saveThumbnail(projectPath, dataUrl);
1191
+ }
1192
+ return dataUrl;
1191
1193
  } catch {
1192
1194
  return null;
1193
1195
  }
1194
1196
  },
1195
- getThumbnail(projectPath) {
1196
- return loadThumbnail(projectPath);
1197
+ async getThumbnail(projectPath) {
1198
+ if (provider.getThumbnail) {
1199
+ return await provider.getThumbnail(projectPath) ?? null;
1200
+ }
1201
+ return null;
1197
1202
  },
1203
+ // `getProjectPages` reads the project's own `app.json` from disk and is
1204
+ // independent of the project registry — it stays on the repo helper.
1198
1205
  getProjectPages: (projectPath) => getProjectPages(projectPath),
1199
- getCompileConfig: (projectPath) => getCompileConfig(projectPath),
1200
- saveCompileConfig: (projectPath, config) => saveCompileConfig(projectPath, config),
1206
+ getCompileConfig: async (projectPath) => provider.getCompileConfig ? await provider.getCompileConfig(projectPath) : DEFAULT_COMPILE_CONFIG,
1207
+ saveCompileConfig: async (projectPath, config) => {
1208
+ if (provider.saveCompileConfig) {
1209
+ await provider.saveCompileConfig(projectPath, config);
1210
+ }
1211
+ },
1212
+ // Per-project settings (uploadWithSourceMap etc.) live in the project's
1213
+ // own `project.config.json`, not the registry — keep direct repo calls.
1201
1214
  getProjectSettings: (projectPath) => getProjectSettings(projectPath),
1202
1215
  updateProjectSettings: (projectPath, patch) => updateProjectSettings(projectPath, patch)
1203
1216
  };
1204
1217
  }
1205
1218
 
1219
+ // src/main/services/projects/thumbnail.ts
1220
+ import { createHash } from "crypto";
1221
+ import fs4 from "fs";
1222
+ import path8 from "path";
1223
+ import { app as app7 } from "electron";
1224
+ function getThumbnailDir() {
1225
+ return path8.join(app7.getPath("userData"), "thumbnails");
1226
+ }
1227
+ function hashProjectPath(projectPath) {
1228
+ return createHash("sha256").update(projectPath).digest("hex").slice(0, 16);
1229
+ }
1230
+ function getThumbnailPath(projectPath) {
1231
+ return path8.join(getThumbnailDir(), `${hashProjectPath(projectPath)}.png`);
1232
+ }
1233
+ var DATA_URL_PNG_PREFIX = "data:image/png;base64,";
1234
+ function saveThumbnailFromDataUrl(projectPath, imageDataUrl) {
1235
+ if (!imageDataUrl.startsWith(DATA_URL_PNG_PREFIX)) return;
1236
+ const png = Buffer.from(imageDataUrl.slice(DATA_URL_PNG_PREFIX.length), "base64");
1237
+ fs4.mkdirSync(getThumbnailDir(), { recursive: true });
1238
+ fs4.writeFileSync(getThumbnailPath(projectPath), png);
1239
+ }
1240
+ function loadThumbnail(projectPath) {
1241
+ const filePath = getThumbnailPath(projectPath);
1242
+ try {
1243
+ const buf = fs4.readFileSync(filePath);
1244
+ return `${DATA_URL_PNG_PREFIX}${buf.toString("base64")}`;
1245
+ } catch {
1246
+ return null;
1247
+ }
1248
+ }
1249
+
1250
+ // src/main/services/projects/local-provider.ts
1251
+ function createLocalProjectsProvider() {
1252
+ return {
1253
+ listProjects: () => listProjects(),
1254
+ addProject: (dirPath) => addProject(dirPath),
1255
+ removeProject: (dirPath) => removeProject(dirPath),
1256
+ validateProjectDir: (dirPath) => validateProjectDir(dirPath),
1257
+ updateLastOpened: (dirPath) => updateLastOpened(dirPath),
1258
+ getCompileConfig: (dirPath) => getCompileConfig(dirPath),
1259
+ saveCompileConfig: (dirPath, cfg) => saveCompileConfig(dirPath, cfg),
1260
+ saveThumbnail: (dirPath, dataUrl) => saveThumbnailFromDataUrl(dirPath, dataUrl),
1261
+ getThumbnail: (dirPath) => loadThumbnail(dirPath)
1262
+ };
1263
+ }
1264
+
1265
+ // src/main/services/projects/templates.ts
1266
+ function resolveTemplates(builtin, injected, mode) {
1267
+ let kept;
1268
+ if (mode === "none") {
1269
+ kept = [];
1270
+ } else if (mode === "all") {
1271
+ kept = [...builtin];
1272
+ } else {
1273
+ const byId = new Map(builtin.map((t) => [t.id, t]));
1274
+ kept = [];
1275
+ for (const id of mode) {
1276
+ const t = byId.get(id);
1277
+ if (t) kept.push(t);
1278
+ }
1279
+ }
1280
+ const injectedIds = new Set(injected.map((t) => t.id));
1281
+ const remaining = kept.filter((t) => !injectedIds.has(t.id));
1282
+ return [...injected, ...remaining];
1283
+ }
1284
+ function sanitizeTemplates(templates) {
1285
+ return templates.map((t) => {
1286
+ const { generate: _generate, ...rest } = t;
1287
+ return rest;
1288
+ });
1289
+ }
1290
+
1291
+ // src/main/services/projects/builtin-templates.ts
1292
+ import path9 from "node:path";
1293
+ var TEMPLATES_DIR = path9.join(devtoolsPackageRoot, "templates");
1294
+ var BUILTIN_TEMPLATES = [
1295
+ {
1296
+ id: "blank",
1297
+ name: "Blank",
1298
+ description: "\u6700\u5C0F\u9AA8\u67B6\uFF1Aapp.* + \u4E00\u4E2A index \u9875\u9762",
1299
+ source: { type: "directory", path: path9.join(TEMPLATES_DIR, "blank") }
1300
+ },
1301
+ {
1302
+ id: "taro-todo",
1303
+ name: "Taro Todo",
1304
+ description: "Taro \u7F16\u8BD1\u4EA7\u7269\u7684 Todo \u793A\u4F8B",
1305
+ source: { type: "directory", path: path9.join(TEMPLATES_DIR, "taro-todo") }
1306
+ }
1307
+ ];
1308
+
1206
1309
  // src/main/services/workbench-context.ts
1207
1310
  function hasBuiltinPanel(ctx, panelId) {
1208
1311
  return ctx.panels.includes(panelId);
@@ -1229,6 +1332,13 @@ function createWorkbenchContext(opts) {
1229
1332
  ctx.windows = createWindowService(ctx.mainWindow);
1230
1333
  ctx.views = createViewManager(ctx);
1231
1334
  ctx.notify = createRendererNotifier(ctx);
1335
+ ctx.projectsProvider = opts.projectsProvider ?? createLocalProjectsProvider();
1336
+ ctx.projectTemplates = resolveTemplates(
1337
+ BUILTIN_TEMPLATES,
1338
+ opts.projectTemplates ?? [],
1339
+ opts.builtinTemplates ?? "all"
1340
+ );
1341
+ ctx.customCreateProjectDialog = opts.customCreateProjectDialog;
1232
1342
  ctx.workspace = createWorkspaceService(ctx);
1233
1343
  ctx.senderPolicy = createWorkbenchSenderPolicy(ctx);
1234
1344
  return ctx;
@@ -1471,7 +1581,8 @@ var WorkbenchSettingsSaveSchema = z.tuple([
1471
1581
  compile: z.looseObject({
1472
1582
  watch: z.boolean()
1473
1583
  }),
1474
- theme: z.enum(["system", "light", "dark"])
1584
+ theme: z.enum(["system", "light", "dark"]),
1585
+ lastCreateBaseDir: z.union([z.string(), z.null()])
1475
1586
  })
1476
1587
  ]);
1477
1588
  var WorkbenchSettingsSetThemeSchema = z.tuple([
@@ -1593,7 +1704,74 @@ var popoverModule = {
1593
1704
  };
1594
1705
 
1595
1706
  // src/main/ipc/projects.ts
1596
- import { dialog } from "electron";
1707
+ import { app as app8, dialog } from "electron";
1708
+ import path11 from "path";
1709
+
1710
+ // src/main/services/projects/create-project-service.ts
1711
+ import fs6 from "node:fs";
1712
+ import path10 from "node:path";
1713
+ var DEFAULT_TEMPLATE_ID = "blank";
1714
+ async function createProject(input, ctx) {
1715
+ const name = (input.name ?? "").trim();
1716
+ if (name.length === 0) {
1717
+ throw new Error("Project name cannot be empty");
1718
+ }
1719
+ const target = input.path;
1720
+ if (!target || typeof target !== "string") {
1721
+ throw new Error("Project path is required");
1722
+ }
1723
+ if (fs6.existsSync(target)) {
1724
+ const stat = fs6.statSync(target);
1725
+ if (!stat.isDirectory()) {
1726
+ throw new Error(`Project path exists and is not a directory: ${target}`);
1727
+ }
1728
+ const entries = fs6.readdirSync(target);
1729
+ if (entries.length > 0) {
1730
+ throw new Error(
1731
+ `Project path is not empty (refusing to overwrite): ${target}`
1732
+ );
1733
+ }
1734
+ }
1735
+ const templateId = input.templateId ?? DEFAULT_TEMPLATE_ID;
1736
+ const template = ctx.templates.find((t) => t.id === templateId);
1737
+ if (!template) {
1738
+ throw new Error(`Template not found: ${templateId}`);
1739
+ }
1740
+ if (!template.source && !template.generate) {
1741
+ throw new Error(
1742
+ `Template '${templateId}' has neither a source directory nor a generate function`
1743
+ );
1744
+ }
1745
+ fs6.mkdirSync(target, { recursive: true });
1746
+ if (template.generate) {
1747
+ await template.generate(target, { name });
1748
+ } else if (template.source) {
1749
+ if (!fs6.existsSync(template.source.path)) {
1750
+ throw new Error(
1751
+ `Template source missing on disk: ${template.source.path}`
1752
+ );
1753
+ }
1754
+ fs6.cpSync(template.source.path, target, {
1755
+ recursive: true,
1756
+ force: true
1757
+ });
1758
+ }
1759
+ const cfgPath = path10.join(target, "project.config.json");
1760
+ let cfg = {};
1761
+ if (fs6.existsSync(cfgPath)) {
1762
+ try {
1763
+ cfg = JSON.parse(fs6.readFileSync(cfgPath, "utf-8"));
1764
+ } catch {
1765
+ cfg = {};
1766
+ }
1767
+ }
1768
+ cfg.projectname = name;
1769
+ fs6.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
1770
+ const created = await ctx.projectsProvider.addProject(target);
1771
+ return created;
1772
+ }
1773
+
1774
+ // src/main/ipc/projects.ts
1597
1775
  function registerProjectsIpc(ctx) {
1598
1776
  return new IpcRegistry(ctx.senderPolicy).handle(ProjectsChannel.List, () => {
1599
1777
  return ctx.workspace.listProjects();
@@ -1605,7 +1783,7 @@ function registerProjectsIpc(ctx) {
1605
1783
  return result.canceled ? null : result.filePaths[0];
1606
1784
  }).handle(ProjectsChannel.Add, async (_event, ...args) => {
1607
1785
  const [dirPath] = validate(ProjectsChannel.Add, ProjectsAddSchema, args);
1608
- const dirError = ctx.workspace.validateProjectDir(dirPath);
1786
+ const dirError = await ctx.workspace.validateProjectDir(dirPath);
1609
1787
  if (dirError) {
1610
1788
  await dialog.showMessageBox(ctx.mainWindow, {
1611
1789
  type: "error",
@@ -1616,8 +1794,8 @@ function registerProjectsIpc(ctx) {
1616
1794
  });
1617
1795
  throw new Error(dirError);
1618
1796
  }
1619
- const duplicate = ctx.workspace.hasProject(dirPath);
1620
- const project = ctx.workspace.addProject(dirPath);
1797
+ const duplicate = await ctx.workspace.hasProject(dirPath);
1798
+ const project = await ctx.workspace.addProject(dirPath);
1621
1799
  if (duplicate) {
1622
1800
  await dialog.showMessageBox(ctx.mainWindow, {
1623
1801
  type: "info",
@@ -1631,8 +1809,60 @@ function registerProjectsIpc(ctx) {
1631
1809
  }).handle(ProjectsChannel.Remove, (_event, ...args) => {
1632
1810
  const [dirPath] = validate(ProjectsChannel.Remove, ProjectsRemoveSchema, args);
1633
1811
  return ctx.workspace.removeProject(dirPath);
1812
+ }).handle(ProjectsChannel.ListTemplates, () => {
1813
+ return sanitizeTemplates(ctx.projectTemplates ?? []);
1814
+ }).handle(ProjectsChannel.OpenCreateDialog, async () => {
1815
+ if (!ctx.customCreateProjectDialog) return null;
1816
+ const sanitized = sanitizeTemplates(ctx.projectTemplates ?? []);
1817
+ return await ctx.customCreateProjectDialog({
1818
+ parentWindow: ctx.mainWindow,
1819
+ templates: sanitized
1820
+ });
1821
+ }).handle(ProjectsChannel.Create, async (_event, ...args) => {
1822
+ const [raw] = args;
1823
+ if (!raw || typeof raw !== "object") {
1824
+ throw new Error("projects:create expects a CreateProjectInput object");
1825
+ }
1826
+ const input = raw;
1827
+ let project;
1828
+ try {
1829
+ project = await createProject(input, {
1830
+ templates: ctx.projectTemplates ?? [],
1831
+ projectsProvider: ctx.projectsProvider
1832
+ });
1833
+ } catch (err) {
1834
+ await dialog.showMessageBox(ctx.mainWindow, {
1835
+ type: "error",
1836
+ title: "\u521B\u5EFA\u9879\u76EE\u5931\u8D25",
1837
+ message: "\u65E0\u6CD5\u521B\u5EFA\u9879\u76EE",
1838
+ detail: err instanceof Error ? err.message : String(err),
1839
+ buttons: ["\u786E\u5B9A"]
1840
+ });
1841
+ throw err;
1842
+ }
1843
+ try {
1844
+ const settings = loadWorkbenchSettings();
1845
+ const newBase = path11.dirname(input.path);
1846
+ if (newBase && settings.lastCreateBaseDir !== newBase) {
1847
+ saveWorkbenchSettings({ ...settings, lastCreateBaseDir: newBase });
1848
+ }
1849
+ } catch (err) {
1850
+ console.warn("[projects:create] failed to persist lastCreateBaseDir", err);
1851
+ }
1852
+ return project;
1853
+ }).handle(ProjectsChannel.GetCreateDefaults, () => {
1854
+ const settings = loadWorkbenchSettings();
1855
+ const baseDir = settings.lastCreateBaseDir ?? safeAppPath("documents") ?? safeAppPath("home") ?? "";
1856
+ return { baseDir };
1634
1857
  });
1635
1858
  }
1859
+ function safeAppPath(name) {
1860
+ try {
1861
+ return app8.getPath(name);
1862
+ } catch {
1863
+ return null;
1864
+ }
1865
+ }
1636
1866
  var projectsModule = {
1637
1867
  setup: (ctx) => registerProjectsIpc(ctx)
1638
1868
  };
@@ -1682,7 +1912,7 @@ var sessionModule = {
1682
1912
  };
1683
1913
 
1684
1914
  // src/main/ipc/settings.ts
1685
- import { app as app8 } from "electron";
1915
+ import { app as app9 } from "electron";
1686
1916
  function registerSettingsIpc(ctx) {
1687
1917
  return new IpcRegistry(ctx.senderPolicy).handle(WorkbenchSettingsChannel.Get, () => {
1688
1918
  return loadWorkbenchSettings();
@@ -1704,8 +1934,8 @@ function registerSettingsIpc(ctx) {
1704
1934
  applyTheme(theme);
1705
1935
  }).handle(WorkbenchSettingsChannel.GetCdpStatus, () => {
1706
1936
  const settings = loadWorkbenchSettings();
1707
- const switchValue = app8.commandLine.getSwitchValue("remote-debugging-port");
1708
- const implicitDevDefault = !app8.isPackaged && !settings.cdp.enabled && switchValue === "9222";
1937
+ const switchValue = app9.commandLine.getSwitchValue("remote-debugging-port");
1938
+ const implicitDevDefault = !app9.isPackaged && !settings.cdp.enabled && switchValue === "9222";
1709
1939
  return {
1710
1940
  configured: settings.cdp.enabled,
1711
1941
  port: settings.cdp.port,
@@ -1728,26 +1958,25 @@ function registerSettingsIpc(ctx) {
1728
1958
  const [visible] = validate(SettingsChannel.SetVisible, SettingsSetVisibleSchema, args);
1729
1959
  if (visible) {
1730
1960
  await ctx.views.showSettings();
1961
+ const projectPath = ctx.workspace.getProjectPath();
1731
1962
  ctx.notify.settingsInit({
1732
- projectPath: ctx.workspace.getProjectPath(),
1733
- config: ctx.workspace.getCompileConfig(ctx.workspace.getProjectPath()),
1734
- projectSettings: ctx.workspace.getProjectSettings(ctx.workspace.getProjectPath())
1963
+ projectPath,
1964
+ config: await ctx.workspace.getCompileConfig(projectPath),
1965
+ projectSettings: ctx.workspace.getProjectSettings(projectPath)
1735
1966
  });
1736
1967
  } else {
1737
1968
  ctx.views.hideSettings();
1738
1969
  ctx.notify.settingsClosed();
1739
1970
  }
1740
- }).on(SettingsChannel.ConfigChanged, (_, ...args) => {
1971
+ }).on(SettingsChannel.ConfigChanged, async (_, ...args) => {
1741
1972
  const [config] = validate(
1742
1973
  SettingsChannel.ConfigChanged,
1743
1974
  SettingsConfigChangedSchema,
1744
1975
  args
1745
1976
  );
1746
- if (ctx.workspace.getProjectPath()) {
1747
- ctx.workspace.saveCompileConfig(
1748
- ctx.workspace.getProjectPath(),
1749
- config
1750
- );
1977
+ const projectPath = ctx.workspace.getProjectPath();
1978
+ if (projectPath) {
1979
+ await ctx.workspace.saveCompileConfig(projectPath, config);
1751
1980
  }
1752
1981
  ctx.notify.settingsChanged(config);
1753
1982
  }).on(SettingsChannel.ProjectSettingsChanged, (_, ...args) => {
@@ -2959,7 +3188,7 @@ function startMcpServer(resolvedCdpPort, mcpPort) {
2959
3188
  }
2960
3189
 
2961
3190
  // src/main/services/simulator-storage/index.ts
2962
- import { app as app9, webContents as wcStatic } from "electron";
3191
+ import { app as app10, webContents as wcStatic } from "electron";
2963
3192
  function isSimulatorWebview(wc) {
2964
3193
  if (wc.isDestroyed()) return false;
2965
3194
  if (wc.getType() !== "webview") return false;
@@ -3209,9 +3438,9 @@ function setupSimulatorStorage(host, options) {
3209
3438
  wcSubs.delete(wc);
3210
3439
  });
3211
3440
  };
3212
- app9.on("web-contents-created", onWcCreated);
3441
+ app10.on("web-contents-created", onWcCreated);
3213
3442
  registry.add(() => {
3214
- app9.removeListener("web-contents-created", onWcCreated);
3443
+ app10.removeListener("web-contents-created", onWcCreated);
3215
3444
  });
3216
3445
  registry.add(async () => {
3217
3446
  const subs = Array.from(wcSubs.values());
@@ -3272,7 +3501,7 @@ async function clearElementInspection() {
3272
3501
  }
3273
3502
 
3274
3503
  // src/main/services/update/update-manager.ts
3275
- import { app as app10, shell as shell3 } from "electron";
3504
+ import { app as app11, shell as shell3 } from "electron";
3276
3505
  var UpdateManager = class {
3277
3506
  checker;
3278
3507
  mainWindow;
@@ -3285,7 +3514,7 @@ var UpdateManager = class {
3285
3514
  constructor(opts) {
3286
3515
  this.checker = opts.checker;
3287
3516
  this.mainWindow = opts.mainWindow;
3288
- this.getCurrentVersion = opts.getCurrentVersion ?? (() => app10.getVersion());
3517
+ this.getCurrentVersion = opts.getCurrentVersion ?? (() => app11.getVersion());
3289
3518
  this.ipc = new IpcRegistry(opts.senderPolicy);
3290
3519
  this.registerIpc();
3291
3520
  this.startPeriodicCheck(opts.checkInterval ?? 60 * 60 * 1e3, opts.initialDelay ?? 5e3);
@@ -3326,7 +3555,7 @@ var UpdateManager = class {
3326
3555
  install() {
3327
3556
  if (this.downloadedPath) {
3328
3557
  shell3.openPath(this.downloadedPath);
3329
- app10.quit();
3558
+ app11.quit();
3330
3559
  }
3331
3560
  }
3332
3561
  /**
@@ -3359,7 +3588,7 @@ var UpdateManager = class {
3359
3588
  };
3360
3589
 
3361
3590
  // src/main/services/update/github-release-checker.ts
3362
- import { app as app11 } from "electron";
3591
+ import { app as app12 } from "electron";
3363
3592
 
3364
3593
  // src/main/app/app.ts
3365
3594
  var DEFAULT_MODULES = {
@@ -3408,7 +3637,7 @@ async function disposeContext(ctx) {
3408
3637
  function createConfiguredMainWindow(config, rendererDir2) {
3409
3638
  const mainWindow = createMainWindow({
3410
3639
  title: config.appName ?? "Dimina DevTools",
3411
- indexHtml: path9.join(rendererDir2, "entries/main/index.html"),
3640
+ indexHtml: path12.join(rendererDir2, "entries/main/index.html"),
3412
3641
  width: config.window?.width,
3413
3642
  height: config.window?.height,
3414
3643
  minWidth: config.window?.minWidth,
@@ -3435,7 +3664,15 @@ function createContext(config, mainWindow, rendererDir2) {
3435
3664
  appName: config.appName,
3436
3665
  apiNamespaces: config.apiNamespaces,
3437
3666
  brandingProvider: config.brandingProvider,
3438
- toolbarActions: config.toolbarActions
3667
+ toolbarActions: config.toolbarActions,
3668
+ // The host-supplied ProjectsProvider / template types in `shared/types`
3669
+ // are structurally compatible with the main-process equivalents —
3670
+ // these casts are safe; we re-narrow at the workspace-service /
3671
+ // create-project-service boundary.
3672
+ projectsProvider: config.projectsProvider,
3673
+ projectTemplates: config.projectTemplates,
3674
+ builtinTemplates: config.builtinTemplates,
3675
+ customCreateProjectDialog: config.customCreateProjectDialog
3439
3676
  });
3440
3677
  }
3441
3678
  function registerBuiltinModules(config, context) {
@@ -3463,7 +3700,7 @@ async function setupAutomation(instance) {
3463
3700
  function setupMcp() {
3464
3701
  const settings = loadWorkbenchSettings();
3465
3702
  if (!settings.mcp.enabled) return null;
3466
- const cdpPortSwitch = app12.commandLine.getSwitchValue("remote-debugging-port");
3703
+ const cdpPortSwitch = app13.commandLine.getSwitchValue("remote-debugging-port");
3467
3704
  const cdpPort2 = cdpPortSwitch ? parseInt(cdpPortSwitch, 10) : settings.cdp.port;
3468
3705
  return startMcpServer(cdpPort2, settings.mcp.port);
3469
3706
  }
@@ -3484,12 +3721,12 @@ function wireAppWindowEvents(config, instance) {
3484
3721
  });
3485
3722
  }
3486
3723
  function enableDevRendererAutoReload(rendererDir2) {
3487
- if (app12.isPackaged) {
3724
+ if (app13.isPackaged) {
3488
3725
  return toDisposable(() => {
3489
3726
  });
3490
3727
  }
3491
3728
  let reloadTimer = null;
3492
- const watcher = fs6.watch(rendererDir2, { recursive: true }, () => {
3729
+ const watcher = fs7.watch(rendererDir2, { recursive: true }, () => {
3493
3730
  if (reloadTimer) clearTimeout(reloadTimer);
3494
3731
  reloadTimer = setTimeout(() => {
3495
3732
  for (const win of BrowserWindow3.getAllWindows()) {
@@ -3511,7 +3748,7 @@ function createWorkbenchApp(config = {}) {
3511
3748
  setupCdpPort();
3512
3749
  async function setup() {
3513
3750
  if (setupPromise) return setupPromise;
3514
- setupPromise = app12.whenReady().then(async () => {
3751
+ setupPromise = app13.whenReady().then(async () => {
3515
3752
  applyTheme(loadWorkbenchSettings().theme);
3516
3753
  const rendererDir2 = config.rendererDir ?? rendererDir;
3517
3754
  const mainWindow = createConfiguredMainWindow(config, rendererDir2);
@@ -3575,7 +3812,7 @@ function createWorkbenchApp(config = {}) {
3575
3812
 
3576
3813
  // src/main/windows/settings-window/create.ts
3577
3814
  import { BrowserWindow as BrowserWindow4 } from "electron";
3578
- import path10 from "path";
3815
+ import path13 from "path";
3579
3816
  async function createSettingsWindow(parent, rendererDir2) {
3580
3817
  const win = new BrowserWindow4({
3581
3818
  width: 420,
@@ -3593,7 +3830,7 @@ async function createSettingsWindow(parent, rendererDir2) {
3593
3830
  }
3594
3831
  });
3595
3832
  applyNavigationHardening(win.webContents, rendererDir2);
3596
- await win.loadFile(path10.join(rendererDir2, "entries/workbench-settings/index.html"));
3833
+ await win.loadFile(path13.join(rendererDir2, "entries/workbench-settings/index.html"));
3597
3834
  return win;
3598
3835
  }
3599
3836
 
@@ -1,6 +1,8 @@
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
- export declare function registerProjectsIpc(ctx: Pick<WorkbenchContext, 'workspace' | 'mainWindow' | 'senderPolicy'>): Disposable;
4
+ type ProjectsIpcCtx = Pick<WorkbenchContext, 'workspace' | 'mainWindow' | 'senderPolicy' | 'projectsProvider' | 'projectTemplates' | 'customCreateProjectDialog'>;
5
+ export declare function registerProjectsIpc(ctx: ProjectsIpcCtx): Disposable;
5
6
  export declare const projectsModule: WorkbenchModule;
7
+ export {};
6
8
  //# sourceMappingURL=projects.d.ts.map