Page title dropdown is back (Files ⌄ sub-view menu); titles must be plain — sub-views only in the sidebar #526

Closed
opened 2026-09-30 15:07:55 +00:00 by kayg · 10 comments
Owner

Owner report (2026-09-30, screenshot): "h1 dropdown is back?"

With the sidebar hidden, the big page title ("Files ⌄") opens a sub-view menu (Files / Shared / Recent / Trash). The owner does not want a title dropdown: sub-views live in the sidebar (chrome rules, §34). ModeHeader.svelte documents "the title opens a sub-view menu when the sidebar is hidden", and #487's header snapshot (56ee96ef1) now passes subviews to it again.
Fix: the page title is always plain text (no chevron, no menu) in every tab and at every width. Sub-views are reached only through the sidebar (and the sidebar toggle when it is hidden) plus the Cmd+K palette. Remove the title-menu code path from ModeHeader and its callers, update the comment and DESIGN §34, and add a test that no mode header renders a title menu. Screenshots of Files, Photos, Mail and Money with the sidebar hidden at 1440 and 390. Web gates.

## Owner report (2026-09-30, screenshot): "h1 dropdown is back?" With the sidebar hidden, the big page title ("Files ⌄") opens a sub-view menu (Files / Shared / Recent / Trash). The owner does not want a title dropdown: sub-views live in the sidebar (chrome rules, §34). ModeHeader.svelte documents "the title opens a sub-view menu when the sidebar is hidden", and #487's header snapshot (56ee96ef1) now passes subviews to it again. **Fix:** the page title is always plain text (no chevron, no menu) in every tab and at every width. Sub-views are reached only through the sidebar (and the sidebar toggle when it is hidden) plus the Cmd+K palette. Remove the title-menu code path from ModeHeader and its callers, update the comment and DESIGN §34, and add a test that no mode header renders a title menu. Screenshots of Files, Photos, Mail and Money with the sidebar hidden at 1440 and 390. Web gates.
Author
Owner

#526 (revised by the owner, 2026-09-30): keep the path breadcrumbs, drop the sub-view list

The owner, looking at a folder page ("2026-09-29 ⌄" showing Files/Shared/Recent/Trash and then "Folders in this path: Photos, 2026"): "the breadcrumbs in the dropdown make perfect sense."
Rule:

  • The title menu exists ONLY when the page has a path (a folder inside Files or Photos, a nested Note, a Money budget sub-page, and so on). It lists the ancestors to jump to ("Folders in this path"), nearest last, each a deep link, with keyboard support.
  • It never lists the Tab's sub-views (Files / Shared / Recent / Trash, Calendar views, and so on). Those live in the sidebar and Cmd+K only.
  • At a Tab's root (no path), the title is plain: no chevron, no menu.
  • The menu dismisses when the sidebar opens or the Tab switches (earlier owner bug reports).
    Implement it in ModeHeader and its callers; update the comment and DESIGN §34. Tests: a root title has no menu; a nested folder's title menu lists only its ancestors; the menu closes on sidebar open and Tab switch. Screenshots of a root and a nested folder in Files and Photos with the sidebar hidden, at 1440 and 390 in both themes. Web gates.
## #526 (revised by the owner, 2026-09-30): keep the path breadcrumbs, drop the sub-view list The owner, looking at a folder page ("2026-09-29 ⌄" showing Files/Shared/Recent/Trash and then "Folders in this path: Photos, 2026"): "the breadcrumbs in the dropdown make perfect sense." **Rule:** - The title menu exists ONLY when the page has a path (a folder inside Files or Photos, a nested Note, a Money budget sub-page, and so on). It lists the ancestors to jump to ("Folders in this path"), nearest last, each a deep link, with keyboard support. - It never lists the Tab's sub-views (Files / Shared / Recent / Trash, Calendar views, and so on). Those live in the sidebar and Cmd+K only. - At a Tab's root (no path), the title is plain: no chevron, no menu. - The menu dismisses when the sidebar opens or the Tab switches (earlier owner bug reports). Implement it in ModeHeader and its callers; update the comment and DESIGN §34. Tests: a root title has no menu; a nested folder's title menu lists only its ancestors; the menu closes on sidebar open and Tab switch. Screenshots of a root and a nested folder in Files and Photos with the sidebar hidden, at 1440 and 390 in both themes. Web gates.
Author
Owner

