Key-file picker in the connection manager

Choose... button beside the private-key field opens the native file dialog
starting in ~/.ssh (Utils.openFileDialog via a pickKeyFile RPC).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jokin
2026-07-11 23:50:44 +02:00
co-authored by Claude Fable 5
parent a0fc96fc44
commit aed565e424
4 changed files with 41 additions and 7 deletions
+14
View File
@@ -203,6 +203,20 @@ export function createAppRPC({
return { path };
}),
pickKeyFile: () =>
wrap(async () => {
const { homedir } = await import("node:os");
const picked = await Utils.openFileDialog({
startingFolder: `${homedir()}/.ssh`,
allowedFileTypes: "*",
canChooseFiles: true,
canChooseDirectory: false,
allowsMultipleSelection: false,
});
const path = picked.find((p) => p.trim().length > 0) ?? null;
return { path };
}),
getEditLeftovers: () =>
wrap(async () => ({ names: await edits.listLeftovers() })),
+4
View File
@@ -91,6 +91,10 @@ export type AppRPC = {
params: Record<string, never>;
response: Result<{ path: string | null }>;
};
pickKeyFile: {
params: Record<string, never>;
response: Result<{ path: string | null }>;
};
getEditLeftovers: {
params: Record<string, never>;
response: Result<{ names: string[] }>;
@@ -196,13 +196,22 @@
} @else {
<label class="flex flex-col gap-1 text-xs">
<span class="text-muted-foreground">Private key file</span>
<div class="flex items-center gap-1">
<input
[value]="d.privateKeyPath"
(input)="patch('privateKeyPath', $any($event.target).value)"
placeholder="~/.ssh/id_ed25519"
spellcheck="false"
class="h-7 rounded-md border border-input bg-transparent px-2 font-mono outline-none focus:ring-2 focus:ring-ring"
class="h-7 min-w-0 flex-1 rounded-md border border-input bg-transparent px-2 font-mono outline-none focus:ring-2 focus:ring-ring"
/>
<button
type="button"
(click)="pickKeyFile()"
class="h-7 shrink-0 rounded-md border border-border px-2 text-[11px] hover:bg-accent"
>
Choose…
</button>
</div>
</label>
<label class="flex flex-col gap-1 text-xs">
<span class="text-muted-foreground">Passphrase (optional)</span>
@@ -4,6 +4,7 @@ import type { SavedConnection } from '@shared/types';
import { ConnectionsService } from '../connections.service';
import { DialogsService } from '../dialogs.service';
import { PanelsService } from '../panels.service';
import { RpcService } from '../rpc.service';
interface Draft {
id: string;
@@ -64,6 +65,7 @@ export class ConnectionManager {
readonly store = inject(ConnectionsService);
readonly dialogs = inject(DialogsService);
readonly panels = inject(PanelsService);
private readonly rpc = inject(RpcService);
readonly draft = signal<Draft | null>(null);
readonly busy = signal(false);
@@ -149,6 +151,11 @@ export class ConnectionManager {
});
}
async pickKeyFile(): Promise<void> {
const res = await this.rpc.request.pickKeyFile({});
if (res.ok && res.path) this.patch('privateKeyPath', res.path);
}
openFolder(connections: SavedConnection[]): void {
this.panels.openInNewTabs(connections);
this.close();