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:
@@ -203,6 +203,20 @@ export function createAppRPC({
|
|||||||
return { path };
|
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: () =>
|
getEditLeftovers: () =>
|
||||||
wrap(async () => ({ names: await edits.listLeftovers() })),
|
wrap(async () => ({ names: await edits.listLeftovers() })),
|
||||||
|
|
||||||
|
|||||||
@@ -91,6 +91,10 @@ export type AppRPC = {
|
|||||||
params: Record<string, never>;
|
params: Record<string, never>;
|
||||||
response: Result<{ path: string | null }>;
|
response: Result<{ path: string | null }>;
|
||||||
};
|
};
|
||||||
|
pickKeyFile: {
|
||||||
|
params: Record<string, never>;
|
||||||
|
response: Result<{ path: string | null }>;
|
||||||
|
};
|
||||||
getEditLeftovers: {
|
getEditLeftovers: {
|
||||||
params: Record<string, never>;
|
params: Record<string, never>;
|
||||||
response: Result<{ names: string[] }>;
|
response: Result<{ names: string[] }>;
|
||||||
|
|||||||
@@ -196,13 +196,22 @@
|
|||||||
} @else {
|
} @else {
|
||||||
<label class="flex flex-col gap-1 text-xs">
|
<label class="flex flex-col gap-1 text-xs">
|
||||||
<span class="text-muted-foreground">Private key file</span>
|
<span class="text-muted-foreground">Private key file</span>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
<input
|
<input
|
||||||
[value]="d.privateKeyPath"
|
[value]="d.privateKeyPath"
|
||||||
(input)="patch('privateKeyPath', $any($event.target).value)"
|
(input)="patch('privateKeyPath', $any($event.target).value)"
|
||||||
placeholder="~/.ssh/id_ed25519"
|
placeholder="~/.ssh/id_ed25519"
|
||||||
spellcheck="false"
|
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>
|
||||||
<label class="flex flex-col gap-1 text-xs">
|
<label class="flex flex-col gap-1 text-xs">
|
||||||
<span class="text-muted-foreground">Passphrase (optional)</span>
|
<span class="text-muted-foreground">Passphrase (optional)</span>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { SavedConnection } from '@shared/types';
|
|||||||
import { ConnectionsService } from '../connections.service';
|
import { ConnectionsService } from '../connections.service';
|
||||||
import { DialogsService } from '../dialogs.service';
|
import { DialogsService } from '../dialogs.service';
|
||||||
import { PanelsService } from '../panels.service';
|
import { PanelsService } from '../panels.service';
|
||||||
|
import { RpcService } from '../rpc.service';
|
||||||
|
|
||||||
interface Draft {
|
interface Draft {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -64,6 +65,7 @@ export class ConnectionManager {
|
|||||||
readonly store = inject(ConnectionsService);
|
readonly store = inject(ConnectionsService);
|
||||||
readonly dialogs = inject(DialogsService);
|
readonly dialogs = inject(DialogsService);
|
||||||
readonly panels = inject(PanelsService);
|
readonly panels = inject(PanelsService);
|
||||||
|
private readonly rpc = inject(RpcService);
|
||||||
|
|
||||||
readonly draft = signal<Draft | null>(null);
|
readonly draft = signal<Draft | null>(null);
|
||||||
readonly busy = signal(false);
|
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 {
|
openFolder(connections: SavedConnection[]): void {
|
||||||
this.panels.openInNewTabs(connections);
|
this.panels.openInNewTabs(connections);
|
||||||
this.close();
|
this.close();
|
||||||
|
|||||||
Reference in New Issue
Block a user