Summary
Add a reusable SortToggle element — a small Mantine Select with two options, "Sort: Oldest First" and "Sort: Newest First" — and wire it into the toolbars of the Logs, History, and Network screens so the user can flip chronological order. The control owns no list state itself; it just emits the selected direction.
None of those three screens currently exposes a chronological sort affordance, so this issue covers both creating the element and adopting it in the three places it belongs.
Proposed change
1. New element
clients/web/src/components/elements/SortToggle/SortToggle.tsx, mirroring the file layout of sibling elements (ListToggle/, ConnectionToggle/, etc.):
export type SortDirection = \"oldest-first\" | \"newest-first\";
export interface SortToggleProps {
value: SortDirection;
onChange: (next: SortDirection) => void;
// Optional, for forms / tests
\"aria-label\"?: string;
}
Implementation notes:
- Use Mantine
Select with data of two options whose label is the user-facing string (\"Sort: Oldest First\", \"Sort: Newest First\") and value is the SortDirection literal. Single-select, no clear button, no search.
- Controlled component — value/onChange only, no internal state. Each screen owns its own direction.
- Width should be intrinsic to the longest label so the control doesn't resize when the user flips direction. A fixed
w (e.g. 180–200) is fine; pick whatever matches the existing ServerListControls / AppControls width conventions.
- Theming: configure via
Select.withProps() if a project-wide default applies (e.g. size=\"sm\", allowDeselect={false}). Per AGENTS.md, prefer props/theme variants over CSS classes.
2. Wire into the three screens
LoggingScreen (clients/web/src/components/screens/LoggingScreen/LoggingScreen.tsx) — add sortDirection and onSortChange props, render the toggle in the existing controls row, and sort the rendered entries based on direction (don't mutate the prop array — clone or useMemo the sorted view).
HistoryScreen (clients/web/src/components/screens/HistoryScreen/HistoryScreen.tsx) — same shape. The screen already does derived work via useMemo (see the entries.map(extractMethod) block at HistoryScreen.tsx:46); the sorted view fits the same pattern.
NetworkScreen (clients/web/src/components/screens/NetworkScreen/NetworkScreen.tsx) — same shape.
For each screen, the parent (App.tsx / InspectorView) holds the sortDirection state and passes it down. Default direction is \"newest-first\" for all three — that matches the typical "tail of a log" mental model and avoids the user having to scroll on first open. Confirm during implementation if any of the three has a stronger reason to default differently.
The underlying state sources (MessageLogState, FetchRequestLogState, the requestor-task history) should not have their internal ordering changed — sorting stays a presentation concern in the screen.
3. Persist the preference in localStorage
The user's chosen direction must survive page reloads. Use useLocalStorage from @mantine/hooks (already a dependency at clients/web/package.json:30) — it handles SSR-safe hydration, JSON serialization, and cross-tab sync via the storage event automatically, so we don't have to roll our own.
- Three independent keys, one per screen, so toggling sort on the Logs screen doesn't change History or Network:
inspector.sortDirection.logs
inspector.sortDirection.history
inspector.sortDirection.network
- Namespace with a shared
inspector. prefix (matches what an mcp.json-flavored app would use as its localStorage namespace) so future preferences can co-exist without collisions and are easy to clear in bulk.
- Default value passed to
useLocalStorage is \"newest-first\". If the stored value isn't one of the two valid SortDirection literals (manual edit, schema drift, future option added then removed), fall back to the default rather than passing it through to the Select — invalid values would put the toggle in an unselectable state.
- The hook lives in the parent that owns
sortDirection (currently scoped to App.tsx / InspectorView). The SortToggle element itself stays purely controlled — it does not know about localStorage. This keeps the element trivially reusable in stories, tests, and any future non-persistent context.
Note that no current code in the repo uses localStorage directly (grep -rn localStorage clients/web/src returns nothing). This issue establishes the convention; later preferences (theme, compact-mode default, etc.) should follow the same inspector.<scope>.<key> pattern.
Acceptance criteria
Out of scope
- Multi-column sort, custom sort keys, or sort-by-anything-other-than-timestamp. Two options only.
Summary
Add a reusable
SortToggleelement — a small MantineSelectwith two options, "Sort: Oldest First" and "Sort: Newest First" — and wire it into the toolbars of the Logs, History, and Network screens so the user can flip chronological order. The control owns no list state itself; it just emits the selected direction.None of those three screens currently exposes a chronological sort affordance, so this issue covers both creating the element and adopting it in the three places it belongs.
Proposed change
1. New element
clients/web/src/components/elements/SortToggle/SortToggle.tsx, mirroring the file layout of sibling elements (ListToggle/,ConnectionToggle/, etc.):Implementation notes:
Selectwithdataof two options whoselabelis the user-facing string (\"Sort: Oldest First\",\"Sort: Newest First\") andvalueis theSortDirectionliteral. Single-select, no clear button, no search.w(e.g. 180–200) is fine; pick whatever matches the existingServerListControls/AppControlswidth conventions.Select.withProps()if a project-wide default applies (e.g.size=\"sm\",allowDeselect={false}). PerAGENTS.md, prefer props/theme variants over CSS classes.2. Wire into the three screens
LoggingScreen(clients/web/src/components/screens/LoggingScreen/LoggingScreen.tsx) — addsortDirectionandonSortChangeprops, render the toggle in the existing controls row, and sort the rendered entries based on direction (don't mutate the prop array — clone oruseMemothe sorted view).HistoryScreen(clients/web/src/components/screens/HistoryScreen/HistoryScreen.tsx) — same shape. The screen already does derived work viauseMemo(see theentries.map(extractMethod)block atHistoryScreen.tsx:46); the sorted view fits the same pattern.NetworkScreen(clients/web/src/components/screens/NetworkScreen/NetworkScreen.tsx) — same shape.For each screen, the parent (
App.tsx/InspectorView) holds thesortDirectionstate and passes it down. Default direction is\"newest-first\"for all three — that matches the typical "tail of a log" mental model and avoids the user having to scroll on first open. Confirm during implementation if any of the three has a stronger reason to default differently.The underlying state sources (
MessageLogState,FetchRequestLogState, the requestor-task history) should not have their internal ordering changed — sorting stays a presentation concern in the screen.3. Persist the preference in
localStorageThe user's chosen direction must survive page reloads. Use
useLocalStoragefrom@mantine/hooks(already a dependency atclients/web/package.json:30) — it handles SSR-safe hydration, JSON serialization, and cross-tab sync via thestorageevent automatically, so we don't have to roll our own.inspector.sortDirection.logsinspector.sortDirection.historyinspector.sortDirection.networkinspector.prefix (matches what anmcp.json-flavored app would use as its localStorage namespace) so future preferences can co-exist without collisions and are easy to clear in bulk.useLocalStorageis\"newest-first\". If the stored value isn't one of the two validSortDirectionliterals (manual edit, schema drift, future option added then removed), fall back to the default rather than passing it through to theSelect— invalid values would put the toggle in an unselectable state.sortDirection(currently scoped toApp.tsx/InspectorView). TheSortToggleelement itself stays purely controlled — it does not know aboutlocalStorage. This keeps the element trivially reusable in stories, tests, and any future non-persistent context.Note that no current code in the repo uses
localStoragedirectly (grep -rn localStorage clients/web/srcreturns nothing). This issue establishes the convention; later preferences (theme, compact-mode default, etc.) should follow the sameinspector.<scope>.<key>pattern.Acceptance criteria
SortToggleexists atclients/web/src/components/elements/SortToggle/with.tsx,.test.tsx, and.stories.tsx.onChangeis called with the new direction.LoggingScreen,HistoryScreen, andNetworkScreeneach render the toggle in their toolbar and reorder their list in response. Screen-level tests cover the reorder.\"newest-first\"on all three screens unless implementation surfaces a reason to differ.localStoragekey under theinspector.sortDirection.*namespace and is restored on reload.\"newest-first\"rather than rendering an unselectable state..withProps()constants. No inline styles.Out of scope