Skip to content

feat: display session ID and custom title in chat UI#129

Open
dbasclpy wants to merge 2 commits intomatt1398:mainfrom
dbasclpy:feat/session-id-display
Open

feat: display session ID and custom title in chat UI#129
dbasclpy wants to merge 2 commits intomatt1398:mainfrom
dbasclpy:feat/session-id-display

Conversation

@dbasclpy
Copy link
Copy Markdown

@dbasclpy dbasclpy commented Mar 23, 2026

Summary

Adds session ID visibility and /rename custom title support. Closes #115.

  • New SessionInfoBar between SearchBar and ChatHistory: shows truncated session UUID, copy button, and claude --resume <id> button
  • Custom titles from /rename appear in sidebar session list and tab labels, replacing the first message preview
  • Both light (sidebar listing) and deep (session detail) metadata paths extract custom-title JSONL entries

Changes

  • SessionInfoBar.tsx (new): compact info strip with session ID and resume command
  • metadataExtraction.ts: extractCustomTitle() fast scanner for light metadata path
  • jsonl.ts: custom-title detection in deep metadata path
  • domain.ts: customTitle field on Session interface
  • ProjectScanner.ts: wire custom title through both metadata paths
  • SessionItem.tsx: prefer custom title over first message in sidebar
  • sessionDetailSlice.ts: prefer custom title in tab label updates
  • MiddlePanel.tsx: mount SessionInfoBar

Screenshot

Validation

  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (663/663, 10 new)
  • pnpm build passes
  • Tested with live session data in standalone mode

Summary by CodeRabbit

  • New Features

    • Sessions can include user-defined custom titles that override default names across the app (sidebar, tabs, session details).
    • A compact Session Info bar was added to the chat panel with session ID display and a one-click "Resume" copy action.
  • Performance

    • Custom titles are detected and cached to speed session listing and fallback flows.
  • Tests

    • Added tests for custom-title extraction and Session Info bar behavior.

Add SessionInfoBar component between SearchBar and ChatHistory showing
the session UUID (truncated, full on hover), copy-to-clipboard, and a
Resume button that copies `claude --resume <id>`.

Parse custom-title JSONL entries written by /rename and surface them
in the sidebar, tab label, and session detail. Both light and deep
metadata paths extract the title, with the light path using a fast
string-match scan to avoid full JSON parsing.

Closes matt1398#115
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the user experience by providing better session management and identification. It introduces the ability to view and copy session IDs directly within the chat UI and allows users to assign custom titles to their chat sessions. These custom titles are then reflected in various parts of the UI, improving navigation and organization of conversations.

Highlights

  • Session ID Visibility: A new SessionInfoBar component has been introduced to display the truncated session UUID, along with a copy button and a 'claude --resume ' command button, making it easier to access session identifiers.
  • Custom Session Titles: The UI now supports custom titles set via the /rename command. These custom titles are displayed in the sidebar session list and as tab labels, overriding the default first message preview.
  • Metadata Extraction for Custom Titles: Both light (sidebar listing) and deep (session detail) metadata paths have been updated to correctly extract and utilize custom-title JSONL entries, ensuring consistent display across the application.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai coderabbitai bot added the feature request New feature or request label Mar 23, 2026
@dbasclpy
Copy link
Copy Markdown
Author

SSt

For reference

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively adds the ability to display session IDs and custom titles in the UI. The changes are well-structured, spanning across the backend for data extraction and the frontend for display. The inclusion of both deep and light metadata extraction paths for custom titles is a thoughtful performance consideration. The new SessionInfoBar component is a nice addition for user convenience. I've added a couple of suggestions to improve maintainability and type safety. Overall, this is a solid feature implementation with good test coverage.

