@dimina-kit/devtools 0.3.2-dev.20260525152421 → 0.3.2-dev.20260601115343
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -7
- package/dist/main/api.d.ts +1 -1
- package/dist/main/app/app.js +17 -0
- package/dist/main/index.bundle.js +1006 -584
- package/dist/main/ipc/project-fs.d.ts +57 -0
- package/dist/main/ipc/project-fs.js +448 -0
- package/dist/main/ipc/simulator-module.js +2 -0
- package/dist/main/ipc/views.d.ts +19 -0
- package/dist/main/ipc/views.js +27 -0
- package/dist/main/services/layout/index.d.ts +1 -1
- package/dist/main/services/layout/index.js +3 -3
- package/dist/main/services/simulator-temp-files/fs-channels.js +1 -1
- package/dist/main/services/simulator-temp-files/request-handler.js +1 -1
- package/dist/main/services/toolbar/toolbar-store.d.ts +3 -3
- package/dist/main/services/toolbar/toolbar-store.js +18 -1
- package/dist/main/services/update/update-manager.d.ts +10 -0
- package/dist/main/services/update/update-manager.js +16 -3
- package/dist/main/services/views/view-manager.d.ts +17 -4
- package/dist/main/services/views/view-manager.js +71 -14
- package/dist/main/services/workbench-context.d.ts +6 -1
- package/dist/main/services/workspace/workspace-service.d.ts +8 -0
- package/dist/main/services/workspace/workspace-service.js +22 -4
- package/dist/main/utils/ipc-registry.d.ts +10 -1
- package/dist/main/utils/ipc-registry.js +43 -0
- package/dist/preload/shared/expose.js +2 -2
- package/dist/preload/windows/main.cjs +4 -0
- package/dist/preload/windows/main.cjs.map +2 -2
- package/dist/preload/windows/simulator.js +2 -2
- package/dist/renderer/assets/abap-DiwvWnMr.js +2 -0
- package/dist/renderer/assets/apex-CmtZjKlf.js +2 -0
- package/dist/renderer/assets/azcli-DL2My_i-.js +2 -0
- package/dist/renderer/assets/bat-B-nC98wG.js +2 -0
- package/dist/renderer/assets/bicep-Ju5MwOgh.js +3 -0
- package/dist/renderer/assets/cameligo-8Eu1TyBr.js +2 -0
- package/dist/renderer/assets/chunk-DPI0Y_ll.js +1 -0
- package/dist/renderer/assets/clojure-u-RpMkH3.js +2 -0
- package/dist/renderer/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/dist/renderer/assets/coffee-CdA7bbTe.js +2 -0
- package/dist/renderer/assets/constants-CLAQX6VZ.js +2 -0
- package/dist/renderer/assets/cpp-CzNFP8ks.js +2 -0
- package/dist/renderer/assets/csharp-j1LThmcE.js +2 -0
- package/dist/renderer/assets/csp-CLRC61y6.js +2 -0
- package/dist/renderer/assets/css-r6rC_7P2.js +4 -0
- package/dist/renderer/assets/css.worker-DyDu5ynT.js +90 -0
- package/dist/renderer/assets/cssMode-BCBgxrnA.js +2 -0
- package/dist/renderer/assets/cypher-CW08XVUh.js +2 -0
- package/dist/renderer/assets/dart-Cs9aL5T_.js +2 -0
- package/dist/renderer/assets/dockerfile-BWM0M184.js +2 -0
- package/dist/renderer/assets/ecl-MJJuer5P.js +2 -0
- package/dist/renderer/assets/editor-CWI9TnMM.css +1 -0
- package/dist/renderer/assets/editor.api2-BTaYmRq7.js +873 -0
- package/dist/renderer/assets/editor.worker-Dpt_xPrs.js +27 -0
- package/dist/renderer/assets/elixir-D2AIuXqn.js +2 -0
- package/dist/renderer/assets/flow9-B2H24giC.js +2 -0
- package/dist/renderer/assets/freemarker2-DGDGzimW.js +4 -0
- package/dist/renderer/assets/fsharp-CMk2OIJN.js +2 -0
- package/dist/renderer/assets/go-BrMkuJg0.js +2 -0
- package/dist/renderer/assets/graphql-PSR1UKGv.js +2 -0
- package/dist/renderer/assets/handlebars-bOHhw3EG.js +2 -0
- package/dist/renderer/assets/hcl-DAQrbDOW.js +2 -0
- package/dist/renderer/assets/html-BMyuX0_c.js +2 -0
- package/dist/renderer/assets/html.worker-IWNXhnIC.js +503 -0
- package/dist/renderer/assets/htmlMode-DR4kXJ7l.js +2 -0
- package/dist/renderer/assets/index-BDMxXUfn.js +50 -0
- package/dist/renderer/assets/ini-0TG5BxW0.js +2 -0
- package/dist/renderer/assets/input-CCYS8Ave.js +2 -0
- package/dist/renderer/assets/{ipc-transport-BzPzxPwf.js → ipc-transport-Bn4KXvhu.js} +5 -5
- package/dist/renderer/assets/ipc-transport-Cu7tubyg.css +1 -0
- package/dist/renderer/assets/java-rgorz17v.js +2 -0
- package/dist/renderer/assets/javascript-Bu-tl1P7.js +2 -0
- package/dist/renderer/assets/json.worker-c4h5s80L.js +59 -0
- package/dist/renderer/assets/jsonMode-MQV_ffLR.js +8 -0
- package/dist/renderer/assets/julia-C8VMdHm8.js +2 -0
- package/dist/renderer/assets/kotlin-AhhC4Qhm.js +2 -0
- package/dist/renderer/assets/less-Cgca25AP.js +3 -0
- package/dist/renderer/assets/lexon-D0GHdBaw.js +2 -0
- package/dist/renderer/assets/liquid-ClkfOvhB.js +2 -0
- package/dist/renderer/assets/lspLanguageFeatures-Bke_4Ie9.js +5 -0
- package/dist/renderer/assets/lua-DmRsNG-P.js +2 -0
- package/dist/renderer/assets/m3-BgL5dNKT.js +2 -0
- package/dist/renderer/assets/markdown-BuJfycGS.js +2 -0
- package/dist/renderer/assets/mdx-RZdUskPu.js +2 -0
- package/dist/renderer/assets/mips-C9m_93PR.js +2 -0
- package/dist/renderer/assets/monaco.contribution-B7rUMaEv.js +3 -0
- package/dist/renderer/assets/msdax-CpFHC9OI.js +2 -0
- package/dist/renderer/assets/mysql-qFvltsqN.js +2 -0
- package/dist/renderer/assets/objective-c-Bnmr858J.js +2 -0
- package/dist/renderer/assets/pascal-WP0_D5AO.js +2 -0
- package/dist/renderer/assets/pascaligo-Blom4Rij.js +2 -0
- package/dist/renderer/assets/perl-B-vk8g64.js +2 -0
- package/dist/renderer/assets/pgsql-Cgvz6v67.js +2 -0
- package/dist/renderer/assets/php-8a3Lrw9m.js +2 -0
- package/dist/renderer/assets/pla-DuFqEZ8V.js +2 -0
- package/dist/renderer/assets/popover-CH4rwpdt.js +2 -0
- package/dist/renderer/assets/postiats-DkLtSgkp.js +2 -0
- package/dist/renderer/assets/powerquery-BJ1aNepW.js +2 -0
- package/dist/renderer/assets/powershell-rE98k687.js +2 -0
- package/dist/renderer/assets/protobuf-CUheFacr.js +3 -0
- package/dist/renderer/assets/pug-LDcAMD8w.js +2 -0
- package/dist/renderer/assets/python-Cobrm_-8.js +2 -0
- package/dist/renderer/assets/qsharp-DUKSQoR1.js +2 -0
- package/dist/renderer/assets/r-D-QApv87.js +2 -0
- package/dist/renderer/assets/razor-BEUIQFNJ.js +2 -0
- package/dist/renderer/assets/redis-SXdDyWR9.js +2 -0
- package/dist/renderer/assets/redshift-Y6lsCryn.js +2 -0
- package/dist/renderer/assets/restructuredtext-edObr9a8.js +2 -0
- package/dist/renderer/assets/ruby-CNnUfF-8.js +2 -0
- package/dist/renderer/assets/rust-IHUZWzBr.js +2 -0
- package/dist/renderer/assets/sb-DrUvY44N.js +2 -0
- package/dist/renderer/assets/scala-B4hbXGLM.js +2 -0
- package/dist/renderer/assets/scheme-BGrd12j3.js +2 -0
- package/dist/renderer/assets/scss-x5G1ES4U.js +4 -0
- package/dist/renderer/assets/select-Ct4-sijm.js +2 -0
- package/dist/renderer/assets/settings-CMTB-t0x.js +2 -0
- package/dist/renderer/assets/{settings-api-BXCw9vOZ.js → settings-api-Csdngf9Z.js} +2 -2
- package/dist/renderer/assets/shell-DOehe2Y8.js +2 -0
- package/dist/renderer/assets/solidity-BeRvcwWV.js +2 -0
- package/dist/renderer/assets/sophia-DZbkUNjy.js +2 -0
- package/dist/renderer/assets/sparql-B7_oi5-h.js +2 -0
- package/dist/renderer/assets/sql-CTlsFWVE.js +2 -0
- package/dist/renderer/assets/st-DJVEJdPE.js +2 -0
- package/dist/renderer/assets/swift-CwhT3fYa.js +2 -0
- package/dist/renderer/assets/systemverilog-BQN63pkN.js +2 -0
- package/dist/renderer/assets/tcl-DqwfpskA.js +2 -0
- package/dist/renderer/assets/ts.worker-BwM5Ha3L.js +67720 -0
- package/dist/renderer/assets/tsMode-BHa7dl9v.js +12 -0
- package/dist/renderer/assets/twig-BiyenUgc.js +2 -0
- package/dist/renderer/assets/typescript-15aT0Yj8.js +2 -0
- package/dist/renderer/assets/typespec-CWOJribt.js +2 -0
- package/dist/renderer/assets/vb-Cq5F87m3.js +2 -0
- package/dist/renderer/assets/wgsl-BAvW2lVr.js +299 -0
- package/dist/renderer/assets/workbenchSettings-CODFCzOg.js +8 -0
- package/dist/renderer/assets/workers-CRBS0W-v.js +2 -0
- package/dist/renderer/assets/xml-bR2Zh-HV.js +2 -0
- package/dist/renderer/assets/yaml-Bdk54Qrb.js +2 -0
- package/dist/renderer/entries/main/index.html +29 -6
- package/dist/renderer/entries/popover/index.html +7 -6
- package/dist/renderer/entries/settings/index.html +6 -5
- package/dist/renderer/entries/workbench-settings/index.html +6 -5
- package/dist/shared/constants.d.ts +0 -4
- package/dist/shared/constants.js +0 -4
- package/dist/shared/ipc-channels.d.ts +24 -0
- package/dist/shared/ipc-channels.js +37 -0
- package/dist/shared/ipc-schemas.d.ts +6 -0
- package/dist/shared/ipc-schemas.js +15 -0
- package/dist/shared/types.d.ts +37 -0
- package/dist/shared/vpath.d.ts +42 -0
- package/dist/shared/vpath.js +105 -0
- package/dist/simulator/assets/simulator-B4e3c6_O.js +9 -0
- package/dist/simulator/simulator.html +1 -1
- package/package.json +3 -2
- package/dist/renderer/assets/constants-DI382mTP.js +0 -2
- package/dist/renderer/assets/index-4lkyF644.js +0 -46
- package/dist/renderer/assets/input-D4byFOLS.js +0 -2
- package/dist/renderer/assets/ipc-transport-DqbRdZNO.css +0 -1
- package/dist/renderer/assets/popover-cxKrg_os.js +0 -2
- package/dist/renderer/assets/select-BgSkkdHW.js +0 -2
- package/dist/renderer/assets/settings-oD2K1jdT.js +0 -2
- package/dist/renderer/assets/workbenchSettings-CTNBpTft.js +0 -8
- package/dist/simulator/assets/simulator-CVOFTpSj.js +0 -9
|
@@ -96,6 +96,20 @@ export declare const ProjectChannel: {
|
|
|
96
96
|
readonly CaptureThumbnail: "project:captureThumbnail";
|
|
97
97
|
readonly GetThumbnail: "project:getThumbnail";
|
|
98
98
|
};
|
|
99
|
+
export declare const ProjectFsChannel: {
|
|
100
|
+
readonly GetRoot: "project:fs:getRoot";
|
|
101
|
+
readonly ReadFile: "project:fs:readFile";
|
|
102
|
+
readonly WriteFile: "project:fs:writeFile";
|
|
103
|
+
/**
|
|
104
|
+
* Synchronous (blocking) write, used ONLY by the editor's `beforeunload`
|
|
105
|
+
* flush: a hard window/app close tears the renderer down before an async
|
|
106
|
+
* `WriteFile` IPC can round-trip, so the last in-debounce-window edit would
|
|
107
|
+
* be lost. `sendSync` blocks page teardown until the bytes hit disk. Runs the
|
|
108
|
+
* SAME sandbox as `WriteFile` (see project-fs `writeFileSync`).
|
|
109
|
+
*/
|
|
110
|
+
readonly WriteFileSync: "project:fs:writeFileSync";
|
|
111
|
+
readonly ListFiles: "project:fs:listFiles";
|
|
112
|
+
};
|
|
99
113
|
export declare const ProjectsChannel: {
|
|
100
114
|
readonly List: "projects:list";
|
|
101
115
|
readonly Add: "projects:add";
|
|
@@ -123,6 +137,16 @@ export declare const PanelChannel: {
|
|
|
123
137
|
readonly Select: "panel:select";
|
|
124
138
|
readonly SelectSimulator: "panel:selectSimulator";
|
|
125
139
|
};
|
|
140
|
+
export declare const ViewChannel: {
|
|
141
|
+
/** Update the simulator DevTools view's bounds (or hide if w/h = 0). */
|
|
142
|
+
readonly SimulatorDevtoolsBounds: "view:simulator:devtools-bounds";
|
|
143
|
+
};
|
|
144
|
+
export interface ViewBounds {
|
|
145
|
+
x: number;
|
|
146
|
+
y: number;
|
|
147
|
+
width: number;
|
|
148
|
+
height: number;
|
|
149
|
+
}
|
|
126
150
|
export declare const PopoverChannel: {
|
|
127
151
|
readonly Show: "popover:show";
|
|
128
152
|
readonly Hide: "popover:hide";
|
|
@@ -74,6 +74,27 @@ export const ProjectChannel = {
|
|
|
74
74
|
CaptureThumbnail: 'project:captureThumbnail',
|
|
75
75
|
GetThumbnail: 'project:getThumbnail',
|
|
76
76
|
};
|
|
77
|
+
// ── Project file system (sandboxed to active project root) ────────────────
|
|
78
|
+
//
|
|
79
|
+
// Read/write access to the active project's files, used by the in-renderer
|
|
80
|
+
// Monaco editor. Every path is verified against the active project root in
|
|
81
|
+
// the main process (see `services/project-fs`). Replaces the OpenSumi
|
|
82
|
+
// editor's `editor:fs:*` bridge — same sandbox, callable from the main
|
|
83
|
+
// renderer instead of a separate WebContentsView.
|
|
84
|
+
export const ProjectFsChannel = {
|
|
85
|
+
GetRoot: 'project:fs:getRoot',
|
|
86
|
+
ReadFile: 'project:fs:readFile',
|
|
87
|
+
WriteFile: 'project:fs:writeFile',
|
|
88
|
+
/**
|
|
89
|
+
* Synchronous (blocking) write, used ONLY by the editor's `beforeunload`
|
|
90
|
+
* flush: a hard window/app close tears the renderer down before an async
|
|
91
|
+
* `WriteFile` IPC can round-trip, so the last in-debounce-window edit would
|
|
92
|
+
* be lost. `sendSync` blocks page teardown until the bytes hit disk. Runs the
|
|
93
|
+
* SAME sandbox as `WriteFile` (see project-fs `writeFileSync`).
|
|
94
|
+
*/
|
|
95
|
+
WriteFileSync: 'project:fs:writeFileSync',
|
|
96
|
+
ListFiles: 'project:fs:listFiles',
|
|
97
|
+
};
|
|
77
98
|
// ── Project list / workspace ─────────────────────────────────────────────
|
|
78
99
|
export const ProjectsChannel = {
|
|
79
100
|
List: 'projects:list',
|
|
@@ -99,6 +120,22 @@ export const PanelChannel = {
|
|
|
99
120
|
Select: 'panel:select',
|
|
100
121
|
SelectSimulator: 'panel:selectSimulator',
|
|
101
122
|
};
|
|
123
|
+
// ── Embedded views (renderer → main) ─────────────────────────────────────
|
|
124
|
+
//
|
|
125
|
+
// The main window's React layout owns the *positions* of the editor +
|
|
126
|
+
// simulator-DevTools WebContentsView overlays — each visible placeholder
|
|
127
|
+
// `<div>` measures its client rect via ResizeObserver and pushes the
|
|
128
|
+
// rectangle to the main process. The view manager caches the latest
|
|
129
|
+
// payload per kind and applies it to the overlay; no payload means the
|
|
130
|
+
// overlay is hidden.
|
|
131
|
+
//
|
|
132
|
+
// Payload (after schema validation): `{ x, y, width, height }` in CSS
|
|
133
|
+
// pixels relative to the window's content area (origin = top-left,
|
|
134
|
+
// not including the OS chrome).
|
|
135
|
+
export const ViewChannel = {
|
|
136
|
+
/** Update the simulator DevTools view's bounds (or hide if w/h = 0). */
|
|
137
|
+
SimulatorDevtoolsBounds: 'view:simulator:devtools-bounds',
|
|
138
|
+
};
|
|
102
139
|
// ── Popover ──────────────────────────────────────────────────────────────
|
|
103
140
|
export const PopoverChannel = {
|
|
104
141
|
Show: 'popover:show',
|
|
@@ -29,6 +29,12 @@ export declare const PopoverShowSchema: z.ZodTuple<[z.ZodObject<{}, z.core.$loos
|
|
|
29
29
|
export declare const SimulatorAttachSchema: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
|
|
30
30
|
/** simulator:resize — simulator width. */
|
|
31
31
|
export declare const SimulatorResizeSchema: z.ZodTuple<[z.ZodNumber], null>;
|
|
32
|
+
export declare const ViewBoundsSchema: z.ZodTuple<[z.ZodObject<{
|
|
33
|
+
x: z.ZodNumber;
|
|
34
|
+
y: z.ZodNumber;
|
|
35
|
+
width: z.ZodNumber;
|
|
36
|
+
height: z.ZodNumber;
|
|
37
|
+
}, z.core.$strip>], null>;
|
|
32
38
|
/** simulator:setVisible — visible flag + simulator width. */
|
|
33
39
|
export declare const SimulatorSetVisibleSchema: z.ZodTuple<[z.ZodBoolean, z.ZodNumber], null>;
|
|
34
40
|
/**
|
|
@@ -57,6 +57,21 @@ export const SimulatorAttachSchema = z.tuple([
|
|
|
57
57
|
]);
|
|
58
58
|
/** simulator:resize — simulator width. */
|
|
59
59
|
export const SimulatorResizeSchema = z.tuple([SimWidth]);
|
|
60
|
+
/**
|
|
61
|
+
* view:*:bounds — renderer-measured CSS pixel rectangle in window content
|
|
62
|
+
* coordinates. Width/height = 0 is the canonical "overlay hidden" signal.
|
|
63
|
+
* No upper bound is enforced (window can be very large); we only refuse
|
|
64
|
+
* negative/NaN values that would break setBounds.
|
|
65
|
+
*/
|
|
66
|
+
const NonNegInt = z.number().int().min(0).max(100_000);
|
|
67
|
+
export const ViewBoundsSchema = z.tuple([
|
|
68
|
+
z.object({
|
|
69
|
+
x: NonNegInt,
|
|
70
|
+
y: NonNegInt,
|
|
71
|
+
width: NonNegInt,
|
|
72
|
+
height: NonNegInt,
|
|
73
|
+
}),
|
|
74
|
+
]);
|
|
60
75
|
/** simulator:setVisible — visible flag + simulator width. */
|
|
61
76
|
export const SimulatorSetVisibleSchema = z.tuple([z.boolean(), SimWidth]);
|
|
62
77
|
/**
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -27,9 +27,33 @@ export type BuiltinModuleId = 'projects' | 'session' | 'simulator' | 'popover' |
|
|
|
27
27
|
* Cross-IPC / renderer shape of a toolbar action. Carries no `handler` — the
|
|
28
28
|
* handler is non-serialisable and never crosses the IPC boundary.
|
|
29
29
|
*/
|
|
30
|
+
export type ToolbarActionPlacement = 'leading' | 'primary' | 'trailing';
|
|
31
|
+
export type ToolbarActionKind = 'button' | 'avatar';
|
|
32
|
+
export type BeforeOpenProjectResult = {
|
|
33
|
+
allow: true;
|
|
34
|
+
statusMessage?: string;
|
|
35
|
+
} | {
|
|
36
|
+
allow: false;
|
|
37
|
+
reason: string;
|
|
38
|
+
userMessage?: string;
|
|
39
|
+
};
|
|
30
40
|
export interface ToolbarAction {
|
|
31
41
|
id: string;
|
|
32
42
|
label: string;
|
|
43
|
+
/** Visual treatment. `avatar` keeps an account entry circular even without an image URL. */
|
|
44
|
+
kind?: ToolbarActionKind;
|
|
45
|
+
/**
|
|
46
|
+
* Optional visual placement in the project-window header.
|
|
47
|
+
* Omitted actions default to `primary` so existing hosts remain visible
|
|
48
|
+
* near the compile controls.
|
|
49
|
+
*/
|
|
50
|
+
placement?: ToolbarActionPlacement;
|
|
51
|
+
/** Short text glyph rendered before the label for compact host actions. */
|
|
52
|
+
icon?: string;
|
|
53
|
+
/** Explicit fallback text for avatar actions when no image URL is available. */
|
|
54
|
+
displayInitial?: string;
|
|
55
|
+
/** Avatar image URL for account-style actions. Falls back to label initials. */
|
|
56
|
+
avatarUrl?: string;
|
|
33
57
|
}
|
|
34
58
|
/**
|
|
35
59
|
* Host-facing toolbar action passed to `instance.toolbar.set()`. Includes the
|
|
@@ -39,6 +63,11 @@ export interface ToolbarAction {
|
|
|
39
63
|
export interface ToolbarActionInput {
|
|
40
64
|
id: string;
|
|
41
65
|
label: string;
|
|
66
|
+
kind?: ToolbarActionKind;
|
|
67
|
+
placement?: ToolbarActionPlacement;
|
|
68
|
+
icon?: string;
|
|
69
|
+
displayInitial?: string;
|
|
70
|
+
avatarUrl?: string;
|
|
42
71
|
handler: () => void | Promise<void>;
|
|
43
72
|
}
|
|
44
73
|
export interface WorkbenchConfig {
|
|
@@ -132,6 +161,10 @@ export interface WorkbenchHostInstance {
|
|
|
132
161
|
set(actions: ToolbarActionInput[]): void;
|
|
133
162
|
};
|
|
134
163
|
}
|
|
164
|
+
export type BeforeOpenProjectHook = (ctx: {
|
|
165
|
+
projectPath: string;
|
|
166
|
+
instance: WorkbenchHostInstance;
|
|
167
|
+
}) => BeforeOpenProjectResult | Promise<BeforeOpenProjectResult>;
|
|
135
168
|
/**
|
|
136
169
|
* Pluggable backend for the project-list panel. Hosts may supply this to
|
|
137
170
|
* take over project storage from the default `<userData>/dimina-projects.json`.
|
|
@@ -149,6 +182,8 @@ export interface ProjectsProvider {
|
|
|
149
182
|
updateLastOpened?(dirPath: string): void | Promise<void>;
|
|
150
183
|
getCompileConfig?(dirPath: string): unknown;
|
|
151
184
|
saveCompileConfig?(dirPath: string, cfg: unknown): void | Promise<void>;
|
|
185
|
+
saveThumbnail?(dirPath: string, imageDataUrl: string): void | Promise<void>;
|
|
186
|
+
getThumbnail?(dirPath: string): string | null | Promise<string | null>;
|
|
152
187
|
}
|
|
153
188
|
export interface ProjectTemplate {
|
|
154
189
|
id: string;
|
|
@@ -182,6 +217,8 @@ export interface WorkbenchAppConfig extends WorkbenchConfig {
|
|
|
182
217
|
menuBuilder?: (mainWindow: import('electron').BrowserWindow, menuContext: MenuContext) => void;
|
|
183
218
|
/** Called after window and context are created but before start() resolves. Use to register custom IPC handlers. */
|
|
184
219
|
onSetup?: (instance: WorkbenchHostInstance) => void | Promise<void>;
|
|
220
|
+
/** Called after project-directory validation but before an active session is closed and a new project compile starts. */
|
|
221
|
+
onBeforeOpenProject?: BeforeOpenProjectHook;
|
|
185
222
|
/** Called before window close when a session is active. Session disposal happens automatically after this hook. */
|
|
186
223
|
onBeforeClose?: (instance: WorkbenchHostInstance) => void | Promise<void>;
|
|
187
224
|
/** Custom update checker. If provided, enables the check-for-updates feature. */
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 0 vpath resolver.
|
|
3
|
+
*
|
|
4
|
+
* Spec: `docs/file-system.md` §3.3 — single resolver shared by every FSM
|
|
5
|
+
* entry, the renderer-side temp store, and (Phase 1) the main-process
|
|
6
|
+
* protocol.handle / disk reader.
|
|
7
|
+
*
|
|
8
|
+
* resolveVPath(url): { kind, writable, realPath? } | null
|
|
9
|
+
*
|
|
10
|
+
* - `difile://_tmp/<id>` → kind=tmp, writable=false, no realPath
|
|
11
|
+
* - `difile://_store/<id>` → kind=store, writable=false, realPath in base
|
|
12
|
+
* - `difile://<rel>` → kind=usr, writable=true, realPath in base
|
|
13
|
+
*
|
|
14
|
+
* Security invariants (the resolver returns `null` instead of throwing):
|
|
15
|
+
* - any non-`difile://` scheme is rejected outright;
|
|
16
|
+
* - any `..` segment (raw or URL-encoded) is rejected before canonicalize;
|
|
17
|
+
* - after `path.normalize` the resulting `realPath` MUST stay inside the
|
|
18
|
+
* sandbox base; otherwise `null`;
|
|
19
|
+
* - reserved prefixes `_tmp/` and `_store/` are case-sensitive — `_TMP/`,
|
|
20
|
+
* `_Tmp/`, etc. fall through to the user-data namespace.
|
|
21
|
+
*/
|
|
22
|
+
export type VPathKind = 'tmp' | 'store' | 'usr';
|
|
23
|
+
export interface ResolvedVPath {
|
|
24
|
+
kind: VPathKind;
|
|
25
|
+
/** `tmp` and `store` are runtime-owned and read-only; `usr` is writable. */
|
|
26
|
+
writable: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Canonical local path on disk for `store` / `usr` kinds (always within the
|
|
29
|
+
* USER_DATA_PATH base directory after canonicalize). `tmp` kind has no
|
|
30
|
+
* realPath — the bytes live in the renderer-side Blob Map.
|
|
31
|
+
*/
|
|
32
|
+
realPath?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* USER_DATA_PATH sandbox base. Looked up dynamically on every call so test
|
|
36
|
+
* doubles (e.g. monkey-patching `os.homedir`) and the `DIMINA_HOME` env
|
|
37
|
+
* override apply per invocation. The env override exists for headless test
|
|
38
|
+
* affordance and to keep the renderer/main store sharing one base.
|
|
39
|
+
*/
|
|
40
|
+
export declare function sandboxBase(): string;
|
|
41
|
+
export declare function resolveVPath(url: unknown): ResolvedVPath | null;
|
|
42
|
+
//# sourceMappingURL=vpath.d.ts.map
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 0 vpath resolver.
|
|
3
|
+
*
|
|
4
|
+
* Spec: `docs/file-system.md` §3.3 — single resolver shared by every FSM
|
|
5
|
+
* entry, the renderer-side temp store, and (Phase 1) the main-process
|
|
6
|
+
* protocol.handle / disk reader.
|
|
7
|
+
*
|
|
8
|
+
* resolveVPath(url): { kind, writable, realPath? } | null
|
|
9
|
+
*
|
|
10
|
+
* - `difile://_tmp/<id>` → kind=tmp, writable=false, no realPath
|
|
11
|
+
* - `difile://_store/<id>` → kind=store, writable=false, realPath in base
|
|
12
|
+
* - `difile://<rel>` → kind=usr, writable=true, realPath in base
|
|
13
|
+
*
|
|
14
|
+
* Security invariants (the resolver returns `null` instead of throwing):
|
|
15
|
+
* - any non-`difile://` scheme is rejected outright;
|
|
16
|
+
* - any `..` segment (raw or URL-encoded) is rejected before canonicalize;
|
|
17
|
+
* - after `path.normalize` the resulting `realPath` MUST stay inside the
|
|
18
|
+
* sandbox base; otherwise `null`;
|
|
19
|
+
* - reserved prefixes `_tmp/` and `_store/` are case-sensitive — `_TMP/`,
|
|
20
|
+
* `_Tmp/`, etc. fall through to the user-data namespace.
|
|
21
|
+
*/
|
|
22
|
+
// Main-process (Node ESM) resolves these natively. Simulator (vite browser
|
|
23
|
+
// bundle) externalizes node builtins as no-op stubs — that's safe here only
|
|
24
|
+
// because the simulator never enters the `store`/`usr` branches below
|
|
25
|
+
// (renderer-side FSM forwards disk-backed reads/writes to main via IPC).
|
|
26
|
+
import os from 'node:os';
|
|
27
|
+
import path from 'node:path';
|
|
28
|
+
const DIFILE_PREFIX = 'difile://';
|
|
29
|
+
/**
|
|
30
|
+
* USER_DATA_PATH sandbox base. Looked up dynamically on every call so test
|
|
31
|
+
* doubles (e.g. monkey-patching `os.homedir`) and the `DIMINA_HOME` env
|
|
32
|
+
* override apply per invocation. The env override exists for headless test
|
|
33
|
+
* affordance and to keep the renderer/main store sharing one base.
|
|
34
|
+
*/
|
|
35
|
+
export function sandboxBase() {
|
|
36
|
+
const env = (typeof process !== 'undefined' && process.env && process.env.DIMINA_HOME) || '';
|
|
37
|
+
if (env) {
|
|
38
|
+
return path.join(env, 'files');
|
|
39
|
+
}
|
|
40
|
+
return path.join(os.homedir(), '.dimina', 'files');
|
|
41
|
+
}
|
|
42
|
+
export function resolveVPath(url) {
|
|
43
|
+
if (typeof url !== 'string')
|
|
44
|
+
return null;
|
|
45
|
+
if (!url.startsWith(DIFILE_PREFIX))
|
|
46
|
+
return null;
|
|
47
|
+
const raw = url.slice(DIFILE_PREFIX.length);
|
|
48
|
+
if (raw.length === 0)
|
|
49
|
+
return null;
|
|
50
|
+
// Decode URL-encoded sequences first so `%2e%2e` style escapes are caught
|
|
51
|
+
// by the segment check below.
|
|
52
|
+
let decoded;
|
|
53
|
+
try {
|
|
54
|
+
decoded = decodeURIComponent(raw);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// Reject NUL bytes (raw or %00) — Node `fs.*` throws TypeError on a NUL
|
|
60
|
+
// in the path, bypassing the API's `fail` callback and crashing the
|
|
61
|
+
// renderer. Pull the rejection up to the validator.
|
|
62
|
+
if (decoded.includes('\0'))
|
|
63
|
+
return null;
|
|
64
|
+
// Developer convention: `wx.env.USER_DATA_PATH = 'difile://'` so a write
|
|
65
|
+
// like `${USER_DATA_PATH}/foo.txt` produces `difile:///foo.txt`. Strip
|
|
66
|
+
// leading slashes so that path-joins cleanly under the sandbox base.
|
|
67
|
+
decoded = decoded.replace(/^[/\\]+/, '');
|
|
68
|
+
if (decoded.length === 0)
|
|
69
|
+
return null;
|
|
70
|
+
// Reject any '..' or '.' segment after decoding. We split on both '/' and
|
|
71
|
+
// '\\' so Windows-style separators cannot smuggle a traversal segment past
|
|
72
|
+
// the validator on POSIX hosts.
|
|
73
|
+
const segments = decoded.split(/[/\\]+/);
|
|
74
|
+
if (segments.some(s => s === '..' || s === '.'))
|
|
75
|
+
return null;
|
|
76
|
+
// Classify by reserved prefix (case-sensitive — `_TMP/` is a user file).
|
|
77
|
+
let kind;
|
|
78
|
+
let writable;
|
|
79
|
+
if (decoded.startsWith('_tmp/')) {
|
|
80
|
+
kind = 'tmp';
|
|
81
|
+
writable = false;
|
|
82
|
+
}
|
|
83
|
+
else if (decoded.startsWith('_store/')) {
|
|
84
|
+
kind = 'store';
|
|
85
|
+
writable = false;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
kind = 'usr';
|
|
89
|
+
writable = true;
|
|
90
|
+
}
|
|
91
|
+
// `tmp` lives in the renderer Blob Map — no realPath.
|
|
92
|
+
if (kind === 'tmp') {
|
|
93
|
+
return { kind, writable };
|
|
94
|
+
}
|
|
95
|
+
// `store` / `usr`: anchor under the sandbox base and assert containment
|
|
96
|
+
// after normalization. Defense in depth against any traversal that the
|
|
97
|
+
// segment check above missed (e.g. backslash trickery on POSIX).
|
|
98
|
+
const base = sandboxBase();
|
|
99
|
+
const joined = path.join(base, decoded);
|
|
100
|
+
const normalized = path.normalize(joined);
|
|
101
|
+
if (normalized !== base && !normalized.startsWith(base + path.sep))
|
|
102
|
+
return null;
|
|
103
|
+
return { kind, writable, realPath: normalized };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=vpath.js.map
|