feat(desktop): reference graph visual polish — Shiki highlighting & original styling#148
feat(desktop): reference graph visual polish — Shiki highlighting & original styling#148
Conversation
- Add Shiki syntax highlighting with dark-plus theme via CodePreview component and lazy-loaded highlighter module - Change edge type from animated bezier to smoothstep (straight/orthogonal) - Match original ELK layout options: ORTHOGONAL edge routing, DEPTH_FIRST cycle breaking, LAYER_SWEEP crossing minimization, dynamic node widths - Add reference-graph.css matching original VS Code extension styling: node headers, location bars, code preview, handle colors - Use BackgroundVariant.Dots matching the original extension - Add getCursorPosition to CodeEditorAdapter for DiffViewerContextMenu
Replace hardcoded dark-plus Shiki theme with the app's active theme from Settings > Appearance. Uses createShikiTheme() to dynamically register the app theme with Shiki, matching how chat code blocks and diff viewers already work. Theme changes are reflected in real-time.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthrough参照グラフにShikiベースのコードハイライトとPNGエクスポートを追加。複数のリサイズハンドルにアクセシビリティLint抑止を追加。検索ロジックといくつかのUIユーティリティを改良し、依存関係を更新。 Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant ReferenceGraphPane as ReferenceGraphPane
participant ReactFlow as React Flow
participant CodePreview as CodePreview
participant Highlighter as highlighter.ts
participant ShikiLib as Shiki
participant htmlToImage as html-to-image
User->>ReferenceGraphPane: ノード表示 / ビュー操作
ReferenceGraphPane->>ReactFlow: ノード・レイアウト取得
ReactFlow->>ReferenceGraphPane: ビューポート/ノード境界返却
ReferenceGraphPane->>CodePreview: コード+言語+shikiTheme を渡す
CodePreview->>Highlighter: highlightCode(code, language, theme)
Highlighter->>ShikiLib: 高亮要求(テーマを必要なら登録)
ShikiLib-->>Highlighter: ハイライト済HTMLを返却
Highlighter-->>CodePreview: HTML を返す
CodePreview->>ReferenceGraphPane: ハイライト表示(或いはプレーンテキスト)
User->>ReferenceGraphPane: 「Export PNG」クリック
ReferenceGraphPane->>ReactFlow: 隠す/ビューポート調整
ReferenceGraphPane->>htmlToImage: ビューをキャプチャ
htmlToImage-->>ReferenceGraphPane: PNG Blob を返却
ReferenceGraphPane->>User: タイムスタンプ付きPNGをダウンロード
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
…switch - Add Export PNG button using html-to-image, matching original extension behavior (captures viewport, hides controls/background during export) - Fix graph state reset on tab/workspace switch by adding "reference-graph" to PersistentTabRenderer's persistent pane types, keeping the component mounted when switching away
Match apps/desktop dependency versions to packages/ui to fix sherif multiple-dependency-versions errors in CI.
Lint fixes: - Add biome-ignore for resize handle divs in ChatSearch, MarkdownSearch, TerminalSearch, and CodeEditorSearchOverlay (noStaticElementInteractions) - Fix terminal-shim.ts biome suppression to target specific rule (noExplicitAny) instead of blanket lint suppression - Remove stale biome-ignore in CodeMirrorDiffViewer (unused suppression) Test fixes: - Fix settings search to split query into words and match all of them independently, so "sidebar split width" finds the setting - Fix ChatMessageList subagent test: use regex negative lookbehind to distinguish SUBAGENT_EXECUTION_MESSAGE from INLINE_SUBAGENT_EXECUTION_MESSAGE - Fix searchFiles to strip leading "./" from queries before fuzzy lookup
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 611249f4b7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/components/CodeEditorSearchOverlay/CodeEditorSearchOverlay.tsx (1)
175-180:⚠️ Potential issue | 🟠 Majorアクセシビリティlintの抑止だけでなく、キーボード操作も実装してください
Line 175 で
noStaticElementInteractionsを無効化していますが、Line 177 のリサイズ操作はマウス前提のままです。role="separator"・tabIndex・onKeyDownを追加して、抑止コメント不要の実装に寄せるのが安全です。♿ 提案差分
@@ const handleResizeMouseDown = useCallback( @@ ); + + const handleResizeKeyDown = useCallback( + (event: ReactKeyboardEvent<HTMLDivElement>) => { + if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") { + return; + } + event.preventDefault(); + const delta = event.key === "ArrowLeft" ? 16 : -16; + setWidth((prev) => Math.max(MIN_WIDTH, Math.min(800, prev + delta))); + }, + [], + ); @@ - {/* biome-ignore lint/a11y/noStaticElementInteractions: resize handle */} <div + role="separator" + aria-orientation="vertical" + aria-label="Resize search panel" + tabIndex={0} onMouseDown={handleResizeMouseDown} + onKeyDown={handleResizeKeyDown} className="absolute left-0 top-0 h-full w-1 cursor-ew-resize rounded-l opacity-0 transition-opacity hover:opacity-100 hover:bg-blue-500/40" title="Drag to resize" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/components/CodeEditorSearchOverlay/CodeEditorSearchOverlay.tsx` around lines 175 - 180, The resize handle currently only supports mouse via the onMouseDown handler (handleResizeMouseDown) and is suppressed by the biome-ignore; implement keyboard accessibility by adding role="separator", a tabIndex (e.g. tabIndex={0}), and an onKeyDown handler that maps arrow keys / Home / End (and optionally PageUp/PageDown) to the same resize logic or to a new helper (e.g. handleResizeKeyDown) that adjusts the width and calls the same resizing primitive; update focus/aria attributes as needed (aria-orientation="vertical", aria-valuenow/valuemin/valuemax if tracking value), attach onFocus/onBlur for visual feedback, and remove the biome-ignore lint comment so the element no longer bypasses the a11y rule.
🧹 Nitpick comments (3)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/ReferenceGraphPane.tsx (1)
68-73: 空文字列に対するエッジケース処理
codeSnippetが空文字列の場合、linesは[""]となりmaxLineLengthは0になります。これは問題ありませんが、Math.max(...lines.map())は空配列に対して-Infinityを返すため、理論上のエッジケースがあります。現状の実装ではsplit("\n")が必ず少なくとも1要素を返すため問題ありませんが、防御的にデフォルト値を追加することを検討してください。♻️ 防御的な実装案
function estimateNodeWidth(codeSnippet: string): number { const lines = codeSnippet.split("\n"); - const maxLineLength = Math.max(...lines.map((line) => line.length)); + const maxLineLength = Math.max(0, ...lines.map((line) => line.length)); const estimatedWidth = maxLineLength * CHAR_WIDTH + NODE_PADDING; return Math.min(NODE_MAX_WIDTH, Math.max(NODE_MIN_WIDTH, estimatedWidth)); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/ReferenceGraphPane.tsx` around lines 68 - 73, The max-length calculation in estimateNodeWidth can produce -Infinity if the mapped array is empty; update the function (estimateNodeWidth) to defensively provide a default when computing max — e.g., compute maxLineLength as Math.max(0, ...lines.map(line => line.length)) or use a fallback like lines.length ? Math.max(...mapped) : 0 — so the subsequent estimatedWidth calculation never uses -Infinity.apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/CodePreview.tsx (1)
67-77: フォールバックレンダリングのkeyについて
key={${lineNum}:${line}}は行番号と内容を組み合わせているため、同じ内容の行が異なる位置にあってもユニークになります。ただし、配列のインデックスを含めることでより堅牢になります。♻️ より堅牢なkey
- <div key={`${lineNum}:${line}`} className="ref-graph-code-line"> + <div key={`${lineNum}-${index}`} className="ref-graph-code-line">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/CodePreview.tsx` around lines 67 - 77, The current JSX key in the lines.map render uses key={`${lineNum}:${line}`}, which can still collide for identical line content moved around; update the key in the lines.map callback (the render inside lines.map in CodePreview.tsx) to include the index (or startLine and index) so it becomes unique and stable across reorders (e.g., combine lineNum, index, and line into the key), ensuring you reference the variables used there (lines.map, startLine, index, line).apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/ReferenceNode.tsx (1)
51-53: useCallbackの依存配列でdataオブジェクト全体を指定
dataオブジェクト全体を依存配列に含めると、ノードが再レンダリングされるたびにコールバックが再生成されます。memoでラップされているため大きな問題にはなりませんが、必要な値のみを依存配列に含める方がより正確です。♻️ リファクタリング案
function ReferenceNodeComponent({ data }: { data: ReferenceNodeData }) { + const { onDoubleClick, absolutePath, line } = data; const handleClick = useCallback(() => { - data.onDoubleClick(data.absolutePath, data.line); - }, [data]); + onDoubleClick(absolutePath, line); + }, [onDoubleClick, absolutePath, line]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/ReferenceNode.tsx` around lines 51 - 53, handleClick の useCallback が依存配列に data オブジェクト全体を使っているためコールバックが不必要に再生成されます。handleClick の依存を個別プロパティに置き換え、例えば data.onDoubleClick, data.absolutePath, data.line のみを依存配列に含める(あるいは関数コンポーネント内で const { onDoubleClick, absolutePath, line } = data してそれらを依存にする)ように修正してください(参照: handleClick, data.onDoubleClick, data.absolutePath, data.line)。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/main/lib/vscode-shim/api/terminal-shim.ts`:
- Around line 58-62: Replace the unsafe any casts by importing the
DaemonTerminalManager type from "../../terminal" and use it in the shim: change
the getDaemonTerminalManager signature to return DaemonTerminalManager (i.e.,
getDaemonTerminalManager: () => DaemonTerminalManager) and cast the dynamic
module call to DaemonTerminalManager when returning
mod.getDaemonTerminalManager(); remove the biome-ignore noExplicitAny comments
since any is no longer used.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/reference-graph.css`:
- Around line 7-8: The CSS max-width (reference-graph.css: max-width: 400px) is
inconsistent with the JavaScript constant NODE_MAX_WIDTH in
ReferenceGraphPane.tsx (500px); update one so both match—either set CSS
max-width to 500px or expose NODE_MAX_WIDTH to CSS (e.g., via a CSS variable or
inline style) and use that value in reference-graph.css—to ensure the ELK layout
and rendered node container widths are aligned and prevent overlapping.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/ReferenceGraphPane.tsx`:
- Around line 234-291: The export routine (handleExportPng) hides
.react-flow__controls and .react-flow__background before calling toPng but
restores them only after the await, so if toPng throws the UI elements remain
hidden; fix by declaring controls and background variables outside the try,
perform the display = "none" inside the try as you do now, and move the
restoration logic (if (controls) controls.style.display = ""; if (background)
background.style.display = "";) into the finally block alongside
setIsExporting(false) so they are always restored even on exceptions; reference
handleExportPng, controls, background and toPng when making this change.
---
Outside diff comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/components/CodeEditorSearchOverlay/CodeEditorSearchOverlay.tsx`:
- Around line 175-180: The resize handle currently only supports mouse via the
onMouseDown handler (handleResizeMouseDown) and is suppressed by the
biome-ignore; implement keyboard accessibility by adding role="separator", a
tabIndex (e.g. tabIndex={0}), and an onKeyDown handler that maps arrow keys /
Home / End (and optionally PageUp/PageDown) to the same resize logic or to a new
helper (e.g. handleResizeKeyDown) that adjusts the width and calls the same
resizing primitive; update focus/aria attributes as needed
(aria-orientation="vertical", aria-valuenow/valuemin/valuemax if tracking
value), attach onFocus/onBlur for visual feedback, and remove the biome-ignore
lint comment so the element no longer bypasses the a11y rule.
---
Nitpick comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/CodePreview.tsx`:
- Around line 67-77: The current JSX key in the lines.map render uses
key={`${lineNum}:${line}`}, which can still collide for identical line content
moved around; update the key in the lines.map callback (the render inside
lines.map in CodePreview.tsx) to include the index (or startLine and index) so
it becomes unique and stable across reorders (e.g., combine lineNum, index, and
line into the key), ensuring you reference the variables used there (lines.map,
startLine, index, line).
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/ReferenceGraphPane.tsx`:
- Around line 68-73: The max-length calculation in estimateNodeWidth can produce
-Infinity if the mapped array is empty; update the function (estimateNodeWidth)
to defensively provide a default when computing max — e.g., compute
maxLineLength as Math.max(0, ...lines.map(line => line.length)) or use a
fallback like lines.length ? Math.max(...mapped) : 0 — so the subsequent
estimatedWidth calculation never uses -Infinity.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/ReferenceNode.tsx`:
- Around line 51-53: handleClick の useCallback が依存配列に data
オブジェクト全体を使っているためコールバックが不必要に再生成されます。handleClick の依存を個別プロパティに置き換え、例えば
data.onDoubleClick, data.absolutePath, data.line のみを依存配列に含める(あるいは関数コンポーネント内で
const { onDoubleClick, absolutePath, line } = data してそれらを依存にする)ように修正してください(参照:
handleClick, data.onDoubleClick, data.absolutePath, data.line)。
🪄 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: d124883e-a659-4026-8e54-e0075db1d792
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (18)
apps/desktop/package.jsonapps/desktop/src/main/lib/vscode-shim/api/terminal-shim.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatPane/components/WorkspaceChatInterface/components/ChatMessageList/ChatMessageList.test.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/ChatPane/components/WorkspaceChatInterface/components/ChatMessageList/components/ChatSearch/ChatSearch.tsxapps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/PersistentTabRenderer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ChatPane/ChatPaneInterface/components/ChatMessageList/components/ChatSearch/ChatSearch.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/DiffViewerContextMenu/DiffViewerContextMenu.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/MarkdownSearch/MarkdownSearch.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/CodePreview.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/ReferenceGraphPane.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/ReferenceNode.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/lib/highlighter.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/ReferenceGraphPane/reference-graph.cssapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/TerminalSearch/TerminalSearch.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/components/CodeEditorSearchOverlay/CodeEditorSearchOverlay.tsxpackages/workspace-fs/src/search.ts
💤 Files with no reviewable changes (1)
- apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsx
…re UI on error - Use container ref instead of global document.querySelector to scope viewport/controls/background queries to the active pane (fixes P1 when multiple reference-graph panes are mounted via PersistentTabRenderer) - Move controls/background visibility restore to finally block so UI is always restored even when toPng throws (fixes P2)
Summary
PR #147 のマージ後の追加改善。reference-graphの見た目を元の拡張機能に合わせ、シンタックスハイライトをアプリテーマに統合。
変更内容
dark-plusハードコードからcreateShikiTheme(activeTheme)に変更。Settings > Appearanceのテーマがリアルタイム反映smoothstep(直角折れ線)に変更ORTHOGONALルーティング、DEPTH_FIRSTサイクル破壊、LAYER_SWEEP交差最小化を追加Test plan
Summary by CodeRabbit
リリースノート
新機能
改善