Starting #526 on job/title-plain-526, based on origin/dev at cd3cea757508d9f581d00fb7d5d7973fbc0a75fd. I have read CLAUDE.md, CONTEXT.md, DESIGN §34, and the issue. The job prompt contains the owner’s 2026-09-30 revised rule, which supersedes the stale issue body: keep path breadcrumbs, remove sub-view lists. I am checking ModeHeader, its page-chrome callers, and existing header coverage before changing the code.

Starting #526 on `job/title-plain-526`, based on `origin/dev` at `cd3cea757508d9f581d00fb7d5d7973fbc0a75fd`. I have read CLAUDE.md, CONTEXT.md, DESIGN §34, and the issue. The job prompt contains the owner’s 2026-09-30 revised rule, which supersedes the stale issue body: keep path breadcrumbs, remove sub-view lists. I am checking ModeHeader, its page-chrome callers, and existing header coverage before changing the code.
Author
Owner

Finding: ModeHeader.svelte derives its title menu from subviews, page titleMenu, and folded overflow; it appends path ancestors only on phones. Thus Files root can show a title menu without a path, and desktop nested folder titles do not get the ancestor list. The existing header tests assert this old behavior (modeHeader.svelte.test.ts), and the header sweep expects root sub-view menus. I will replace the title-menu content with path ancestors only and keep page actions in ⋯.

Finding: `ModeHeader.svelte` derives its title menu from `subviews`, page `titleMenu`, and folded `overflow`; it appends path ancestors only on phones. Thus Files root can show a title menu without a path, and desktop nested folder titles do not get the ancestor list. The existing header tests assert this old behavior (`modeHeader.svelte.test.ts`), and the header sweep expects root sub-view menus. I will replace the title-menu content with path ancestors only and keep page actions in ⋯.
Author
Owner

Finding during E2E script validation: node --check apps/web/e2e/header-sweep.mjs rejected a TypeScript as HTMLButtonElement cast in JavaScript at line 573 (Unexpected identifier 'as'). I removed both casts from the .mjs callbacks; the script will be syntax-checked again before the browser run.

Finding during E2E script validation: `node --check apps/web/e2e/header-sweep.mjs` rejected a TypeScript `as HTMLButtonElement` cast in JavaScript at line 573 (`Unexpected identifier 'as'`). I removed both casts from the `.mjs` callbacks; the script will be syntax-checked again before the browser run.
Author
Owner

Production header sweep finding: selecting the “Second level…” ancestor by keyboard navigates to the expected Files path, but focus is not on the title menu trigger after navigation. I am tracing the route callback and menu close ordering before changing it.

Production header sweep finding: selecting the “Second level…” ancestor by keyboard navigates to the expected Files path, but focus is not on the title menu trigger after navigation. I am tracing the route callback and menu close ordering before changing it.
Author
Owner

Finding: the production browser sweep could not click the Photos date title menu at 390px. The locator resolved to the expected date-title button, but the click timed out because the phone-only inline title stayed hidden until it scrolled into the header. A short date page may have no scroll range. I am keeping the date title in ModeHeader so its ancestor menu is reachable from the initial view, and removing the duplicate phone-only title from the page body.

Finding: the production browser sweep could not click the Photos date title menu at 390px. The locator resolved to the expected date-title button, but the click timed out because the phone-only inline title stayed hidden until it scrolled into the header. A short date page may have no scroll range. I am keeping the date title in ModeHeader so its ancestor menu is reachable from the initial view, and removing the duplicate phone-only title from the page body.
Author
Owner

The title/sidebar matrix passed after the phone Photos fix. I also isolated a separate Files breadcrumb sweep failure: at 820px the menu occupied x=8–519 and the test clicked x=53 on a row starting at x=33, so the menu intercepted the test click. The sweep now finds an exposed point on the row before clicking or dragging; HEADER_SECTIONS=breadcrumbs passes.

The title/sidebar matrix passed after the phone Photos fix. I also isolated a separate Files breadcrumb sweep failure: at 820px the menu occupied x=8–519 and the test clicked x=53 on a row starting at x=33, so the menu intercepted the test click. The sweep now finds an exposed point on the row before clicking or dragging; `HEADER_SECTIONS=breadcrumbs` passes.
Author
Owner

Forgejo #526 complete

Branch: job/title-plain-526
Head: 29f0416ae0cf00c5f27d443ce2047cb64fe43944

Built

  • The ModeHeader title menu now exists only below a Tab root. It lists linked path ancestors, nearest last, and never lists sub-views. Root titles have no chevron or menu.
  • The menu closes when the sidebar opens or the Tab changes. Keyboard selection follows the ancestor and restores focus after navigation.
  • Updated path ancestry for Files, Photos, Notes, Money, Tags and Analytics; Calendar sub-views remain in the sidebar and Cmd+K.
  • Kept the Photos date title in the header on phones so the path menu remains reachable on short pages.
  • Added browser coverage and a production-SPA hot-path profile.

