@dimina-kit/devtools 0.4.0-dev.20260707110006 → 0.4.0-dev.20260711062001

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 (57) hide show
  1. package/dist/main/index.bundle.js +48 -14
  2. package/dist/main/ipc/project-fs.d.ts +32 -3
  3. package/dist/main/ipc/project-fs.js +33 -5
  4. package/dist/main/services/render-inspect/index.d.ts +1 -1
  5. package/dist/main/services/settings/index.d.ts +7 -1
  6. package/dist/main/services/settings/index.js +22 -2
  7. package/dist/main/services/workbench-coi-server.js +9 -2
  8. package/dist/main/services/workspace/workspace-service.js +7 -7
  9. package/dist/preload/instrumentation/wxml.js +1 -1
  10. package/dist/preload/runtime/bridge.d.ts +3 -3
  11. package/dist/preload/runtime/bridge.js +2 -2
  12. package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
  13. package/dist/preload/windows/main.cjs +27 -2
  14. package/dist/preload/windows/main.cjs.map +2 -2
  15. package/dist/preload/windows/simulator.cjs +12 -4
  16. package/dist/preload/windows/simulator.cjs.map +3 -3
  17. package/dist/preload/windows/simulator.js +12 -4
  18. package/dist/render-host/render-inspect.js +189 -88
  19. package/dist/renderer/assets/index-WbvXdhWQ.js +49 -0
  20. package/dist/renderer/assets/{input-CTb_le7F.js → input-COtRON-n.js} +2 -2
  21. package/dist/renderer/assets/{ipc-transport-BPWIV5hA.css → ipc-transport-CNvORIEX.css} +1 -1
  22. package/dist/renderer/assets/{ipc-transport-Ck5Xa8Tu.js → ipc-transport-SuI2rOB_.js} +1 -1
  23. package/dist/renderer/assets/{popover-B1Zu2UQu.js → popover-B73T3z-m.js} +2 -2
  24. package/dist/renderer/assets/{select-ozSk5Pt6.js → select-Bwb8O0Mf.js} +2 -2
  25. package/dist/renderer/assets/{settings-CNwGOI22.js → settings-BispnvJU.js} +2 -2
  26. package/dist/renderer/assets/{settings-api-DMbMp1Eq.js → settings-api-HIBz8BFz.js} +2 -2
  27. package/dist/renderer/assets/workbenchSettings-CtRBr7gS.js +8 -0
  28. package/dist/renderer/entries/main/index.html +6 -6
  29. package/dist/renderer/entries/popover/index.html +5 -5
  30. package/dist/renderer/entries/settings/index.html +5 -5
  31. package/dist/renderer/entries/workbench-settings/index.html +4 -4
  32. package/dist/shared/ipc-channels.d.ts +2 -44
  33. package/dist/shared/ipc-schemas.d.ts +4 -1
  34. package/dist/shared/ipc-schemas.js +4 -1
  35. package/dist/simulator/assets/simulator-A1vGZVBM.js +10 -0
  36. package/dist/simulator/simulator.html +1 -1
  37. package/dist/vscode-workbench/assets/__vite-browser-external-B1y6A_ue.js +1 -0
  38. package/dist/vscode-workbench/assets/{dist-OuMzPA6f.js → dist-UYlh9rWP.js} +3 -3
  39. package/dist/vscode-workbench/assets/fs-core-worker-urls-BGhWkdqX.js +1 -0
  40. package/dist/vscode-workbench/assets/fs-core.worker-Bs1XABE2.js +1 -0
  41. package/dist/vscode-workbench/assets/fs-query.worker-cZ-7Fjch.js +2 -0
  42. package/dist/vscode-workbench/assets/{iconv-lite-umd-a5yQx8hi.js → iconv-lite-umd-P85lbjJb.js} +1 -1
  43. package/dist/vscode-workbench/assets/{index-CVEUw5jw.js → index-nrKFi46w.js} +445 -445
  44. package/dist/vscode-workbench/assets/{jschardet-BYkY0M2Q.js → jschardet-CLQONAKz.js} +1 -1
  45. package/dist/vscode-workbench/index.html +1 -1
  46. package/package.json +5 -4
  47. package/dist/preload/instrumentation/wxml-extract.d.ts +0 -11
  48. package/dist/preload/instrumentation/wxml-extract.js +0 -382
  49. package/dist/preload/shared/sid-registry.d.ts +0 -11
  50. package/dist/preload/shared/sid-registry.js +0 -38
  51. package/dist/renderer/assets/index-BjCKGJDg.js +0 -49
  52. package/dist/renderer/assets/workbenchSettings-Cr4EHo5w.js +0 -8
  53. package/dist/simulator/assets/simulator-DU3-fS3v.js +0 -10
  54. package/dist/vscode-workbench/assets/__vite-browser-external-D6-QnwSb.js +0 -1
  55. package/dist/vscode-workbench/assets/fs-core-worker-urls-CLvv1Q_0.js +0 -1
  56. package/dist/vscode-workbench/assets/fs-core.worker-VoMLk-PY.js +0 -1
  57. package/dist/vscode-workbench/assets/fs-query.worker-fXLkQK9l.js +0 -2
