- FilesControlsRow (and Photos' own controls row) wrapped its Row in a
SingleChildScrollView, which gives a child unbounded width - silently
breaking the trailing Spacer (a flex child needs bounded width) and
leaving the List/Grid toggle sitting right after the filter chip
instead of pinned to the row's right edge, "not enough controls to
fill the row" or not. Removed the scroll wrapper (two chips and an
icon toggle never need it) and the dead scroll-hint plumbing that
went with it in files_view.dart.
- Photos' All/Photos/Videos chips moved into the filter sheet as a
full-width segmented control (matching Settings' Theme control),
instead of their own row of chips competing for space with the sort
and filter chips.
- Settings' Theme row: the System/Light/Dark segmented control now
sits on its own row below the label instead of squeezed into a
trailing slot beside it, where three icon+label segments were too
cramped to read comfortably - same shape as the sort sheet's
Ascending/Descending control.
- NooBanner (Trash's "Empty trash") and NooSummaryCard (Offline's
"Sync now") both move their action onto its own row below the
description text instead of squeezed onto the same line, where a
longer caption had no room to share with it.
- SharesController now caches each scope (with you / by you, which
also covers links) after its first fetch instead of re-fetching on
every single switch, including switching straight back to a scope
already loaded seconds ago.
- Shares' per-row overflow menu jumped straight to the unshare
confirmation with no menu at all - a link share had no way to copy
its own URL short of opening the full Share sheet. It's a real menu
now: "Copy link" (link shares only) plus remove/unshare.
- Settings gained a trailing jump rail on mobile (one small icon per
section) so a specific section can be reached in one tap instead of
scrolling the whole list by hand; desktop's 2-column grid doesn't
need one.
- The media viewer's PageView now disables its own left/right swipe
while the current photo is zoomed in, so panning around it doesn't
also swipe to the next item.
- The PDF preview no longer sets minScale below 1.0: pdfx's
PdfViewPinch hard-codes an *infinite* pan boundary whenever minScale
< 1 (not an exposed parameter, so there's no way to override it),
which let a page be panned arbitrarily far off-screen without ever
zooming.
showNooSheet's peek detection previously measured content once, right
after the first frame - Details' Versions/Activity tabs fetch their
data asynchronously, so that one-shot measurement only ever saw the
initial loading-spinner height, never the eventual (often long) list,
and the sheet still snapped to full height once data arrived. Replaced
it with a ScrollMetricsNotification-based check that keeps content
capped at the peek height from frame one and upgrades to a draggable
sheet whenever it actually overflows that cap, however and whenever
that happens - fixed for good this time, with a regression test that
mirrors the async-load shape.
Also, in the same pass:
- Upload/Move/Copy's bottom bar meta line now always reads as a count
("Uploading 3 items"), never a filename for the single-item case,
and is centered rather than left-aligned - matches the approved
design and removes a bespoke marquee/chip implementation that only
Upload had.
- Upload/Move/Copy now show the same FilesControlsRow (sort, filter,
List/Grid) Files itself uses, including a folder-grid rendering
path for when List/Grid is set to grid.
- Files' filter chip reads "Filters" instead of "All types" when idle.
- Trash's overflow button - whose only action is "Delete forever" -
is now a direct delete icon (mobile) / "Delete" danger pill
(desktop) instead of a generic "..." menu that hid a single action
behind an extra tap.
- The media viewer's floating chrome (top bar, bottom action bar,
video transport row) now follows the app's light/dark theme instead
of a fixed dark panel - a dark panel read as out of place over a
light-theme stage. Panel tint and icon/text colors come from the
theme's own surface/fg1 tokens (at a higher blur opacity to stay
legible over arbitrary photo/video brightness), and the edge scrim
gradients fade toward the panel's own tint instead of a fixed black.
Mirrored the same change into the design canvas's Main.dc.html/
MainAndroid.dc.html mockups (now with a theme prop) so they match.
Long-content bottom sheets (Details' Versions/Activity tabs with many
rows) opened locked to nearly full screen height with no room left to
adjust. showNooSheet now measures its content once against the
available height: anything that fits comfortably still sizes to
content as before, but content tall enough to need scrolling opens at
a native-style draggable peek instead, which the user can drag up to
reveal more of (and drag back down to dismiss).
The media viewer's stage was pinned to black regardless of theme,
which read as jarringly out of place in light mode - it now follows
the app's own `bg`. The floating top/bottom bars stay a fixed dark
panel either way (they need to read over arbitrary photo/video
brightness, not over the stage), but both had their SafeArea nested
the wrong way around: it wrapped the whole panel instead of just its
content, so the panel's background stopped short of the actual screen
edge - most visibly the bottom bar ending above the gesture area
instead of extending behind it. The background now wraps the SafeArea
instead.
NooSelectionBar's fixed inline slots (3 mobile / 4 desktop, the rest
behind "More") previously just took the first N actions in whatever
order each screen happened to build them in. Give bulk actions a
stable SelectionActionKind identity (independent of their dynamic
icon/label, e.g. favorite vs. unfavorite) so Settings can offer a
drag-to-reorder list - mirroring the existing Tabs section, minus the
pin/hide concept, which doesn't apply here - and each screen applies
that saved priority order via orderSelectionActions before handing
its action list to the bar.
- Bottom bar: labels now sit below each icon instead of beside it, and
the active-tab pill is one shared indicator that slides between tabs
instead of popping in/out per item.
- Upload FAB stays mounted across every tab and collapses to an
icon-only circle off Files/Photos, instead of the Scaffold popping it
fully in/out on every tab switch.
- Tapping the top-bar avatar now opens the accounts sheet instead of
jumping straight to Settings.
- Settings tab reordering only starts a drag from the grip icon now -
previously the whole row claimed vertical drags, so scrolling the
section could trigger an accidental reorder.
The multi-select action bar (Files/Photos/Favorites) hid bulk actions
behind a horizontally-scrolling row with no visible cue that there was
more to find, once there were more than a handful (up to 9: favorite,
share, download, delete, copy, move, rename, sync, details).
Designed the replacement on the linked design canvas first (Selection
action bar boards), then implemented it: NooSelectionBar now shows a
fixed number of actions inline (3 on mobile, 4 on desktop) with the rest
behind a trailing "More" button that opens the same overflow sheet a
file row's own "..." menu already uses. Removed the now-dead
scroll-hint plumbing from the three screens that render it.
Replaces the Material 3 screens with the Noo design system: warm
neutrals, one violet accent, pill controls, Schibsted Grotesk/
Instrument Sans, Lucide icons, no gradients/shadows.
- New lib/widgets/noo/ component kit (core, lists, files, media, nav,
overlays) plus lib/theme/design_tokens.dart for the color/type/space/
radius/motion tokens.
- Rebuilt the app shell (top/bottom bars, drawer, desktop sidebar and
toolbar), every tab (Files/Offline, Photos, Favorites, Recent,
Activity, Trash, Shares), Settings, the lock and login screens, the
media viewer, search, the details/share sheets, and the share-upload
and move/copy destination pickers.
- Added a design canvas (linked from DESIGN_SYSTEM.md) covering the
screens the spec didn't already describe, with matching Android and
iOS chrome; wrote up the approved recipes into DESIGN_SYSTEM.md §4.
- Removed now-dead legacy widgets (media_grid_tile, swipeable_item,
sync_status_badge, selectable_thumbnail) and updated
architecture.md/styling.md/standards.md to describe the new
structure and component/testing conventions.
- Added widget tests for the noo/ component kit.
This is a UI-only rework: no provider/model/service behavior changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Moved into .claude/context/ (alongside the other topic docs CLAUDE.md
points agents at) rather than the repo root, so it's discovered the same
way as architecture.md/styling.md/etc. Flags styling.md as documenting the
pre-rework theme in the meantime, so the two don't read as contradictory.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>