Skip to content

Commit 05bb6c3

Browse files
feat(Wind): Add ShowOpenDialog and ShowSaveDialog to FilesService
Add native file dialog methods to the FilesService interface, stub, and live implementation. ShowOpenDialog returns selected URIs (with options for title, filters, multi-select, folder selection), while ShowSaveDialog returns a selected path (with options for title, defaultUri, filters). The live implementation uses IPC to invoke UserInterface.ShowOpenDialog and UserInterface.ShowSaveDialog on the Mountain backend, completing the file dialog integration in Wind's service layer.
1 parent 2c94230 commit 05bb6c3

3 files changed

Lines changed: 53 additions & 1 deletion

File tree

Source/Effect/Files/Implementation/FilesStub.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@ import { Effect } from "effect";
22

33
import type { FilesService } from "../Interface/FilesService.js";
44

5-
export const StubFilesService: FilesService = {} as FilesService;
5+
export const StubFilesService: FilesService = {
6+
ReadFile: () => Effect.die(new Error("stub")),
7+
WriteFile: () => Effect.void,
8+
Stat: () => Effect.die(new Error("stub")),
9+
ReadDir: () => Effect.succeed([]),
10+
CreateDirectory: () => Effect.void,
11+
Delete: () => Effect.void,
12+
Rename: () => Effect.void,
13+
Copy: () => Effect.void,
14+
Exists: () => Effect.succeed(false),
15+
Watch: () => Effect.succeed({ dispose: () => {} }),
16+
ShowOpenDialog: () => Effect.succeed([]),
17+
ShowSaveDialog: () => Effect.succeed(undefined),
18+
};
619

720
export default StubFilesService;

Source/Effect/Files/Interface/FilesService.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,23 @@ export interface FilesService {
4444
readonly Watch: (
4545
uri: string,
4646
) => Effect.Effect<{ readonly dispose: () => void }, FilesProblem>;
47+
/** Show a native file-open dialog. Returns selected URIs or empty array. */
48+
readonly ShowOpenDialog: (options?: {
49+
readonly title?: string;
50+
readonly filters?: ReadonlyArray<{
51+
readonly name: string;
52+
readonly extensions: ReadonlyArray<string>;
53+
}>;
54+
readonly canSelectMany?: boolean;
55+
readonly canSelectFolders?: boolean;
56+
}) => Effect.Effect<readonly string[], FilesProblem>;
57+
/** Show a native file-save dialog. Returns selected URI or undefined. */
58+
readonly ShowSaveDialog: (options?: {
59+
readonly title?: string;
60+
readonly defaultUri?: string;
61+
readonly filters?: ReadonlyArray<{
62+
readonly name: string;
63+
readonly extensions: ReadonlyArray<string>;
64+
}>;
65+
}) => Effect.Effect<string | undefined, FilesProblem>;
4766
}

Source/Effect/Files/Live.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,26 @@ export const LiveFilesServiceLayer = Layer.effect(
177177
Effect.mapError(MakeFilesProblem),
178178
);
179179
},
180+
181+
ShowOpenDialog: (Options) =>
182+
IPCService.invoke("UserInterface.ShowOpenDialog")([
183+
Options ?? {},
184+
]).pipe(
185+
Effect.map((Result) =>
186+
Array.isArray(Result) ? (Result as string[]) : [],
187+
),
188+
Effect.mapError(MakeFilesProblem),
189+
),
190+
191+
ShowSaveDialog: (Options) =>
192+
IPCService.invoke("UserInterface.ShowSaveDialog")([
193+
Options ?? {},
194+
]).pipe(
195+
Effect.map((Result) =>
196+
typeof Result === "string" ? Result : undefined,
197+
),
198+
Effect.mapError(MakeFilesProblem),
199+
),
180200
};
181201

182202
return Service;

0 commit comments

Comments
 (0)