Files

  • UI and chrome: packages/ui/src/components/ModeHeader.svelte, packages/ui/src/components/pageChrome.ts, packages/ui/src/components/calendar/CalendarTools.svelte.
  • Callers and navigation: apps/web/src/lib/components/analytics/AnalyticsSidebar.svelte, apps/web/src/lib/files/FilesBrowser.svelte, apps/web/src/lib/navigation.ts, apps/web/src/lib/navigation.test.ts, apps/web/src/lib/notes/NoteView.svelte, apps/web/src/lib/photos/PhotosView.svelte, apps/web/src/routes/+layout.svelte, apps/web/src/routes/analytics/[period]/[date]/+page.svelte, apps/web/src/routes/calendar/[view]/[date]/+page.svelte, apps/web/src/routes/money/[budget]/[month]/+page.svelte, apps/web/src/routes/money/[budget]/accounts/[[account]]/+page.svelte, apps/web/src/routes/tag/[tag]/+page.svelte.
  • Verification: apps/web/src/lib/modeHeader.svelte.test.ts, apps/web/e2e/header-sweep.mjs, apps/web/e2e/layout-sweep.mjs, apps/web/e2e/analytics.mjs, apps/web/e2e/calendar-doc-stack.mjs, apps/web/e2e/phone-chrome.mjs, apps/web/e2e/photos.mjs, apps/web/e2e/search.mjs, apps/web/e2e/touch-369.mjs.
  • Docs and benchmark: docs/DESIGN.md, bench/header-path-menu-526.mjs, bench/run.sh.

Web gates

bun run check:

$ node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/title-plain-526/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run test:

Test Files  138 passed (138)
     Tests  903 passed (903)
  Start at  21:19:36
  Duration  259.55s (transform 55%, environment 18%, import 14%, tests 10%, setup 3%)

  Transform  |component| transforming modules took 479.35s · 47% of tracked time, re-done on every run
             persist transforms across runs with fsModuleCache: true
             learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns

Header sweep:

CSP REPORTS layout-sweep: 0 across 3 pages
checked 0 screens
LAYOUT SWEEP PASSED

Screenshots

All 24 Files and Photos root/path screenshots are attached below for 390, 820 and 1440 px in Paper and Tokyo Night. These are production-build captures; the visual review is left to the orchestrator.

Performance profile

Measured locally on the production SPA. The host load average before the run was [32.47, 36.06, 32.04]. No path-title-menu baseline exists in docs/perf/baseline.json, so these numbers have no before/after comparison. Values below are p50/p95 click-to-visible latency in ms, browser CPU percent, and RSS mean/peak in bytes.

Viewport and case p50 / p95 ms CPU RSS mean / peak bytes
1440, two ancestors average 404 / 881.2 37.8% 555,275,605 / 562,282,496
1440, two ancestors burst 8 405.6 / 862.8 30.1% 556,972,260 / 567,287,808
1440, six long ancestors average 551 / 1451.7 34.8% 513,302,528 / 524,095,488
1440, six long ancestors burst 12 709.7 / 1021.1 27.5% 503,805,479 / 509,456,384
390, two ancestors average 102.7 / 213.4 35.9% 453,336,503 / 462,917,632
390, two ancestors burst 8 55.8 / 86.1 44.2% 466,340,523 / 469,438,464
390, six long ancestors average 120.3 / 213.2 40.7% 506,720,646 / 519,741,440
390, six long ancestors burst 12 108.5 / 261.8 34.9% 499,135,094 / 500,879,360

Decisions and known gaps

  • Tags uses the Tags root as its path ancestor. Photos date paths use Photos and the date's month path as ancestors. The Money account page links to the current month for its budget ancestor because the app has no separate budget-root page.
  • No known functional gaps. The profile is local on a busy host and has no baseline comparison. No Rust source changed, so no Rust crate gates were needed.