@@ -27,7 +27,10 @@ var DEFAULTS = {
27
27
  port: 7789
28
28
  },
29
29
  compile: {
30
- watch: true
30
+ autoBuild: true
31
+ },
32
+ preview: {
33
+ autoReload: true
31
34
  },
32
35
  theme: "system",
33
36
  lastCreateBaseDir: null
@@ -35,6 +38,9 @@ var DEFAULTS = {
35
38
  function getSettingsFile() {
36
39
  return path.join(app.getPath("userData"), "dimina-workbench-settings.json");
37
40
  }
41
+ function pickBool(value, fallback) {
42
+ return typeof value === "boolean" ? value : fallback;
43
+ }
38
44
  function loadWorkbenchSettings() {
39
45
  try {
40
46
  const data = JSON.parse(fs.readFileSync(getSettingsFile(), "utf-8"));
@@ -49,7 +55,16 @@ function loadWorkbenchSettings() {
49
55
  port: data.mcp?.port ?? DEFAULTS.mcp.port
50
56
  },
51
57
  compile: {
52
- watch: data.compile?.watch ?? DEFAULTS.compile.watch
58
+ // Legacy files persisted a single `compile.watch`; the new schema
59
+ // splits it into `compile.autoBuild` (recompile on save) and
60
+ // `preview.autoReload` (reload the simulator afterwards). Prefer the new
61
+ // key, fall back to the legacy one so an old settings file keeps its
62
+ // "auto-compile" choice; `preview.autoReload` defaults to true (the old
63
+ // always-reload behavior) when the file predates the split.
64
+ autoBuild: pickBool(data.compile?.autoBuild, pickBool(data.compile?.watch, DEFAULTS.compile.autoBuild))
65
+ },
66
+ preview: {
67
+ autoReload: pickBool(data.preview?.autoReload, DEFAULTS.preview.autoReload)
53
68
  },
54
69
  theme,
55
70
  lastCreateBaseDir: typeof data.lastCreateBaseDir === "string" ? data.lastCreateBaseDir : DEFAULTS.lastCreateBaseDir
@@ -59,7 +74,8 @@ function loadWorkbenchSettings() {
59
74
  ...DEFAULTS,
60
75
  cdp: { ...DEFAULTS.cdp },
61
76
  mcp: { ...DEFAULTS.mcp },
62
- compile: { ...DEFAULTS.compile }
77
+ compile: { ...DEFAULTS.compile },
78
+ preview: { ...DEFAULTS.preview }
63
79
  };
64
80
  }
65
81
  }
@@ -502,12 +518,10 @@ var UpdateChannel = {
502
518
  };
503
519
 
504
520
  // src/main/ipc/project-fs.ts
521
+ import { WATCH_IGNORE_DIRS } from "@dimina-kit/devkit/watch-ignore";
505
522
  var LIST_FILE_LIMIT = 5e3;
506
523
  var SKIP_DIRS = /* @__PURE__ */ new Set([
507
- "node_modules",
508
- ".git",
509
- ".svn",
510
- ".hg",
524
+ ...WATCH_IGNORE_DIRS,
511
525
  "dist",
512
526
  "build",
513
527
  ".next",
@@ -665,7 +679,18 @@ async function statWithin(root, rel) {
665
679
  }
666
680
  async function readdirWithin(root, rel) {
667
681
  const safe = await resolveWithinProjectRoot(joinRel(root, rel), root);
668
- return fs2.readdir(safe, { withFileTypes: true });
682
+ const dirents = await fs2.readdir(safe, { withFileTypes: true });
683
+ return Promise.all(
684
+ dirents.map(async (d) => {
685
+ if (d.isDirectory()) return { name: d.name, isDirectory: true };
686
+ try {
687
+ const st = await fs2.stat(path2.join(safe, d.name));
688
+ return { name: d.name, isDirectory: false, size: st.size, mtimeMs: st.mtimeMs };
689
+ } catch {
690
+ return { name: d.name, isDirectory: false };
691
+ }
692
+ })
693
+ );
669
694
  }
670
695
  async function readFileBufferWithin(root, rel) {
671
696
  return readFileBuffer(root, joinRel(root, rel));
@@ -4095,16 +4120,18 @@ function createWorkspaceService(ctx) {
4095
4120
  }
4096
4121
  }
4097
4122
  async function runCompile(projectPath, sessionGeneration) {
4098
- const { compile } = loadWorkbenchSettings();
4123
+ const { compile, preview } = loadWorkbenchSettings();
4099
4124
  try {
4100
4125
  const session5 = await ctx.adapter.openProject({
4101
4126
  projectPath,
4102
4127
  sourcemap: true,
4103
4128
  fileTypes: ctx.fileTypes,
4104
- watch: compile.watch,
4129
+ // Two independent gates: autoBuild = recompile on save; autoReload = refresh the simulator afterwards (off ⇒ page/form state survives; hotReload below stays false so the native simulator, which has no SSE reload, isn't refreshed).
4130
+ watch: compile.autoBuild,
4131
+ autoReload: preview.autoReload,
4105
4132
  onRebuild: () => {
4106
4133
  const { pages } = getProjectPages(projectPath);
4107
- sendStatus("ready", "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u91CD\u542F", true, pages.length ? pages : void 0);
4134
+ sendStatus("ready", preview.autoReload ? "\u7F16\u8BD1\u5B8C\u6210\uFF0C\u5DF2\u91CD\u542F" : "\u7F16\u8BD1\u5B8C\u6210", preview.autoReload, pages.length ? pages : void 0);
4108
4135
  },
4109
4136
  onBuildError: (err2) => sendStatus("error", String(err2)),
4110
4137
  // Watcher died mid-session (EMFILE, permission loss, …): non-fatal, so 'ready' stays but `watcher: 'dead'` flags that saves no longer auto-rebuild.
@@ -4631,7 +4658,10 @@ var WorkbenchSettingsSaveSchema = z3.tuple([
4631
4658
  port: z3.number().int().min(0).max(65535)
4632
4659
  }),
4633
4660
  compile: z3.looseObject({
4634
- watch: z3.boolean()
4661
+ autoBuild: z3.boolean()
4662
+ }),
4663
+ preview: z3.looseObject({
4664
+ autoReload: z3.boolean()
4635
4665
  }),
4636
4666
  theme: z3.enum(["system", "light", "dark"]),
4637
4667
  lastCreateBaseDir: z3.union([z3.string(), z3.null()])
@@ -11044,8 +11074,12 @@ async function fsStat(ctx) {
11044
11074
  }
11045
11075
  async function fsReaddir(ctx) {
11046
11076
  const entries = await readdirWithin(ctx.projectRoot, ctx.rel);
11047
- const visible = entries.filter((e) => !(e.isDirectory() && SKIP_DIRS.has(e.name)));
11048
- jsonRes(ctx.res, 200, visible.map((e) => [e.name, e.isDirectory() ? 2 : 1]));
11077
+ const visible = entries.filter((e) => !(e.isDirectory && SKIP_DIRS.has(e.name)));
11078
+ jsonRes(
11079
+ ctx.res,
11080
+ 200,
11081
+ visible.map((e) => e.isDirectory ? [e.name, 2] : [e.name, 1, e.size, e.mtimeMs])
11082
+ );
11049
11083
  }
11050
11084
  async function fsRead(ctx) {
11051
11085
  const buf = await readFileBufferWithin(ctx.projectRoot, ctx.rel);
@@ -2,7 +2,13 @@ import type { WorkbenchContext } from '../services/workbench-context.js';
2
2
  import type { Disposable } from '@dimina-kit/electron-deck/main';
3
3
  /** Dependency/VCS/build directories invisible to every project-tree consumer:
4
4
  * IPC `listFiles`, the `/__fs/readdir` bridge (so the workbench mirror and the
5
- * WAL ledger seed never walk into them), and the `/__fs/watch` change stream. */
5
+ * WAL ledger seed never walk into them), and the `/__fs/watch` change stream.
6
+ * Derived from devkit's `WATCH_IGNORE_DIRS` (the shared never-source core:
7
+ * node_modules + VCS) so the `node_modules` omission that once wedged
8
+ * `watcher.close()` can never drift back on this side, PLUS the editor mirror's
9
+ * own build-output names (`dist`/`build`/tool caches) — which the devkit
10
+ * recompile watcher deliberately does NOT ignore, since app.json may declare
11
+ * pages under such paths. */
6
12
  export declare const SKIP_DIRS: ReadonlySet<string>;
7
13
  /**
8
14
  * Synchronous, string-level guard: reject empty root (ENOACTIVE), NUL bytes
@@ -79,8 +85,31 @@ export declare function writeFile(root: string, p: string, content: string | Buf
79
85
  * out-of-sandbox metadata. `rel` is a project-root-relative bridge path.
80
86
  */
81
87
  export declare function statWithin(root: string, rel: string): Promise<import('node:fs').Stats>;
82
- /** Realpath-contained `fs.readdir` twin (see {@link statWithin}). */
83
- export declare function readdirWithin(root: string, rel: string): Promise<import('node:fs').Dirent[]>;
88
+ /**
89
+ * One `readdirWithin` result entry. Directories carry no stat (the caller —
90
+ * the `/__fs/readdir` bridge handler — never needs it: a directory recurses
91
+ * instead of being compared). `size`/`mtimeMs` are omitted (not `null`) when
92
+ * the entry raced a concurrent delete between `readdir` and `stat` — the
93
+ * bridge wire format then drops them too, which the sync engine's watch
94
+ * expansion (dimina-kit workbench's wal-audit-watch-expand.ts) treats as
95
+ * "always changed" rather than risking a false stat match.
96
+ */
97
+ export interface ReaddirStatEntry {
98
+ name: string;
99
+ isDirectory: boolean;
100
+ size?: number;
101
+ mtimeMs?: number;
102
+ }
103
+ /**
104
+ * Realpath-contained `fs.readdir` twin (see {@link statWithin}) that also
105
+ * `fs.stat`s every FILE entry (size + mtimeMs) — the disk-side half of the
106
+ * sync engine's stat-diffing (see wal-audit-watch-expand.ts's module doc):
107
+ * a watch-reported directory is only worth a full content re-read when a
108
+ * file's stat inside it actually moved, and the `/__fs/readdir` bridge is
109
+ * the one HTTP round trip cheap enough to do that check on every watch
110
+ * event without itself re-reading file bytes.
111
+ */
112
+ export declare function readdirWithin(root: string, rel: string): Promise<ReaddirStatEntry[]>;
84
113
  /**
85
114
  * Raw-byte read twin of {@link readFileBuffer} taking a project-root-relative
86
115
  * bridge path (the COI `/__fs/read` bridge), with the same realpath +
@@ -47,14 +47,21 @@ import { z } from 'zod';
47
47
  import { IpcRegistry } from '../utils/ipc-registry.js';
48
48
  import { validate } from '../utils/ipc-schema.js';
49
49
  import { ProjectFsChannel } from '../../shared/ipc-channels.js';
50
+ import { WATCH_IGNORE_DIRS } from '@dimina-kit/devkit/watch-ignore';
50
51
  /** Cap for `listFiles` — huge monorepos would otherwise stall the renderer. */
51
52
  const LIST_FILE_LIMIT = 5000;
52
53
  /** Dependency/VCS/build directories invisible to every project-tree consumer:
53
54
  * IPC `listFiles`, the `/__fs/readdir` bridge (so the workbench mirror and the
54
- * WAL ledger seed never walk into them), and the `/__fs/watch` change stream. */
55
+ * WAL ledger seed never walk into them), and the `/__fs/watch` change stream.
56
+ * Derived from devkit's `WATCH_IGNORE_DIRS` (the shared never-source core:
57
+ * node_modules + VCS) so the `node_modules` omission that once wedged
58
+ * `watcher.close()` can never drift back on this side, PLUS the editor mirror's
59
+ * own build-output names (`dist`/`build`/tool caches) — which the devkit
60
+ * recompile watcher deliberately does NOT ignore, since app.json may declare
61
+ * pages under such paths. */
55
62
  export const SKIP_DIRS = new Set([
56
- 'node_modules', '.git', '.svn', '.hg', 'dist', 'build',
57
- '.next', '.cache', '.turbo', '.parcel-cache', '.nuxt', '.output', '.vite',
63
+ ...WATCH_IGNORE_DIRS,
64
+ 'dist', 'build', '.next', '.cache', '.turbo', '.parcel-cache', '.nuxt', '.output', '.vite',
58
65
  ]);
59
66
  const PathArg = z.string().min(1);
60
67
  const ReadFileSchema = z.tuple([PathArg]);
@@ -319,10 +326,31 @@ export async function statWithin(root, rel) {
319
326
  const safe = await resolveWithinProjectRoot(joinRel(root, rel), root);
320
327
  return fs.stat(safe);
321
328
  }
322
- /** Realpath-contained `fs.readdir` twin (see {@link statWithin}). */
329
+ /**
330
+ * Realpath-contained `fs.readdir` twin (see {@link statWithin}) that also
331
+ * `fs.stat`s every FILE entry (size + mtimeMs) — the disk-side half of the
332
+ * sync engine's stat-diffing (see wal-audit-watch-expand.ts's module doc):
333
+ * a watch-reported directory is only worth a full content re-read when a
334
+ * file's stat inside it actually moved, and the `/__fs/readdir` bridge is
335
+ * the one HTTP round trip cheap enough to do that check on every watch
336
+ * event without itself re-reading file bytes.
337
+ */
323
338
  export async function readdirWithin(root, rel) {
324
339
  const safe = await resolveWithinProjectRoot(joinRel(root, rel), root);
325
- return fs.readdir(safe, { withFileTypes: true });
340
+ const dirents = await fs.readdir(safe, { withFileTypes: true });
341
+ return Promise.all(dirents.map(async (d) => {
342
+ if (d.isDirectory())
343
+ return { name: d.name, isDirectory: true };
344
+ try {
345
+ const st = await fs.stat(path.join(safe, d.name));
346
+ return { name: d.name, isDirectory: false, size: st.size, mtimeMs: st.mtimeMs };
347
+ }
348
+ catch {
349
+ // Entry vanished (or became unreadable) between readdir and stat —
350
+ // report it stat-less; see this function's doc for why that is safe.
351
+ return { name: d.name, isDirectory: false };
352
+ }
353
+ }));
326
354
  }
327
355
  /**
328
356
  * Raw-byte read twin of {@link readFileBuffer} taking a project-root-relative
@@ -16,7 +16,7 @@
16
16
  import type { WebContents } from 'electron';
17
17
  import type { ConnectionRegistry } from '@dimina-kit/electron-deck/main';
18
18
  import type { ElementInspection } from '../../../shared/ipc-channels.js';
19
- import type { WxmlNode } from '../../../preload/shared/sid-registry.js';
19
+ import type { WxmlNode } from '@dimina-kit/inspect';
20
20
  export interface RenderInspector {
21
21
  /** Inject (once per wc) the inspector IIFE, then read the WXML tree. */
22
22
  getWxml(wc: WebContents): Promise<WxmlNode | null>;
@@ -10,7 +10,13 @@ export interface WorkbenchSettings {
10
10
  };
11
11
  compile: {
12
12
  /** Watch project files and auto-recompile on change. */
13
- watch: boolean;
13
+ autoBuild: boolean;
14
+ };
15
+ preview: {
16
+ /** Reload the simulator once a watcher-triggered rebuild lands. Independent
17
+ * of `compile.autoBuild`: "auto-compile on save, reload manually" is valid
18
+ * and preserves the running page stack / form state across a save. */
19
+ autoReload: boolean;
14
20
  };
15
21
  theme: ThemeSource;
16
22
  /**
@@ -12,7 +12,10 @@ const DEFAULTS = {
12
12
  port: 7789,
13
13
  },
14
14
  compile: {
15
- watch: true,
15
+ autoBuild: true,
16
+ },
17
+ preview: {
18
+ autoReload: true,
16
19
  },
17
20
  theme: 'system',
18
21
  lastCreateBaseDir: null,
@@ -20,6 +23,13 @@ const DEFAULTS = {
20
23
  function getSettingsFile() {
21
24
  return path.join(app.getPath('userData'), 'dimina-workbench-settings.json');
22
25
  }
26
+ /** Accept `value` only when it is a real boolean, else `fallback`. A bare `??`
27
+ * chain guards only null/undefined, so a hand-edited or corrupt config with a
28
+ * stringy `"false"` would pass through and read as truthy downstream — silently
29
+ * keeping a toggle ON when the user meant OFF. */
30
+ function pickBool(value, fallback) {
31
+ return typeof value === 'boolean' ? value : fallback;
32
+ }
23
33
  export function loadWorkbenchSettings() {
24
34
  try {
25
35
  const data = JSON.parse(fs.readFileSync(getSettingsFile(), 'utf-8'));
@@ -36,7 +46,16 @@ export function loadWorkbenchSettings() {
36
46
  port: data.mcp?.port ?? DEFAULTS.mcp.port,
37
47
  },
38
48
  compile: {
39
- watch: data.compile?.watch ?? DEFAULTS.compile.watch,
49
+ // Legacy files persisted a single `compile.watch`; the new schema
50
+ // splits it into `compile.autoBuild` (recompile on save) and
51
+ // `preview.autoReload` (reload the simulator afterwards). Prefer the new
52
+ // key, fall back to the legacy one so an old settings file keeps its
53
+ // "auto-compile" choice; `preview.autoReload` defaults to true (the old
54
+ // always-reload behavior) when the file predates the split.
55
+ autoBuild: pickBool(data.compile?.autoBuild, pickBool(data.compile?.watch, DEFAULTS.compile.autoBuild)),
56
+ },
57
+ preview: {
58
+ autoReload: pickBool(data.preview?.autoReload, DEFAULTS.preview.autoReload),
40
59
  },
41
60
  theme,
42
61
  lastCreateBaseDir: typeof data.lastCreateBaseDir === 'string'
@@ -50,6 +69,7 @@ export function loadWorkbenchSettings() {
50
69
  cdp: { ...DEFAULTS.cdp },
51
70
  mcp: { ...DEFAULTS.mcp },
52
71
  compile: { ...DEFAULTS.compile },
72
+ preview: { ...DEFAULTS.preview },
53
73
  };
54
74
  }
55
75
  }
@@ -206,8 +206,15 @@ async function fsReaddir(ctx) {
206
206
  // every level: the workbench mirror and the WAL ledger seed walk whatever
207
207
  // this returns, so listing them would pull entire dependency trees into the
208
208
  // editor memfs and the OPFS ledger. Same-named FILES stay visible.
209
- const visible = entries.filter((e) => !(e.isDirectory() && SKIP_DIRS.has(e.name)));
210
- jsonRes(ctx.res, 200, visible.map((e) => [e.name, e.isDirectory() ? 2 : 1]));
209
+ const visible = entries.filter((e) => !(e.isDirectory && SKIP_DIRS.has(e.name)));
210
+ // Wire shape: `[name, type]` for a directory, `[name, type, size, mtimeMs]`
211
+ // for a file — the sync engine's watch-batch stat-diffing (workbench's
212
+ // wal-audit-watch-expand.ts) needs size+mtimeMs to tell a stat-unchanged
213
+ // survivor from an actually-modified file without re-reading its content.
214
+ // `size`/`mtimeMs` are simply absent (not `null`) for a stat-less entry
215
+ // (readdirWithin's readdir/stat race) — the client side then always
216
+ // treats it as changed.
217
+ jsonRes(ctx.res, 200, visible.map((e) => (e.isDirectory ? [e.name, 2] : [e.name, 1, e.size, e.mtimeMs])));
211
218
  }
212
219
  async function fsRead(ctx) {
213
220
  const buf = await readFileBufferWithin(ctx.projectRoot, ctx.rel);
@@ -120,20 +120,20 @@ export function createWorkspaceService(ctx) {
120
120
  * a superseded session's buffered lines.
121
121
  */
122
122
  async function runCompile(projectPath, sessionGeneration) {
123
- const { compile } = loadWorkbenchSettings();
123
+ const { compile, preview } = loadWorkbenchSettings();
124
124
  try {
125
125
  const session = await ctx.adapter.openProject({
126
126
  projectPath,
127
127
  sourcemap: true,
128
128
  fileTypes: ctx.fileTypes,
129
- watch: compile.watch,
129
+ // Two independent gates: autoBuild = recompile on save; autoReload = refresh the simulator afterwards (off ⇒ page/form state survives; hotReload below stays false so the native simulator, which has no SSE reload, isn't refreshed).
130
+ watch: compile.autoBuild,
131
+ autoReload: preview.autoReload,
130
132
  onRebuild: () => {
131
- // getProjectPages never throws failure yields the empty-pages
132
- // shape. Empty means "read failed or degenerate project" (a valid
133
- // app.json names ≥1 page) and is withheld so the renderer keeps its
134
- // previous dropdown instead of blanking; the push is never blocked.
133
+ // getProjectPages never throws; empty pages (read failed / degenerate
134
+ // project) are withheld so the renderer keeps its previous dropdown.
135
135
  const { pages } = repo.getProjectPages(projectPath);
136
- sendStatus('ready', '编译完成,已重启', true, pages.length ? pages : undefined);
136
+ sendStatus('ready', preview.autoReload ? '编译完成,已重启' : '编译完成', preview.autoReload, pages.length ? pages : undefined);
137
137
  },
138
138
  onBuildError: (err) => sendStatus('error', String(err)),
139
139
  // Watcher died mid-session (EMFILE, permission loss, …): non-fatal, so 'ready' stays but `watcher: 'dead'` flags that saves no longer auto-rebuild.
@@ -1,6 +1,6 @@
1
1
  import { clearWxmlSnapshot, setWxmlSnapshot, unhighlightElement, } from '../runtime/bridge.js';
2
2
  import { WXML_DEBOUNCE_MS, WXML_RETRY_INTERVAL_MS, WXML_RETRY_TIMEOUT_MS, NAVIGATION_POLL_INTERVAL_MS, NAVIGATION_TIMEOUT_MS, } from '../shared/constants.js';
3
- import { walkInstance } from './wxml-extract.js';
3
+ import { walkInstance } from '@dimina-kit/inspect';
4
4
  import { getActivePageIframe } from '../shared/page-iframe.js';
5
5
  function getVueAppFromIframe(iframe) {
6
6
  try {
@@ -1,7 +1,7 @@
1
1
  import type { ElementInspection } from '../../shared/ipc-channels.js';
2
- import { type WxmlNode } from '../shared/sid-registry.js';
3
- export type { WxmlNode } from '../shared/sid-registry.js';
4
- export { registerSyntheticSid } from '../shared/sid-registry.js';
2
+ import { type WxmlNode } from '@dimina-kit/inspect';
3
+ export type { WxmlNode } from '@dimina-kit/inspect';
4
+ export { registerSyntheticSid } from '@dimina-kit/inspect';
5
5
  export interface Snapshot<T> {
6
6
  gen: number;
7
7
  ready: boolean;
@@ -1,7 +1,7 @@
1
1
  import { exposeOnMainWorld } from '../shared/expose.js';
2
2
  import { getActivePageIframe } from '../shared/page-iframe.js';
3
- import { findElementBySid } from '../shared/sid-registry.js';
4
- export { registerSyntheticSid } from '../shared/sid-registry.js';
3
+ import { findElementBySid } from '@dimina-kit/inspect';
4
+ export { registerSyntheticSid } from '@dimina-kit/inspect';
5
5
  const state = {
6
6
  appdata: { gen: 0, ready: true, data: {} },
7
7
  storage: { gen: 0, ready: false, namespace: null, data: {} },