feat(ui): show session names alongside IDs#183
Conversation
|
✅ All contributors have signed the CLA. Thank you! |
Display the session name next to its ID in the AgentCard footer, the
Dashboard and the Run picker. Since hooks don't provide real names,
renaming is done via PATCH /api/sessions/:id {name}.
Co-Authored-By: Claude <noreply@anthropic.com>
|
recheck |
b3c0dab to
37bd55d
Compare
There was a problem hiding this comment.
Code Review
This pull request enhances the dashboard, agent cards, and session picker by fetching and displaying user-defined session names instead of auto-generated session IDs. The feedback suggests refactoring the inline IIFE and duplicated session name parsing logic in Dashboard.tsx into a reusable helper function, and adding a maximum width constraint to the session name in Run.tsx to prevent layout overflow.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const [recentEvents, setRecentEvents] = useState<DashboardEvent[]>([]); | ||
| const [totalCost, setTotalCost] = useState<number | null>(null); | ||
| const [allSubagents, setAllSubagents] = useState<Agent[]>([]); | ||
| const [sessionsById, setSessionsById] = useState<Map<string, Session>>(new Map()); |
There was a problem hiding this comment.
To avoid duplicating the session name parsing logic and using an IIFE inside the JSX render loop, we can define a helper function getSessionLabel right after the sessionsById state definition. This keeps the component clean and maintainable.
| const [sessionsById, setSessionsById] = useState<Map<string, Session>>(new Map()); | |
| const [sessionsById, setSessionsById] = useState<Map<string, Session>>(new Map()); | |
| const getSessionLabel = useCallback((sessionId: string) => { | |
| const name = sessionsById.get(sessionId)?.name?.trim() || ""; | |
| return name && !/^Session [0-9a-f]{8}$/i.test(name) ? name : null; | |
| }, [sessionsById]); |
| {(() => { | ||
| // Session label: real name when one exists, else the | ||
| // short ID — keeps every activity row attributable. | ||
| const sname = sessionsById.get(event.session_id)?.name?.trim() || ""; | ||
| const isAuto = /^Session [0-9a-f]{8}$/i.test(sname); | ||
| return ( | ||
| <span | ||
| className="text-[11px] text-gray-500 truncate max-w-[9rem] flex-shrink-0" | ||
| title={event.session_id} | ||
| > | ||
| {sname && !isAuto ? ( | ||
| sname | ||
| ) : ( | ||
| <span className="font-mono">{event.session_id.slice(0, 8)}</span> | ||
| )} | ||
| </span> | ||
| ); | ||
| })()} |
There was a problem hiding this comment.
Using an IIFE inside JSX makes the render tree harder to read and maintain. We can simplify this by using the getSessionLabel helper function we defined earlier, resulting in much cleaner and more idiomatic React code.
<span
className="text-[11px] text-gray-500 truncate max-w-[9rem] flex-shrink-0"
title={event.session_id}
>
{getSessionLabel(event.session_id) || (
<span className="font-mono">{event.session_id.slice(0, 8)}</span>
)}
</span>
| {s.name?.trim() && ( | ||
| <span className="text-[11px] text-gray-200 truncate">{s.name.trim()}</span> | ||
| )} |
There was a problem hiding this comment.
If a session has an extremely long name, it can push other elements (like the ID and date) out of the container or cause layout overflow because the span lacks a width constraint. Adding max-w-[10rem] ensures it truncates gracefully, matching the truncation behavior used in AgentCard.tsx and Dashboard.tsx.
| {s.name?.trim() && ( | |
| <span className="text-[11px] text-gray-200 truncate">{s.name.trim()}</span> | |
| )} | |
| {s.name?.trim() && ( | |
| <span className="text-[11px] text-gray-200 truncate max-w-[10rem]">{s.name.trim()}</span> | |
| )} |
|
I have read the CLA Document and I hereby sign the CLA |
…mmary; strip em dashes from UI Session names (the PR's display had nothing to show — names were only the "Session <id8>" placeholder): - TranscriptCache now extracts the latest custom-title (/rename, `claude -n`, picker Ctrl+R) and ai-title (auto / plan-accept) from the JSONL. - The hook ingestor and the 15s watchdog keep sessions.name in sync and broadcast session_updated for live UI updates. custom-title always wins; ai-title only fills placeholder/auto names, so a user-chosen name is never clobbered. - import-history backfills names from the transcript title instead of the cwd folder name. Transcript viewer: - Surface /rename (custom-title) as an inline "Renamed session" marker, deduped; ai-title is excluded from the stream (it repeats every turn). Config Explorer: - Add a "Current configuration" summary that resolves the options /config controls (model, verbose, theme, output style, effort, auto-compact, notifications, ...) across user/project/project-local scopes, showing defaults when unset. Em dashes: - Replace every em dash with a plain hyphen across the client frontend (components, pages, i18n locales, index.html, sw.js) and the server-generated strings that render in the UI (agent names, event summaries). Screen snapshots regenerated. Tests: add server/__tests__/session-name-rename.test.js (cache extraction, name precedence, rename-marker dedupe). Server 366, client 229, typecheck + prettier clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
……) + document naming/config across all docs Transcript viewer: - Newer Claude Code builds write a local slash command's invocation and captured output (/color, /rename, /clear, custom commands) as `system`/`local_command` lines with the TUI markup (`<command-name>`, `<local-command-stdout>`/`stderr`) in a top-level `content` string instead of `user` messages. The transcript endpoint now surfaces those as user-side text so the client's `tuiSegments` parser renders the command pill + its captured output — e.g. `/color` now shows a "/color" pill plus "Session color set to: cyan". Content-less `local_command` lines (`/clear`) and every other `system` subtype (turn_duration, stop_hook_summary, …) are dropped as noise. Adds a regression test. Docs — session-name sync, rename markers, /config "Current configuration" summary, and the /color fix, kept consistent across every surface: - README-CN.md, README-VN.md — Conversation caption, Sessions row, Config Explorer row (translated). - ARCHITECTURE.md — hooks / sessions / transcript-cache / import-history / cc-config rows + the `system`/`local_command` surfacing. - index.html (landing) and wiki/index.html — with matching zh + vi entries in wiki/i18n-content.js and cache busts (i18n-content.js?v=8, wiki/sw.js CACHE_NAME wiki-v17). - docs/DATABASE.md, docs/HOOKS.md, docs/API.md, server/README.md, client/README.md. Verified: server 367, client 229, prettier clean, wiki i18n key parity checked with jsdom. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Shows the human-readable session name next to the session ID in three places: the AgentCard footer, the Dashboard, and the Run picker.
Why
Session IDs alone are hard to tell apart. A name makes it much easier to identify what each session is doing at a glance.
How
namealongsideidinAgentCard.tsx,Dashboard.tsxandRun.tsx(falls back to the ID when no name is set).PATCH /api/sessions/:id { name }endpoint (hooks don't supply real names).Notes
client/src/components/AgentCard.tsx,client/src/pages/Dashboard.tsx,client/src/pages/Run.tsx.