# Forgejo #526 complete Branch: `job/title-plain-526` Head: `29f0416ae0cf00c5f27d443ce2047cb64fe43944` ## Built - The ModeHeader title menu now exists only below a Tab root. It lists linked path ancestors, nearest last, and never lists sub-views. Root titles have no chevron or menu. - The menu closes when the sidebar opens or the Tab changes. Keyboard selection follows the ancestor and restores focus after navigation. - Updated path ancestry for Files, Photos, Notes, Money, Tags and Analytics; Calendar sub-views remain in the sidebar and Cmd+K. - Kept the Photos date title in the header on phones so the path menu remains reachable on short pages. - Added browser coverage and a production-SPA hot-path profile. ## Files - UI and chrome: `packages/ui/src/components/ModeHeader.svelte`, `packages/ui/src/components/pageChrome.ts`, `packages/ui/src/components/calendar/CalendarTools.svelte`. - Callers and navigation: `apps/web/src/lib/components/analytics/AnalyticsSidebar.svelte`, `apps/web/src/lib/files/FilesBrowser.svelte`, `apps/web/src/lib/navigation.ts`, `apps/web/src/lib/navigation.test.ts`, `apps/web/src/lib/notes/NoteView.svelte`, `apps/web/src/lib/photos/PhotosView.svelte`, `apps/web/src/routes/+layout.svelte`, `apps/web/src/routes/analytics/[period]/[date]/+page.svelte`, `apps/web/src/routes/calendar/[view]/[date]/+page.svelte`, `apps/web/src/routes/money/[budget]/[month]/+page.svelte`, `apps/web/src/routes/money/[budget]/accounts/[[account]]/+page.svelte`, `apps/web/src/routes/tag/[tag]/+page.svelte`. - Verification: `apps/web/src/lib/modeHeader.svelte.test.ts`, `apps/web/e2e/header-sweep.mjs`, `apps/web/e2e/layout-sweep.mjs`, `apps/web/e2e/analytics.mjs`, `apps/web/e2e/calendar-doc-stack.mjs`, `apps/web/e2e/phone-chrome.mjs`, `apps/web/e2e/photos.mjs`, `apps/web/e2e/search.mjs`, `apps/web/e2e/touch-369.mjs`. - Docs and benchmark: `docs/DESIGN.md`, `bench/header-path-menu-526.mjs`, `bench/run.sh`. ## Web gates `bun run check`: ```text $ node scripts/check-type-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/title-plain-526/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test`: ```text Test Files 138 passed (138) Tests 903 passed (903) Start at 21:19:36 Duration 259.55s (transform 55%, environment 18%, import 14%, tests 10%, setup 3%) Transform |component| transforming modules took 479.35s · 47% of tracked time, re-done on every run persist transforms across runs with fsModuleCache: true learn more: https://vitest.dev/guide/improving-performance#caching-between-reruns ``` Header sweep: ```text CSP REPORTS layout-sweep: 0 across 3 pages checked 0 screens LAYOUT SWEEP PASSED ``` ## Screenshots All 24 Files and Photos root/path screenshots are attached below for 390, 820 and 1440 px in Paper and Tokyo Night. These are production-build captures; the visual review is left to the orchestrator. - Files root: [390 Paper](https://git.kayg.org/attachments/69945c95-48ad-4056-a821-c655b3d2b30a), [390 Tokyo Night](https://git.kayg.org/attachments/7763f9f8-6832-460a-8571-eae3fd898dda), [820 Paper](https://git.kayg.org/attachments/3a0a4ae2-c778-470a-820a-9e9052837cd1), [820 Tokyo Night](https://git.kayg.org/attachments/95739ec4-2795-4e57-a02b-4fa3d367d2f5), [1440 Paper](https://git.kayg.org/attachments/3bacf1ba-6f33-45cc-bb1e-11f99b89233f), [1440 Tokyo Night](https://git.kayg.org/attachments/d2da2256-5711-4d66-8e8c-28bada9c638c). - Files ancestors: [390 Paper](https://git.kayg.org/attachments/7daa6c41-ffed-4f4c-aed8-e15f1a577b45), [390 Tokyo Night](https://git.kayg.org/attachments/4e711486-a30a-4771-ae3e-1141b5f41a1f), [820 Paper](https://git.kayg.org/attachments/a03f54da-3af4-4948-9c83-23909bb287b4), [820 Tokyo Night](https://git.kayg.org/attachments/cc963eef-3cf8-4115-ae49-2c5395e1dc11), [1440 Paper](https://git.kayg.org/attachments/68d9b5dc-fb78-4db5-8352-315c95221632), [1440 Tokyo Night](https://git.kayg.org/attachments/5358368d-a536-451a-8165-c8db1cb9fe88). - Photos root: [390 Paper](https://git.kayg.org/attachments/93f3fe02-98e4-447a-ad28-1fba66c41c3e), [390 Tokyo Night](https://git.kayg.org/attachments/b6b67017-c43e-497c-b6ec-9f29a5c3467d), [820 Paper](https://git.kayg.org/attachments/2e3dd800-1600-494a-b884-9893888b7e94), [820 Tokyo Night](https://git.kayg.org/attachments/72f6b7d0-85ae-4eff-8a95-ee046c5b7921), [1440 Paper](https://git.kayg.org/attachments/08936ac4-a963-45a1-b663-a2c2bd3f1b4d), [1440 Tokyo Night](https://git.kayg.org/attachments/5ef3d6e1-a7ce-483e-a602-722ae0b3da4b). - Photos ancestors: [390 Paper](https://git.kayg.org/attachments/f6a1341e-07e8-4e6c-84c3-fb2a772cdfe2), [390 Tokyo Night](https://git.kayg.org/attachments/64168747-42e5-43c2-bd6a-8bf4875b0578), [820 Paper](https://git.kayg.org/attachments/462d44eb-53a4-4729-9db4-adb7b32bf231), [820 Tokyo Night](https://git.kayg.org/attachments/cdcbd3dc-b2e8-45ca-8761-6343ee47ec98), [1440 Paper](https://git.kayg.org/attachments/3ecaa443-eb30-4c41-9acb-e2d598668b63), [1440 Tokyo Night](https://git.kayg.org/attachments/2fa8c10d-8149-4c3e-a203-b678be196100). ## Performance profile Measured locally on the production SPA. The host load average before the run was `[32.47, 36.06, 32.04]`. No path-title-menu baseline exists in `docs/perf/baseline.json`, so these numbers have no before/after comparison. Values below are p50/p95 click-to-visible latency in ms, browser CPU percent, and RSS mean/peak in bytes. | Viewport and case | p50 / p95 ms | CPU | RSS mean / peak bytes | |---|---:|---:|---:| | 1440, two ancestors average | 404 / 881.2 | 37.8% | 555,275,605 / 562,282,496 | | 1440, two ancestors burst 8 | 405.6 / 862.8 | 30.1% | 556,972,260 / 567,287,808 | | 1440, six long ancestors average | 551 / 1451.7 | 34.8% | 513,302,528 / 524,095,488 | | 1440, six long ancestors burst 12 | 709.7 / 1021.1 | 27.5% | 503,805,479 / 509,456,384 | | 390, two ancestors average | 102.7 / 213.4 | 35.9% | 453,336,503 / 462,917,632 | | 390, two ancestors burst 8 | 55.8 / 86.1 | 44.2% | 466,340,523 / 469,438,464 | | 390, six long ancestors average | 120.3 / 213.2 | 40.7% | 506,720,646 / 519,741,440 | | 390, six long ancestors burst 12 | 108.5 / 261.8 | 34.9% | 499,135,094 / 500,879,360 | ## Decisions and known gaps - Tags uses the Tags root as its path ancestor. Photos date paths use Photos and the date's month path as ancestors. The Money account page links to the current month for its budget ancestor because the app has no separate budget-root page. - No known functional gaps. The profile is local on a busy host and has no baseline comparison. No Rust source changed, so no Rust crate gates were needed.
Author
Owner

