* fix(nav): derive route icons from the URL across all nav surfaces (MUL-4370)
The same route rendered different icons in the sidebar and the desktop tab
bar because the mapping was maintained in three places. Projects had no tab
icon at all; autopilots/chat/squads/usage fell back to ListTodo.
Establish one contract instead: `@multica/core/paths` maps a route segment to
a stable icon *name* (React-free), and `@multica/views/layout` maps that name
to a Lucide component. Every nav surface — sidebar, desktop tab bar, and the
search palette — resolves through `routeIconForPath(path)`, so a route cannot
render two different icons.
Crucially the icon is now derived, not stored. `TabSession.icon` is removed,
so persisted tab state can no longer hold a stale icon name: a user who had
an /autopilots tab from an older build gets the correct icon after upgrade
rather than the one that was persisted. Legacy `icon` values in v4 payloads
are ignored on rehydration and dropped on the next write.
Builds on the design in #5204 by LiangliangSui.
Tests: stale/unknown/absent persisted icon on rehydration, derived icon
rendering per route in the tab bar, name→component registry totality, and
nav-route icon coverage.
Co-authored-by: multica-agent <github@multica.ai>
* feat(desktop): tab presentation by object identity, not route segment (MUL-4370)
Replace the "route segment → icon" tab mapping with a semantic Tab
Presentation Contract: a tab's leading visual and title are derived live
from its URL + the query cache, so a tab shows *what it points at*, not the
module it lives under.
- core `parseTabSubject(url)` classifies a URL as page / resource / actor /
container (inbox, chat) / flow / unknown, purely (no React, no Lucide).
- core `resolveTabPresentation(subject, data)` maps that + cached entity data
to a leading visual (issue StatusIcon, ProjectIcon, ActorAvatar, or a type
icon) and a title spec. Exhaustive: a new route forces an explicit choice.
- views `useTabPresentation` reads the cache (enabled:false, no fetch from the
tab bar) and `ResourceLeadingVisual` renders it in a fixed 16×16 slot.
- Containers keep their icon; only the title tracks the selection
(`/inbox?issue=`, `/chat?session=`), so an inbox-opened issue reads
differently from a direct issue tab.
- Titles are plain text; the project 📁 and autopilot ⚡ glyphs are dropped
from document.title.
- Pin no longer replaces the resource visual. Persisted `tab.icon` cleanup
from the prior revision is kept; the active tab persists its resolved title
as a first-frame fallback (the document.title→observer path is removed).
Supersedes the route-segment approach in #5204 per the agreed PRD. No schema,
API, or migration changes; reuses existing queries/caches.
Tests: table-driven parseTabSubject over every desktop route; the
URL/data → visual+title matrix incl. pending/loading, containers, unknown,
runtime custom-name; views cache-integration; tab-bar pin + active-tab
title persistence.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
* fix(desktop): archived inbox title sync + attachment filename in tab (MUL-4370)
Addresses two PRD gaps from review:
1. Archived Inbox selection now syncs the tab title. `parseTabSubject` captures
`?view=archived` on the inbox subject, and the presentation hook resolves the
selection against `archivedInboxListOptions` (its own cache, the one the
InboxPage populates) instead of only the active list. An
`/inbox?view=archived&issue=<id>` tab now shows the archived item's title —
issue (`identifier: title`) or non-issue (display title) — and, being purely
URL+cache derived, restores correctly on refresh. Previously it fell back to
"Inbox" and persisted that wrong title.
2. Attachment tabs use the filename. `parseTabSubject` captures the `?name=`
the preview route already carries; the resolver shows the filename as the
title and picks a file-type icon from its extension (image/video/audio/
archive/code/text), falling back to the generic File glyph + "Attachment"
label only when the name is missing.
Tests: parseTabSubject archived/name cases; the presentation matrix for
attachment filename/extension + missing fallback and iconForAttachment edge
cases; views cache-integration for archived issue/non-issue selection (must
resolve against the archived list, not the active one) and attachment filename.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: multica-agent <github@multica.ai>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>