Skip to content

Add a Resume Session button to the transcript HTML toolbar - #308

Open
max-nothacker wants to merge 2 commits into
daaain:mainfrom
max-nothacker:feat/resume-session-button
Open

Add a Resume Session button to the transcript HTML toolbar#308
max-nothacker wants to merge 2 commits into
daaain:mainfrom
max-nothacker:feat/resume-session-button

Conversation

@max-nothacker

@max-nothacker max-nothacker commented Aug 1, 2026

Copy link
Copy Markdown

Summary

Adds a floating ▶ Resume Session button to transcript HTML pages, next to the existing md / uuid / timeline / search / details / scroll-to-top buttons. Clicking it copies

cd "C:\Users\me" && claude -r <session-id>     (Windows-recorded session)
cd /Users/me/project && claude -r <session-id> (POSIX-recorded session)

to the clipboard and shows a toast prompting the user to paste it into a terminal. The TUI already resumes sessions (the c binding runs claude -r directly); this brings the same capability to the HTML output, where spawning a process isn't possible — the clipboard is the next best thing.

Behavior

  • Single-session pages only. The button renders only when the page holds exactly one trunk session (session IDs collected via collect_trunk_session_ids, so agent sidechains coalesce onto their parent and warmup-only sessions are ignored). Combined pages spanning several sessions don't get it — claude -r would be ambiguous there.
  • OS-aware quoting. The command is built server-side (resume_command_for_session in utils.py) from the session's recorded cwd (the same canonical cwd already computed for SHA linkification). Path shape decides the quoting — PureWindowsPath(cwd).drive → double quotes for Windows shells, otherwise shlex.quote for POSIX — following the host-decoupled path-shape idiom of path_looks_absolute (Support Obsidian-friendly output #151). Detecting the viewer's OS would be wrong: the session must be resumed on the machine that recorded it, wherever the HTML is being read.
  • No recorded cwd → falls back to a bare claude -r <session-id>.
  • Clipboard fallback. If the Clipboard API is unavailable or refused, a window.prompt shows the command for manual copying.

Tests

  • test/test_resume_session_button.py — unit tests for resume_command_for_session (Windows/POSIX/spaces/no-cwd) and for button presence/absence + attribute escaping in generated HTML.
  • test/test_resume_session_browser.py — Playwright tests: clicking copies the exact command (clipboard stubbed) and shows the toast; multi-session pages have no button.
  • HTML snapshots regenerated serially (-n0 --snapshot-update); the .ambr diff is purely additive (+768/−0), and the button element appears only in single-session snapshots.
  • Full gate run locally on Windows: unit + TUI + browser suites, ruff format, ruff check, pyright (0 errors), ty check.

🤖 Generated with Claude Code in the session with id 4229268a

Summary by CodeRabbit

  • New Features
    • Added a floating “Resume Session” button to applicable transcript pages.
    • Clicking the button copies a ready-to-run resume command and shows confirmation.
    • Added a fallback prompt when clipboard access is unavailable.
  • Bug Fixes
    • Resume actions are hidden for transcripts containing multiple sessions or unsafe session data.
  • Tests
    • Added coverage for browser interactions, command formatting, and supported operating systems.

Single-session HTML pages get a floating '▶ Resume Session' button that
copies `cd <cwd> && claude -r <session-id>` to the clipboard and shows
a toast prompting the user to paste it into a terminal. The command's
quoting follows the OS the transcript was recorded on, detected from
the recorded cwd's path shape (the host-decoupled idiom of
path_looks_absolute, daaain#151) — the session must be resumed on the machine
that recorded it, wherever the page is viewed. Combined pages spanning
several trunk sessions don't render the button since `claude -r`
would be ambiguous there.

The TUI already resumes sessions (the 'c' binding execs claude -r);
this brings the same capability to the HTML output, where spawning a
process isn't possible.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> in the session with id 4229268a
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The HTML renderer now creates a resume command for single-session transcripts. The transcript adds a floating button that copies the command or uses a prompt fallback. CSS, snapshots, unit tests, and Playwright tests cover the new behavior.

Changes

Resume Session workflow

Layer / File(s) Summary
Resume command generation and rendering
claude_code_log/utils.py, claude_code_log/html/renderer.py, test/test_resume_session_button.py
The renderer passes the working directory into command generation. A command is rendered only for one trunk session. Tests cover path quoting, unsafe input, missing directories, and session-count cases.
Resume button interaction and styling
claude_code_log/html/templates/transcript.html, claude_code_log/html/templates/components/global_styles.css, test/__snapshots__/test_snapshot_html.ambr
The transcript provides clipboard copying, a five-second success toast, and prompt fallback behavior. Styles and snapshots cover the rendered control.
Browser interaction validation
test/test_resume_session_browser.py
Playwright tests verify command copying, toast feedback, and button suppression on multi-session pages.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HTMLRenderer
  participant transcript.html
  participant Browser
  participant ClipboardAPI
  HTMLRenderer->>transcript.html: pass resume_command
  transcript.html-->>Browser: render Resume Session button
  Browser->>ClipboardAPI: copy resume command
  ClipboardAPI-->>Browser: return success or failure
  Browser-->>Browser: show toast or prompt fallback
