ChangeStack.svelte
1<script lang="ts">
2 import { changes, mainChange, currentChange, selectChange } from '$lib/stores/changes';
3 import { vcs } from '$lib/stores/files';
4
5 let busy = $state<string | null>(null);
6
7 async function select(changeId: string) {
8 if (busy || changeId === $currentChange) return;
9 busy = changeId;
10 try {
11 await selectChange(changeId);
12 } catch (err) {
13 console.error(err);
14 } finally {
15 busy = null;
16 }
17 }
18</script>
19
20{#if $vcs === 'jj'}
21 <div class="border-t border-border max-h-72 overflow-y-auto shrink-0">
22 <div class="px-3 py-2 text-xs font-semibold text-tertiary uppercase tracking-wider flex items-center justify-between">
23 <span>Changes ({$changes.length})</span>
24 {#if $mainChange}
25 <span class="text-[10px] text-muted normal-case tracking-normal font-mono" title="Bookmark main is at {$mainChange.changeId}">
26 main: {$mainChange.changeId}
27 </span>
28 {/if}
29 </div>
30
31 {#if $changes.length === 0}
32 <div class="px-3 pb-3 text-xs text-muted">No changes since main</div>
33 {:else}
34 <div class="space-y-px px-1 pb-2">
35 {#each $changes as change (change.changeId)}
36 {@const active = change.changeId === $currentChange}
37 {@const pending = busy === change.changeId}
38 <button
39 class="w-full text-left px-2 py-1.5 rounded flex items-start gap-2 text-xs transition-colors {active ? 'bg-hover/80 text-primary' : 'text-secondary hover:bg-hover/50'} {pending ? 'opacity-60' : ''}"
40 onclick={() => select(change.changeId)}
41 title={change.description || '(no description)'}
42 >
43 <span class="font-mono text-[10px] text-muted shrink-0 mt-0.5">{change.changeId}</span>
44 <span class="flex-1 truncate {active ? 'font-medium' : ''}">
45 {change.description || '(no description)'}
46 </span>
47 {#each change.bookmarks as bm}
48 <span class="shrink-0 text-[10px] px-1 rounded bg-accent-green/20 text-accent-green">{bm}</span>
49 {/each}
50 {#if change.isWorkingCopy}
51 <span class="shrink-0 text-[10px] px-1 rounded bg-accent-blue/20 text-accent-blue" title="Working copy @">@</span>
52 {/if}
53 </button>
54 {/each}
55 </div>
56 {/if}
57 </div>
58{/if}