Skip to content

feat: Implement Pair Writing Mode (#369)#379

Merged
rjroy merged 19 commits intomainfrom
feat/369-pair-writing
Jan 21, 2026
Merged

feat: Implement Pair Writing Mode (#369)#379
rjroy merged 19 commits intomainfrom
feat/369-pair-writing

Conversation

@rjroy
Copy link
Copy Markdown
Owner

@rjroy rjroy commented Jan 21, 2026

Summary

Implements Pair Writing Mode, a desktop-only feature for AI-assisted text revision within the Recall (Browse) tab.

Key capabilities:

  • Split-screen layout with document editor on left, Discussion on right
  • Context menu with Quick Actions (Tighten, Embellish, Polish, Correct) for selected text
  • Advisory Actions (Validate, Critique, Compare) for document-level feedback
  • Integrated with Discussion session so conversations persist in Recent Sessions
  • Full-width layout with smooth animations when entering/exiting the mode

Architecture changes:

  • Pair Writing shares the Discussion session rather than creating isolated task-scoped sessions
  • Quick/Advisory Actions send structured messages that appear in conversation history
  • Sessions persist to disk and show in Recent Sessions on HomeView

UX improvements:

  • Animated transitions for layout changes (max-width, tree collapse)
  • Header collapse now works on all screen sizes (not just mobile)
  • Hidden on touch devices via CSS media query

Files changed

  • 54 files across backend, frontend, and shared packages
  • ~8,700 lines added (new components, hooks, tests, handlers)
  • Full test coverage with all pre-commit checks passing

Test plan

  • All backend tests pass (pair-writing-handlers, browser-handlers)
  • All frontend tests pass (PairWritingMode, usePairWritingState, etc.)
  • Full pre-commit checks pass
  • Manual testing: enter Pair Writing, use Quick Actions, verify conversation persists
  • Manual testing: verify Recent Sessions shows Pair Writing sessions
  • Manual testing: verify layout animations are smooth

Closes #369

🤖 Generated with Claude Code

@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 21, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements Pair Writing Mode, a desktop-only feature for AI-assisted text revision within the Recall (Browse) tab. The implementation adds a split-screen interface with a document editor on the left and the Discussion conversation on the right, enabling context menu-based Quick Actions (transformative edits) and Advisory Actions (analytical feedback).

Changes:

  • Protocol extensions for Quick/Advisory action messages in shared package
  • New frontend hooks for text selection tracking, pair writing state, and long-press gestures
  • Split-screen PairWritingMode component with integrated Discussion session
  • Backend handlers for processing Quick Actions (using Claude's Edit tool) and Advisory Actions (streaming feedback)
  • Full test coverage with 54 files changed (~8,700 lines added)

Reviewed changes

Copilot reviewed 41 out of 54 changed files in this pull request and generated no comments.

Show a summary per file
File Description
shared/src/protocol.ts Added Quick/Advisory action message schemas with selection context
shared/src/index.ts Exported new Pair Writing types
frontend/src/hooks/useTextSelection.ts Tracks text selection with line numbers and paragraph context extraction
frontend/src/hooks/usePairWritingState.ts Manages editor state (content, snapshot, unsaved changes)
frontend/src/hooks/useLongPress.ts Touch gesture detection for mobile context menu
frontend/src/components/PairWritingMode.tsx Main split-screen container with toolbar and Discussion integration
frontend/src/components/PairWritingEditor.tsx Editor component with Quick Action support and toast notifications
frontend/src/components/PairWritingToolbar.tsx Toolbar with Snapshot, Save, and Exit buttons
frontend/src/components/EditorContextMenu.tsx Context menu with Quick/Advisory actions and keyboard navigation
frontend/src/components/ConversationPane.tsx Extracted conversation display shared with Discussion
frontend/src/components/Discussion.tsx Refactored to support shared WebSocket connection
frontend/src/components/MarkdownViewer.tsx Added Pair Writing entry point button
frontend/src/components/BrowseMode.tsx Integration point with mode switching logic
frontend/src/App.css Header collapse improvements and full-width layout support
backend/src/handlers/pair-writing-handlers.ts Quick/Advisory action handlers with streaming support
backend/src/pair-writing-prompts.ts Action-specific prompt templates with position hints
backend/src/websocket-handler.ts Routing for new message types
Test files (×15) Comprehensive test coverage for all new functionality
CSS files (×5) Styling for new components with glass morphism consistency

rjroy and others added 19 commits January 20, 2026 18:28
Adds the foundational infrastructure for AI-assisted text revision:

Protocol (shared):
- QuickActionRequest, AdvisoryActionRequest, PairChatRequest schemas
- Action type enums for transformative and advisory actions

Hooks (frontend):
- useLongPress: Reusable long-press detection (500ms threshold)
- useTextSelection: Selection tracking with line numbers and paragraph context
- usePairWritingState: Session state management for Pair Writing Mode

Components (frontend):
- ConversationPane: Extracted from Discussion.tsx for reuse in Pair Writing

Prompts (backend):
- Action-specific prompt templates for Tighten, Embellish, Correct, Polish
- Position hint logic for document location context

Test coverage: 520 new tests across all modules.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds the context menu infrastructure for Quick Actions:

Components (frontend):
- EditorContextMenu: Portal-rendered menu with keyboard navigation
  - Right-click trigger for desktop
  - Long-press trigger for mobile (uses useLongPress hook)
  - Shows 4 Quick Actions: Tighten, Embellish, Correct, Polish
  - Accessible with role="menu" and keyboard navigation

Integration (frontend):
- MemoryEditor wired with context menu and selection tracking
  - Uses useTextSelection for selection context extraction
  - Only shows menu when text is selected
  - Prevents browser context menu when our menu opens

Test coverage: 49 new tests for EditorContextMenu and integration.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds the complete Quick Action flow for AI-assisted text revision:

Backend (handlers):
- handleQuickAction: Creates task-scoped Claude session with Read/Edit tools
  - Validates file path within vault
  - Builds action-specific prompts from templates
  - Streams events using existing infrastructure
  - Claude's Edit tool writes directly to vault file

Frontend (MemoryEditor):
- WebSocket integration for quick_action_request messages
- Loading indicator overlay during processing
- Toast notification for Claude's confirmation
- File reload on completion via onQuickActionComplete callback

Quick Actions (Tighten, Embellish, Correct, Polish) now fully functional
on both desktop (right-click) and mobile (long-press).

Test coverage: 41 new tests across handlers and frontend.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 4 tasks complete:
- TASK-011: PairWritingMode layout with split-screen editor/conversation
- TASK-012: Entry point in BrowseMode with "Enter Pair Writing" button
- TASK-013: Advisory action handlers (Validate, Critique, Compare)
- TASK-014: Extended context menu with advisory action icons

Features implemented:
- Advisory Actions stream responses via Anthropic API (no tool use)
- PairChat for freeform discussion about writing
- Context menu shows advisory actions in pair-writing mode
- Compare action appears when snapshot exists
- Icons for all action types (quick + advisory)

All checks passing: typecheck, lint, 548 unit tests.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Problem: PairWritingMode was incorrectly using MemoryEditor which
is hardcoded to load/display memory.md, not the file being viewed.

Solution:
- Revert MemoryEditor to original state (memory.md only)
- Create PairWritingEditor that:
  - Takes content via props (not fetching memory.md)
  - Supports text selection for Quick Actions
  - Supports Advisory Actions (Validate, Critique, Compare)
  - Handles streaming responses for Quick Actions
- Update PairWritingMode to use PairWritingEditor
- Update BrowseMode to pass sendMessage/lastMessage to PairWritingMode

The backend handlers and protocol remain unchanged.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Integrates Pair Writing Mode with Discussion tab so they share the same
AI session. Quick Actions and Advisory Actions now appear in the
conversation history alongside regular chat messages.

Key changes:

Backend:
- Remove handlePairChat (pair_chat_request removed from protocol)
- Update handleQuickAction/handleAdvisoryAction to use resumeSession
  when existing session available
- Remove Anthropic SDK import, use Claude Agent SDK throughout
- Add resumeSession/appendMessage to handler dependencies

Frontend:
- Discussion accepts optional sendMessage/connectionStatus/lastMessage
  props for shared WebSocket connection
- PairWritingMode embeds Discussion instead of ConversationPane
- BrowseMode skips streaming messages when PairWritingMode is active
  (fixes double-speak bug where text was duplicated)
- Simplify usePairWritingState: remove conversation/selection state

Protocol:
- Remove PairChatRequestMessage (no longer needed)

This refactor removes ~250 lines of duplicate state management while
maintaining full functionality.

Note: Some pre-existing SessionContext tests are failing (unrelated to
this change).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When no session exists, Quick Actions and Advisory Actions now use
ctx.deps.createSession instead of task-scoped query functions. This
ensures:

1. Sessions are persisted to the vault's session directory
2. Sessions appear in Recent Sessions on HomeView
3. Conversation history is preserved for future interactions

Changes:
- Add createSession to HandlerDependencies and RequiredHandlerDependencies
- Update pair-writing-handlers.ts to use ctx.deps.createSession
- Refactor pair-writing-handlers.test.ts to use createMockCreateSession
- Add createSession mock to browser-handlers.test.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The 1000px max-width constraint on app-main is good for most views but
too narrow for Pair Writing's side-by-side editor + discussion layout.

Sets a data-pair-writing attribute on <html> when pair writing is active
(following the existing pattern for holiday backgrounds), and overrides
max-width to 100% via CSS.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a 0.3s ease transition so the layout smoothly expands/contracts
when entering or exiting Pair Writing mode. Changed target from 100%
to 100vw for reliable CSS interpolation between pixel values.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a 0.3s ease transition to the grid-template-columns so the tree
pane smoothly collapses from 280px to 48px (and vice versa) when the
collapse button is clicked.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The collapsible header was previously mobile-only. Now clicking the
title collapses the header on desktop too, and clicking the logo
button expands it back. This gives users more vertical space when
they want to focus on content.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The test file was using mock.module to mock SessionContext globally,
which persisted across test files and caused 138 SessionContext tests
to fail when run in the full suite.

Fixed by:
- Removing the SessionContext mock.module call
- Using a real SessionProvider wrapper instead
- Updating all render calls to use the wrapper

This follows the pattern of using dependency injection (wrapper) instead
of global module mocking, which is more reliable for test isolation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@rjroy rjroy force-pushed the feat/369-pair-writing branch from f4f48a0 to c567c9a Compare January 21, 2026 02:28
@rjroy rjroy merged commit 46aad7c into main Jan 21, 2026
2 checks passed
@rjroy rjroy deleted the feat/369-pair-writing branch January 21, 2026 02:35
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.

Pair Writing Mode

2 participants