feat(ui): add collapsible Worktree and Tags panels to left sidebar
Add two new sidebar components (WorktreePanel, TagsPanel) and integrate them into the compact left navigation layout: - Wire up imports and rendering in App.svelte, including collapse toggles and resize handles for both panels. Persisted heights (localStorage) and keyboard resizing are supported; heights are clamped between 80 and 420px with a 140px default. - Extend buildLeftSidebarRows and left-sidebar grid/template styles to include the new panels and reduce panel-handle thickness. Add extensive CSS for the compact accordion navigation, worktree and tags lists. - Move tag and worktree management UI out of BranchPanel (remove tag props), and add dedicated handlers in App.svelte for the new panels. - Refactor worktree loading: introduce a worktreeLoadId to guard async refreshWorktrees() calls and avoid race conditions. refreshWorktrees(path?) now takes an optional path and updates worktree state only when the request is still relevant. - Small behavioral tweaks: stash panel default collapsed state changed to true and the explorer resize-handle visibility condition adjusted. This commit only adds the UI/UX integration and local persistence for the new panels and their resizing/refresh behavior.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Archive, ChevronDown, ChevronRight, Download, Trash2, Upload } from "@lucide/svelte";
|
||||
import { Archive, ChevronDown, ChevronRight, Download, Plus, Trash2, Upload } from "@lucide/svelte";
|
||||
import type { GitStash } from "../types";
|
||||
|
||||
interface Props {
|
||||
@@ -28,6 +28,7 @@
|
||||
onToggleCollapsed = () => {},
|
||||
}: Props = $props();
|
||||
|
||||
let createOpen = $state(false);
|
||||
let message = $state("");
|
||||
let includeUntracked = $state(true);
|
||||
|
||||
@@ -43,11 +44,13 @@
|
||||
|
||||
<section class="panel stash-panel overflow-hidden" class:collapsed aria-label="Git stash">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<span class="eyebrow">Stash</span>
|
||||
<h2 class="mt-0.5 text-ink text-base font-bold leading-tight">Shelved changes</h2>
|
||||
</div>
|
||||
<h2 class="sidebar-section-title"><Archive size={16} aria-hidden="true" />Stashes</h2>
|
||||
<div class="stash-head-actions">
|
||||
<button class="stash-toggle" type="button" title="Create stash" aria-label="Create stash"
|
||||
disabled={!hasRepository || isBusy || changedCount === 0}
|
||||
onclick={() => { createOpen = !createOpen; if (collapsed) { createOpen = true; onToggleCollapsed(); } }}>
|
||||
<Plus size={14} aria-hidden="true" />
|
||||
</button>
|
||||
<span class="pill pill-count">{stashes.length}</span>
|
||||
<button
|
||||
class="stash-toggle panel-collapse-toggle"
|
||||
@@ -71,12 +74,14 @@
|
||||
{:else if !hasRepository}
|
||||
<div class="blank-state">No repository loaded.</div>
|
||||
{:else}
|
||||
{#if createOpen}
|
||||
<div class="stash-create">
|
||||
<input
|
||||
class="stash-input"
|
||||
type="text"
|
||||
bind:value={message}
|
||||
placeholder="Optional message"
|
||||
aria-label="Stash message"
|
||||
disabled={isBusy || changedCount === 0}
|
||||
onkeydown={(event) => {
|
||||
if (event.key === "Enter" && changedCount > 0 && !isBusy) submitPush();
|
||||
@@ -98,6 +103,8 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
|
||||
{#if stashes.length === 0}
|
||||
<div class="blank-state stash-empty">No stashes saved.</div>
|
||||
{:else}
|
||||
|
||||
Reference in New Issue
Block a user