Owner update (2026-10-01): the owner saw the sub-view menu again on calternal.cloud (cc25c441b): the root Files title showed "Files ⌄" with the list Files / Shared / Recent / Trash. Owner: "why is this back?"

This is the exact case the revised rule removes: a root title has no menu, and a title menu (when a path exists) lists only "Folders in this path", never the Tab's sub-views. The fix is still on job/title-plain-526 and is not deployed.

In the same message the owner asked to "put Files in another pill and join both sidebar and h1 pills". Ten minutes later the owner dropped it: "forget about the title in a pill request. not important right now." So the title stays plain text, not a pill. Do not build a title pill.

**Owner update (2026-10-01):** the owner saw the sub-view menu again on calternal.cloud (cc25c441b): the root Files title showed "Files ⌄" with the list Files / Shared / Recent / Trash. Owner: "why is this back?" This is the exact case the revised rule removes: a root title has **no** menu, and a title menu (when a path exists) lists only "Folders in this path", never the Tab's sub-views. The fix is still on `job/title-plain-526` and is not deployed. In the same message the owner asked to "put Files in another pill and join both sidebar and h1 pills". Ten minutes later the owner dropped it: "forget about the title in a pill request. not important right now." So the title stays plain text, not a pill. Do not build a title pill.
Author
Owner

Shipped in merge round 4, deployed to calternal.cloud in 1af8ead26 (healthy).

Shipped in merge round 4, deployed to calternal.cloud in 1af8ead26 (healthy).
kayg closed this issue 2026-10-01 09:17:40 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#526
No description provided.