Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
Svelte
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171<script module lang="ts"> export interface WelcomeKey { rkey: string; name: string; fingerprint?: string; createdAt: string; }</script>
<script lang="ts"> import { cubicOut } from "svelte/easing"; import { prefersReducedMotion } from "svelte/motion"; import { fade, fly } from "svelte/transition"; import { flip } from "svelte/animate"; import Plus from "$icon/plus"; import Trash from "$icon/trash-2"; import Button from "$lib/components/ui/Button.svelte"; import ErrorAlert from "$lib/components/ui/Error.svelte"; import Field from "$lib/components/ui/Field.svelte"; import Input from "$lib/components/ui/Input.svelte"; import Modal from "$lib/components/ui/Modal.svelte"; import Textarea from "$lib/components/ui/Textarea.svelte"; import KeyCard from "$lib/components/settings/KeyCard.svelte"; import StepHeader from "$lib/components/welcome/StepHeader.svelte";
let { keys, onAdd, onDelete }: { keys: WelcomeKey[]; onAdd: (name: string, key: string) => Promise<void>; onDelete: (rkey: string) => Promise<void>; } = $props();
let name = $state(""); let key = $state(""); let adding = $state(false); let deleting = $state<string>(); let error = $state<string>(); let pendingDelete = $state<WelcomeKey>(); let confirmOpen = $state(false);
const canAdd = $derived(name.trim() !== "" && key.trim() !== "");
const add = async (event: SubmitEvent) => { event.preventDefault(); if (!canAdd || adding) return; adding = true; error = undefined; try { await onAdd(name.trim(), key.trim()); name = ""; key = ""; } catch (cause) { error = cause instanceof Error ? cause.message : "Could not add key. Try again."; } finally { adding = false; } };
const requestDelete = (pubkey: WelcomeKey) => { if (deleting) return; pendingDelete = pubkey; confirmOpen = true; };
const remove = async () => { if (!pendingDelete || deleting) return; const pubkey = pendingDelete; deleting = pubkey.rkey; error = undefined; try { await onDelete(pubkey.rkey); } catch (cause) { error = cause instanceof Error ? cause.message : "Could not delete key. Try again."; } finally { deleting = undefined; confirmOpen = false; pendingDelete = undefined; } };</script>
<div class="flex flex-col gap-6"> <StepHeader title="Add SSH keys" subtitle="Add a public SSH key to push to your repositories over SSH." />
<ErrorAlert label={error} />
{#if keys.length > 0} <ul class="flex flex-col gap-3"> {#each keys as pubkey (pubkey.rkey)} <li in:fly={{ y: -8, duration: prefersReducedMotion.current ? 0 : 200, easing: cubicOut }} out:fade={{ duration: prefersReducedMotion.current ? 0 : 140 }} animate:flip={{ duration: prefersReducedMotion.current ? 0 : 200, easing: cubicOut }} > <KeyCard name={pubkey.name} fingerprint={pubkey.fingerprint} createdAt={pubkey.createdAt} deleting={deleting === pubkey.rkey} onDelete={() => requestDelete(pubkey)} /> </li> {/each} </ul> {/if}
<form class="flex flex-col gap-4" onsubmit={add}> <Field label="Key name" labelCase="normal"> {#snippet children({ id })} <Input {id} placeholder="my-laptop" required bind:value={name} /> {/snippet} </Field>
<Field label="Public key" labelCase="normal"> {#snippet children({ id })} <Textarea {id} size="small" rows={3} required placeholder="ssh-ed25519 AAAA..." bind:value={key} /> {/snippet} </Field>
<Button type="submit" icon={Plus} disabled={!canAdd} loading={adding} class="ml-auto"> Add key </Button> </form></div>
<Modal bind:open={confirmOpen} title={`Delete ${pendingDelete?.name ?? "key"}?`} titleIcon={Trash}> <p class="text-foreground-muted"> This removes the key from your account. You will need to add it again to push with it. </p>
{#snippet footer()} <Button variant="default" class="flex-1" disabled={!!deleting} onclick={() => (confirmOpen = false)} > Cancel </Button> <Button variant="danger" icon={Trash} class="flex-1" loading={!!deleting} onclick={() => void remove()} > Delete key </Button> {/snippet}</Modal>