Loading

Suggested reviewers: cboos, daaain

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a Resume Session button to transcript HTML.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
test/test_resume_session_browser.py (2)

15-16: 📐 Maintainability & Code Quality | 🔵 Trivial

Run the required validation before pushing.

As per coding guidelines, run Ruff formatting and linting plus pyright or ty for this Python change. Run just ci before pushing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/test_resume_session_browser.py` around lines 15 - 16, Before pushing,
run the repository’s required validation for the Python test changes in
test_resume_session_browser.py: execute Ruff formatting and linting, run pyright
or ty, and complete the full just ci check.

Source: Coding guidelines


85-96: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add browser coverage for the window.prompt fallback.

This helper always provides a successful navigator.clipboard.writeText. The browser suite cannot detect a regression in the fallback required by this workflow. Add a test that makes clipboard writes fail or unavailable, stubs window.prompt, and verifies the command and toast.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/test_resume_session_browser.py` around lines 85 - 96, Extend the browser
coverage around the clipboard setup in test_resume_session_browser.py with a
case where navigator.clipboard.writeText fails or is unavailable, stub
window.prompt, and verify the expected command plus success toast from the
fallback workflow. Keep the existing successful clipboard path intact while
ensuring the fallback behavior is exercised.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@claude_code_log/html/templates/components/global_styles.css`:
- Around line 367-376: Update the font-family declaration in
.resume-session.floating-btn to remove the quotes around SFMono-Regular while
preserving the existing fallback fonts and styling.

In `@claude_code_log/html/templates/transcript.html`:
- Around line 291-301: Update the toast creation block for resumeToast to set
role="status" and aria-live="polite" before appending the element to
document.body, ensuring copy confirmations are announced by assistive
technology.

In `@claude_code_log/utils.py`:
- Around line 215-238: Harden resume_command_for_session by validating and
normalizing session_id as the expected UUID before constructing any command,
rejecting invalid values. For Windows paths, reject embedded double quotes (or
otherwise generate syntax safe for one explicitly supported Windows shell)
before inserting cwd into the command. Preserve the existing POSIX/Windows
command behavior for valid inputs, and add regression coverage for shell
metacharacters in session_id and quoted Windows cwd values.

In `@test/__snapshots__/test_snapshot_html.ambr`:
- Around line 5214-5227: Add role="status" and aria-live="polite" to the
resumeToast element created in showResumeToast, then regenerate the affected
HTML snapshots to capture the accessibility attributes.

In `@test/test_resume_session_browser.py`:
- Line 98: Replace the file URL construction in both browser navigation calls in
the resume-session test with the HTML path’s Path.as_uri() result, passing that
portable URI to Page.goto. Before pushing the changes, remind the user to run
just ci.

---

Nitpick comments:
In `@test/test_resume_session_browser.py`:
- Around line 15-16: Before pushing, run the repository’s required validation
for the Python test changes in test_resume_session_browser.py: execute Ruff
formatting and linting, run pyright or ty, and complete the full just ci check.
- Around line 85-96: Extend the browser coverage around the clipboard setup in
test_resume_session_browser.py with a case where navigator.clipboard.writeText
fails or is unavailable, stub window.prompt, and verify the expected command
plus success toast from the fallback workflow. Keep the existing successful
clipboard path intact while ensuring the fallback behavior is exercised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c40b1757-7232-4c39-85e1-80985c736b7b

📥 Commits

Reviewing files that changed from the base of the PR and between 0a3327d and 6f4e42c.

📒 Files selected for processing (7)
  • claude_code_log/html/renderer.py
  • claude_code_log/html/templates/components/global_styles.css
  • claude_code_log/html/templates/transcript.html
  • claude_code_log/utils.py
  • test/__snapshots__/test_snapshot_html.ambr
  • test/test_resume_session_browser.py
  • test/test_resume_session_button.py

Comment on lines +367 to +376
.resume-session.floating-btn {
bottom: 340px;
border-radius: 6px;
width: auto;
height: 28px;
padding: 0 10px;
font-size: 0.65em;
font-family: 'SFMono-Regular', Consolas, monospace;
font-weight: 600;
white-space: nowrap;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the Stylelint font-family violation.

Line 374 quotes SFMono-Regular, but the configured Stylelint rule rejects these quotes. Remove them so linting passes.

Proposed fix
-    font-family: 'SFMono-Regular', Consolas, monospace;
+    font-family: SFMono-Regular, Consolas, monospace;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.resume-session.floating-btn {
bottom: 340px;
border-radius: 6px;
width: auto;
height: 28px;
padding: 0 10px;
font-size: 0.65em;
font-family: 'SFMono-Regular', Consolas, monospace;
font-weight: 600;
white-space: nowrap;
.resume-session.floating-btn {
bottom: 340px;
border-radius: 6px;
width: auto;
height: 28px;
padding: 0 10px;
font-size: 0.65em;
font-family: SFMono-Regular, Consolas, monospace;
font-weight: 600;
white-space: nowrap;
🧰 Tools
🪛 Stylelint (17.14.1)

[error] 374-374: Expected no quotes around "SFMono-Regular" (font-family-name-quotes)

(font-family-name-quotes)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@claude_code_log/html/templates/components/global_styles.css` around lines 367
- 376, Update the font-family declaration in .resume-session.floating-btn to
remove the quotes around SFMono-Regular while preserving the existing fallback
fonts and styling.

