feat: workspace-linked notes for @file: and Claude cwd (#461)#476
Conversation
- 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
|
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:
📝 WalkthroughWalkthroughノートごとにローカルワークスペースを紐付け可能にし、Tauri 側に register/clear/read/list ハンドラを追加。フロントで FileReference 拡張、ファイルプレビュー UI、NoteWorkspace コンテキスト/ツールバー、パス補完と Claude Code 実行時の cwd 伝播を導入した。 Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Editor as TipTap Editor
participant FileRef as FileReference
participant Window as Browser Window
participant Frontend as Frontend Invoke
participant Rust as Tauri/Rust
participant Dialog as FilePreviewDialogHost
User->>Editor: クリック `@file:...`
Editor->>FileRef: クリックイベント処理
FileRef->>Window: getWorkspaceRoot() / getNoteId()
alt no workspace
FileRef->>Window: dispatchFilePreview({ noWorkspace: true })
else has workspace
FileRef->>Frontend: invoke("read_note_workspace_file", { noteId, relativePath })
Frontend->>Rust: read_note_workspace_file(noteId, relativePath)
Rust->>Rust: resolve_under_root + read (cap)
Rust-->>Frontend: content | error
Frontend-->>FileRef: content | error
FileRef->>Window: dispatchFilePreview({ content | error, truncated? })
end
Window->>Dialog: FILE_PREVIEW_EVENT を受信して表示
Dialog->>User: プレビュー表示
sequenceDiagram
participant User as User
participant Slash as Slash Menu
participant Agent as Agent Executor
participant AI as AI Service / Sidecar
User->>Slash: スラッシュコマンド実行
Slash->>Agent: executeAgentSlashCommand(..., claudeCwd)
Agent->>AI: streamAssistantCompletion(request with cwd)
AI-->>Agent: completion
Agent-->>Slash: 結果を挿入
Slash->>User: 応答表示
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces note-linked workspaces for the desktop application, allowing users to associate local folders with specific notes to enable file path completion, in-editor file previews, and context-aware AI agent commands. The implementation includes a new Tiptap extension for @file: references, a workspace management toolbar, and secure Tauri-based file system operations. Feedback focuses on optimizing the Rust path resolution logic to minimize system calls, preventing disruptive editor re-initializations when workspace links change by using a stable getter, and adopting more idiomatic TypeScript patterns for local storage management.
| pub(crate) fn resolve_under_root(root_canon: &PathBuf, relative: &str) -> Result<PathBuf, String> { | ||
| let trimmed = relative.trim(); | ||
| if trimmed.is_empty() { | ||
| return Ok(root_canon.clone()); | ||
| } | ||
| let mut acc = root_canon.clone(); | ||
| for comp in Path::new(trimmed).components() { | ||
| match comp { | ||
| Component::Normal(c) => { | ||
| acc.push(c); | ||
| if acc.exists() { | ||
| let canon = acc.canonicalize().map_err(|e| e.to_string())?; | ||
| if !canon.starts_with(root_canon) { | ||
| return Err("path outside workspace".into()); | ||
| } | ||
| acc = canon; | ||
| } | ||
| } | ||
| Component::ParentDir => { | ||
| acc.pop(); | ||
| if !acc.starts_with(root_canon) { | ||
| return Err("path outside workspace".into()); | ||
| } | ||
| } | ||
| Component::CurDir => {} | ||
| Component::RootDir | Component::Prefix(_) => { | ||
| return Err("invalid path".into()); | ||
| } | ||
| } | ||
| } | ||
| if acc.exists() { | ||
| let canon = acc.canonicalize().map_err(|e| e.to_string())?; | ||
| if !canon.starts_with(root_canon) { | ||
| return Err("path outside workspace".into()); | ||
| } | ||
| return Ok(canon); | ||
| } | ||
| if !acc.starts_with(root_canon) { | ||
| return Err("path outside workspace".into()); | ||
| } | ||
| Ok(acc) | ||
| } |
There was a problem hiding this comment.
The resolve_under_root function is inefficient because it performs exists() and canonicalize() syscalls for every component of the path in a loop. Since this function is primarily used for reading existing files or listing existing directories, a more efficient approach is to perform lexical path resolution first to prevent traversal, and then perform a single canonicalize() call at the end to handle symlinks and verify the final path is still within the root.
pub(crate) fn resolve_under_root(root_canon: &Path, relative: &str) -> Result<PathBuf, String> {
let mut acc = root_canon.to_path_buf();
for comp in Path::new(relative).components() {
match comp {
Component::Normal(c) => acc.push(c),
Component::ParentDir => {
acc.pop();
}
Component::CurDir => {}
Component::RootDir | Component::Prefix(_) => {
return Err("invalid path".into());
}
}
if !acc.starts_with(root_canon) {
return Err("path outside workspace".into());
}
}
if acc.exists() {
let canon = acc.canonicalize().map_err(|e| e.to_string())?;
if !canon.starts_with(root_canon) {
return Err("path outside workspace".into());
}
Ok(canon)
} else {
Ok(acc)
}
}There was a problem hiding this comment.
対応しました。resolve_under_root を字句結合ベースにし、存在する終端パス、または非存在時は最長の存在接頭辞へ遡って一度 canonicalize する形に変更しました(セグメント毎の exists/canonicalize 呼び出しを削減)。シンボリックリンク越しのエスケープ検出は維持しています。
| }, | ||
| }, | ||
| [pageId, useCollaborationMode], | ||
| [pageId, useCollaborationMode, workspaceRoot], |
There was a problem hiding this comment.
Including workspaceRoot in the useEditor dependency array causes the entire Tiptap editor to be re-initialized whenever a workspace is linked or unlinked. This is disruptive as it can cause loss of focus, cursor position, and scroll state.
Instead, you should use a useRef to store the current workspaceRoot and pass a stable getter function to the FileReference extension. This allows the extension to access the latest workspace root without requiring an editor restart.
| [pageId, useCollaborationMode, workspaceRoot], | |
| [pageId, useCollaborationMode], |
There was a problem hiding this comment.
対応しました。useEditor の依存から workspaceRoot を外し、useWorkspaceRootRef(useLayoutEffect で同期)経由で getWorkspaceRoot が最新値を返すようにしました。createEditorExtensions 周りは react-hooks/refs のためブロック抑制を付与しています。
| export function clearNoteWorkspacePath(noteId: string): void { | ||
| const map = readMap(); | ||
| if (!(noteId in map)) return; | ||
| const { [noteId]: _removed, ...rest } = map; | ||
| void _removed; | ||
| writeMap(rest); | ||
| } |
There was a problem hiding this comment.
The implementation of clearNoteWorkspacePath uses a non-idiomatic destructuring pattern to remove a key from the map. Since map is a local object, using the delete operator is simpler and more readable.
export function clearNoteWorkspacePath(noteId: string): void {
const map = readMap();
if (!(noteId in map)) return;
delete map[noteId];
writeMap(map);
}There was a problem hiding this comment.
意図的に分割代入にしています。@typescript-eslint/no-dynamic-delete が error のため delete 演算子は使えません。
There was a problem hiding this comment.
Pull request overview
Adds a per-note “linked workspace folder” for the desktop app, enabling @file: relative file references with click-to-preview, and propagating that workspace root as Claude Code’s working directory (cwd) for chat, agent slash commands, and executable code.
Changes:
- Introduces a local-only noteId → workspaceRoot store + React context, plus a toolbar to link/change/clear and browse the workspace tree.
- Adds
@file:support in the editor (mark + click handler) and a global dialog host to preview file contents via Tauri commands. - Extends Claude Code integrations (AI chat, agent slash, executable code) to pass
cwd, and adds new Tauri commands for workspace-rooted file read/list with traversal protection.
Reviewed changes
Copilot reviewed 35 out of 37 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types/aiChat.ts | Extends page context with noteId and claudeWorkspaceRoot for local desktop-only behavior. |
| src/pages/NotePageView.tsx | Wraps editor with NoteWorkspaceProvider and injects workspace root into AI chat page context; adds toolbar. |
| src/lib/noteWorkspace/pickNoteWorkspaceDirectory.ts | Desktop-only folder picker using Tauri dialog plugin. |
| src/lib/noteWorkspace/noteWorkspaceStore.ts | LocalStorage persistence for note → workspace path mapping. |
| src/lib/noteWorkspace/noteWorkspaceStore.test.ts | Tests for local workspace persistence helpers. |
| src/lib/noteWorkspace/noteWorkspaceIo.ts | Tauri invoke wrappers for reading files and listing entries under a note-linked root. |
| src/lib/noteWorkspace/filePreviewEvents.ts | Defines and dispatches a window CustomEvent for preview requests/results. |
| src/lib/noteWorkspace/filePreviewEvents.test.ts | Tests for preview event dispatch behavior. |
| src/lib/executableCode/executeExecutableCode.ts | Adds optional cwd forwarding into Claude Code execution options. |
| src/lib/aiService.ts | Adds cwd option and forwards it to the Claude Code provider query options. |
| src/lib/agentSlashCommands/executeAgentSlashCommand.ts | Adds optional claudeCwd and forwards as cwd to Claude Code query options. |
| src/i18n/locales/ja/editor.json | Adds JA strings for workspace toolbar and file preview dialog. |
| src/i18n/locales/en/editor.json | Adds EN strings for workspace toolbar and file preview dialog. |
| src/hooks/useAIChatExecuteRegenerate.ts | Sends cwd when provider is claude-code and context has a workspace root. |
| src/hooks/useAIChatExecute.ts | Sends cwd when provider is claude-code and context has a workspace root. |
| src/contexts/NoteWorkspaceContext.tsx | New context/provider for per-note workspace root and actions (pick/clear/set). |
| src/components/note/NoteWorkspaceToolbar.tsx | UI for linking a folder, browsing entries, and clearing the workspace link. |
| src/components/note/FilePreviewDialogHost.tsx | Global event listener that shows a dialog with file preview content/errors. |
| src/components/editor/TiptapEditor/useWorkspacePathCompletions.ts | Path completion now supports listing from note workspace root or process cwd. |
| src/components/editor/TiptapEditor/useTiptapEditorController.ts | Plumbs workspaceRoot through editor setup and returns claudeWorkspaceRoot for slash menu/cwd. |
| src/components/editor/TiptapEditor/useSlashSuggestionMenuData.ts | Passes noteWorkspaceRoot to path completion hook. |
| src/components/editor/TiptapEditor/useSlashSuggestionMenu.ts | Forwards claudeWorkspaceRoot for path completion and as cwd for agent slash execution. |
| src/components/editor/TiptapEditor/useEditorSetup.ts | Adds fileReference extension wiring and workspace root propagation into editor extensions. |
| src/components/editor/TiptapEditor/slashSuggestionMenuProps.ts | Adds claudeWorkspaceRoot to slash menu props. |
| src/components/editor/TiptapEditor/SlashSuggestionLayer.tsx | Passes claudeWorkspaceRoot through to slash suggestion menu. |
| src/components/editor/TiptapEditor/editorConfig.ts | Registers FileReference extension with getWorkspaceRoot option. |
| src/components/editor/TiptapEditor.tsx | Passes claudeWorkspaceRoot into SlashSuggestionLayer. |
| src/components/editor/extensions/FileReferenceExtension.ts | New @file: mark + click handler that reads file via Tauri and dispatches preview events. |
| src/components/editor/executableCodeBlock/useExecutableCodeBlockController.ts | Forwards note workspace root as cwd when running executable notebook code. |
| src/App.tsx | Mounts the global FilePreviewDialogHost. |
| src-tauri/src/workspace_paths.rs | Adds traversal-safe root resolution + new Tauri commands to read/list under a note-linked workspace root. |
| src-tauri/src/lib.rs | Registers dialog plugin and new workspace commands in the invoke handler. |
| src-tauri/Cargo.toml | Adds tauri-plugin-dialog dependency and tempfile dev-dependency for tests. |
| src-tauri/Cargo.lock | Lockfile updates for new Rust dependencies. |
| src-tauri/capabilities/default.json | Enables dialog capability. |
| package.json | Adds @tauri-apps/plugin-dialog dependency. |
| bun.lock | Lockfile updates for @tauri-apps/plugin-dialog and related dependency shifts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -1,4 +1,4 @@ | |||
| import { useRef, useState } from "react"; | |||
| import { useRef, useState, type MutableRefObject } from "react"; | |||
There was a problem hiding this comment.
MutableRefObject is imported twice (once as a value+type import and again via import type), which will cause a TypeScript compile error due to duplicate import bindings. Remove one of the imports (e.g., keep the import type line and drop type MutableRefObject from the first import).
| import { useRef, useState, type MutableRefObject } from "react"; | |
| import { useRef, useState } from "react"; |
There was a problem hiding this comment.
対応しました。MutableRefObject の重複 import を1行にまとめています。
| /** | ||
| * | ||
| */ | ||
| export function useEditorSetup(options: UseEditorSetupOptions) { | ||
| /** | ||
| * | ||
| */ | ||
| const { |
There was a problem hiding this comment.
These JSDoc blocks have blank descriptions (only *), which violates the repo ESLint rule jsdoc/no-blank-block-descriptions (configured as error) and will fail lint. Either add a real description body or remove the empty JSDoc blocks entirely.
There was a problem hiding this comment.
対応しました。空の JSDoc を削除し、useEditorSetup の説明・handleInsertMermaid の説明を追加しました。
| /** | ||
| * | ||
| */ | ||
| const r = await fetch(url, { |
There was a problem hiding this comment.
This empty JSDoc block inside getAuthenticatedImageUrl will trip jsdoc/no-blank-block-descriptions and fail lint. Remove it or replace it with a meaningful description.
There was a problem hiding this comment.
同じく空の JSDoc を削除しました(getAuthenticatedImageUrl 内)。
| /** | ||
| * | ||
| */ | ||
| const { from, to } = editor.state.selection; |
There was a problem hiding this comment.
This blank JSDoc block in onSelectionUpdate violates jsdoc/no-blank-block-descriptions and will fail lint. Please remove it or add an actual description.
There was a problem hiding this comment.
同じく onSelectionUpdate 前の空 JSDoc を削除しました。
| }, [editor, editorRef]); | ||
|
|
||
| /** | ||
| * |
There was a problem hiding this comment.
This empty JSDoc block before handleInsertMermaid violates jsdoc/no-blank-block-descriptions and will fail lint. Remove it or replace with a proper description.
| * | |
| * Inserts a Mermaid diagram at the current selection. |
There was a problem hiding this comment.
handleInsertMermaid に説明コメントを追加しました(上のまとめコメント参照)。
| }, | ||
| [pageId, useCollaborationMode], | ||
| [pageId, useCollaborationMode, workspaceRoot], | ||
| ); |
There was a problem hiding this comment.
Including workspaceRoot in the useEditor dependency array will destroy/recreate the Tiptap editor whenever the workspace is linked/changed, which can reset selection/history and cause visible editor state loss while editing. Prefer keeping the editor instance stable and pass a getWorkspaceRoot that reads from a ref (updated on workspace changes), so the extension can see the latest root without re-initializing the editor.
There was a problem hiding this comment.
上記と同じ対応です(useEditor の依存から workspaceRoot を外し、useWorkspaceRootRef + createEditorExtensions 周りの eslint ブロック)。
…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
|
レビューコメントへの対応をコミットしました(fb9df0a)。最新の変更に対する再レビューをお願いします。 @coderabbitai review |
|
🧠 Learnings used✅ Actions performedReview triggered.
|
- 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
|
CodeRabbit の指摘に対応しました(036e1db)。再レビューをお願いします。 @coderabbitai review |
|
🧠 Learnings used✅ Actions performedReview triggered.
|
- 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
|
レビュー(CodeRabbit Major 2 件)に対応しました( @coderabbitai review |
|
🧠 Learnings used✅ Actions performedReview triggered.
|
…/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
|
CodeRabbit の未返信 3 件に対応しました( @coderabbitai review |
|
🧠 Learnings used✅ Actions performedReview triggered.
|
- Remount NoteWorkspaceProvider on note.id to avoid noteId/workspaceRoot mismatch - Queue register/clear invocations so async completions cannot reorder Made-with: Cursor
|
未返信レビュー 2 件に対応しました( @coderabbitai review |
|
🧠 Learnings used✅ Actions performedReview triggered.
|
- 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
|
レビュー 3 件に対応しました( @coderabbitai review |
|
🧠 Learnings used✅ Actions performedReview triggered.
|
- 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
|
レビュー 3 件に対応しました( @coderabbitai review |
|
🧠 Learnings used✅ Actions performedReview triggered.
|
- 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
|
レビュー 2 件に対応しました( @coderabbitai review |
|
🧠 Learnings used✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/contexts/NoteWorkspaceContext.tsx (1)
78-87: useEffect での localStorage 再読み込みについて
useStateの初期化関数(Line 56-58)で既にreadNoteWorkspacePath(noteId)を呼び出しているため、useEffect内(Line 79)での再読み込みは冗長に見えます。ただし、key={note.id}によるリマウント時に Rust レジストリ同期を確実に行う目的であれば、現在のworkspaceRootstate を使用する方が一貫性があります。💡 提案差分(state を直接使用)
useEffect(() => { - const path = readNoteWorkspacePath(noteId); enqueueRustRegistrySync(async () => { - if (path) { + if (workspaceRoot) { await registerNoteWorkspaceRoot(noteId, workspaceRoot); } else { await clearNoteWorkspaceRoot(noteId); } }); - }, [noteId, enqueueRustRegistrySync]); + }, [noteId, workspaceRoot, enqueueRustRegistrySync]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/contexts/NoteWorkspaceContext.tsx` around lines 78 - 87, The effect is re-reading localStorage via readNoteWorkspacePath(noteId) even though the component already initializes workspaceRoot state with that value; change the useEffect to use the existing workspaceRoot state (instead of calling readNoteWorkspacePath again) when deciding whether to call registerNoteWorkspaceRoot(noteId, path) or clearNoteWorkspaceRoot(noteId), and update the dependency array to include workspaceRoot (and keep noteId and enqueueRustRegistrySync) so the Rust registry sync uses the current state; ensure the effect still wraps calls with enqueueRustRegistrySync and references registerNoteWorkspaceRoot and clearNoteWorkspaceRoot.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/contexts/NoteWorkspaceContext.tsx`:
- Around line 78-87: The effect is re-reading localStorage via
readNoteWorkspacePath(noteId) even though the component already initializes
workspaceRoot state with that value; change the useEffect to use the existing
workspaceRoot state (instead of calling readNoteWorkspacePath again) when
deciding whether to call registerNoteWorkspaceRoot(noteId, path) or
clearNoteWorkspaceRoot(noteId), and update the dependency array to include
workspaceRoot (and keep noteId and enqueueRustRegistrySync) so the Rust registry
sync uses the current state; ensure the effect still wraps calls with
enqueueRustRegistrySync and references registerNoteWorkspaceRoot and
clearNoteWorkspaceRoot.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 21854ed1-836a-4328-b532-81325a121215
📒 Files selected for processing (2)
src-tauri/src/workspace_paths.rssrc/contexts/NoteWorkspaceContext.tsx
| if !target.is_dir() { | ||
| return Ok(vec![]); | ||
| } | ||
| list_directory_names(&target, DEFAULT_NOTE_WORKSPACE_MAX_ENTRIES, None) |
There was a problem hiding this comment.
🟡 cwd-based list_workspace_directory_entries now truncates before client-side prefix filter, missing matches in large directories
The refactored list_workspace_directory_entries now delegates to list_directory_names with DEFAULT_NOTE_WORKSPACE_MAX_ENTRIES (500) as the cap and None for name_prefix (src-tauri/src/workspace_paths.rs:258). Previously, this function returned all entries with no cap. The client-side code in useWorkspacePathCompletions.ts:53-55 applies prefix filtering AFTER receiving the Rust results. Because entries are sorted alphabetically then truncated to 500 server-side (without a prefix filter), any matching entries alphabetically beyond the 500th position are silently dropped before the client can filter them.
For example, in a directory with 1000 non-hidden entries, files starting with letters near z would be truncated away before the client-side startsWith(fp) filter runs. The note-workspace variant (list_note_workspace_entries) doesn't have this problem because it passes name_prefix to Rust for server-side filtering before the cap.
Contrast with note-workspace path which handles this correctly
In useWorkspacePathCompletions.ts:51-52, the note-workspace branch calls listNoteWorkspaceEntries(noteWorkspaceNoteId, dir, 40, filePrefix) which passes filePrefix as namePrefix to Rust for server-side filtering before truncation. The cwd branch does not pass a prefix, relying entirely on post-truncation client filtering.
Prompt for agents
The function list_workspace_directory_entries at src-tauri/src/workspace_paths.rs:258 now calls list_directory_names with DEFAULT_NOTE_WORKSPACE_MAX_ENTRIES (500) as a cap but None for name_prefix. The client-side code in src/components/editor/TiptapEditor/useWorkspacePathCompletions.ts:53-55 does prefix filtering AFTER receiving the capped results, meaning matching entries beyond the 500th alphabetically-sorted position are lost.
Two possible fixes:
1. Either remove the cap for list_workspace_directory_entries (e.g. pass u32::MAX or a very large value to list_directory_names), matching the old unbounded behavior.
2. Or propagate the filePrefix from the TypeScript side through to list_workspace_directory_entries as a name_prefix parameter so filtering happens server-side before truncation (matching the pattern used by list_note_workspace_entries).
Was this helpful? React with 👍 or 👎 to provide feedback.
概要
ノートごとにローカルディレクトリを紐付け(デスクトップ)、
@file:で相対パスをマークしてクリック時にプレビューし、Claude Code / エージェントスラッシュ・実行可能コード・AI チャットにそのルートを cwd として渡します。パスはルート配下に正規化してから読み取ります(Issue #461)。変更点
src-tauri/workspace_paths(正規化・読取・一覧)、tauri-plugin-dialog、capabilitiessrc/lib/noteWorkspace/src/contexts/・src/components/note/NoteWorkspaceContext、ツールバー、FilePreviewDialogHostsrc/components/editor/FileReferenceExtension(@file:)、useEditorSetupのワークスペース連携、スラッシュ補完・実行コードsrc/hooks/・src/lib/aiService・エージェントスラッシュにclaudeWorkspaceRootを伝播package.json/ lock@tauri-apps/plugin-dialogほか変更の種類
テスト方法
bun run lint(エラー 0)、変更ファイルに対するprettier --check、bun run test:runを実行済み。@file:相対パスを入力し、クリックでプレビューダイアログが開くことを確認する。チェックリスト
スクリーンショット(UI 変更がある場合)
ワークスペースツールバー・
@file:プレビューがあるため、可能なら Before/After を追記してください。関連 Issue
Closes #461
Summary by CodeRabbit
New Features
Tests