Conversation
chore: sync main to develop
* feat(tauri): tauri 2.0 プロジェクト初期化 (#49) - src-tauri/ ディレクトリを作成し Rust バックエンドを構成 - tauri.conf.json でアプリ設定(ポート30000, WebView ウィンドウ等) - vite.config.ts に Tauri 互換設定を追加(strictPort, envPrefix, build target 等) - tauri-plugin-shell, tauri-plugin-store を先行導入(Phase F 準備) - アプリアイコンを既存 PWA アイコンから生成 - package.json に tauri:dev, tauri:build スクリプトを追加 - .gitignore に src-tauri/target/, src-tauri/gen/ を追加 Made-with: Cursor * fix(tauri): app.title フィールドを削除し設定スキーマに準拠 tauri-build が認識しない unknown field `title` エラーを修正。 タイトルは windows[].title で設定済み。 Made-with: Cursor * fix(tauri): tauri 環境で IndexedDB にフォールバックし起動可能にする TauriStorageAdapter 未実装 (#50) の間、Tauri 環境でも暫定的に IndexedDB を使用してアプリが起動・表示できるようにする。 Made-with: Cursor * docs: add desktop app (Tauri) setup instructions to README - Desktop セクション追加(前提条件、起動コマンド、Windows 注意事項) - Tech Stack に Tauri 2.0 を追記、React バージョンを 19 に更新 - Project Structure に src-tauri/ と server/ を追記 Made-with: Cursor * docs: remove completed tasks from roadmap in README - Removed "Ghost Link System" and "Semantic Search(ベクトル検索)" from the roadmap section as they are no longer relevant. * test: update createStorageAdapter test for Tauri IndexedDB fallback Align with createStorageAdapter behavior when __TAURI_INTERNALS__ is set. Made-with: Cursor * chore: ignore Tauri JS packages in knip @tauri-apps/api and plugins are wired from Rust; not yet imported in src. Made-with: Cursor * fix: address PR #464 review comments - vite: merge process.env with loadEnv for Tauri; envPrefix TAURI_ENV_; build uses same vars - tauri.conf: remove csp null (use framework default CSP) - storage: TSDoc for createStorageAdapter; warn once for IndexedDB fallback - test: afterEach restoreAllMocks; assert warn called once across two calls Made-with: Cursor * fix: address PR #464 review comments (README MD028, TAURI_ENV_DEBUG parsing) Made-with: Cursor
… (#465) * feat(tauri): add claude code sidecar bridge and desktop commands (#456) - Add packages/claude-sidecar JSONL bridge for @anthropic-ai/claude-agent-sdk - Rust: claude_query, claude_abort, claude_status, check_claude_installation - Emit claude-stream-chunk, claude-stream-complete, claude-error - Frontend bridge src/lib/claudeCode; externalBin + shell capability - Build/ensure scripts and README; tests for protocol and bridge Made-with: Cursor * fix: address pr review for claude sidecar (#465) - Rust: UTF-8 line buffer, error string emit, drain pending RPC, optional repo_root, block_in_place write, rpc_json error mapping - capabilities: shell args false for sidecar - sidecar: abort emits error not stream-complete; default maxTurns 25; Windows claude check via cmd - tokio: enable rt for block_in_place Made-with: Cursor * fix: address pr #465 review round 2 - tokio: rt-multi-thread for block_in_place safety - status: rename abortAll to clearAll (tracks only, no abort) - index: reject duplicate query ids - build script: shell: true on Windows for bun spawn - types.ts: bilingual JSDoc per AGENTS.md Made-with: Cursor
…it (#466) * feat(ai): claude code provider, unified providers, and ai service split - Add claude-code provider via Tauri bridge; platform detection (platform.ts) - Split aiService into direct/server/models modules; registry + OpenAI/Anthropic/Google providers - AI settings UI: ProviderSelector, useAISettingsForm helpers, i18n - Wiki/mermaid: unsupported paths for claude code where applicable - Tests: platform, providers, registry, claudeCodeProvider Made-with: Cursor * chore: remove unused aiProviders barrel for knip src/lib/aiProviders/index.ts was never imported; knip reported it as an unused file. Made-with: Cursor * fix: address PR #466 review comments - claudeCodeProvider: stream wait race, single bridge import incl. claudeAbort, abort captures reqId - mergeAbortSignals + OpenAI/Anthropic/Google: caller abort and provider.abort both cancel SDK - isAIConfigured: claude-code gated on Tauri + checkClaudeInstallation - getAISettingsOrThrow (wiki/mermaid): early return for claude-code before api key check - JSDoc: fill or remove blank blocks (ai types, aiService, mermaid, useAISettings, aiClient, FetchServerModelsError) - AIServiceRequest.provider typed as AIProviderType - tests: mergeAbortSignals.test.ts Made-with: Cursor * fix: address PR #466 second-round review comments - i18n: fix useTranslation("aiSettings") → useTranslation() with aiSettings. prefix (ProviderSelector, CapabilityMatrix, ClaudeCodeSection) - useAISettings: preserve apiKey when switching to claude-code (prevent key erasure) - claudeCodeProvider: hoist resolveWait/wake to closure; abort() now releases wait - registry: api_server mode skips requiresApiKey check for availability - aiServiceDirectProviders: separate system messages into Anthropic system field - aiServiceModels: runtime type guards (normalizeToAIModel, fetchUsage), return type UserTier, explicit getAIAPIBaseUrl return annotation - aiServiceServer: flush TextDecoder and process trailing SSE buffer after read loop - platform.test: replace any cast with typed TauriWindow interface Made-with: Cursor * fix: address PR #466 third-round review comments - claudeCodeProvider: listen to signal abort event during await Promise to prevent indefinite hang when user cancels during stream wait - aiService: always fire onComplete callback when for-await loop exits (even when fullContent is empty) to prevent UI stuck in streaming state - registry: remove unused isProviderAvailable export (api_server inconsistency and no call sites) Made-with: Cursor * fix: route claude-code through callAIService in wiki/mermaid generators When apiMode is "user_api_key" and provider is "claude-code", generators previously fell into the direct-SDK switch where claude-code throws "not supported". Now check settings.provider === "claude-code" before the effectiveMode branch so callAIService correctly routes to the Claude Code sidecar regardless of apiMode. Made-with: Cursor * fix: prevent data loss when text and done chunks arrive simultaneously Move `done` check from while-loop condition to an else-if branch after chunk consumption. This ensures all queued text chunks are yielded before the loop exits, even when the completion event sets done=true in the same event-loop turn as a text chunk event. Made-with: Cursor
* feat(ai-chat): サイドパネル・エージェントチャット (#458) ノートエディタ横のAIチャットパネルに以下の機能を追加: - 「ノートに挿入」ボタン: アシスタントの回答をエディタのカーソル位置に挿入 - ツール実行状況の可視化: Claude Code のツール使用をリアルタイム表示 (Read, Write, Bash, WebSearch 等のステータス表示) - パネルリサイズ: ドラッグハンドルによる幅変更(280px〜600px) - メッセージコピーボタン: アシスタントメッセージのコピー機能 Full-stack changes: - sidecar protocol: tool-use-start/complete events - Rust host: forward tool events as Tauri events - bridge/provider: listen for and yield tool chunks - ChatMessage type: toolExecutions tracking - i18n: ja/en translation keys for new UI Made-with: Cursor * feat(ai-settings): 3モード切替UI・モデルセレクター改修・モードバッジ (#458) AI利用モードを「デフォルト / APIキー / Claude Code」の3択で 明確に選択できるよう設定画面とチャットUIを改修: - types/ai.ts: AIInteractionMode 型と getInteractionMode() 追加 - AIChatModelSelector: Claude Code 時はセレクター非表示、 APIキーモード時はプロバイダーでフィルタ - AIChatHeader: 現在のモードバッジを表示(クリックで設定へ遷移) - AISettingsForm: プロバイダー+トグル → 3モードセグメントコントロールに再設計 - useAISettingsForm: handleModeChange で適切な設定リセット - ProviderSelector: apiProvidersOnly prop 追加 - i18n: ja/en のモードラベル・設定画面テキスト追加 Made-with: Cursor * feat(ai-settings): claude code 前提条件チェックリスト (#458) Claude Code モード選択時に、利用に必要な前提条件を チェックリストとして表示し、各項目の状態をリアルタイムに確認: 1. デスクトップアプリ (Tauri) で実行中か 2. Claude Code CLI がインストール済みか(バージョン表示付き) 3. サイドカープロセスと通信可能か - ClaudeCodePrerequisites コンポーネントを新規作成 - 「再確認」ボタンで再チェック可能 - 全項目パス時にメッセージ表示 - i18n: ja/en の前提条件チェックテキスト追加 Made-with: Cursor * fix(ai-chat): claude code 選択時のモデルセレクター無限ループを修正 loadModels の useCallback 依存配列に selectedModel が含まれており、 Claude Code モード時に setSelectedModel → 再レンダー → loadModels 再生成 → useEffect 再実行の無限ループが発生していた。 selectedModel を依存配列から除外し、useAIChatStore.getState() で レンダー外から最新値を取得するよう変更。 Made-with: Cursor * refactor(ai-chat): サイドパネルのリサイズ機能を削除 panelWidth / setPanelWidth / MIN_PANEL_WIDTH / MAX_PANEL_WIDTH と ドラッグハンドルを削除し、CSS 変数 --ai-chat-width による固定幅に戻す。 Made-with: Cursor * feat(ai-chat): claude code モデル動的取得・選択機能 サイドカー経由で Claude Code の利用可能モデル一覧を動的に取得し、 チャットパネルのモデルセレクターで選択できるようにする。 フルスタック変更: - sidecar protocol: list_models リクエスト / models-list レスポンス追加、 query に model フィールド追加 - sidecar handler: SDK Query.initializationResult() でモデル一覧取得 - sidecar query: 選択されたモデルを SDK options.model に渡す - Rust host: claude_list_models コマンド追加、claude_query に model 引数追加 - bridge: claudeListModels() 関数追加、claudeQuery に model オプション追加 - provider: request.model を claudeQuery に透過的に渡す - AIChatModelSelector: Claude Code 時にサイドカーからモデル取得しドロップダウン表示 Made-with: Cursor * fix: address PR #467 review comments - 空 JSDoc ブロックを削除/修正 (AIChatContext, models.ts) - DisplayModel.provider を AIProviderType に変更し不正な型キャストを除去 - navigator.clipboard.writeText のエラーハンドリングを追加 (try/catch + toast) - ModeSelector を RadioGroup/RadioGroupItem ベースに置換 (a11y 改善) Made-with: Cursor * fix: テストを新しい bridge API に合わせて更新 - bridge.test.ts: claudeQuery の期待値に model: null を追加 - claudeCodeProvider.test.ts: モックに onClaudeToolUseStart / onClaudeToolUseComplete を追加 Made-with: Cursor * fix: address PR #467 review (list_models RPC, mode snapshots, tool UI on error) Made-with: Cursor * fix: model selector resync, Claude mode always selectable, mode group a11y Made-with: Cursor * fix: claude code fallback models outside tauri (#467) Made-with: Cursor * fix: address PR 467 review (empty models, tool switch, mode badge) Made-with: Cursor
… code blocks (#458 #459) (#474) * feat(ai-chat): サイドパネル・エージェントチャット (#458) ノートエディタ横のAIチャットパネルに以下の機能を追加: - 「ノートに挿入」ボタン: アシスタントの回答をエディタのカーソル位置に挿入 - ツール実行状況の可視化: Claude Code のツール使用をリアルタイム表示 (Read, Write, Bash, WebSearch 等のステータス表示) - パネルリサイズ: ドラッグハンドルによる幅変更(280px〜600px) - メッセージコピーボタン: アシスタントメッセージのコピー機能 Full-stack changes: - sidecar protocol: tool-use-start/complete events - Rust host: forward tool events as Tauri events - bridge/provider: listen for and yield tool chunks - ChatMessage type: toolExecutions tracking - i18n: ja/en translation keys for new UI Made-with: Cursor * feat(ai-settings): 3モード切替UI・モデルセレクター改修・モードバッジ (#458) AI利用モードを「デフォルト / APIキー / Claude Code」の3択で 明確に選択できるよう設定画面とチャットUIを改修: - types/ai.ts: AIInteractionMode 型と getInteractionMode() 追加 - AIChatModelSelector: Claude Code 時はセレクター非表示、 APIキーモード時はプロバイダーでフィルタ - AIChatHeader: 現在のモードバッジを表示(クリックで設定へ遷移) - AISettingsForm: プロバイダー+トグル → 3モードセグメントコントロールに再設計 - useAISettingsForm: handleModeChange で適切な設定リセット - ProviderSelector: apiProvidersOnly prop 追加 - i18n: ja/en のモードラベル・設定画面テキスト追加 Made-with: Cursor * feat(ai-settings): claude code 前提条件チェックリスト (#458) Claude Code モード選択時に、利用に必要な前提条件を チェックリストとして表示し、各項目の状態をリアルタイムに確認: 1. デスクトップアプリ (Tauri) で実行中か 2. Claude Code CLI がインストール済みか(バージョン表示付き) 3. サイドカープロセスと通信可能か - ClaudeCodePrerequisites コンポーネントを新規作成 - 「再確認」ボタンで再チェック可能 - 全項目パス時にメッセージ表示 - i18n: ja/en の前提条件チェックテキスト追加 Made-with: Cursor * fix(ai-chat): claude code 選択時のモデルセレクター無限ループを修正 loadModels の useCallback 依存配列に selectedModel が含まれており、 Claude Code モード時に setSelectedModel → 再レンダー → loadModels 再生成 → useEffect 再実行の無限ループが発生していた。 selectedModel を依存配列から除外し、useAIChatStore.getState() で レンダー外から最新値を取得するよう変更。 Made-with: Cursor * refactor(ai-chat): サイドパネルのリサイズ機能を削除 panelWidth / setPanelWidth / MIN_PANEL_WIDTH / MAX_PANEL_WIDTH と ドラッグハンドルを削除し、CSS 変数 --ai-chat-width による固定幅に戻す。 Made-with: Cursor * feat(ai-chat): claude code モデル動的取得・選択機能 サイドカー経由で Claude Code の利用可能モデル一覧を動的に取得し、 チャットパネルのモデルセレクターで選択できるようにする。 フルスタック変更: - sidecar protocol: list_models リクエスト / models-list レスポンス追加、 query に model フィールド追加 - sidecar handler: SDK Query.initializationResult() でモデル一覧取得 - sidecar query: 選択されたモデルを SDK options.model に渡す - Rust host: claude_list_models コマンド追加、claude_query に model 引数追加 - bridge: claudeListModels() 関数追加、claudeQuery に model オプション追加 - provider: request.model を claudeQuery に透過的に渡す - AIChatModelSelector: Claude Code 時にサイドカーからモデル取得しドロップダウン表示 Made-with: Cursor * fix: address PR #467 review comments - 空 JSDoc ブロックを削除/修正 (AIChatContext, models.ts) - DisplayModel.provider を AIProviderType に変更し不正な型キャストを除去 - navigator.clipboard.writeText のエラーハンドリングを追加 (try/catch + toast) - ModeSelector を RadioGroup/RadioGroupItem ベースに置換 (a11y 改善) Made-with: Cursor * fix: テストを新しい bridge API に合わせて更新 - bridge.test.ts: claudeQuery の期待値に model: null を追加 - claudeCodeProvider.test.ts: モックに onClaudeToolUseStart / onClaudeToolUseComplete を追加 Made-with: Cursor * fix: address PR #467 review (list_models RPC, mode snapshots, tool UI on error) Made-with: Cursor * fix: model selector resync, Claude mode always selectable, mode group a11y Made-with: Cursor * fix: claude code fallback models outside tauri (#467) Made-with: Cursor * fix: address PR 467 review (empty models, tool switch, mode badge) Made-with: Cursor * feat(editor): executable code blocks via Claude Code (#459) - Add ExecutableCodeBlock Tiptap node, slash command, and split NodeView UI - runQueryToCompletion with pre-id event buffer; sidecar allows empty allowedTools - General setting to confirm before run; i18n en/ja - Tests for runQueryToCompletion, executeExecutableCode, parseExecutionResult Made-with: Cursor * fix: address PR #474 review comments Made-with: Cursor
* feat(editor): agent slash commands via claude code sidecar (#460) Wire /analyze, /git-summary, /run, /research, /review, /test, /explain, /summarize to the sidecar; add tauri path listing, loading overlay, and split menu modules. Made-with: Cursor * fix(ci): add agentSlashCommands index to knip entry points Knip treated the barrel as an unused file; list it as an entry like claudeCode. Made-with: Cursor * fix: address PR #475 review comments Made-with: Cursor * fix: address PR #475 review comments Made-with: Cursor * fix: address PR #475 review comments Made-with: Cursor
* feat: workspace-linked notes for @file: and Claude cwd (#461) - Tauri: safe path resolution under root, read/list workspace files, dialog plugin - Editor: FileReference @file: mark, preview dialog host, slash path completions - AI: workspace root as Claude cwd for agent slash, executable code, chat - ESLint: pass workspaceRoot string + useEditor dep instead of ref during render Made-with: Cursor * fix: address PR #476 review (workspace root ref, Rust path, imports, JSDoc) - useEditor: stable deps + useWorkspaceRootRef + eslint block for extension config - workspace_paths: lexical resolve_under_root with fewer canonicalize calls - useTiptapEditorController: single React import line Made-with: Cursor * fix: address CodeRabbit PR review (paths, @file:, preview, docs) - Rust: re-canonicalize before open; document openat limitation - FileReference: spaces in path regex; stale preview guard - FilePreviewDialogHost: CustomEvent + detail guard - Note workspace: clear entries on tree open; normalize empty root; safe map keys - JSDoc: Japanese for useEditorSetup, aiService cwd, PageContext noteId Made-with: Cursor * fix: note workspace registry + noteId IPC for Tauri reads (#476) - Rust: persist note_id→root registry, read/list by note_id only, enforce size cap with single File::take - TS: register/clear registry from NoteWorkspaceContext; readNoteWorkspaceFile/listNoteWorkspaceEntries use noteId - Editor @file: and slash path completion thread noteId; agent cwd still uses absolute workspace path Made-with: Cursor * fix: atomic registry write, async tree guard, surface noteWorkspace I/O errors (#476) - Rust: write registry via temp file + rename; mutex around register/clear RMW - Toolbar: sequence ref so only the latest list result applies - noteWorkspaceIo: propagate invoke errors; callers log or clear UI Made-with: Cursor * fix: note workspace provider key + serialized Rust registry sync (#476) - Remount NoteWorkspaceProvider on note.id to avoid noteId/workspaceRoot mismatch - Queue register/clear invocations so async completions cannot reorder Made-with: Cursor * fix: address PR #476 review comments - NotePageEditorEditable: explicit React.JSX.Element return type - NoteWorkspace: Rust sync only in effect (remove duplicate enqueue from setters) - workspace_paths: parse_note_id_key returns trimmed key for registry I/O Made-with: Cursor * fix: address PR #476 review comments - atomic_write_file: rely on fs::rename replace on Windows (drop pre-remove) - list_note_workspace_entries: optional namePrefix filter before cap - NotePageView: NoteWorkspaceProvider wraps read-only editor for @file: context Made-with: Cursor * fix: address PR #476 review comments - list_directory_names: allow .zedi directory in listings (Issue #461) - NoteWorkspace: enqueue Rust sync on mount + in setters; drop deferred workspaceRoot effect Made-with: Cursor
* feat(ai-chat): add Claude Code multi-step workflow panel (#462) - Workflow tab, templates, save/import JSON, run/pause/resume/stop - streamClaudeQuery + runWorkflowExecution; note updates via editor handler - parseWorkflowDefinitionImport for safe JSON import Made-with: Cursor * fix: address PR #477 review comments - Only refresh workflow base snapshot on fresh runs (avoid duplicate blocks on resume). - On error/stop: clear paused state, set progress to aborted, sync UI with Copilot/Gemini/Devin. - Skip claudeQuery when already aborted. - Fix buildWorkflowStepPrompt option name (resumeFromPartial) and simplify prior outputs slice. Made-with: Cursor * fix: address CodeRabbit PR #477 review (workflow UI, a11y, streamClaudeQuery) - Gate Run/Resume on desktop + editor; sync stop/error UI state and streaming guard. - Keep workflow panel mounted after first visit so tab switches do not reset runs. - Meta/steps a11y (labels, aria-labels); empty done outputs in note markdown; dedupe import IDs. - streamClaudeQuery: buffer pre-request tool events; cleanup listeners if subscribe fails. - Defer revokeObjectURL after export; tighten pause test to use aborted step signal. Made-with: Cursor * fix: address PR #477 Devin review (workflow height, resume by step id) - Use h-full on workflow panel wrapper so ScrollArea gets a bounded height. - Pause outcome carries pausedStepId + stepOutputsById; resume resolves index by id. - Toast when paused step is missing after draft edits; extract resolveResumeFromPaused. Made-with: Cursor * fix: address PR #477 review (chat input mount, progress step alignment) - Keep AIChatInput mounted when workflow tab is active (hidden) so drafts are not cleared. - On completed/stopped/error, keep activeRunSteps as validSteps so progress matches stepStatuses. Made-with: Cursor
* feat: mcp server integration for claude code on desktop - Sidecar: pass mcpServers, emit mcp-status; Tauri forwards claude-mcp-status - UI: MCP settings, chat header indicator, editor MCP resource embed - Persist config without secrets in localStorage; tests for helpers and store Made-with: Cursor * fix: add tauri-plugin-fs for mcp config import - Declare @tauri-apps/plugin-fs so vite build and knip resolve the module - Register tauri_plugin_fs and allow read_text_file for home Claude config paths Made-with: Cursor * fix: address PR #478 review comments - McpResourceNodeView: register Tauri listeners before claudeQuery; buffer until requestId; unmount cleanup; require non-empty resource - parseStdioArgsLine for quoted stdio args; isValidMcpServerConfig on import - Regenerate path passes MCP servers to buildSystemPrompt; claudeCodeProvider normalizes MCP status - JSDoc/comment fixes (mcpConfigStore, aiChatPrompt, McpResource, McpServerRow); extension header matches behavior - Slash command prompts for server/resource; MCP status button aria-label; i18n for new strings Made-with: Cursor
Regenerate bun.lock so CI bun install --frozen-lockfile succeeds. Resolves Dependabot PR #454 (lockfile was out of sync). Made-with: Cursor
chore(deps): bump @anthropic-ai/sdk from 0.80.0 to 0.82.0 in the npm_and_yarn group across 1 directory
* fix(security): replace custom HTML sanitizer with DOMPurify (#380) カスタム sanitizeHtml を DOMPurify に置き換え、Mutation XSS・未処理要素 (svg/math/template 等)・危険スキームの網羅的対策を導入。 ホワイトリスト設定を sanitizeHtmlConfig.ts に分離し、テスト 23 件を追加。 Made-with: Cursor * chore: remove redundant @types/dompurify dompurify v3 bundles TypeScript types; knip flagged @types/dompurify as unused. Made-with: Cursor
* feat(editor): 外部URLのビジュアル変換(Gyazo / YouTube)を追加 Gyazo パーマリンクを画像として、YouTube URLをiframe埋め込みとして ペースト時・入力時に自動変換する機能を実装。 - Gyazo permalink → i.gyazo.com 画像URLに変換して表示 - YouTube URL → レスポンシブiframeで動画を埋め込み表示 - URL変換ユーティリティ (urlTransform.ts) を新規作成 - YouTubeEmbed ノード拡張とNodeViewを新規作成 - セキュリティ制約(localhost/プライベートIP拒否)を維持 Closes #363 https://claude.ai/code/session_01B65Y1xTeRncHhMokAMpfTo * fix(editor): address PR review comments for external URL transformation - Remove src attribute from youtubeEmbed schema; derive embed URL from videoId only to prevent URL injection and data inconsistency - Add videoId validation (11-char alphanumeric pattern) - Add case-insensitive flag to YouTube URL regex patterns - Replace deprecated frameBorder/frameborder with CSS border:0 - Add bilingual JSDoc to StorageImageOptions and StorageImage exports - Remove empty JSDoc blocks from InputRule handlers https://claude.ai/code/session_01B65Y1xTeRncHhMokAMpfTo * fix(editor): use boolean for allowfullscreen in renderHTML Align allowfullscreen attribute type with NodeView (boolean instead of string "true") for consistency. https://claude.ai/code/session_01B65Y1xTeRncHhMokAMpfTo --------- Co-authored-by: Claude <noreply@anthropic.com>
* fix: モバイルでカード上スクロールが効かない問題を修正 Radix UI ContextMenuTrigger がモバイルでタッチイベントを preventDefault() するためスクロールが阻害されていた。 モバイルでは ContextMenu の代わりに独自の長押し検出フック (useLongPress) + DropdownMenu を使用するように変更し、 スクロールと長押しメニューを両立させる。 - useLongPress フックを新規追加(タッチ移動量による閾値判定付き) - PageCard: モバイルでは DropdownMenu、デスクトップでは ContextMenu を使い分け - メニュー項目は共通関数で共有 https://claude.ai/code/session_01S9srRSXaC2drSWTCJ7ymfC * feat: add GitHub MCP server configuration プロジェクトルートに .mcp.json を追加し、Claude Code から GitHub MCP ツール(PR作成・Issue管理等)を利用可能にする。 トークンは環境変数 GITHUB_PERSONAL_ACCESS_TOKEN で参照する。 https://claude.ai/code/session_01S9srRSXaC2drSWTCJ7ymfC --------- Co-authored-by: Claude <noreply@anthropic.com>
* feat(editor): 外部URLのビジュアル変換(Gyazo / YouTube)を追加 Gyazo パーマリンクを画像として、YouTube URLをiframe埋め込みとして ペースト時・入力時に自動変換する機能を実装。 - Gyazo permalink → i.gyazo.com 画像URLに変換して表示 - YouTube URL → レスポンシブiframeで動画を埋め込み表示 - URL変換ユーティリティ (urlTransform.ts) を新規作成 - YouTubeEmbed ノード拡張とNodeViewを新規作成 - セキュリティ制約(localhost/プライベートIP拒否)を維持 Closes #363 https://claude.ai/code/session_01B65Y1xTeRncHhMokAMpfTo * fix(editor): address PR review comments for external URL transformation - Remove src attribute from youtubeEmbed schema; derive embed URL from videoId only to prevent URL injection and data inconsistency - Add videoId validation (11-char alphanumeric pattern) - Add case-insensitive flag to YouTube URL regex patterns - Replace deprecated frameBorder/frameborder with CSS border:0 - Add bilingual JSDoc to StorageImageOptions and StorageImage exports - Remove empty JSDoc blocks from InputRule handlers https://claude.ai/code/session_01B65Y1xTeRncHhMokAMpfTo * feat(ui): add wikilink hover card preview for editor and ai chat - shared WikiLinkPreviewContent and unit tests - Tiptap WikiLinkHoverCardLayer / useWikiLinkHover (long-press, typing suppress) - AIChatWikiLink with HoverCard and long-press - expose handleLinkClick from useTiptapEditorController Made-with: Cursor * fix(editor): remove duplicate YouTubeEmbed import in editorConfig Resolves vite build PARSE_ERROR: Identifier YouTubeEmbed has already been declared (merge artifact). Made-with: Cursor * fix: address PR #487 gemini review (hover card edge cases) - AIChatWikiLink: reset preventClickRef after 100ms; clear timers on unmount - useWikiLinkHover: add isVisible to effect deps so click closes stale card - WikiLinkPreviewContent: show create CTA only when onClick; div when no action Made-with: Cursor * fix(editor): remove duplicate YouTubeEmbed in editorConfig (PR #487) Made-with: Cursor * fix: address PR #487 CodeRabbit review (wiki link preview) Made-with: Cursor * fix: address PR #487 review comments - AIChatWikiLink: handleTouchCancel clears long-press timers; ghost link uses focusable button with navigation - WikiLinkHoverCardLayer: derive exists/referenced from page store (not stale DOM attrs) - WikiLinkPreviewContent: phrasing-only markup inside interactive buttons (span layout) Made-with: Cursor * fix: address PR #487 Devin review (hover re-enter, bilingual JSDoc) - useWikiLinkHover: cancel close timer when pointer returns to same wiki-link from card - AIChatWikiLink: add English line to handleTouchCancel JSDoc (AGENTS.md) Made-with: Cursor * fix: address PR #487 CodeRabbit review (useWikiLinkHover) - Clear open/long-press timers on keydown; re-check typing window in delayed open - touchcancel clears long-press timer (same as AIChat chat path) - Capture-phase click guard after long-press open to block synthetic navigation - isWithinTypingSuppressWindow helper; eslint max-lines exception for cohesive hook Made-with: Cursor * fix: add explicit return type UseWikiLinkHoverResult to useWikiLinkHover Address PR #487 CodeRabbit review nitpick: define an explicit return interface so internal changes do not break consumers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore(agents): consolidate pr review workflow into spec-driven skill - Expand handle-pr-review skill with TSDoc/test/type alignment steps - Trim AGENTS.md and point to the skill - Add Claude Code Bash allowlist for gh api and bun run in settings.local.json - Remove obsolete .claude/commands/review-pr-comments.md Made-with: Cursor * fix(editor): align WikiLink hover refs with CodeRabbit review - Use explicit undefined initial values for timer refs (React 19 typings). - Reset currentElementRef on keydown and defer assignment until after typing suppression so the same link can reopen after suppress/interrupt. Made-with: Cursor --------- Co-authored-by: Claude <noreply@anthropic.com>
* fix(sync): include content_preview in PUT /content to sync page list summary across devices CollaborationManager.buildPutContentBody() now generates a 120-char content_preview from the plain text and includes it in the PUT payload. The server PUT /pages/:id/content endpoint accepts content_preview and persists it to the pages table. Hocuspocus saveDocumentToDb() also extracts text from Y.Doc and updates pages.content_preview so collaborative editing keeps the preview in sync. Closes #489 https://claude.ai/code/session_01WPpwEVBuAB2MGshdr7Xi5S * refactor: address review feedback on content_preview sync - Extract duplicated pages-table update logic into applyPagesMetadataUpdate() - Change buildContentPreview() to accept plain text instead of re-traversing Y.Doc - Wrap Hocuspocus saveDocumentToDb() in a transaction for atomicity https://claude.ai/code/session_01WPpwEVBuAB2MGshdr7Xi5S * fix: add missing Database type import in pages route https://claude.ai/code/session_01WPpwEVBuAB2MGshdr7Xi5S --------- Co-authored-by: Claude <noreply@anthropic.com>
📝 WalkthroughWalkthroughTauri デスクトップアプリと Claude Code サイドカーの導入、TipTap エディタ拡張(実行可能コードブロック、MCP リソース、スラッシュエージェント等)、ワークフロー実行・管理、ワークスペース連携、多プロバイダ AI 統合、および関連 UI/テスト/ローカライズの大規模追加。 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant UI as App (Renderer)
participant Tauri as Tauri (Rust)
participant Sidecar as Sidecar (JSONL proc)
participant Claude as Claude SDK
UI->>Tauri: invoke `claude_query` (prompt, options)
Tauri->>Sidecar: write JSONL request to stdin
Sidecar->>Claude: run query via Claude SDK / agent
Claude-->>Sidecar: stream chunk events (tool start/stop, text deltas)
Sidecar-->>Tauri: emit JSONL response lines (stream-chunk / tool-use / mcp-status)
Tauri-->>UI: emit Tauri events (claude-stream-chunk / claude-tool-use-*)
UI->>Tauri: (optionally) invoke `claude_abort`
Tauri->>Sidecar: write abort JSONL
Sidecar->>Claude: abort underlying request
Sidecar-->>Tauri: emit stream-complete / error
Tauri-->>UI: emit completion/error event
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 分 Possibly related PRs
Suggested labels
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Code Review
This pull request introduces a desktop-native workflow feature using Tauri and Claude Code, including a sidecar process for secure communication, a new workflow UI panel, and support for MCP resources. I have reviewed the changes and found an issue in the text extraction logic for content previews in server/hocuspocus/src/index.ts, where improper newline insertion can lead to fragmented text. A suggestion has been provided to use spaces instead of newlines for block elements to ensure better formatting.
| } else if (child instanceof Y.XmlElement) { | ||
| text += extractTextFromFragment(child) + "\n"; | ||
| } |
There was a problem hiding this comment.
現在のテキスト抽出ロジックでは、インライン要素(<strong>など)の後にも改行(\n)が追加されてしまい、不自然なスペースがプレビューや検索用テキストに含まれる可能性があります。
例えば、<p>Hello <strong>world</strong>!</p> は Hello world\n! のように変換されてしまいます。
ブロック要素とインライン要素を区別するのは難しいですが、ここでは改行の代わりにスペースを追加することで、後続の replace(/\s+/g, " ") 処理で適切に整形されるようにするのが安全です。
| } else if (child instanceof Y.XmlElement) { | |
| text += extractTextFromFragment(child) + "\n"; | |
| } | |
| } else if (child instanceof Y.XmlElement) { | |
| text += extractTextFromFragment(child) + " "; | |
| } |
There was a problem hiding this comment.
ご指摘のとおりでした。インライン要素のあとに一律 \n を付けないよう、bold / italic などインライン相当の nodeName を列挙し、その XmlElement の後はスペース、ブロック要素の後は改行とする実装に変更しました(server/hocuspocus/src/extractPlainTextFromYXml.ts)。単体テストも追加済みです。対応は develop 向け PR #493 経由でマージ済みです。
Implemented inline vs block suffixes in extractPlainTextFromYXml.ts (not a blanket space for every element), with tests. Landed via #493.
- Hocuspocus: extract inline mark elements (bold, etc.) without spurious newlines; add extractPlainTextFromYXml module + tests (Gemini review). - aiClient: allow claude-code branch when API key is empty (CodeRabbit review). - AIChatWikiLink: useRef initial values for React 19 types (CodeRabbit review). Made-with: Cursor
#493) - Hocuspocus: extract inline mark elements (bold, etc.) without spurious newlines; add extractPlainTextFromYXml module + tests (Gemini review). - aiClient: allow claude-code branch when API key is empty (CodeRabbit review). - AIChatWikiLink: useRef initial values for React 19 types (CodeRabbit review). Made-with: Cursor
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
server/hocuspocus/src/extractPlainTextFromYXml.test.ts (1)
54-62: 境界値テストの追加を検討してください(任意)
buildContentPreviewのテストに、ちょうど 120 文字の境界値ケースを追加すると、切り詰めロジックの正確性をより確実に検証できます。🧪 追加テストの提案
describe("buildContentPreview", () => { it("collapses whitespace and truncates", () => { expect(buildContentPreview(" a \n b ")).toBe("a b"); const long = "x".repeat(200); const prev = buildContentPreview(long); expect(prev.endsWith("...")).toBe(true); expect(prev.length).toBeLessThanOrEqual(124); }); + + it("does not truncate text exactly at max length", () => { + const exact = "x".repeat(120); + expect(buildContentPreview(exact)).toBe(exact); + expect(buildContentPreview(exact).endsWith("...")).toBe(false); + }); + + it("returns empty string for empty input", () => { + expect(buildContentPreview("")).toBe(""); + expect(buildContentPreview(" ")).toBe(""); + }); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/hocuspocus/src/extractPlainTextFromYXml.test.ts` around lines 54 - 62, Add a boundary test for buildContentPreview that supplies an input string exactly at the truncation threshold (e.g., 120 characters) to verify the cut-off logic; create a 120-char string (using repeat or concatenation), call buildContentPreview with it, and assert that the result is not suffixed with "..." and that its length meets the expected non-truncated length (or matches the original preview length rules), so you validate the edge-case behavior of buildContentPreview.server/hocuspocus/src/extractPlainTextFromYXml.ts (1)
10-21: インライン要素のカスタマイズについて(任意の改善)現在の
INLINE_XML_ELEMENT_NAMESは一般的な TipTap マークをカバーしていますが、カスタムマークやエディタ拡張が追加された場合、不自然な改行が発生する可能性があります。将来的には、この Set を設定可能にするか、フォールバック検出ロジック(例:子要素が XmlText のみの場合はインラインとみなす)を検討してください。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/hocuspocus/src/extractPlainTextFromYXml.ts` around lines 10 - 21, The hardcoded INLINE_XML_ELEMENT_NAMES Set can cause incorrect block/inline detection for custom marks; update the code to allow configuration and add a fallback that treats an XmlXMLElement as inline when its children are all XmlText (or empty) — expose INLINE_XML_ELEMENT_NAMES as a configurable parameter (or accept an optional set in extractPlainTextFromYXml and/or its caller), and inside the detection logic in extractPlainTextFromYXml use: first check the configured set, and if not present, treat elements as inline when every child node is of type XmlText (or there are no child elements); ensure to reference and update the INLINE_XML_ELEMENT_NAMES constant and the element-kind check in extractPlainTextFromYXml accordingly.server/hocuspocus/src/index.ts (1)
199-200: 空文字列とnullの扱いの一貫性について(任意の改善)
clipAndCreate.tsではcontentText || nullパターンを使用していますが、ここでは空文字列をそのまま保存しています。動作上は問題ありませんが、他のコードとの一貫性を保つ場合は以下の変更を検討してください。♻️ 一貫性のための修正案
- const contentText = extractTextFromYXml(document.getXmlFragment("default")); - const contentPreview = buildContentPreview(contentText); + const rawText = extractTextFromYXml(document.getXmlFragment("default")); + const contentText = rawText.trim() || null; + const contentPreview = contentText ? buildContentPreview(rawText) : null;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/hocuspocus/src/index.ts` around lines 199 - 200, The code currently assigns contentText = extractTextFromYXml(document.getXmlFragment("default")) and leaves empty string as-is whereas other code (clipAndCreate.ts) uses contentText || null for consistency; update this location to normalize empty content to null (e.g., set contentText = extractTextFromYXml(...) || null) or adjust buildContentPreview to accept nulls, and ensure contentPreview is derived from that normalized value so the module uses the same empty-string/null convention as clipAndCreate.ts; reference extractTextFromYXml, buildContentPreview, contentText, and contentPreview when making the change.
🤖 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/lib/aiClient.ts`:
- Around line 8-9: The exported type AIClient and the exported
ConnectionTestResult need bilingual JSDoc/TSDoc comments (Japanese and English)
per repo guidelines; update the JSDoc above the AIClient type declaration and
above the ConnectionTestResult declaration to include both a short Japanese
description and an English description, mention that AIClient is a union of SDK
clients (OpenAI, Anthropic, GoogleGenAI) and that ConnectionTestResult
represents the result shape for connection tests so reviewers can find them by
symbol names AIClient and ConnectionTestResult.
---
Nitpick comments:
In `@server/hocuspocus/src/extractPlainTextFromYXml.test.ts`:
- Around line 54-62: Add a boundary test for buildContentPreview that supplies
an input string exactly at the truncation threshold (e.g., 120 characters) to
verify the cut-off logic; create a 120-char string (using repeat or
concatenation), call buildContentPreview with it, and assert that the result is
not suffixed with "..." and that its length meets the expected non-truncated
length (or matches the original preview length rules), so you validate the
edge-case behavior of buildContentPreview.
In `@server/hocuspocus/src/extractPlainTextFromYXml.ts`:
- Around line 10-21: The hardcoded INLINE_XML_ELEMENT_NAMES Set can cause
incorrect block/inline detection for custom marks; update the code to allow
configuration and add a fallback that treats an XmlXMLElement as inline when its
children are all XmlText (or empty) — expose INLINE_XML_ELEMENT_NAMES as a
configurable parameter (or accept an optional set in extractPlainTextFromYXml
and/or its caller), and inside the detection logic in extractPlainTextFromYXml
use: first check the configured set, and if not present, treat elements as
inline when every child node is of type XmlText (or there are no child
elements); ensure to reference and update the INLINE_XML_ELEMENT_NAMES constant
and the element-kind check in extractPlainTextFromYXml accordingly.
In `@server/hocuspocus/src/index.ts`:
- Around line 199-200: The code currently assigns contentText =
extractTextFromYXml(document.getXmlFragment("default")) and leaves empty string
as-is whereas other code (clipAndCreate.ts) uses contentText || null for
consistency; update this location to normalize empty content to null (e.g., set
contentText = extractTextFromYXml(...) || null) or adjust buildContentPreview to
accept nulls, and ensure contentPreview is derived from that normalized value so
the module uses the same empty-string/null convention as clipAndCreate.ts;
reference extractTextFromYXml, buildContentPreview, contentText, and
contentPreview when making the change.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b01516f7-c303-49aa-9205-c243b5f1bb0e
📒 Files selected for processing (6)
server/hocuspocus/src/extractPlainTextFromYXml.test.tsserver/hocuspocus/src/extractPlainTextFromYXml.tsserver/hocuspocus/src/index.tssrc/components/ai-chat/AIChatWikiLink.tsxsrc/lib/aiClient.test.tssrc/lib/aiClient.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/ai-chat/AIChatWikiLink.tsx
| /** Union of SDK client types used for user-API-key mode. */ | ||
| export type AIClient = OpenAI | Anthropic | GoogleGenAI; |
There was a problem hiding this comment.
公開型のJSDocを日英併記にしてください。
AIClient と ConnectionTestResult の新規JSDocが英語のみで、リポジトリのコメント規約を満たしていません。
✍️ 提案差分
-/** Union of SDK client types used for user-API-key mode. */
+/**
+ * ユーザーAPIキー方式で使用するSDKクライアントのユニオン型。
+ * Union of SDK client types used for user-API-key mode.
+ */
export type AIClient = OpenAI | Anthropic | GoogleGenAI;
@@
-/** Result of a user-API-key connection test from the settings UI. */
+/**
+ * 設定画面におけるユーザーAPIキー接続テストの結果。
+ * Result of a user-API-key connection test from the settings UI.
+ */
export interface ConnectionTestResult {As per coding guidelines Export functions, types, and interfaces must include TSDoc or JSDoc comments. Comments and documentation must include both Japanese and English explanations where applicable.
Also applies to: 43-44
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/aiClient.ts` around lines 8 - 9, The exported type AIClient and the
exported ConnectionTestResult need bilingual JSDoc/TSDoc comments (Japanese and
English) per repo guidelines; update the JSDoc above the AIClient type
declaration and above the ConnectionTestResult declaration to include both a
short Japanese description and an English description, mention that AIClient is
a union of SDK clients (OpenAI, Anthropic, GoogleGenAI) and that
ConnectionTestResult represents the result shape for connection tests so
reviewers can find them by symbol names AIClient and ConnectionTestResult.
| <Button | ||
| variant="link" | ||
| size="sm" | ||
| className="ml-2 h-auto p-0 text-xs" | ||
| onClick={handleFetch} | ||
| > | ||
| {t("common.retry", "Retry")} | ||
| </Button> |
There was a problem hiding this comment.
🟡 McpResourceNodeView Retry button lacks disabled guard, allowing concurrent fetches that leak Tauri event listeners
The "Retry" button inside the error block (src/components/editor/McpResourceNodeView.tsx:213-220) does not set disabled={isLoading}, unlike the main Fetch button at line 177. If the user clicks Retry while a previous fetch is already in-flight (possible with quick double-clicks), a new set of onClaudeStreamComplete / onClaudeError listeners is registered and activeUnlistenRef.current is overwritten at line 142, orphaning the previous listeners. The old listeners are never unsubscribed, causing a resource leak. Each leaked listener also risks processing stale events for a different request id.
| <Button | |
| variant="link" | |
| size="sm" | |
| className="ml-2 h-auto p-0 text-xs" | |
| onClick={handleFetch} | |
| > | |
| {t("common.retry", "Retry")} | |
| </Button> | |
| <Button | |
| variant="link" | |
| size="sm" | |
| className="ml-2 h-auto p-0 text-xs" | |
| onClick={handleFetch} | |
| disabled={isLoading} | |
| > |
Was this helpful? React with 👍 or 👎 to provide feedback.
| const text = extractAssistantText(msg); | ||
| const delta = text.length > 0 ? text.slice(aggregated.length) : ""; | ||
| if (delta.length > 0) { | ||
| aggregated += delta; | ||
| emit({ type: "stream-chunk", id, content: delta }); | ||
| } |
There was a problem hiding this comment.
🟡 Sidecar assistant-message delta uses positional slice instead of prefix-aware diff, producing corrupt text when SDK message differs from streamed aggregation
In packages/claude-sidecar/src/handlers/query.ts:255, the delta for an assistant message is computed as text.slice(aggregated.length). This assumes the assistant message's full text always starts with exactly the same characters as aggregated (built from prior stream_event text deltas). If the SDK sends an assistant message whose text is shorter than aggregated (e.g. the model revised its output), the slice produces an empty string and the revision is silently dropped. If the text diverges from aggregated (same length, different prefix), the slice produces incorrect trailing characters that are emitted as a "delta" to the host, corrupting the displayed output.
Prompt for agents
In runQuery (packages/claude-sidecar/src/handlers/query.ts), the assistant-message handler at line 255 computes the delta as text.slice(aggregated.length). This is fragile because it assumes the full assistant text always starts with the exact same bytes as the aggregated stream content. A safer approach would be to check whether text starts with aggregated (e.g. text.startsWith(aggregated)) and only emit the suffix as a delta in that case. When the text does NOT start with aggregated, the handler should either replace the entire aggregated content and emit the full new text as a delta, or diff the two strings. The key functions involved are extractAssistantText and the msg.type === 'assistant' branch in runQuery.
Was this helpful? React with 👍 or 👎 to provide feedback.
| let currentRequestId: string | null = null; | ||
| let aborted = false; | ||
| let resolveWait: (() => void) | null = null; |
There was a problem hiding this comment.
🔴 claudeCodeProvider closure shares mutable aborted/currentRequestId state across concurrent query() calls
In src/lib/aiProviders/claudeCodeProvider.ts:43-44, currentRequestId and aborted are declared as closure variables outside the query() generator. If two consumers call query() concurrently (e.g. chat + workflow panel), the second call resets aborted = false (line 63), overwrites currentRequestId (line 143), and steals all subsequent sidecar events. The first query silently stops receiving chunks and never completes. Calling abort() only cancels whichever query last wrote to currentRequestId.
Affected variables
currentRequestId(line 43): overwritten by eachclaudeQuery()callaborted(line 44): reset at the start of eachquery()resolveWait(line 45): overwritten by the wait loop
Prompt for agents
The createClaudeCodeProvider factory in src/lib/aiProviders/claudeCodeProvider.ts uses closure-scoped variables (currentRequestId, aborted, resolveWait) that are shared across all invocations of query(). Move these variables inside the query() async generator so each call has its own isolated state. The abort() method needs a way to signal the active query - consider storing per-request AbortControllers in a Map keyed by request id, or having query() accept an AbortSignal (which it already does via the signal parameter) instead of relying on the shared aborted flag. The currentRequestId used by event listeners should also be scoped per invocation (e.g. captured by the listener closures registered in that specific call).
Was this helpful? React with 👍 or 👎 to provide feedback.
| const unlistenChunk = await onClaudeStreamChunk((payload) => { | ||
| if (currentRequestId && payload.id === currentRequestId) { | ||
| chunks.push({ type: "text", content: payload.content }); | ||
| wake(); | ||
| } | ||
| }); | ||
|
|
||
| const unlistenComplete = await onClaudeStreamComplete((payload) => { | ||
| if (currentRequestId && payload.id === currentRequestId) { | ||
| chunks.push({ type: "done", content: "" }); | ||
| done = true; | ||
| wake(); | ||
| } | ||
| }); | ||
|
|
||
| const unlistenError = await onClaudeError((payload) => { | ||
| if (currentRequestId && payload.id === currentRequestId) { | ||
| chunks.push({ type: "error", content: payload.error }); | ||
| done = true; | ||
| wake(); | ||
| } | ||
| }); | ||
|
|
||
| const unlistenToolStart = await onClaudeToolUseStart((payload) => { | ||
| if (currentRequestId && payload.id === currentRequestId) { | ||
| chunks.push({ | ||
| type: "tool_use_start", | ||
| content: payload.toolInput, | ||
| toolName: payload.toolName, | ||
| }); | ||
| wake(); | ||
| } | ||
| }); | ||
|
|
||
| const unlistenToolComplete = await onClaudeToolUseComplete((payload) => { | ||
| if (currentRequestId && payload.id === currentRequestId) { | ||
| chunks.push({ | ||
| type: "tool_use_complete", | ||
| content: "", | ||
| toolName: payload.toolName, | ||
| }); | ||
| wake(); | ||
| } | ||
| }); | ||
|
|
||
| const unlistenMcpStatus = await onClaudeMcpStatus((payload) => { | ||
| if (currentRequestId && payload.id === currentRequestId && payload.servers) { | ||
| const store = useMcpConfigStore.getState(); | ||
| store.updateStatuses( | ||
| payload.servers.map((s) => ({ | ||
| name: s.name, | ||
| status: normalizeMcpConnectionStatus(s.status), | ||
| error: s.error, | ||
| tools: s.tools as McpServerTool[] | undefined, | ||
| })), | ||
| ); | ||
| } | ||
| }); |
There was a problem hiding this comment.
🟡 Tauri event listener leak when early onClaude* registration throws in claudeCodeProvider
In src/lib/aiProviders/claudeCodeProvider.ts:80-118, all six Tauri event listeners (onClaudeStreamChunk, onClaudeStreamComplete, etc.) are registered sequentially before the try block at line 139. If, for example, onClaudeToolUseComplete (line 110) throws, the previously registered listeners (unlistenChunk, unlistenComplete, unlistenError, unlistenToolStart) are never cleaned up because the finally block at line 180 is inside a separate try that is never entered. The leaked listeners continue receiving events from all future sidecar queries.
Prompt for agents
In createClaudeCodeProvider (src/lib/aiProviders/claudeCodeProvider.ts), the six listener registrations at lines 80-118 happen before the try/finally block at lines 139-188. If any registration throws partway through, the already-registered listeners are leaked. Move all listener registrations inside the try block, and collect them into an array (const cleanups: Array<() => void> = []). In the finally block, iterate and call each cleanup. The streamClaudeQuery function in src/lib/claudeCode/streamClaudeQuery.ts already uses this pattern correctly (lines 110-183, cleanups array) and can serve as a reference.
Was this helpful? React with 👍 or 👎 to provide feedback.
概要
developの最新をmainに取り込むリリース用 PR です。Tauri / Claude Code 連携、AI チャット・エディタ拡張、MCP、HTML サニタイズの強化、各種 UI 改善などを含みます。Release PR merging the current
developintomain.変更点
@file:/ cwd)@anthropic-ai/sdkなどの更新変更の種類
テスト方法
チェックリスト
マージ方法
プロジェクト方針に合わせ、Create a merge commit でのマージを推奨します。
スクリーンショット(UI 変更がある場合)
UI 変更を含みます。必要に応じてリリースノート用に主要画面のキャプチャを添付してください。
関連 Issue
Summary by CodeRabbit
New Features
改善
Bug Fixes