Skip to content

feat(ui): sticky restore action bar and virtualized large lists - #54

Merged
pmaxhogan merged 3 commits into
mainfrom
feat/restore-sticky-virtual
Jun 27, 2026
Merged

pmaxhogan merged 3 commits into
mainfrom
feat/restore-sticky-virtual

Conversation

@pmaxhogan

Copy link
Copy Markdown
Owner

Summary

Fixes the Restore browser for large folders (issue #47).

  • Sticky action bar. The selection / destination / Restore / Cancel bar is now position: sticky; bottom: 0 within the page scroll container, with a page-matching background (bg-zinc-50 dark:bg-zinc-950), a top border, and a z-index so it sits above the list. The primary actions stay reachable without scrolling a huge folder all the way down. It reads correctly in both light and dark mode, and a small pb-4 buffer on the section keeps the last rows clear of it when scrolled to the bottom.
  • Virtualized (windowed) file list. A new self-contained composable useVirtualList (and its pure, unit-tested core computeVirtualRange) renders only the visible window of rows (plus a small overscan) at a fixed row height and replaces the off-screen rows with top/bottom spacer paddings on the <ul>, so the scrollbar still reflects the full list while the DOM node count stays bounded. A folder with thousands of files mounts ~26 <li> nodes instead of thousands.
  • No new dependency. The windowing is dependency-free (no virtual-scroll npm package), in line with the repo's minimal-supply-chain / Dependabot policy.
  • Behavior preserved. Selection, the keyOf keying, breadcrumb navigation, search-result vs tree-node rendering, the treeTruncated notice, keyboard/scroll behavior, and the empty states are all unchanged. The store still holds every row in memory; only the DOM is windowed. UI-only, no Rust touched.

Testing

All run from the worktree in ui/:

  • pnpm install --frozen-lockfile - ok
  • pnpm lint - clean
  • pnpm format:check - all files match Prettier style
  • pnpm test:unit - 25 files, 218 tests pass (11 new: 9 windowing-math + 2 Restore.vue component tests; existing restore-store and restore-empty-dropdown tests still green)
  • pnpm build (vue-tsc --noEmit && vite build) - typecheck + build succeed

New tests:

  • ui/src/__tests__/virtual-list.test.ts - visible-range math, the spacer invariant (top + window + bottom == full content height), bounded slices when scrolled into / past / before the list, and the zero-measure fallback.
  • ui/src/__tests__/restore-virtual-list.test.ts - mounts the real Restore.vue against a 5000-entry folder and asserts a bounded <li> count + a large bottom spacer, plus that the action bar carries the sticky/bottom-0/border-t/z-10/background classes and contains the Restore action.

Closes #47

🤖 Generated with Claude Code

https://claude.ai/code/session_01MZQh3ZfwtZsM6c5qnTuWZP

Make the Restore browser usable on huge folders.

- Sticky action bar: the selection / destination / Restore / Cancel bar is
  now position: sticky; bottom: 0 within the page scroll container, with a
  page-matching background, top border, and z-index so it stays reachable
  without scrolling a large folder all the way down. Readable in light and
  dark mode; a small section bottom buffer keeps the last rows clear of it.
- Virtualized file list: a new dependency-free windowing composable
  (useVirtualList + the pure, unit-tested computeVirtualRange) renders only
  the visible window (+ overscan) of fixed-height rows and replaces the rest
  with top/bottom spacer paddings, so a folder with thousands of entries
  mounts a bounded number of <li> nodes instead of one per file. Selection,
  keyOf keying, breadcrumb nav, search-vs-tree rendering, the truncated
  notice, and empty states are all preserved (the store still holds every
  row; only the DOM is windowed).

Tests: pure windowing math (visible-range, spacer invariant, bounded slice,
fallback) plus a Restore.vue test asserting a 5000-entry folder mounts a
bounded row count with a large bottom spacer and that the action bar is
sticky.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZQh3ZfwtZsM6c5qnTuWZP
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Coverage

Area main this PR delta
Rust (lib crates) 76.71% 76.71% +0.00 (OK)
UI (vue/ts) 87.96% 88.12% +0.16 (OK)

Gate: passed - no coverage regression (epsilon 0.1 pp).

@pmaxhogan
pmaxhogan marked this pull request as ready for review June 27, 2026 01:47
…ex P2)

Addresses codex /codex review [P2] findings before merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZQh3ZfwtZsM6c5qnTuWZP
@pmaxhogan
pmaxhogan enabled auto-merge (squash) June 27, 2026 02:04
@pmaxhogan
pmaxhogan merged commit 4631225 into main Jun 27, 2026
18 checks passed
@pmaxhogan
pmaxhogan deleted the feat/restore-sticky-virtual branch June 27, 2026 03:29
@github-project-automation github-project-automation Bot moved this from Todo to Done in Driven Jun 27, 2026
pmaxhogan added a commit that referenced this pull request Jun 27, 2026
🤖 I have created a release *beep* *boop*
---


## [0.4.0](v0.3.1...v0.4.0)
(2026-06-27)


### Features

* **ui:** global backup progress bar in the app header
([#55](#55))
([b7513b5](b7513b5))
* **ui:** sticky restore action bar and virtualized large lists
([#54](#54))
([4631225](4631225))


### Bug Fixes

* **ui:** keep the activity screen smooth during uploads
([#56](#56))
([44f3853](44f3853))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

enhancement: make footer on restore page sticky so you don't have to scroll all the way down

1 participant