Add a native workspace directory picker to onboarding
Onboarding only accepted a typed workspace path, which is error-prone and diverged from the original wizard. Add a pickDirectory IPC backed by Electron's open-directory dialog and a Choose button beside the path field, so the founder can select a real folder for agent artifacts.
This commit is contained in:
1 parent
b65e059da6
commit
db679cfeb9
5 files changed
+23
-2
No files matched your search
@@ -1,4 +1,4 @@
|
|||||||
import { ipcMain, shell, type BrowserWindow } from "electron";
|
import { dialog, ipcMain, shell, type BrowserWindow } from "electron";
|
||||||
import type { DatabaseConnection } from "../database/connection";
|
import type { DatabaseConnection } from "../database/connection";
|
||||||
import { IPC_CHANNELS } from "../../shared/ipc";
|
import { IPC_CHANNELS } from "../../shared/ipc";
|
||||||
import { fail, ok, type DesktopResult } from "../../shared/result";
|
import { fail, ok, type DesktopResult } from "../../shared/result";
|
||||||
@@ -196,6 +196,16 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle(IPC_CHANNELS.pickDirectory, async () => {
|
||||||
|
try {
|
||||||
|
const result = await dialog.showOpenDialog(deps.mainWindow, { properties: ["openDirectory", "createDirectory"] });
|
||||||
|
if (result.canceled || result.filePaths.length === 0) return ok(null);
|
||||||
|
return ok(result.filePaths[0]);
|
||||||
|
} catch (error) {
|
||||||
|
return fail("pick_directory_failed", error instanceof Error ? error.message : String(error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle(IPC_CHANNELS.createMigrationReport, (_event, input) => mutate(() => migrations.createReport(input)));
|
ipcMain.handle(IPC_CHANNELS.createMigrationReport, (_event, input) => mutate(() => migrations.createReport(input)));
|
||||||
ipcMain.handle(IPC_CHANNELS.createRollbackSnapshot, (_event, input) => mutate(() => rollback.createSnapshot(input)));
|
ipcMain.handle(IPC_CHANNELS.createRollbackSnapshot, (_event, input) => mutate(() => rollback.createSnapshot(input)));
|
||||||
ipcMain.handle(IPC_CHANNELS.setRollbackSnapshotActive, (_event, input) => mutate(() => rollback.setActive(input)));
|
ipcMain.handle(IPC_CHANNELS.setRollbackSnapshotActive, (_event, input) => mutate(() => rollback.setActive(input)));
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ const api: DesktopApi = {
|
|||||||
createScheduledPost: (input) => invoke(IPC_CHANNELS.createScheduledPost, input),
|
createScheduledPost: (input) => invoke(IPC_CHANNELS.createScheduledPost, input),
|
||||||
setScheduledPostState: (input) => invoke(IPC_CHANNELS.setScheduledPostState, input),
|
setScheduledPostState: (input) => invoke(IPC_CHANNELS.setScheduledPostState, input),
|
||||||
importFile: (input) => invoke(IPC_CHANNELS.importFile, input),
|
importFile: (input) => invoke(IPC_CHANNELS.importFile, input),
|
||||||
|
pickDirectory: () => invoke(IPC_CHANNELS.pickDirectory),
|
||||||
setFilePinned: (input) => invoke(IPC_CHANNELS.setFilePinned, input),
|
setFilePinned: (input) => invoke(IPC_CHANNELS.setFilePinned, input),
|
||||||
createMigrationReport: (input) => invoke(IPC_CHANNELS.createMigrationReport, input),
|
createMigrationReport: (input) => invoke(IPC_CHANNELS.createMigrationReport, input),
|
||||||
createRollbackSnapshot: (input) => invoke(IPC_CHANNELS.createRollbackSnapshot, input),
|
createRollbackSnapshot: (input) => invoke(IPC_CHANNELS.createRollbackSnapshot, input),
|
||||||
|
|||||||
@@ -312,6 +312,11 @@ function authBadge(authType: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CompanyStep({ draft, onChange, onBack, onNext }: StepProps) {
|
function CompanyStep({ draft, onChange, onBack, onNext }: StepProps) {
|
||||||
|
const chooseWorkspace = () => {
|
||||||
|
void window.agentCompany.pickDirectory().then((result) => {
|
||||||
|
if (result.ok && result.data) onChange({ workspacePath: result.data });
|
||||||
|
});
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="form-grid">
|
<div className="form-grid">
|
||||||
<h2 className="page-h1">Company</h2>
|
<h2 className="page-h1">Company</h2>
|
||||||
@@ -325,7 +330,10 @@ function CompanyStep({ draft, onChange, onBack, onNext }: StepProps) {
|
|||||||
<textarea className="input" value={draft.companyDescription} onChange={(event) => onChange({ companyDescription: event.target.value })} required />
|
<textarea className="input" value={draft.companyDescription} onChange={(event) => onChange({ companyDescription: event.target.value })} required />
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Workspace path (optional)">
|
<Field label="Workspace path (optional)">
|
||||||
<input className="input" value={draft.workspacePath} onChange={(event) => onChange({ workspacePath: event.target.value })} />
|
<div className="row" style={{ gap: 6 }}>
|
||||||
|
<input className="input" value={draft.workspacePath} onChange={(event) => onChange({ workspacePath: event.target.value })} placeholder="Choose a folder for agent artifacts" />
|
||||||
|
<Button tone="ghost" onClick={chooseWorkspace}><Icon name="folder" size={13} /> Choose</Button>
|
||||||
|
</div>
|
||||||
</Field>
|
</Field>
|
||||||
<Actions onBack={onBack} onNext={onNext} />
|
<Actions onBack={onBack} onNext={onNext} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ export interface DesktopApi {
|
|||||||
createScheduledPost(input: CreateScheduledPostInput): Promise<DesktopResult<AppSnapshot>>;
|
createScheduledPost(input: CreateScheduledPostInput): Promise<DesktopResult<AppSnapshot>>;
|
||||||
setScheduledPostState(input: SetScheduledPostStateInput): Promise<DesktopResult<AppSnapshot>>;
|
setScheduledPostState(input: SetScheduledPostStateInput): Promise<DesktopResult<AppSnapshot>>;
|
||||||
importFile(input: ImportFileInput): Promise<DesktopResult<AppSnapshot>>;
|
importFile(input: ImportFileInput): Promise<DesktopResult<AppSnapshot>>;
|
||||||
|
pickDirectory(): Promise<DesktopResult<string | null>>;
|
||||||
setFilePinned(input: SetFilePinnedInput): Promise<DesktopResult<AppSnapshot>>;
|
setFilePinned(input: SetFilePinnedInput): Promise<DesktopResult<AppSnapshot>>;
|
||||||
createMigrationReport(input: CreateMigrationReportInput): Promise<DesktopResult<AppSnapshot>>;
|
createMigrationReport(input: CreateMigrationReportInput): Promise<DesktopResult<AppSnapshot>>;
|
||||||
createRollbackSnapshot(input: CreateRollbackSnapshotInput): Promise<DesktopResult<AppSnapshot>>;
|
createRollbackSnapshot(input: CreateRollbackSnapshotInput): Promise<DesktopResult<AppSnapshot>>;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export const IPC_CHANNELS = {
|
|||||||
createScheduledPost: "agentcompany:create-scheduled-post",
|
createScheduledPost: "agentcompany:create-scheduled-post",
|
||||||
setScheduledPostState: "agentcompany:set-scheduled-post-state",
|
setScheduledPostState: "agentcompany:set-scheduled-post-state",
|
||||||
importFile: "agentcompany:import-file",
|
importFile: "agentcompany:import-file",
|
||||||
|
pickDirectory: "agentcompany:pick-directory",
|
||||||
setFilePinned: "agentcompany:set-file-pinned",
|
setFilePinned: "agentcompany:set-file-pinned",
|
||||||
createMigrationReport: "agentcompany:create-migration-report",
|
createMigrationReport: "agentcompany:create-migration-report",
|
||||||
createRollbackSnapshot: "agentcompany:create-rollback-snapshot",
|
createRollbackSnapshot: "agentcompany:create-rollback-snapshot",
|
||||||
|
|||||||
Reference in new issue
Block a user