Comment on lines +419 to +422
const rawEntry = entry as unknown as Record<string, unknown>;
if (rawEntry.type === 'custom-title' && typeof rawEntry.customTitle === 'string') {
customTitle = rawEntry.customTitle;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This type assertion to Record<string, unknown> is necessary because the entry variable was optimistically cast to ChatHistoryEntry when parsed from JSON. However, a custom-title entry is not a valid ChatHistoryEntry, leading to this type-unsound workaround.

A more robust and type-safe approach would be to parse the JSON into a generic type (like unknown or Record<string, unknown>) and then use type guards to safely narrow it down to either a custom-title entry or a ChatHistoryEntry.

While a full fix would involve changing code outside of this diff, I recommend refactoring this in the future to improve type safety and avoid these kinds of workarounds.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Added mtime+size cache matching the existing sessionPreviewCache pattern. Fixed in 481c382.

@dbasclpy
Copy link
Copy Markdown
Author

Thanks for the review.

Type cast in jsonl.ts: Agreed the double-cast isn't ideal, but analyzeSessionFileMetadata already parses every line as ChatHistoryEntry before we get to it. Changing that would mean refactoring the whole entry parsing loop, which is outside the scope here. The inline comment documents why the cast is needed.

Inline styles vs Tailwind arbitrary values: The codebase consistently uses style={{ color: 'var(--color-text-muted)' }} (100 occurrences across 33 files) and never uses text-[var(--color-text-muted)]. Switching just this component would break consistency rather than improve it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 41e62d92-f229-4b32-ad3d-3fcdc04dfdad

📥 Commits

Reviewing files that changed from the base of the PR and between 3e62d58 and 481c382.

📒 Files selected for processing (2)
  • src/main/services/discovery/ProjectScanner.ts
  • src/renderer/components/chat/SessionInfoBar.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/services/discovery/ProjectScanner.ts
  • src/renderer/components/chat/SessionInfoBar.tsx

📝 Walkthrough

Walkthrough

Adds extraction and caching of per-session custom titles from JSONL session files, surfaces customTitle in Session objects and UI, introduces a SessionInfoBar component showing/copying session ID and resume command, and updates UI label logic and tests to prefer/display custom titles.

Changes

Cohort / File(s) Summary
Domain & Types
src/main/types/domain.ts
Added optional customTitle?: string to the Session interface.
Metadata extraction & JSONL parsing
src/main/utils/metadataExtraction.ts, src/main/utils/jsonl.ts
Added exported extractCustomTitle(filePath, fsProvider?) that streams JSONL searching for type === 'custom-title'; updated analyzeSessionFileMetadata and SessionFileMetadata to accumulate/return customTitle; re-exported extractCustomTitle.
Project scanning & caching
src/main/services/discovery/ProjectScanner.ts
Integrated extractCustomTitle into deep and lightweight session builders; added per-file sessionCustomTitleCache keyed by filePath storing { mtimeMs, size, customTitle } and a fast-scan cache revalidation for lightweight scans.
UI: Session info bar & layout
src/renderer/components/chat/SessionInfoBar.tsx, src/renderer/components/layout/MiddlePanel.tsx
Added SessionInfoBar component that shows shortened session ID, copy button, and "Resume" action (copies claude --resume <id>); rendered it in MiddlePanel between search and chat history.
UI: Session labels
src/renderer/components/sidebar/SessionItem.tsx, src/renderer/store/slices/sessionDetailSlice.ts
Updated label computation to prefer session.customTitle over session.firstMessage when present for sidebar rows and tab labels.
Tests
test/main/utils/jsonl.test.ts, test/renderer/components/SessionInfoBar.test.ts
Added tests for customTitle extraction (including multiple entries) and a SessionInfoBar suite verifying store integration, tab-scoped session resolution, resume command formatting, and optional customTitle propagation.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed All PR objectives align with issue #115 requirements: SessionInfoBar displays copyable session ID and resume command, custom titles from /rename are surfaced in sidebar and tab labels, and metadata extraction is efficiently implemented.
Out of Scope Changes check ✅ Passed All code changes directly support the stated objectives of exposing session IDs and custom titles in the UI; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/renderer/components/sidebar/SessionItem.tsx (1)

181-194: Unify and normalize session label derivation in one place.

handleClick and sessionLabel currently duplicate logic. Consider deriving once (with trim()/fallback handling) and reusing it to avoid drift and blank-label edge cases.

♻️ Suggested cleanup
+  const normalizedCustomTitle = session.customTitle?.trim();
+  const sessionLabel =
+    (normalizedCustomTitle && normalizedCustomTitle.length > 0
+      ? normalizedCustomTitle
+      : session.firstMessage?.slice(0, 50)) ?? 'Session';

   const handleClick = (event: React.MouseEvent): void => {
@@
-        label: session.customTitle ?? session.firstMessage?.slice(0, 50) ?? 'Session',
+        label: sessionLabel,
@@
-  const sessionLabel = session.customTitle ?? session.firstMessage?.slice(0, 50) ?? 'Session';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/sidebar/SessionItem.tsx` around lines 181 - 194, The
session label logic is duplicated between handleClick and the sessionLabel
constant and can produce blank labels; create a single normalized label (e.g.,
derive in a helper or at top of SessionItem using session.customTitle ??
session.firstMessage, apply trim(), and fallback to 'Session') and replace uses
in handleClick and sessionLabel with that single value so both navigation
(handleClick) and rendering reuse the same trimmed/fallback label.
test/renderer/components/SessionInfoBar.test.ts (2)

1-5: Consider renaming test file to reflect actual test scope.

The file tests store state and selector logic rather than the SessionInfoBar component itself. The name SessionInfoBar.test.ts suggests component tests, but these are store integration tests that simulate the selector logic manually.

This is acceptable for validating the data flow, but consider either:

  1. Renaming to sessionInfoBarStore.test.ts for clarity, or
  2. Adding actual component render tests using a testing library to verify the UI renders correctly with the store data.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/renderer/components/SessionInfoBar.test.ts` around lines 1 - 5, The test
file currently named SessionInfoBar.test.ts covers store/selector behavior
(store state, selector logic that determines visibility and session data) rather
than rendering the SessionInfoBar component; either rename the file to something
like sessionInfoBarStore.test.ts to reflect it tests selectors/store logic
(update any test imports) or add actual component render tests that mount the
SessionInfoBar component and assert the UI shows the session ID and resume
command using your testing library (refer to the SessionInfoBar component and
the selector(s) used in these tests to wire the store mock/state).

69-76: Test verifies string concatenation rather than component behavior.

This test constructs the resume command inline using template literals and asserts it matches itself. The actual resumeCommand construction logic lives in SessionInfoBar.tsx, not in the store.

Consider either removing this test or converting it to a component render test that verifies the resume command is correctly passed to the button's title attribute or clipboard action.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/renderer/components/SessionInfoBar.test.ts` around lines 69 - 76, The
test currently validates a template literal instead of component behavior;
update the test in SessionInfoBar.test.ts to render the SessionInfoBar component
(with store.setState({ sessionDetail: makeSessionDetail(sessionId) })) and
assert the resume command produced by the component is used in the UI (e.g.,
check the resume button's title/aria-label or the clipboard action) rather than
building the string inline; locate references to makeSessionDetail,
store.getState().sessionDetail, and the resume button in SessionInfoBar.tsx to
implement the render+assert flow, or remove the redundant test if you prefer.
src/renderer/components/chat/SessionInfoBar.tsx (1)

1-6: Import order doesn't follow coding guidelines.

Per coding guidelines, imports should be organized: external packages first, then path aliases (@renderer), then relative imports.

Suggested import reorder
 import React from 'react';
+import { Hash, Terminal } from 'lucide-react';
+import { useShallow } from 'zustand/react/shallow';

 import { CopyButton } from '@renderer/components/common/CopyButton';
 import { useStore } from '@renderer/store';
-import { Hash, Terminal } from 'lucide-react';
-import { useShallow } from 'zustand/react/shallow';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/chat/SessionInfoBar.tsx` around lines 1 - 6, The
import statements in SessionInfoBar.tsx are out of order; reorder them to follow
project guidelines: place external packages first (e.g., 'react',
'lucide-react', 'zustand/react/shallow'), then path-alias imports (e.g.,
'@renderer/components/common/CopyButton', '@renderer/store' which provide
CopyButton and useStore), and finally any relative imports if present; ensure
imports for React, Hash, Terminal, and useShallow come before the `@renderer`
imports to satisfy the project import ordering rule.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/services/discovery/ProjectScanner.ts`:
- Around line 824-825: The light metadata path in ProjectScanner repeatedly
calls extractCustomTitle(filePath, this.fsProvider) causing repeated streaming
of large JSONL; add an LRU cache keyed by filePath plus its mtime and size (use
stats from this.fsProvider.stat or equivalent) and consult it before calling
extractCustomTitle, updating the cache entry when mtime/size change; follow the
same caching pattern used for the preview cache (reuse its key format and
eviction policy) and store the resolved customTitle so subsequent light-listing
builds skip re-parsing unchanged files.

In `@src/renderer/components/chat/SessionInfoBar.tsx`:
- Around line 60-68: The Resume button in SessionInfoBar.tsx uses
navigator.clipboard.writeText(resumeCommand) directly in the onClick handler
which can cause unhandled promise rejections when the Clipboard API is
unavailable; update the onClick to call an async handler (or a promise .catch)
that first checks for navigator.clipboard, then awaits writeText inside a
try/catch and surface errors (e.g., log or show a user notification) so failures
are handled gracefully for the resumeCommand copy action.

---

Nitpick comments:
In `@src/renderer/components/chat/SessionInfoBar.tsx`:
- Around line 1-6: The import statements in SessionInfoBar.tsx are out of order;
reorder them to follow project guidelines: place external packages first (e.g.,
'react', 'lucide-react', 'zustand/react/shallow'), then path-alias imports
(e.g., '@renderer/components/common/CopyButton', '@renderer/store' which provide
CopyButton and useStore), and finally any relative imports if present; ensure
imports for React, Hash, Terminal, and useShallow come before the `@renderer`
imports to satisfy the project import ordering rule.

In `@src/renderer/components/sidebar/SessionItem.tsx`:
- Around line 181-194: The session label logic is duplicated between handleClick
and the sessionLabel constant and can produce blank labels; create a single
normalized label (e.g., derive in a helper or at top of SessionItem using
session.customTitle ?? session.firstMessage, apply trim(), and fallback to
'Session') and replace uses in handleClick and sessionLabel with that single
value so both navigation (handleClick) and rendering reuse the same
trimmed/fallback label.

In `@test/renderer/components/SessionInfoBar.test.ts`:
- Around line 1-5: The test file currently named SessionInfoBar.test.ts covers
store/selector behavior (store state, selector logic that determines visibility
and session data) rather than rendering the SessionInfoBar component; either
rename the file to something like sessionInfoBarStore.test.ts to reflect it
tests selectors/store logic (update any test imports) or add actual component
render tests that mount the SessionInfoBar component and assert the UI shows the
session ID and resume command using your testing library (refer to the
SessionInfoBar component and the selector(s) used in these tests to wire the
store mock/state).
- Around line 69-76: The test currently validates a template literal instead of
component behavior; update the test in SessionInfoBar.test.ts to render the
SessionInfoBar component (with store.setState({ sessionDetail:
makeSessionDetail(sessionId) })) and assert the resume command produced by the
component is used in the UI (e.g., check the resume button's title/aria-label or
the clipboard action) rather than building the string inline; locate references
to makeSessionDetail, store.getState().sessionDetail, and the resume button in
SessionInfoBar.tsx to implement the render+assert flow, or remove the redundant
test if you prefer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e693f8b0-e963-41d6-a088-18429f65565e

📥 Commits

Reviewing files that changed from the base of the PR and between f23e881 and 3e62d58.

📒 Files selected for processing (10)
  • src/main/services/discovery/ProjectScanner.ts
  • src/main/types/domain.ts
  • src/main/utils/jsonl.ts
  • src/main/utils/metadataExtraction.ts
  • src/renderer/components/chat/SessionInfoBar.tsx
  • src/renderer/components/layout/MiddlePanel.tsx
  • src/renderer/components/sidebar/SessionItem.tsx
  • src/renderer/store/slices/sessionDetailSlice.ts
  • test/main/utils/jsonl.test.ts
  • test/renderer/components/SessionInfoBar.test.ts

Cache extractCustomTitle results by mtime+size in the light metadata
path, matching the existing sessionPreviewCache pattern. Avoids
re-streaming JSONL files on every sidebar refresh when nothing changed.

Add .catch() to the Resume button clipboard call to prevent unhandled
promise rejections when the Clipboard API is unavailable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT]Add the session id into the UI

1 participant