Source: Linters/SAST tools

Comment thread claude_code_log/html/templates/transcript.html
Comment thread claude_code_log/utils.py Outdated
Comment thread test/__snapshots__/test_snapshot_html.ambr
Comment thread test/test_resume_session_browser.py Outdated
- resume_command_for_session returns None (no button) for session ids
  outside a conservative charset, Windows cwds containing " % ! $ or
  backtick (cmd/PowerShell expand those inside double quotes), and any
  cwd with a newline (pasting multi-line text can execute each line).
  Regression tests for each rejection plus shlex-escaping of a single
  quote in a POSIX cwd.
- Toast gets role=status + aria-live=polite so screen readers announce
  the copy confirmation; snapshots regenerated serially (additive).
- Browser test navigations use Path.as_uri(); docstrings added to new
  test helpers and methods.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> in the session with id 4229268a
@max-nothacker

Copy link
Copy Markdown
Author

Addressed the review in the latest push — per-finding outcome:

  1. Shell-command injection (utils.py) — fixed. resume_command_for_session now returns None (no button rendered) instead of a command when: the session id doesn't match a conservative [A-Za-z0-9][A-Za-z0-9._-]* charset; a Windows cwd contains " % ! $ or a backtick (cmd expands %var%/!var! and PowerShell expands $var/backtick escapes inside double quotes); or any cwd contains a newline (pasting multi-line text can execute each line immediately, so quoting alone is no defence — this also covers the POSIX branch, where shlex.quote handles everything else). Regression tests added for metacharacter session ids, an embedded quote / expansion characters in Windows cwds, newlines, and a single quote in a POSIX cwd — plus an HTML-level test that an unsafe session id renders no button.
  2. Toast live-region semantics (transcript.html) — fixed. The toast now gets role="status" and aria-live="polite" before being appended; snapshots regenerated (serially, -n0 — diff purely additive).
  3. Path.as_uri() in the browser test — fixed. Both page.goto calls use it now.
  4. Stylelint font-family-name-quotes — intentionally skipped. The repo has no Stylelint config; the quoted 'SFMono-Regular' form matches the three existing floating-button rules directly above in global_styles.css (lines 257/314/356). Unquoting only the new rule would make it inconsistent with its siblings — happy to do a repo-wide sweep in a separate PR if the maintainer wants that rule adopted.
  5. Docstring coverage — addressed. Added docstrings to every new test helper and test method.

Full gate re-run locally: unit + browser suites, ruff format/check, pyright (0 errors), ty check — all green.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@claude_code_log/utils.py`:
- Around line 250-255: Update the Windows command generation in the
drive-handling branch of the relevant utility to switch to the recorded drive as
well as the directory, using syntax compatible with both cmd.exe and PowerShell
(such as pushd). Add a regression test covering a D: working directory while the
terminal starts on C:, and update the corresponding expectations in
test_resume_session_button.py.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b1a04016-d372-463e-9e9a-1a6778a8ef3f

📥 Commits

Reviewing files that changed from the base of the PR and between 6f4e42c and 24e1cb1.

📒 Files selected for processing (5)
  • claude_code_log/html/templates/transcript.html
  • claude_code_log/utils.py
  • test/__snapshots__/test_snapshot_html.ambr
  • test/test_resume_session_browser.py
  • test/test_resume_session_button.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/test_resume_session_browser.py
  • claude_code_log/html/templates/transcript.html
  • test/snapshots/test_snapshot_html.ambr

Comment thread claude_code_log/utils.py
Comment on lines +250 to +255
if PureWindowsPath(cwd).drive:
if _WINDOWS_CWD_UNSAFE_RE.search(cwd):
return None
# Windows shells (PowerShell 7+, cmd): double quotes handle
# spaces; backslashes are literal inside them.
return f'cd "{cwd}" && claude -r {session_id}'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use a command that changes the Windows drive.

cmd.exe keeps its active drive after cd "D:\repo". If the terminal starts on C:, this command starts Claude from the current C: directory instead of the recorded working directory. The comment states that the command supports both cmd and PowerShell.

Generate syntax that works in each declared shell, such as pushd, or explicitly support one shell. Add a regression test for a D: working directory when the terminal starts on C:. Update the command expectations in test/test_resume_session_button.py.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@claude_code_log/utils.py` around lines 250 - 255, Update the Windows command
generation in the drive-handling branch of the relevant utility to switch to the
recorded drive as well as the directory, using syntax compatible with both
cmd.exe and PowerShell (such as pushd). Add a regression test covering a D:
working directory while the terminal starts on C:, and update the corresponding
expectations in test_resume_session_button.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant