Conversation
- Two-column layout with resizable panel (15-60%, persisted) - Token validation via Tauri backend (bypasses CORS) - Streaming responses with real-time updates - Markdown rendering with Mermaid diagram support Commands (type / for autocomplete menu): - /clear - Reset conversation - /summarise - Generate document summary - /model - Select model (haiku-4.5, sonnet-4.5, opus-4.5) - /export - Export chat as markdown in new tab UI: - Ctrl+A toggle, Escape close, Enter send - Purple AI button (customizable in settings) - Model stored in localStorage with validation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a Claude-based AI Chat to the desktop app: Tauri backend commands for token validation and SSE-style streaming, a SolidJS AI Chat panel with token handling, streaming UI and command palette, a Claude client, state store, styles, theme/color additions, and persistent panel width. Changes
Sequence DiagramsequenceDiagram
participant User
participant Frontend as App (UI)
participant Tauri as Tauri Backend
participant Claude as Claude API
rect rgba(220,235,255,0.5)
Note over User,Frontend: Token validation
User->>Frontend: Enter token
Frontend->>Tauri: invoke validate_claude_token(token)
Tauri->>Claude: auth request
Claude-->>Tauri: success/error
Tauri-->>Frontend: validation result
end
rect rgba(220,255,225,0.5)
Note over User,Claude: Streaming chat
User->>Frontend: Send message + model
Frontend->>Tauri: invoke stream_claude_chat(request, requestId)
Tauri->>Claude: POST /messages (streaming)
Claude-->>Tauri: start event
Tauri-->>Frontend: emit "ai-chat-start-<id>"
loop deltas
Claude-->>Tauri: delta chunk
Tauri-->>Frontend: emit "ai-chat-delta-<id>"
Frontend->>Frontend: update assistant message progressively
end
Claude-->>Tauri: done event
Tauri-->>Frontend: emit "ai-chat-done-<id>"
Frontend->>Frontend: finalize message
end
rect rgba(255,240,220,0.5)
Note over User,Frontend: Abort / errors
User->>Frontend: Stop / Close
Frontend->>Tauri: signal abort
Tauri->>Claude: cancel request
Tauri-->>Frontend: emit error/cleanup
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (5)
AGENTS.md (1)
241-241: Capitalize "Markdown" as a proper noun.Line 241 uses lowercase "markdown"; per documentation style conventions, it should be capitalized as "Markdown".
-| `/export` | Export chat as markdown in new tab | +| `/export` | Export chat as Markdown in new tab |packages/app/src-tauri/src/lib.rs (1)
527-531: Consider adding a timeout for the streaming HTTP request.The streaming request has no timeout configured. If the API becomes unresponsive, the request could hang indefinitely, degrading user experience.
🔎 Proposed fix to add a timeout
let client = reqwest::Client::new(); - let mut http_request = client + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(120)) + .build() + .map_err(|e| format!("Failed to create HTTP client: {}", e))?; + + let mut http_request = client .post("https://api.anthropic.com/v1/messages") .header("Content-Type", "application/json") .header("anthropic-version", "2023-06-01");packages/app/src/App.tsx (1)
474-477: Consider potential conflict with standard "Select All" shortcut.
Ctrl+Ais commonly used for "Select All" in most applications. Intercepting it for AI chat toggle may surprise users trying to select text. The PR description mentions "Alt/Ctrl + A" but onlyCtrlis checked here.Consider using
Alt+Ainstead, or checking fore.altKey:🔎 Proposed alternative using Alt key
- case "a": - e.preventDefault(); - toggleAiChat(); - break; + case "a": + if (e.altKey) { + e.preventDefault(); + toggleAiChat(); + } + break;Note: If keeping
Ctrl+A, you may want to only intercept it when not in an input/textarea context.packages/app/src/stores/ai-chat-store.ts (2)
123-132: Potential ID collision withDate.now().Using
Date.now()for message IDs can cause collisions if multiple messages are created within the same millisecond (e.g., rapid user input or programmatic batch creation).🔎 Proposed fix using crypto.randomUUID()
function addUserMessage(content: string): AiMessage { const message: AiMessage = { - id: `user-${Date.now()}`, + id: `user-${crypto.randomUUID()}`, role: "user", content, timestamp: Date.now(), }; setAiMessages([...aiMessages(), message]); return message; } function addAssistantMessage(content: string = "", isStreaming: boolean = false): AiMessage { const message: AiMessage = { - id: `assistant-${Date.now()}`, + id: `assistant-${crypto.randomUUID()}`, role: "assistant", content, timestamp: Date.now(), isStreaming, }; setAiMessages([...aiMessages(), message]); return message; }Also applies to: 137-147
48-51: localStorage access at module initialization.Accessing
localStoragesynchronously during module load can throw in restricted contexts (SSR, sandboxed iframes, or when storage is disabled). Consider wrapping in a try-catch or deferring initialization.🔎 Proposed defensive approach
+function getStoredToken(): string | null { + try { + return localStorage.getItem(TOKEN_STORAGE_KEY); + } catch { + return null; + } +} + /** API token (null if not set) */ const [aiToken, setAiTokenInternal] = createSignal<string | null>( - localStorage.getItem(TOKEN_STORAGE_KEY) + getStoredToken() );
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
bun.lockis excluded by!**/*.lockpackages/app/src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (15)
AGENTS.md(1 hunks)packages/app/package.json(1 hunks)packages/app/src-tauri/Cargo.toml(1 hunks)packages/app/src-tauri/src/lib.rs(3 hunks)packages/app/src/App.tsx(5 hunks)packages/app/src/components/ai-chat-panel.tsx(1 hunks)packages/app/src/components/file-header.tsx(3 hunks)packages/app/src/components/settings-modal.tsx(2 hunks)packages/app/src/services/claude-client.ts(1 hunks)packages/app/src/stores/ai-chat-store.ts(1 hunks)packages/app/src/stores/app-store.ts(1 hunks)packages/app/src/styles/ai-chat.css(1 hunks)packages/app/src/types.ts(2 hunks)packages/app/src/utils.ts(2 hunks)packages/shared/styles/theme.css(2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
packages/app/{package.json,src-tauri/Cargo.toml,src-tauri/tauri.conf.json}
📄 CodeRabbit inference engine (AGENTS.md)
Desktop app version must be updated in: package.json, src-tauri/Cargo.toml, and src-tauri/tauri.conf.json
Files:
packages/app/package.jsonpackages/app/src-tauri/Cargo.toml
packages/app/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
packages/app/src/**/*.{ts,tsx}: Use SolidJS signals for state management, NOT React hooks (useState, useEffect, etc.)
Import SolidJS primitives (createSignal, createEffect, Show, For) for reactivity, not React patterns
Files:
packages/app/src/components/file-header.tsxpackages/app/src/stores/app-store.tspackages/app/src/utils.tspackages/app/src/services/claude-client.tspackages/app/src/App.tsxpackages/app/src/types.tspackages/app/src/components/ai-chat-panel.tsxpackages/app/src/stores/ai-chat-store.tspackages/app/src/components/settings-modal.tsx
packages/{app,extension}/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Shared styles must be imported from @md/shared/styles workspace package (theme.css, markdown.css, print.css)
Files:
packages/app/src/components/file-header.tsxpackages/app/src/stores/app-store.tspackages/app/src/utils.tspackages/app/src/services/claude-client.tspackages/app/src/App.tsxpackages/app/src/types.tspackages/app/src/components/ai-chat-panel.tsxpackages/app/src/stores/ai-chat-store.tspackages/app/src/components/settings-modal.tsx
packages/app/src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Desktop app components must be created in packages/app/src/components/ directory and use SolidJS patterns
Files:
packages/app/src/components/file-header.tsxpackages/app/src/components/ai-chat-panel.tsxpackages/app/src/components/settings-modal.tsx
packages/app/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
State management must be defined in packages/app/src/stores/app-store.ts and exported as signals with setters
Files:
packages/app/src/stores/app-store.tspackages/app/src/stores/ai-chat-store.ts
packages/app/src-tauri/src/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
packages/app/src-tauri/src/**/*.rs: Tauri commands must be defined in packages/app/src-tauri/src/lib.rs with #[tauri::command] decorator and registered in invoke_handler![]
Tauri resource paths with ../prefix in tauri.conf.json must be accessed in Rust code with up/ directory prefix
Files:
packages/app/src-tauri/src/lib.rs
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: dnl-fm/md PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T13:58:12.139Z
Learning: Desktop app flow is: SolidJS UI → Tauri invoke() → Rust backend → File I/O
📚 Learning: 2025-12-18T13:58:12.139Z
Learnt from: CR
Repo: dnl-fm/md PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T13:58:12.139Z
Learning: Applies to packages/{app,extension}/src/**/*.{ts,tsx} : Shared styles must be imported from md/shared/styles workspace package (theme.css, markdown.css, print.css)
Applied to files:
packages/app/src/stores/app-store.ts
📚 Learning: 2025-12-18T13:58:12.139Z
Learnt from: CR
Repo: dnl-fm/md PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T13:58:12.139Z
Learning: Applies to packages/app/{package.json,src-tauri/Cargo.toml,src-tauri/tauri.conf.json} : Desktop app version must be updated in: package.json, src-tauri/Cargo.toml, and src-tauri/tauri.conf.json
Applied to files:
packages/app/src-tauri/Cargo.toml
📚 Learning: 2025-12-18T13:58:12.139Z
Learnt from: CR
Repo: dnl-fm/md PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T13:58:12.139Z
Learning: Markdown rendering in extension uses markdown-it (bundled), Prism.js for syntax highlighting, API for Mermaid/ASCII diagrams
Applied to files:
packages/app/src/App.tsx
📚 Learning: 2025-12-18T13:58:12.139Z
Learnt from: CR
Repo: dnl-fm/md PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T13:58:12.139Z
Learning: Applies to packages/app/src/stores/**/*.{ts,tsx} : State management must be defined in packages/app/src/stores/app-store.ts and exported as signals with setters
Applied to files:
packages/app/src/stores/ai-chat-store.ts
📚 Learning: 2025-12-18T13:58:12.139Z
Learnt from: CR
Repo: dnl-fm/md PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-18T13:58:12.139Z
Learning: Applies to packages/app/src/**/*.{ts,tsx} : Use SolidJS signals for state management, NOT React hooks (useState, useEffect, etc.)
Applied to files:
packages/app/src/stores/ai-chat-store.ts
🧬 Code graph analysis (3)
packages/app/src/components/file-header.tsx (1)
packages/app/src/stores/ai-chat-store.ts (1)
toggleAiChat(250-250)
packages/app/src/services/claude-client.ts (1)
packages/app/src/stores/ai-chat-store.ts (2)
AiMessage(17-23)AiModel(25-25)
packages/app/src/components/ai-chat-panel.tsx (3)
packages/app/src/utils.ts (2)
escapeHtml(121-130)getFilename(105-107)packages/app/src/stores/ai-chat-store.ts (2)
AI_MODELS(27-31)AiModel(25-25)packages/app/src/services/claude-client.ts (2)
validateToken(81-101)streamChat(118-179)
🪛 LanguageTool
AGENTS.md
[uncategorized] ~241-~241: Did you mean the formatting language “Markdown” (= proper noun)?
Context: ...submenu) | | /export | Export chat as markdown in new tab | ### Command Menu Pattern ...
(MARKDOWN_NNP)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: test
- GitHub Check: build-check
🔇 Additional comments (27)
AGENTS.md (1)
228-274: Documentation for AI Chat feature is comprehensive and well-structured.The new section clearly outlines the command interface, menu patterns, and available models with appropriate TypeScript interface examples and persistence details. It integrates naturally with the existing AGENTS.md style and provides users with actionable information for using the Claude integration.
packages/app/src/utils.ts (1)
39-39: LGTM!The AI chat button colors are well-chosen and consistent with the existing color scheme. The purple shades (#8b5cf6 for dark, #7c3aed for light) provide good contrast and align with typical AI feature styling.
Also applies to: 78-78
packages/app/src/services/claude-client.ts (3)
106-113: LGTM!The message filtering logic correctly excludes system messages (which are handled separately via
buildSystemPrompt) and transforms user/assistant messages to the API format.
118-179: LGTM - Well-structured streaming implementation!The streaming implementation follows good practices:
- Event listener is set up before invoking the command (prevents race conditions)
- Abort signal is properly checked to prevent callbacks after cancellation
- Cleanup is guaranteed via finally block
- Text accumulation correctly builds fullText from deltas
37-72: LGTM!The system prompt is well-structured and provides clear context about the application, capabilities, and constraints. The mention of
/tabsas a "future feature" is appropriately documented.packages/app/src/stores/app-store.ts (1)
262-262: LGTM!The CSS custom property for the AI chat button follows the existing pattern and correctly integrates with the theme color system using the
getColorhelper.packages/shared/styles/theme.css (1)
31-31: LGTM!The CSS custom properties for the AI chat button are correctly defined in both dark and light themes, matching the default color values in
packages/app/src/utils.ts.Also applies to: 74-74
packages/app/src/types.ts (1)
74-75: LGTM!The type additions are clean, well-documented, and follow the existing patterns. Making
ai_chat_widthoptional is appropriate for backward compatibility with existing configurations.Also applies to: 106-107
packages/app/src-tauri/Cargo.toml (1)
34-36: Dependencies are current with no identified security issues.The versions used enable proper caret ranges: reqwest 0.12 resolves to latest 0.12.x (0.12.25+), futures 0.3 resolves to latest 0.3.x (0.3.31+), and tokio-stream 0.1 resolves to latest 0.1.x (0.1.17+). All three crates are actively maintained. No CVEs have been identified for these specific crates. Note: CVE-2025-62518 (TARmageddon) affects tokio-tar (archive handling), not tokio-stream (stream utilities).
packages/app/package.json (1)
20-20: @anthropic-ai/sdk version 0.71.2 is current and secure. This is the latest stable version as of now, last published 12 days ago. No known security vulnerabilities have been identified in this release.packages/app/src/components/file-header.tsx (1)
69-75: The Ctrl+A keyboard shortcut is properly implemented in the app's global keyboard event handler (App.tsx, lines 424-476). The handler is attached to the document and correctly callstoggleAiChat()when Ctrl/Cmd+A is pressed.packages/app/src/components/settings-modal.tsx (1)
68-68: LGTM!The new
btn_ai_chatcolor key is properly integrated into the color groups and preview section, following the same pattern as existing button colors.Also applies to: 199-199
packages/app/src/styles/ai-chat.css (1)
1-56: Well-structured CSS with consistent theming.The stylesheet follows good practices with:
- CSS variables for theme consistency
- Clear section organization with comments
- Appropriate use of flexbox for layout
- Smooth animations for UX polish
- Focus states for accessibility
Minor note: Error colors (e.g.,
#ef4444) are hardcoded, which is acceptable for semantic error states, but consider defining a--error-colorvariable for future consistency.Also applies to: 57-139, 140-238, 239-357, 358-393, 394-536
packages/app/src-tauri/src/lib.rs (3)
478-516: Token validation implementation looks good.The validation correctly:
- Distinguishes OAuth vs API key tokens
- Makes a minimal API call to verify validity
- Returns descriptive error messages with status code and body
541-649: Streaming implementation is well-structured.The SSE parsing logic correctly handles:
- Buffer-based chunked parsing with
\n\ndelimiters- JSON extraction from
data:prefixed lines- Content delta accumulation
- Error propagation via events
- Proper lifecycle events (start/delta/done/error)
695-696: Commands properly registered.Both
validate_claude_tokenandstream_claude_chatare correctly added to the invoke handler. As per coding guidelines, Tauri commands are properly defined with#[tauri::command]and registered ininvoke_handler.packages/app/src/App.tsx (1)
95-104: AI chat integration follows existing patterns well.The implementation correctly:
- Uses SolidJS signals and stores (per coding guidelines)
- Follows existing config persistence pattern in
saveAiChatWidth- Properly integrates escape key handling in LIFO order
- Uses conditional flex sizing for the two-column layout
- Imports shared styles from the appropriate location
Also applies to: 194-194, 632-633, 1256-1261, 1265-1265, 1273-1285
packages/app/src/components/ai-chat-panel.tsx (4)
115-204: TokenSetup component handles validation flow correctly.The component:
- Validates stored token on mount with proper loading state
- Shows appropriate UI for validating vs input states
- Clears invalid stored tokens automatically
- Provides clear user feedback for errors
Good use of SolidJS patterns with
Showfor conditional rendering.
214-291: Message component handles streaming and mermaid rendering well.The implementation correctly:
- Uses effects that track streaming state changes
- Avoids unnecessary re-renders with content comparison
- Processes mermaid diagrams only after streaming completes
- Handles render errors gracefully with fallback display
297-666: ChatView component is well-implemented.The command palette, keyboard navigation, and streaming chat integration are well-structured. Good separation between command execution, message sending, and UI state management.
672-795: Resize handling with proper cleanup.The resize implementation:
- Uses proper mouse event handling pattern
- Cleans up listeners in
onCleanuphook- Clamps width to reasonable bounds (15-60%)
- Persists width via callback
Double-click auto-fit is a nice UX touch.
packages/app/src/stores/ai-chat-store.ts (6)
1-11: Coding guideline: State management should be inapp-store.ts.Per the coding guidelines, state management must be defined in
packages/app/src/stores/app-store.ts. This file creates a separate store which deviates from that convention. Consider either:
- Moving these signals to
app-store.ts, or- Confirming this separation is intentional for modularity (AI feature isolation).
The use of SolidJS
createSignalis correct. Based on learnings, this follows the expected pattern.
37-38: Security consideration: API token in localStorage.Storing the API token in
localStoragemakes it accessible to any JavaScript running on the page (XSS risk). For a Tauri desktop app this is lower risk than a web app, but worth noting. The current implementation is acceptable for a desktop context where the trust boundary is different.Also applies to: 101-110
152-172: LGTM: Streaming message update logic.The
updateLastAssistantMessagefunction correctly finds and updates the last assistant message immutably, which is appropriate for SolidJS reactivity.
184-197: Abort controller management is sound.The abort controller pattern for canceling in-flight requests is correctly implemented. The module-level
currentAbortControllervariable appropriately manages the singleton abort state.
220-252: Exports are well-organized.Clean separation between state signals and action functions in exports. The API surface is comprehensive for the chat panel's needs.
25-31: The model identifiers are correct. The Claude API uses the model identifiersclaude-sonnet-4-5andclaude-opus-4-5, andclaude-haiku-4-5is used via the Claude API. These are the current and official identifiers for Claude 4.5 models as of late 2025. No changes required.
| export async function validateToken(token: string): Promise<{ valid: boolean; error?: string }> { | ||
| try { | ||
| await invoke<boolean>("validate_claude_token", { token }); | ||
| return { valid: true }; | ||
| } catch (error) { | ||
| const message = error instanceof Error ? error.message : String(error); | ||
|
|
||
| // Parse common error cases | ||
| if (message.includes("401") || message.includes("authentication") || message.includes("Unauthorized")) { | ||
| return { valid: false, error: "Invalid API key or token" }; | ||
| } | ||
| if (message.includes("403") || message.includes("permission")) { | ||
| return { valid: false, error: "Token lacks required permissions" }; | ||
| } | ||
| if (message.includes("Network") || message.includes("network")) { | ||
| return { valid: false, error: "Network error. Check your connection." }; | ||
| } | ||
|
|
||
| return { valid: false, error: message }; | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find Rust source files and search for the validate_claude_token command
find . -type f -name "*.rs" | head -20Repository: dnl-fm/md
Length of output: 194
🏁 Script executed:
#!/bin/bash
# Search for validate_claude_token in Rust files
rg "validate_claude_token" --type rust -A 20Repository: dnl-fm/md
Length of output: 1913
🏁 Script executed:
#!/bin/bash
# Search for tauri::command pattern related to claude or token
rg "tauri::command" --type rust -A 15 | grep -A 15 -E "(token|claude|validate)" || echo "No matches found with filter"Repository: dnl-fm/md
Length of output: 2204
🏁 Script executed:
#!/bin/bash
# Get the full validate_claude_token function implementation
rg "async fn validate_claude_token" --type rust -A 50Repository: dnl-fm/md
Length of output: 3362
Remove redundant lowercase "network" check and consider structured error handling.
The network error check should only look for "Network" (capital N) since the backend always uses "Network error:" format. The redundant lowercase check adds no value. Additionally, the error parsing for 401/403 depends on Anthropic API response bodies containing specific keywords—consider requesting structured error types from the backend instead of relying on string pattern matching.
if (message.includes("Network")) { // Remove includes("network")
return { valid: false, error: "Network error. Check your connection." };
}
🤖 Prompt for AI Agents
packages/app/src/services/claude-client.ts around lines 81-101: remove the
redundant message.includes("network") check so only message.includes("Network")
is used, and replace brittle string-based permission/auth checks by attempting
to read a structured error from the thrown object (e.g., error.code or
error.type) and branch on that first; if no structured fields exist, fall back
to the existing string checks and final message return.
- Clear mermaidCodeMap on /clear to prevent memory leak - Use TextEncoder for mermaid hash (fixes btoa on non-ASCII) - Remove redundant lowercase network check - Use CSS variables for dark mode support in chat messages
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
packages/app/src/services/claude-client.ts (1)
81-101: Error handling looks correct.The previous review comment about redundant lowercase "network" check has been addressed—the code now only checks for "Network" (capital N). The error parsing for common HTTP status codes and network errors is reasonable.
packages/app/src/styles/ai-chat.css (1)
301-320: Previous hardcoded color issue has been addressed.The user and assistant message backgrounds now correctly use CSS variables with
color-mix()andvar(--bg-primary), properly supporting theme/dark mode.packages/app/src/components/ai-chat-panel.tsx (2)
82-88: Previous memory leak concern has been addressed.The
clearMermaidCache()function is now exported and called inexecuteCommandwhen clearing the chat (line 414). This prevents unbounded growth of the mermaid code map.
96-104: Previous btoa() issue has been addressed.The code now uses
TextEncoderto safely handle Unicode content when generating mermaid diagram IDs, preventingDOMExceptionon non-ASCII characters.
🧹 Nitpick comments (6)
packages/app/src/services/claude-client.ts (1)
37-72: System prompt embeds full document content without size limits.The document content is embedded directly in the system prompt. For very large documents, this could exceed Claude's context window limits or cause performance issues.
Consider adding a character/token limit with truncation and informing the user when content is truncated:
const MAX_CONTENT_LENGTH = 100000; // ~25k tokens rough estimate const truncatedContent = context.content.length > MAX_CONTENT_LENGTH ? context.content.slice(0, MAX_CONTENT_LENGTH) + "\n\n[Document truncated due to size...]" : context.content;packages/app/src/styles/ai-chat.css (2)
57-130: Consider reducing!importantusage.Multiple
!importantdeclarations on.ai-logout-btn(lines 103-115) suggest specificity conflicts with base button styles. While functional, this could make future style maintenance harder.
197-204: Error color is hardcoded instead of using theme variables.The error states use hardcoded
#ef4444which won't adapt to theme changes. Consider introducing an error color CSS variable for consistency.🔎 Proposed fix
.ai-token-error { - color: #ef4444; + color: var(--color-error, #ef4444); font-size: 0.85em; margin-bottom: 12px; padding: 8px 12px; - background: rgba(239, 68, 68, 0.1); + background: color-mix(in srgb, var(--color-error, #ef4444) 10%, transparent); border-radius: 6px; } .ai-stop-btn { - background: #ef4444 !important; - border-color: #ef4444 !important; + background: var(--color-error, #ef4444) !important; + border-color: var(--color-error, #ef4444) !important; color: white !important; font-size: 0.7em; } .ai-stop-btn:hover { - background: #dc2626 !important; - border-color: #dc2626 !important; + background: color-mix(in srgb, var(--color-error, #ef4444) 85%, black) !important; + border-color: color-mix(in srgb, var(--color-error, #ef4444) 85%, black) !important; }Also applies to: 526-535
packages/app/src/components/ai-chat-panel.tsx (3)
123-141: Token validation on mount doesn't handle concurrent navigation.If the user closes and reopens the panel quickly while validation is in progress, or if the component unmounts during validation, the async operation continues and may update state on an unmounted component. SolidJS handles this better than React, but consider adding cleanup:
🔎 Proposed fix with abort handling
function TokenSetup() { const [inputToken, setInputToken] = createSignal(""); + let isMounted = true; + onCleanup(() => { + isMounted = false; + }); + // Validate stored token on mount onMount(async () => { const storedToken = aiToken(); if (storedToken && !aiTokenValid()) { setAiTokenValidating(true); const result = await validateToken(storedToken); + if (!isMounted) return; setAiTokenValidating(false);
250-281: Mermaid rendering uses setTimeout without cleanup.The
setTimeoutfor mermaid diagram processing doesn't get cancelled if the component unmounts, which could cause issues if the user navigates away quickly. Also,diagrams.forEachwithasynccallback doesn't await—errors in individual diagram renders won't prevent others from rendering (which is good), but there's no aggregate error handling.🔎 Proposed fix with timeout cleanup
function Message(props: MessageProps) { let containerRef: HTMLDivElement | undefined; let contentRef: HTMLDivElement | undefined; let lastRenderedContent = ""; let hasMermaidRendered = false; + let mermaidTimeout: number | undefined; + + onCleanup(() => { + if (mermaidTimeout) clearTimeout(mermaidTimeout); + }); // ... existing code ... // Process mermaid diagrams after render createEffect(() => { const msgContent = props.message.content; const isStreaming = props.message.isStreaming; if (!msgContent || isStreaming || !containerRef) return; - setTimeout(() => { + mermaidTimeout = window.setTimeout(() => { // ... existing code ... }, 100); });
432-467: Export creates draft without error handling.
createDraftandupdateDraftcalls don't handle potential errors. If draft creation fails, the subsequent operations would still proceed, potentially leaving the app in an inconsistent state.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/app/src/components/ai-chat-panel.tsx(1 hunks)packages/app/src/services/claude-client.ts(1 hunks)packages/app/src/styles/ai-chat.css(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/app/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
packages/app/src/**/*.{ts,tsx}: Use SolidJS signals for state management, NOT React hooks (useState, useEffect, etc.)
Import SolidJS primitives (createSignal, createEffect, Show, For) for reactivity, not React patterns
Files:
packages/app/src/services/claude-client.tspackages/app/src/components/ai-chat-panel.tsx
packages/{app,extension}/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Shared styles must be imported from @md/shared/styles workspace package (theme.css, markdown.css, print.css)
Files:
packages/app/src/services/claude-client.tspackages/app/src/components/ai-chat-panel.tsx
packages/app/src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Desktop app components must be created in packages/app/src/components/ directory and use SolidJS patterns
Files:
packages/app/src/components/ai-chat-panel.tsx
🧬 Code graph analysis (2)
packages/app/src/services/claude-client.ts (1)
packages/app/src/stores/ai-chat-store.ts (2)
AiMessage(17-23)AiModel(25-25)
packages/app/src/components/ai-chat-panel.tsx (4)
packages/app/src/utils.ts (2)
escapeHtml(121-130)getFilename(105-107)packages/app/src/stores/ai-chat-store.ts (20)
aiToken(224-224)aiTokenValid(226-226)setAiTokenValidating(229-229)setAiTokenValid(227-227)setAiToken(225-225)aiTokenValidating(228-228)aiTokenError(230-230)aiMessages(232-232)setAiLoading(235-235)addAssistantMessage(245-245)getAbortSignal(248-248)AI_MODELS(27-31)AiModel(25-25)abortCurrentRequest(249-249)aiChatWidth(238-238)setAiChatResizing(241-241)setAiChatWidth(239-239)clearAiToken(251-251)showAiChat(222-222)setShowAiChat(223-223)packages/app/src/services/claude-client.ts (2)
validateToken(81-101)streamChat(118-179)packages/app/src/stores/app-store.ts (9)
content(358-358)currentFile(356-356)currentDraftId(413-413)getDraft(418-418)createDraft(415-415)updateDraft(416-416)setCurrentFile(357-357)setCurrentDraftId(414-414)setContent(359-359)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: test
- GitHub Check: build-check
🔇 Additional comments (8)
packages/app/src/services/claude-client.ts (3)
1-9: LGTM!Clean module header with appropriate imports. Using Tauri's
invokeandlistenAPIs for IPC is the correct approach for a desktop app needing to bypass CORS.
14-31: LGTM!Well-defined interfaces for streaming callbacks and document context. The
ChatStreamEventinternal type properly models the expected event payload structure from the backend.
118-179: Race condition and stream completion issues with event-based streaming pattern.The current implementation has two architectural problems:
Race condition risk: The
listen()andinvoke()calls are concurrent rather than sequential. According to Tauri patterns, the listener should be registered (awaited) before the backend command is invoked. Early events can be missed if the backend starts emitting before the JavaScript listener is fully registered.Wrong pattern for streaming: Tauri channels are the recommended mechanism for streaming data like HTTP responses, not events. Events are inefficient for continuous streaming and lose delivery guarantees.
Incomplete stream awaiting: The function resolves after
invoke()completes, not when streaming finishes. If the backend spawns an async task to emit events after the command returns, the caller won't wait for the actual stream to complete.Consider refactoring to use Tauri Channels instead of events for the streaming response, or ensure the backend blocks the invoke command until streaming is actually complete.
packages/app/src/styles/ai-chat.css (1)
1-56: LGTM!Clean layout styles with proper use of CSS variables for theming. The resize handle implementation with transition and hover states is well done.
packages/app/src/components/ai-chat-panel.tsx (4)
13-51: LGTM!Correct use of SolidJS primitives (
createSignal,createEffect,Show,For,onMount,onCleanup) as required by coding guidelines. Clean import organization with proper separation of concerns.
686-723: LGTM! Resize logic is well implemented.The resize handling correctly clamps width between 15-60%, uses proper cleanup in
onCleanup, and persists the width via the callback. The double-click auto-fit feature is a nice UX touch.
746-750: Good defensive cleanup on unmount.The
onCleanuphandler properly removes global event listeners, preventing memory leaks if the component unmounts during a resize operation.
752-804: LGTM!The main panel component correctly uses SolidJS's
Showfor conditional rendering, properly wires up the resize handle, and cleanly switches between TokenSetup and ChatView based on token validity.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/app/src/components/ai-chat-panel.tsx (2)
263-279: Consider the async forEach pattern.Line 263 uses
forEachwith an async callback, which doesn't await the promises. While this works for independent diagrams, it could theoretically lead to race conditions if the same diagram ID appears multiple times.This is likely fine given the hash-based ID generation, but for robustness you could use
for...oforPromise.all():🔎 Alternative approach with Promise.all
- diagrams.forEach(async (el) => { + await Promise.all(Array.from(diagrams).map(async (el) => { if (el.querySelector('svg')) return; // Already rendered const code = mermaidCodeMap.get(el.id); if (!code) return; try { const { svg } = await mermaid.render(el.id + '-svg', code); el.innerHTML = svg; el.classList.remove('mermaid-loading'); hasMermaidRendered = true; } catch (err) { console.error('Mermaid render error:', err); el.innerHTML = `<pre class="mermaid-error">Diagram error: ${err}</pre>`; el.classList.remove('mermaid-loading'); } - }); + }));
728-743: Misleading comment about measuring content width.The comment at line 729 states "measure content and set width accordingly," but the implementation sets a fixed width of 35% (line 737) without measuring the actual content.
🔎 Suggested fix
Either update the comment to reflect the actual behavior:
function handleResizeDoubleClick() { - // Auto-fit: measure content and set width accordingly + // Auto-fit: reset to default width for comfortable reading if (panelRef) {Or remove the unused measurement code:
function handleResizeDoubleClick() { - // Auto-fit: measure content and set width accordingly - if (panelRef) { - const messagesContainer = panelRef.querySelector(".ai-messages"); - if (messagesContainer) { - // Get the natural content width - const container = panelRef.parentElement; - if (container) { - // Set to a reasonable auto-fit width (40% is good for reading) - const autoWidth = 35; - setAiChatWidth(autoWidth); - props.onSaveWidth(autoWidth); - } - } - } + // Reset to default width for comfortable reading + const autoWidth = 35; + setAiChatWidth(autoWidth); + props.onSaveWidth(autoWidth); }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/app/src/components/ai-chat-panel.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/app/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
packages/app/src/**/*.{ts,tsx}: Use SolidJS signals for state management, NOT React hooks (useState, useEffect, etc.)
Import SolidJS primitives (createSignal, createEffect, Show, For) for reactivity, not React patterns
Files:
packages/app/src/components/ai-chat-panel.tsx
packages/{app,extension}/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Shared styles must be imported from @md/shared/styles workspace package (theme.css, markdown.css, print.css)
Files:
packages/app/src/components/ai-chat-panel.tsx
packages/app/src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Desktop app components must be created in packages/app/src/components/ directory and use SolidJS patterns
Files:
packages/app/src/components/ai-chat-panel.tsx
🧬 Code graph analysis (1)
packages/app/src/components/ai-chat-panel.tsx (4)
packages/app/src/utils.ts (2)
escapeHtml(121-130)getFilename(105-107)packages/app/src/stores/ai-chat-store.ts (24)
aiToken(224-224)aiTokenValid(226-226)setAiTokenValidating(229-229)setAiTokenValid(227-227)setAiToken(225-225)setAiTokenError(231-231)aiTokenValidating(228-228)aiTokenError(230-230)aiMessages(232-232)addAssistantMessage(245-245)addUserMessage(244-244)setAiLoading(235-235)aiModel(236-236)updateLastAssistantMessage(246-246)getAbortSignal(248-248)AiModel(25-25)setAiModel(237-237)abortCurrentRequest(249-249)aiChatWidth(238-238)setAiChatResizing(241-241)setAiChatWidth(239-239)clearAiToken(251-251)showAiChat(222-222)setShowAiChat(223-223)packages/app/src/services/claude-client.ts (2)
validateToken(81-101)streamChat(118-179)packages/app/src/stores/app-store.ts (9)
content(358-358)currentFile(356-356)currentDraftId(413-413)getDraft(418-418)createDraft(415-415)updateDraft(416-416)setCurrentFile(357-357)setCurrentDraftId(414-414)setContent(359-359)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: test
- GitHub Check: build-check
🔇 Additional comments (11)
packages/app/src/components/ai-chat-panel.tsx (11)
13-13: Verify SolidJS imports are complete.The imports look correct for SolidJS patterns. Good adherence to the coding guidelines requiring SolidJS (not React hooks).
82-88: Memory leak addressed with clearMermaidCache.The exported
clearMermaidCachefunction properly addresses the previous concern about unbounded memory growth inmermaidCodeMap. This is called from the clear command at line 417.
96-104: Non-ASCII handling improved with TextEncoder.The hash generation now uses
TextEncoderinstead ofbtoa(), which properly handles Unicode characters in Mermaid diagrams. This addresses the previous concern aboutDOMExceptionwith non-ASCII content.
123-212: TokenSetup component looks solid.The token validation flow is well-structured:
- Validates stored tokens on mount
- Clears invalid tokens for security
- Provides clear error messaging
- Uses SolidJS patterns correctly
228-281: Mermaid rendering logic is well-structured.The two-effect pattern (markdown rendering + Mermaid processing) correctly handles streaming updates and diagram rendering. The optimization to skip updates when content hasn't changed (line 236) is good.
380-411: Token validation improved.The token check now occurs before adding the user message (line 381-385), which addresses the previous concern. If no token exists, an error message is shown to the user without adding their message to the chat. This is a reasonable UX approach.
435-470: Chat export functionality is well-implemented.The export flow properly validates messages, formats content as Markdown, creates a new draft, and switches the view. The integration with the app store is correct.
689-726: Resize drag logic is well-implemented.The resize handlers properly track mouse movement, clamp width to 15-60%, and persist the width via the callback. Event cleanup is handled correctly.
750-753: Proper cleanup on unmount.The
onCleanuphook correctly removes event listeners to prevent memory leaks. Good practice.
684-807: Main panel component structure is solid.The AiChatPanel component properly:
- Manages resize state and persistence
- Cleans up event listeners
- Conditionally renders based on token validity
- Uses SolidJS patterns throughout
51-51: No action required—ai-chat.css is component-specific.The file correctly contains only AI Chat Panel-specific styles (all selectors use the
.ai-*prefix) and appropriately uses shared theme variables without redefining them. The reference tomarkdown-bodyat line 314 is a proper dependency on shared styles, not a redefinition.
- Add 'system' theme option that follows OS preference - Poll Tauri theme API for production builds (matchMedia doesn't fire) - Add core:window:default capability for theme detection - Add devtools feature for debugging - Update splash screen to handle system theme on load - Show current theme state in sidebar (not next state) - Prevent flash by waiting for theme before rendering content - Use SVG icons for theme toggle (moon/sun/monitor)
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/app/src-tauri/capabilities/default.json`:
- Line 8: Remove the unnecessary "core:window:default" permission from the
capabilities list in default.json: locate the entry "core:window:default" and
delete it so the AI chat feature no longer requests window-management permission
(leave the rest of the JSON array intact and ensure valid JSON punctuation after
removal).
In `@packages/app/src/App.tsx`:
- Around line 478-481: The global Ctrl+A branch (case "a") is intercepting
keydown and preventing the wasm editor's Select All; update the handler so it
first checks whether the editor is focused (e.g., inspect document.activeElement
or an existing editor focus state exposed by the WasmEditor
component/wasm-editor.tsx) and only call e.preventDefault() and toggleAiChat()
when the editor is NOT focused; alternatively bind toggleAiChat() to a different
shortcut. Specifically, guard the case "a" branch with a focus check before
calling toggleAiChat().
🧹 Nitpick comments (5)
packages/app/src-tauri/Cargo.toml (1)
21-21: Consider conditionally enablingdevtoolsfeature.The
devtoolsfeature is useful for debugging but adds overhead in release builds. Consider using Cargo features to enable it only in debug builds, or verify this is intentional for production.packages/app/src/components/sidebar.tsx (1)
203-234: Consider extracting SVG icons to reduce duplication.The theme SVG icons (moon, sun, monitor) are duplicated between the expanded sidebar (lines 203-234) and collapsed sidebar (lines 259-281). Extracting these to constants or small components would improve maintainability.
♻️ Example refactor to extract icons
// Add at the top of the file or in a shared icons file const MoonIcon = () => ( <svg style="width: 14px; height: 14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" /> </svg> ); const SunIcon = () => ( <svg style="width: 14px; height: 14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="12" cy="12" r="5" /> <line x1="12" y1="1" x2="12" y2="3" /> {/* ... remaining lines */} </svg> ); const MonitorIcon = () => ( <svg style="width: 14px; height: 14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <rect x="2" y="3" width="20" height="14" rx="2" /> <line x1="8" y1="21" x2="16" y2="21" /> <line x1="12" y1="17" x2="12" y2="21" /> </svg> );packages/app/src/stores/app-store.ts (3)
29-38: Module-level mutable state may cause reactivity issues.
systemThemeCacheis a mutable module-level variable that won't trigger SolidJS reactivity when updated. This is likely intentional since it's only used to deriveresolvedThemesignal value, but worth noting for maintainability.
355-367: Consider increasing the theme polling interval.A 500ms polling interval may be too aggressive for checking system theme changes. Consider increasing to 1000-2000ms to reduce CPU usage, especially since system theme changes are infrequent user actions.
♻️ Suggested change
- const THEME_POLL_INTERVAL_MS = 500; + const THEME_POLL_INTERVAL_MS = 1000;
363-370: Empty catch blocks silently swallow errors.The empty catch blocks provide no visibility into failures. Consider at minimum logging at debug level for troubleshooting purposes.
♻️ Add minimal logging
} catch { - // Expected if Tauri API temporarily unavailable + // Expected if Tauri API temporarily unavailable - no action needed } }, THEME_POLL_INTERVAL_MS); } catch { - // Expected in browser dev mode + // Expected in browser dev mode - Tauri APIs not available }
| "windows": ["main"], | ||
| "permissions": [ | ||
| "core:default", | ||
| "core:window:default", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find files related to AI chat feature
fd -type f -name "*.ts" -o -name "*.tsx" | xargs grep -l "chat\|claude\|ai" | head -20Repository: dnl-fm/md
Length of output: 223
🏁 Script executed:
# Search for Tauri window API imports in TypeScript/TSX files
rg -n "@tauri-apps/api/window|getCurrentWindow|appWindow|getAll|create|physicalSize|Monitor" packages/app/src --type ts --type tsx -A 2 -B 2Repository: dnl-fm/md
Length of output: 80
🏁 Script executed:
# Search for window-related operations in Rust backend
rg -n "WindowBuilder|create_window|show_window|focus_window|resize|Window" packages/app/src-tauri/src --type rust -A 1 -B 1 | head -50Repository: dnl-fm/md
Length of output: 270
🏁 Script executed:
# Check the actual changes in the PR - list all modified files
git diff --name-only HEAD^ HEAD 2>/dev/null || echo "Git not available, checking file structure"Repository: dnl-fm/md
Length of output: 95
🏁 Script executed:
# Look for any window-specific code in new or modified files related to streaming/AI
find packages/app/src -type f \( -name "*stream*" -o -name "*chat*" -o -name "*claude*" -o -name "*panel*" \) 2>/dev/null | xargs wc -l 2>/dev/nullRepository: dnl-fm/md
Length of output: 254
🏁 Script executed:
# Search for window API imports and usage
rg "@tauri-apps/api/window|getCurrentWindow|appWindow|createTauriWindow" packages/app/srcRepository: dnl-fm/md
Length of output: 403
🏁 Script executed:
# Check the ai-chat-panel component for window operations
cat -n packages/app/src/components/ai-chat-panel.tsx | head -80Repository: dnl-fm/md
Length of output: 3240
🏁 Script executed:
# Search for any Tauri command invocations in AI chat files
rg "invoke|tauri|window" packages/app/src/components/ai-chat-panel.tsx -iRepository: dnl-fm/md
Length of output: 35
🏁 Script executed:
# Check the claude client for Tauri API usage
cat -n packages/app/src/services/claude-client.tsRepository: dnl-fm/md
Length of output: 6718
🏁 Script executed:
# Search for Tauri imports across the entire src directory
rg "@tauri-apps" packages/app/srcRepository: dnl-fm/md
Length of output: 1010
Remove the unnecessary core:window:default permission.
The AI chat feature does not use any window management APIs. The panel is implemented as a resizable UI component using CSS and state management. The only window API usage in the codebase is in app-store.ts for theme detection, which is unrelated to the new AI chat feature. The new code uses only invoke() for Tauri commands and listen() for event streaming.
🤖 Prompt for AI Agents
In `@packages/app/src-tauri/capabilities/default.json` at line 8, Remove the
unnecessary "core:window:default" permission from the capabilities list in
default.json: locate the entry "core:window:default" and delete it so the AI
chat feature no longer requests window-management permission (leave the rest of
the JSON array intact and ensure valid JSON punctuation after removal).
| case "a": | ||
| e.preventDefault(); | ||
| toggleAiChat(); | ||
| break; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if Ctrl+A is used elsewhere or if there's select-all functionality
echo "=== Searching for select-all references ==="
rg -n "select.*all|selectAll" --type=ts --type=tsx packages/app/src/ || echo "No select-all references found"
echo ""
echo "=== Searching for other Ctrl+A references ==="
rg -n "Ctrl\+A|ctrl.*a" packages/app/src/ || echo "No other Ctrl+A references found"Repository: dnl-fm/md
Length of output: 711
🏁 Script executed:
#!/bin/bash
# Examine the keyboard binding context around lines 478-481
echo "=== Context around the Ctrl+A binding (lines 470-490) ==="
sed -n '470,490p' packages/app/src/App.tsxRepository: dnl-fm/md
Length of output: 628
🏁 Script executed:
#!/bin/bash
# Search for select functionality and editor context
echo "=== Searching for 'select' references (broader context) ==="
rg -n "case \"[a-z]\":" packages/app/src/App.tsx | head -20Repository: dnl-fm/md
Length of output: 404
🏁 Script executed:
#!/bin/bash
# Examine the Select All implementation in wasm-editor.tsx
echo "=== wasm-editor.tsx around line 625 (Select All context) ==="
sed -n '620,630p' packages/app/src/components/wasm-editor.tsxRepository: dnl-fm/md
Length of output: 349
🏁 Script executed:
#!/bin/bash
# Check the broader keyboard event handler in App.tsx to understand event flow
echo "=== Full keyboard event handler in App.tsx ==="
sed -n '420,510p' packages/app/src/App.tsxRepository: dnl-fm/md
Length of output: 2815
🏁 Script executed:
#!/bin/bash
# Find where handleKeyDown is attached
echo "=== Where handleKeyDown is registered ==="
rg -n "handleKeyDown|keydown" packages/app/src/App.tsx | head -20Repository: dnl-fm/md
Length of output: 212
🏁 Script executed:
#!/bin/bash
# Check if there's any focus-aware handling or component isolation
echo "=== Event listener attachment in App.tsx ==="
sed -n '1,100p' packages/app/src/App.tsx | tail -50Repository: dnl-fm/md
Length of output: 1503
🏁 Script executed:
#!/bin/bash
# Check if handleKeyDown has any focus checks to prevent this conflict
echo "=== Full handleKeyDown function with context ==="
sed -n '428,550p' packages/app/src/App.tsxRepository: dnl-fm/md
Length of output: 3724
🏁 Script executed:
#!/bin/bash
# Check wasm-editor to see how it attaches its keydown listener
echo "=== wasm-editor keydown listener attachment ==="
rg -n "addEventListener.*keydown|onkeydown" packages/app/src/components/wasm-editor.tsx -A 2 -B 2Repository: dnl-fm/md
Length of output: 322
Ctrl+A conflict breaks editor Select All functionality.
The document-level Ctrl+A handler (line 478) intercepts the keydown event before the wasm-editor receives it. This prevents the "Select All" functionality in the editor (wasm-editor.tsx:625-628) from working. Users cannot select all text in the editor when this shortcut is active. Add a focus check to prevent the app handler from firing when the editor is focused, or choose a different shortcut for AI chat toggle.
🤖 Prompt for AI Agents
In `@packages/app/src/App.tsx` around lines 478 - 481, The global Ctrl+A branch
(case "a") is intercepting keydown and preventing the wasm editor's Select All;
update the handler so it first checks whether the editor is focused (e.g.,
inspect document.activeElement or an existing editor focus state exposed by the
WasmEditor component/wasm-editor.tsx) and only call e.preventDefault() and
toggleAiChat() when the editor is NOT focused; alternatively bind toggleAiChat()
to a different shortcut. Specifically, guard the case "a" branch with a focus
check before calling toggleAiChat().
Summary
Adds an integrated AI chat panel for document analysis and Q&A using Claude.
Features
Layout & UI
Authentication
sk-ant-api03-...) and OAuth tokensChat
Commands
Type
/for autocomplete menu (arrow keys navigate, Enter selects):/clear/summarise/model/exportModels
claude-haiku-4-5)claude-sonnet-4-5)claude-opus-4-5)Keyboard Shortcuts
Ctrl+A- Toggle panelEscape- Close panelEnter- Send messageShift+Enter- NewlineFiles Added
packages/app/src/components/ai-chat-panel.tsxpackages/app/src/services/claude-client.tspackages/app/src/stores/ai-chat-store.tspackages/app/src/styles/ai-chat.cssSummary by CodeRabbit
New Features
Documentation
Style