Polish media viewer, pull-to-sync, search nav, and add seek bar presets

- Remove peek details sheet from media viewer; scope Activity tab to
  the current file via client-side filtering instead of unreliable
  server-side query params
- Add file rename action (WebDAV MOVE); fix search result taps to
  switch to Files tab and rebuild breadcrumbs from an absolute path
- Add swipe-to-delete confirmation dialog; fix a crash from disposing
  the rename dialog's TextEditingController too early
- Rework pull-to-sync gesture so sync and lock-open thresholds fire
  independently within one gesture, raise thresholds, and add a
  classic Material refresh spinner
- Add "tap active tab to scroll to top" behavior with a settings toggle
- Align selection toolbar padding with the sort/filter controls row
- Replace app icon assets (adaptive icon + splash screen) with
  updated IconKitchen export
- Replace the seek bar style dropdown with a Default/Wavy/Slim/Squiggly
  preset picker, each rendered with a live, perpetually-animated
  preview via a new shared SeekBarPainter/SeekBarPreview

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 10:25:52 -04:00
co-authored by Claude Sonnet 5
parent 1e42fcc0e6
commit d5675d853c
37 changed files with 951 additions and 376 deletions
+43 -17
View File
@@ -180,25 +180,51 @@ class _PhotosViewState extends State<PhotosView> {
if (_isSelecting)
SliverAppBar(
pinned: true,
// This isn't the scroll view's topmost sliver - the Synced
// header above it already reserves status-bar space, so leaving
// this at its default `primary: true` double-reserves it too,
// showing up as a big empty gap above the toolbar.
primary: false,
automaticallyImplyLeading: false,
leading: IconButton(
icon: const Icon(Icons.close_rounded),
tooltip: 'Cancel selection',
onPressed: _clearSelection,
toolbarHeight: 48,
// No leading/actions slots - everything lives in `title`, wrapped
// in the exact same 16px horizontal Padding as the controls row
// above it, so the two rows' content lines up edge-to-edge
// instead of relying on SliverAppBar's own leadingWidth/actions
// insets (which don't match the controls row's).
titleSpacing: 0,
title: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
IconButton(
icon: const Icon(Icons.close_rounded, size: 20),
tooltip: 'Cancel selection',
onPressed: _clearSelection,
visualDensity: VisualDensity.compact,
),
const SizedBox(width: 8),
Expanded(
child: Text(
'${selectedItems.length} selected',
style: theme.textTheme.titleSmall?.copyWith(
fontWeight: FontWeight.w700,
),
),
),
for (final action in _buildSelectionActions(
provider,
selectedItems,
))
IconButton(
icon: Icon(action.icon, size: 20, color: action.color),
tooltip: action.label,
onPressed: action.onTap,
visualDensity: VisualDensity.compact,
),
],
),
),
title: Text('${selectedItems.length} selected'),
actions: [
for (final action in _buildSelectionActions(
provider,
selectedItems,
))
IconButton(
icon: Icon(action.icon, color: action.color),
tooltip: action.label,
onPressed: action.onTap,
),
const SizedBox(width: 4),
],
),
const SliverToBoxAdapter(child: SizedBox(height: 8)),