KeybindingGroup.svelte RawHistoryFolder
1<script lang="ts">
2 import { Button } from "$lib/components/ui/button";
3 import * as Table from "$lib/components/ui/table";
4 import { RotateCcw, Edit3 } from "@lucide/svelte";
5 import { formatKey } from "$lib/utils/keys";
6
7 type KeybindingEntry = {
8 action: string;
9 description: string;
10 keys: string[];
11 default_keys: string[];
12 is_custom: boolean;
13 };
14
15 type KeybindingGroupData = {
16 mode: string;
17 display_name: string;
18 bindings: KeybindingEntry[];
19 activation?: string;
20 };
21
22 interface Props {
23 group: KeybindingGroupData;
24 onEdit: (action: string, entry: KeybindingEntry) => void;
25 onReset: (action: string) => void;
26 }
27
28 let { group, onEdit, onReset }: Props = $props();
29</script>
30
31<div class="space-y-3">
32 <div class="flex items-center gap-2">
33 <h3 class="text-sm font-semibold uppercase tracking-[0.15em] text-muted-foreground">
34 {group.display_name}
35 </h3>
36 {#if group.activation}
37 <span class="rounded-md bg-muted px-2 py-0.5 text-xs font-mono text-muted-foreground">
38 {formatKey(group.activation)}
39 </span>
40 {/if}
41 </div>
42
43 <Table.Root class="rounded-md border border-border">
44 <Table.Header>
45 <Table.Row>
46 <Table.Head class="w-[180px]">Action</Table.Head>
47 <Table.Head>Description</Table.Head>
48 <Table.Head class="w-[200px]">Keybinding</Table.Head>
49 <Table.Head class="w-[100px] text-right">Actions</Table.Head>
50 </Table.Row>
51 </Table.Header>
52 <Table.Body>
53 {#each group.bindings as binding (binding.action)}
54 <Table.Row class={binding.is_custom ? "bg-accent/5" : ""}>
55 <Table.Cell class="font-mono text-sm">{binding.action}</Table.Cell>
56 <Table.Cell class="text-muted-foreground">{binding.description}</Table.Cell>
57 <Table.Cell>
58 <div class="flex flex-wrap gap-1">
59 {#each binding.keys as key (key)}
60 <kbd class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono">
61 {formatKey(key)}
62 </kbd>
63 {/each}
64 {#if binding.keys.length === 0}
65 <span class="text-xs text-muted-foreground">(unbound)</span>
66 {/if}
67 </div>
68 </Table.Cell>
69 <Table.Cell class="text-right">
70 <div class="flex justify-end gap-1">
71 <Button
72 variant="ghost"
73 size="icon"
74 onclick={() => onEdit(binding.action, binding)}
75 aria-label="Edit binding"
76 >
77 <Edit3 class="size-4" />
78 </Button>
79 {#if binding.is_custom}
80 <Button
81 variant="ghost"
82 size="icon"
83 onclick={() => onReset(binding.action)}
84 aria-label="Reset to default"
85 >
86 <RotateCcw class="size-4" />
87 </Button>
88 {/if}
89 </div>
90 </Table.Cell>
91 </Table.Row>
92 {/each}
93 </Table.Body>
94 </Table.Root>
95</div>