Skip to content

feat(desktop): CodeMirrorDiffViewer統一、git blame、GitGraph、マージコンフリクト解消UIの追加#38

Merged
MocA-Love merged 16 commits intomainfrom
feat/codemirror-diff-viewer
Mar 31, 2026
Merged

feat(desktop): CodeMirrorDiffViewer統一、git blame、GitGraph、マージコンフリクト解消UIの追加#38
MocA-Love merged 16 commits intomainfrom
feat/codemirror-diff-viewer

Conversation

@MocA-Love
Copy link
Copy Markdown
Owner

@MocA-Love MocA-Love commented Mar 31, 2026

概要

close #36
close #37

desktopアプリにおける差分表示・コード閲覧体験を全面的に強化するための機能追加および関連バグ修正。CodeMirrorDiffViewerへの統一、git blameインライン表示、GitGraphビューア、VSCodeスタイルのマージコンフリクト解消UIを実装した。

変更内容

  • CodeMirrorDiffViewerへの差分表示を統一し、unified/split表示の切り替えや行番号表示を改善
  • git blameのインライン表示とホバーポップアップを実装(CodeMirrorプラグインとしてcreateBlamePlugin.tsを新規追加)
  • GitGraphビューアを実装し、コミット履歴のグラフ表示・コミット詳細パネルを追加
  • VSCodeインラインスタイルのマージコンフリクト解消UIを追加(ConflictViewer: コンフリクトマーカー解析、アクションウィジェット、専用テーマ)
  • ChangesHeaderにブランチセレクターとAIコミットメッセージ生成ボタンを追加
  • ブランチ切り替え時の確認ダイアログと英語メッセージへの対応
  • GitGraphの詳細パネルがpane外にはみ出る表示崩れを修正
  • git blame表示のトリガータイミングを修正
  • ConflictViewerの表示・スタイル崩れを修正
  • viewMode: "conflict" 時にFileViewerPaneがrawデータを上書きしないよう修正

テスト方法

  • ファイルをクリックして差分がCodeMirrorDiffViewerで表示されること、unified/split切り替えが動作することを確認
  • ファイルを開いた状態でgit blameアイコンをクリックし、各行にblame情報がインライン表示されること、行ホバーでポップアップが表示されることを確認
  • ChangesViewのGitGraphタブを開き、コミット履歴がグラフで表示されること、コミットをクリックして詳細パネルが正しく表示されることを確認
  • マージコンフリクトが発生しているファイルを開き、コンフリクトマーカー箇所にアクションボタン(Accept Current / Accept Incoming / Accept Both)が表示されること、各ボタンで正しく解消されることを確認
  • ChangesHeaderのブランチセレクターからブランチを切り替え、確認ダイアログが表示されることを確認

Summary by CodeRabbit

  • New Features

    • Inline Git blame in file viewer and diff (author/commit/timestamp hover) that persists across file switches
    • One-click merge conflict resolution UI with Accept Current / Accept Incoming / Accept Both actions
    • Interactive Git Graph pane with detailed commit panel, commit file list and navigation
    • Conflicts surfaced as a separate "Conflicts" section and dedicated conflict view in the file viewer
  • Bug Fixes

    • Fixed GitGraph panel positioning/overflow at screen edges

- FileViewerPane の diff を行数によらず常に CodeMirrorDiffViewer で表示
- MergeView の b 側を編集可能にし Mod+S で保存、revertControls で変更を元に戻せるように
- テーマカラー(addition/deletion/modified)を diff ハイライトに適用
- 純追加/純削除行はインラインハイライトを抑制(VSCode 同様の挙動)
- 空きスペースに斜め線パターンを表示
- diff-test-fixtures.ts と /diff-test ルートで全パターンを確認できるテスト環境を追加
- VscSourceControlアイコンをbase→currentブランチ表示に刷新
- base/currentそれぞれ独立したプルダウンで切り替え可能に
- Generate commit message with AIボタンをテキストエリアから2行目に移動
- generateCommitMessage mutationとcommitMessage stateをChangesViewに引き上げ
- 未コミットの変更がある状態でcurrentブランチ切り替え時に確認ダイアログを表示
- gitエラー時にわかりやすいエラートーストを表示
- UIメッセージを英語に統一
- ChangesHeaderにGitGraphボタンを追加(新規タブとして開く)
- SVGベースのブランチグラフをレーン計算アルゴリズムで描画
- コミット行クリックで詳細パネル(CommitDetailsPanel)を展開
- 詳細パネルのファイル名クリックでdiffを表示
- カラム幅のドラッグリサイズ対応
- ブランチバッジのツールチップ表示
- git-graph PaneTypeをMosaicレイアウトに統合
- CodeMirrorDiffViewer・CodeEditor の両エディタに対応
- カーソル行の行末に著者・時刻・コミットメッセージを薄く表示
- ホバーで VS Code 風の詳細ポップアップを表示(アバター・ハッシュ・コピーボタン)
- テーマの CSS 変数に準拠したカラーリング
- ChangesViewにConflictedセクションを追加しコンフリクトファイルを表示
- CodeMirror EditorView + ViewPlugin + Decorationでインラインビューアを実装
- 各コンフリクトブロック上にAccept Current/Incoming/Bothリンクを挿入
- parseConflictMarkersでマーカーをパースしConflictRegion[]を返す
- filesystem.writeFileで解消後のファイルを保存、Cmd+S対応
- ChangeCategory/"conflicted"とFileViewerMode/"conflict"を型定義に追加
- containerWidthをResizeObserverで計測し詳細パネルのmaxWidthに適用
- 900px以上で横並び、未満で縦積みのレスポンシブレイアウトに変更
- eq() を常に false にして DOM 再利用によるhasLeftリセット漏れを修正
- カーソル行変更時に既存トリップを即座に閉じるよう修正
- ホバー滞在時間を1秒に変更
- FileViewerContentにconflict viewMode分岐を追加
- RightSidebarでconflictedカテゴリ時にviewMode: conflictを明示
- ui-state ZodスキーマにconflictとconflictedのEnum値を追加
- ConflictViewerのhandleResolve/handleSaveをrefで保持しエディタ再作成ループを修正
- encoding・maxBytes指定・エラー表示オーバーレイを追加
- conflict-themeをVSCode風の落ち着いた配色に変更
- AcceptボタンにCurrent/Incoming別のhoverカラーを追加
@MocA-Love MocA-Love self-assigned this Mar 31, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

Warning

Rate limit exceeded

@MocA-Love has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 32 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 3 minutes and 32 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6bddf473-9b55-49ca-a5b7-13a6a2031ed1

📥 Commits

Reviewing files that changed from the base of the PR and between 0cfa2a5 and 0633b9e.

📒 Files selected for processing (7)
  • apps/desktop/src/lib/trpc/routers/changes/status.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/CommitDetailsPanel/CommitDetailsPanel.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/CommitDetailsPanel/index.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/GitGraphView.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/GraphRow.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createBlamePlugin.ts
📝 Walkthrough

Walkthrough

Adds inline git-blame support, an inline conflict-resolution editor, and a GitGraph commit browser with commit detail panel; extends status parsing and task handlers to surface conflicted files and commit-graph data; integrates blame and conflict UI into CodeMirror-based viewers and the tab/pane system.

Changes

Cohort / File(s) Summary
TRPC: Git blame & commit-graph
apps/desktop/src/lib/trpc/routers/changes/git-blame.ts, apps/desktop/src/lib/trpc/routers/changes/index.ts, apps/desktop/src/lib/trpc/routers/changes/status.ts, apps/desktop/src/lib/trpc/routers/changes/workers/git-task-types.ts, .../git-task-handlers.ts
Added getGitBlame router and BlameEntry types; added getCommitGraph task/procedure and git-log parsing handlers; updated task types and error handling.
Shared types
apps/desktop/src/shared/changes-types.ts, apps/desktop/src/shared/tabs-types.ts
Added conflicted change category and GitChangesStatus.conflicted; introduced CommitGraphNode/CommitGraphData; added FileViewerMode "conflict" and PaneType "git-graph" plus GitGraphPaneState.
Status parsing & workers
apps/desktop/src/lib/trpc/routers/changes/utils/parse-status.ts, .../workers/git-task-handlers.ts
Detect conflicted files in status parsing and include conflicted in computed status; added parseGitGraphLog and computeCommitGraph worker logic.
Conflict marker parsing
apps/desktop/src/renderer/.../ConflictViewer/parseConflictMarkers.ts
New parser that extracts structured conflict regions (start/separator/end lines, current/incoming lines and labels).
Conflict viewer UI
apps/desktop/src/renderer/.../ConflictViewer/ConflictViewer.tsx, .../ConflictActionWidget.ts, .../conflict-theme.ts, .../index.ts
New CodeMirror-integrated ConflictViewer component rendering action widgets, styling, view plugin sync, resolution application, and file write-back.
Diff & editor integrations
.../CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsx, .../diff-test-fixtures.ts, .../CodeEditor/createBlamePlugin.ts, .../CodeEditor/CodeEditor.tsx, .../createCodeMirrorTheme.ts
Extended diff viewer with onChange/onSave and blameEntries; added blame plugin with inline widget and hover tooltip; added theme rules to support merge/diff visuals and inline suppression.
File viewer wiring
.../FileViewerPane.tsx, .../FileViewerContent/FileViewerContent.tsx
Normalize "conflict" to raw loading, avoid external-disk-change triggers in conflict mode; load blame data and dispatch conflict/diff/editor rendering accordingly.
Changes UI & header
.../ChangesView.tsx, .../ChangesHeader/ChangesHeader.tsx, .../CommitInput/CommitInput.tsx
Track conflicted files in Changes view, add AI commit-message mutation wiring and Git Graph toggle; ChangesHeader layout and controls updated; CommitInput now receives external commitMessage prop.
Ordered sections & infinite scroll
.../useOrderedSections.tsx, .../useOrderedSections.test.tsx, .../InfiniteScrollView.tsx, .../section-order.ts, stores/changes/store.ts
Added "conflicted" section and inputs across hooks, tests, InfiniteScrollView, default section ordering, and persisted store migration (state version bump to 6, default expandedSections.conflicted = true).
Tabs/panes & GitGraph pane
.../tabs/utils.ts, .../tabs/store.ts, .../tabs/types.ts, .../TabView/index.tsx, .../GitGraphPane/GitGraphPane.tsx, .../GitGraphPane/index.ts
Added resolveFileViewerMode"conflict", utilities to create git-graph pane/tab, store action addGitGraphTab, added "git-graph" pane rendering in TabView and new GitGraphPane component.
GitGraph view & layout
.../GitGraphView/GitGraphView.tsx, .../GitGraphView/GraphRow.tsx, .../GitGraphView/CommitDetailsPanel.tsx, .../GitGraphView/constants.ts, .../utils/computeGraphLanes.ts, .../GitGraphView/index.ts
New GitGraph UI: query commit graph, compute lanes/layout, render SVG lanes and rows, expandable commit details with per-commit file listing and open-in-viewer actions; constants and computeGraphLanes utility added.
Misc / tests / docs
README.md, .gitignore, small import reorder, tests updated
Added changelog entry, ignored test repo dir, minor import/test updates.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FileViewer
    participant TRPC
    participant CodeEditor
    participant BlamePlugin
    User->>FileViewer: Open file (worktreePath + path)
    FileViewer->>TRPC: getGitBlame(worktreePath, absolutePath)
    TRPC-->>FileViewer: BlameEntry[] 
    FileViewer->>CodeEditor: render (blameEntries)
    CodeEditor->>BlamePlugin: createBlamePlugin(blameEntries)
    User->>CodeEditor: Move cursor to line
    BlamePlugin-->>CodeEditor: Render inline blame widget & tooltip on hover
Loading
sequenceDiagram
    participant User
    participant ChangesView
    participant TRPC
    participant GraphLayout
    participant GitGraphView
    User->>ChangesView: Click Git Graph toggle
    ChangesView->>TRPC: getCommitGraph(worktreePath, maxCount)
    TRPC-->>ChangesView: CommitGraphData (nodes)
    ChangesView->>GraphLayout: computeGraphLanes(nodes)
    GraphLayout-->>GitGraphView: GraphNodeLayout[]
    GitGraphView->>User: Render scrollable commit graph
    User->>GitGraphView: Expand commit row
    GitGraphView->>TRPC: getCommitFiles(worktreePath, commitHash)
    TRPC-->>GitGraphView: Commit files
    GitGraphView->>User: Show CommitDetailsPanel with file list
Loading
sequenceDiagram
    participant User
    participant FileViewer
    participant ConflictViewer
    participant FS
    User->>FileViewer: Open conflicted file (mode "conflict")
    FileViewer->>ConflictViewer: mount with absoluteFilePath
    ConflictViewer->>FS: readFile(absoluteFilePath, limit)
    FS-->>ConflictViewer: file content
    ConflictViewer->>ConflictViewer: parseConflictMarkers(content)
    ConflictViewer->>User: Render action widgets per region
    User->>ConflictViewer: Click "Accept Current"
    ConflictViewer->>FS: writeFile(updated content)
    FS-->>ConflictViewer: write success
    ConflictViewer->>User: onSave callback / confirmation
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰✨
With markers found and lanes drawn neat,
I hop through blames where cursors meet.
Buttons choose the current or incoming cheer,
Graphs stretch branches far and near.
A rabbit dances — conflicts cleared, code dear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.51% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly describes the main changes: unified CodeMirrorDiffViewer, git blame display, GitGraph, and merge conflict resolution UI.
Description check ✅ Passed The PR description is comprehensive, covering all major changes, related issues (#36, #37), type of change (new feature), testing methods, and detailed implementation notes.
Linked Issues check ✅ Passed The PR addresses both linked issues: #36 (Diff Viewer editing with VSCode-style conflict resolution UI via ConflictViewer) and #37 (unified rendering via CodeMirrorDiffViewer for all file sizes).
Out of Scope Changes check ✅ Passed All substantial code changes align with the stated objectives: new files for git blame plugin, GitGraph components, conflict resolution, and supporting infrastructure modifications are directly related to the feature goals.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/codemirror-diff-viewer

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@MocA-Love MocA-Love marked this pull request as ready for review March 31, 2026 07:57
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 14

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/lib/trpc/routers/ui-state/index.ts (1)

43-47: ⚠️ Potential issue | 🟠 Major

Add git-graph to paneSchema.type to avoid persistence failures.

With the new pane type enabled in tab rendering, tabs.set validation can reject state snapshots that include git-graph panes.

💡 Proposed fix
 const paneSchema = z.object({
 	id: z.string(),
 	tabId: z.string(),
-	type: z.enum(["terminal", "webview", "file-viewer", "chat", "devtools"]),
+	type: z.enum([
+		"terminal",
+		"webview",
+		"file-viewer",
+		"chat",
+		"devtools",
+		"git-graph",
+	]),
 	name: z.string(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/lib/trpc/routers/ui-state/index.ts` around lines 43 - 47,
The paneSchema zod enum for pane types currently excludes "git-graph", causing
validation failures; update the z.enum([...]) in the paneSchema declaration (the
const paneSchema) to include "git-graph" among the allowed values so persisted
state containing git-graph panes passes tabs.set validation.
🧹 Nitpick comments (10)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/useOrderedSections/useOrderedSections.test.tsx (1)

18-40: Consider adding a test case for the conflicted section.

The fixtures are updated to include the "conflicted" category, but there's no test case that verifies the conflicted section behavior (e.g., count calculation when conflictedFiles is non-empty). Adding such a test would ensure the new section is handled correctly.

📝 Example test case
test("counts conflicted files correctly", () => {
  const sections = useOrderedSections({
    ...emptyArgs,
    conflictedFiles: [emptyFile(), emptyFile()],
  });

  const conflictedSection = sections.find(
    (section) => section.id === "conflicted",
  );

  expect(conflictedSection).toBeDefined();
  expect(conflictedSection?.count).toBe(2);
});
🤖 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/RightSidebar/ChangesView/hooks/useOrderedSections/useOrderedSections.test.tsx`
around lines 18 - 40, Add a unit test in useOrderedSections.test.tsx that
verifies the "conflicted" section is present and its count reflects
conflictedFiles; call useOrderedSections with the existing emptyArgs but set
conflictedFiles to an array of fixtures (e.g., two emptyFile() items), find the
section by section.id === "conflicted", and assert the section is defined and
its count equals the number of conflictedFiles provided (2).
apps/desktop/src/renderer/routes/_authenticated/_dashboard/diff-test/page.tsx (1)

1-9: Consider gating this test route for development only.

This diff-test route and its TopBar button appear to be for development/testing purposes. Consider conditionally registering this route or hiding the button in production builds to avoid exposing internal tooling to end users.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/diff-test/page.tsx`
around lines 1 - 9, The diff-test route (Route created by createFileRoute with
component DiffTestPage) should only be registered in non-production builds:
guard the route export/registration with an environment check (e.g.
import.meta.env.DEV or process.env.NODE_ENV !== "production") so Route is not
created or exported in production, and likewise hide or conditionally render the
TopBar button that links to this route (referencing the same DiffTestPage/Route
symbol) behind the same check to prevent exposing dev tooling to end users.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/conflict-theme.ts (1)

43-51: Prefer theme tokens over hard-coded hover RGB values.

Using fixed rgb(...) values for conflict action hover colors can drift from the rest of the theme system. Consider switching these to shared CSS variables/tokens for consistent theming.

🤖 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/FileViewerPane/components/ConflictViewer/conflict-theme.ts`
around lines 43 - 51, Replace the hard-coded hover RGB values in the
conflict-theme styles with the theme's shared tokens/CSS variables so the hover
colors follow the app's design system; specifically update the
".cm-conflict-action-btn-current" and ".cm-conflict-action-btn-incoming" hover
rules in conflict-theme.ts to use the appropriate theme token or CSS variable
(e.g., a success/positive hover token for current and an info/primary hover
token for incoming such as var(--color-success-hover) and
var(--color-info-hover) or the equivalent theme token accessor) instead of
"rgb(35, 197, 94)" and "rgb(56, 154, 230)".
apps/desktop/src/renderer/stores/tabs/store.ts (1)

1666-1701: Consider telemetry parity for Git Graph tab opens.

addGitGraphTab updates state correctly, but unlike other add*Tab actions it doesn’t emit a panel_opened event. If Git Graph usage should be tracked, add the same capture pattern here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/stores/tabs/store.ts` around lines 1666 - 1701, The
addGitGraphTab action correctly creates and sets the new tab/pane but misses
emitting telemetry; after creating the tab/pane (use createGitGraphTabWithPane)
and after the set(...) call but before returning, call the same telemetry
capture used by other add*Tab actions (e.g., capture('panel_opened', {
workspaceId, tabId: tab.id, paneId: pane.id, panel: 'git_graph' })) so Git Graph
opens are tracked consistently; ensure you import/use the same capture
function/name as other tabs and keep the payload keys consistent with existing
panel_opened events.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/constants.ts (1)

7-8: Consider adding as const to MIN_COLUMN_WIDTHS for consistency.

DEFAULT_COLUMN_WIDTHS uses as const for a readonly tuple, while MIN_COLUMN_WIDTHS is a mutable number[]. Using as const on both would ensure consistency and prevent accidental mutations.

✨ Proposed fix
 export const DEFAULT_COLUMN_WIDTHS = [72, 200, 550, 120, 90] as const;
-export const MIN_COLUMN_WIDTHS: number[] = [60, 80, 100, 60, 80];
+export const MIN_COLUMN_WIDTHS = [60, 80, 100, 60, 80] as const;
🤖 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/RightSidebar/ChangesView/components/GitGraphView/constants.ts`
around lines 7 - 8, MIN_COLUMN_WIDTHS is declared as a mutable number[] while
DEFAULT_COLUMN_WIDTHS is a readonly tuple; change MIN_COLUMN_WIDTHS to be a
readonly tuple by adding "as const" (or remove the explicit number[] type so TS
infers the readonly tuple) so it becomes immutable like DEFAULT_COLUMN_WIDTHS;
update any code that expects a mutable array to accept a readonly tuple if
necessary and reference MIN_COLUMN_WIDTHS and DEFAULT_COLUMN_WIDTHS when making
the change.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/GitGraphView.tsx (1)

140-143: Potential undefined access if MIN_COLUMN_WIDTHS[index] is out of bounds.

If index exceeds the array length (unlikely given current usage but possible if columns change), MIN_COLUMN_WIDTHS[index] returns undefined, causing Math.max(undefined, ...) to produce NaN. Consider adding a fallback.

🛡️ Defensive fallback
 const handleMouseMove = (event: MouseEvent) => {
+	const minWidth = MIN_COLUMN_WIDTHS[index] ?? 60;
 	const nextWidth = Math.max(
-		MIN_COLUMN_WIDTHS[index],
+		minWidth,
 		startWidth + event.clientX - startX,
 	);
🤖 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/RightSidebar/ChangesView/components/GitGraphView/GitGraphView.tsx`
around lines 140 - 143, The expression computing nextWidth uses
MIN_COLUMN_WIDTHS[index] directly which can be undefined if index is out of
bounds; fix by deriving a safe minimum first (e.g., const safeMin = (index >= 0
&& index < MIN_COLUMN_WIDTHS.length) ? MIN_COLUMN_WIDTHS[index] :
(MIN_COLUMN_WIDTHS[MIN_COLUMN_WIDTHS.length - 1] ?? 0)) and then compute
nextWidth with Math.max(safeMin, startWidth + event.clientX - startX); update
the code around the nextWidth calculation in GitGraphView.tsx to use safeMin so
Math.max never receives undefined.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/CommitDetailsPanel.tsx (1)

26-37: Hardcoded Japanese locale and timezone may limit international usability.

The date formatting uses ja-JP locale and Asia/Tokyo timezone. If the app targets international users, consider using the user's system locale or making this configurable.

🌐 Consider using system locale
 function formatCommitDate(date: Date): string {
-	return new Date(date).toLocaleString("ja-JP", {
-		timeZone: "Asia/Tokyo",
+	return new Date(date).toLocaleString(undefined, {
 		year: "numeric",
 		month: "2-digit",
 		day: "2-digit",
 		hour: "2-digit",
 		minute: "2-digit",
 		second: "2-digit",
 		hour12: false,
 	});
 }
🤖 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/RightSidebar/ChangesView/components/GitGraphView/CommitDetailsPanel.tsx`
around lines 26 - 37, The formatCommitDate function currently hardcodes "ja-JP"
and "Asia/Tokyo"; change it to use the user's system locale/timezone (or make
them configurable) by either: 1) removing the hardcoded locale and passing
undefined or navigator.language to toLocaleString/Intl.DateTimeFormat, and 2)
obtaining the user's timezone from
Intl.DateTimeFormat().resolvedOptions().timeZone or accepting an optional
timezone parameter on formatCommitDate(date, locale?, timeZone?) so callers can
override; update the function signature (formatCommitDate) and its call sites
accordingly to default to system settings when no overrides are provided.
apps/desktop/src/lib/trpc/routers/changes/git-blame.ts (1)

108-109: Consider logging the error before returning empty entries.

Silently returning empty entries on failure may hide underlying issues (permission errors, invalid paths, etc.). Adding a debug log would aid troubleshooting without changing the graceful degradation behavior.

📝 Proposed enhancement
-			} catch {
+			} catch (error) {
+				console.debug("[git-blame] failed to get blame:", error);
 				return { entries: [] };
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/lib/trpc/routers/changes/git-blame.ts` around lines 108 -
109, The catch block that currently does "return { entries: [] }" swallows
errors; update that catch to log the caught error before returning empty
entries. Locate the catch that returns "{ entries: [] }" and add a debug/error
log (e.g., console.debug/console.error or the local logger if available) that
includes the caught error and context (path/commit/file) so failures are
recorded while preserving the graceful return of empty entries.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/GraphRow.tsx (1)

53-57: Hardcoded Japanese locale for date formatting.

The date is formatted with a hardcoded "ja-JP" locale. Consider using undefined to respect the user's system locale, or implementing a locale preference system.

 	const formattedDate = new Date(node.date).toLocaleDateString("ja-JP", {
+	const formattedDate = new Date(node.date).toLocaleDateString(undefined, {
 		year: "numeric",
 		month: "2-digit",
 		day: "2-digit",
 	});
🤖 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/RightSidebar/ChangesView/components/GitGraphView/GraphRow.tsx`
around lines 53 - 57, The date formatting in GraphRow.tsx uses a hardcoded
"ja-JP" locale for formattedDate which forces Japanese formatting; update the
code that constructs formattedDate (the new
Date(node.date).toLocaleDateString(...) call) to use undefined (or a supplied
user locale preference) instead of "ja-JP" so it respects the system/user locale
or wiring a locale prop/settings value through the component if you need an
app-specific preference.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createBlamePlugin.ts (1)

19-41: Hardcoded Japanese locale strings limit internationalization.

The time formatting functions use hardcoded Japanese strings (分前, 時間前, , 午後, etc.). This will display Japanese text for all users regardless of their locale preference. Consider using the Intl.RelativeTimeFormat and Intl.DateTimeFormat APIs for proper localization.

♻️ Example using Intl APIs
 function formatTimeAgo(timestamp: number): string {
 	const now = Date.now() / 1000;
 	const diff = now - timestamp;
 
-	if (diff < 60) return "just now";
-	if (diff < 3600) return `${Math.floor(diff / 60)}分前`;
-	if (diff < 86400) return `${Math.floor(diff / 3600)}時間前`;
-	if (diff < 86400 * 30) return `${Math.floor(diff / 86400)}日前`;
-	if (diff < 86400 * 365) return `${Math.floor(diff / (86400 * 30))}ヶ月前`;
-	return `${Math.floor(diff / (86400 * 365))}年前`;
+	const rtf = new Intl.RelativeTimeFormat(undefined, { numeric: "auto" });
+	if (diff < 60) return rtf.format(0, "second");
+	if (diff < 3600) return rtf.format(-Math.floor(diff / 60), "minute");
+	if (diff < 86400) return rtf.format(-Math.floor(diff / 3600), "hour");
+	if (diff < 86400 * 30) return rtf.format(-Math.floor(diff / 86400), "day");
+	if (diff < 86400 * 365) return rtf.format(-Math.floor(diff / (86400 * 30)), "month");
+	return rtf.format(-Math.floor(diff / (86400 * 365)), "year");
 }
 
 function formatFullDate(timestamp: number): string {
 	const d = new Date(timestamp * 1000);
-	const month = d.getMonth() + 1;
-	const day = d.getDate();
-	const year = d.getFullYear();
-	const hours = d.getHours();
-	const minutes = String(d.getMinutes()).padStart(2, "0");
-	const ampm = hours < 12 ? "朝" : "午後";
-	const hour12 = hours % 12 || 12;
-	return `${month} ${day}th, ${year} ${hour12}:${minutes} ${ampm}`;
+	return new Intl.DateTimeFormat(undefined, {
+		year: "numeric",
+		month: "long",
+		day: "numeric",
+		hour: "numeric",
+		minute: "2-digit",
+	}).format(d);
 }
🤖 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/createBlamePlugin.ts`
around lines 19 - 41, formatTimeAgo and formatFullDate currently hardcode
Japanese strings; replace them with locale-aware formatting using
Intl.RelativeTimeFormat and Intl.DateTimeFormat. In formatTimeAgo(timestamp:
number) compute the largest appropriate unit (seconds, minutes, hours, days,
months, years), create an Intl.RelativeTimeFormat with a locale parameter
(accept locale or use navigator.language / a passed-in app locale as fallback)
and return rtf.format(value, unit). In formatFullDate(timestamp: number) create
an Intl.DateTimeFormat with the same locale and options { year: "numeric",
month: "long" or "numeric" as desired, day: "numeric", hour: "numeric", minute:
"2-digit", hour12: false/true based on locale } and return formatter.format(new
Date(timestamp * 1000)); ensure functions accept or derive locale rather than
emitting hardcoded Japanese text.
🤖 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/lib/trpc/routers/changes/status.ts`:
- Around line 137-139: The dedupe key used when calling runGitTask (currently
`graph:${input.worktreePath}`) doesn't include the requested graph size, so
different getCommitGraph(maxCount) calls collide; update the dedupeKey to
incorporate the requested maxCount (e.g., include `input.maxCount` or the
argument passed to getCommitGraph) so the key becomes unique per size (for
example `graph:${input.worktreePath}:max:${input.maxCount}`) and handle any
undefined/default maxCount consistently so coalescing only merges identical
requests.

In `@apps/desktop/src/lib/trpc/routers/changes/utils/parse-status.ts`:
- Around line 31-39: CONFLICT_PAIRS currently includes both marker-producing
states and non-marker states; update parse-status.ts so only content-conflict
states (AA, UU) remain in CONFLICT_PAIRS and route non-marker states (DD, AU,
UD, UA, DU) into a separate resolution flow used by ConflictViewer—either remove
those non-marker codes from CONFLICT_PAIRS or implement a bespoke handler that
triggers delete/add-specific actions (e.g., present git rm for DD or staging
actions for AU/DU/UD/UA) so the UI shows appropriate resolution buttons instead
of an empty editor.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx`:
- Around line 33-34: The matchRoute call in TopBar.tsx uses the shorthand
"/diff-test" which doesn't match the actual route; update the string literal to
the full absolute path "/_authenticated/_dashboard/diff-test" where used (e.g.,
in the isDiffTestOpen assignment and any navigate or route checks in TopBar.tsx)
so it matches the route defined in page.tsx and follows the project's
absolute-path convention.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/diff-test/DiffTestPage.tsx`:
- Around line 6-8: The code currently reads diffFixtureList[selectedIndex]
without guards, which will crash if diffFixtureList is empty or selectedIndex is
out of range; update the DiffTestPage state and render logic to handle this:
when initializing, derive a safe index (e.g., Math.max(0,
Math.min(selectedIndex, diffFixtureList.length - 1))) or set selectedIndex to 0
only if diffFixtureList.length > 0, add a useEffect that clamps selectedIndex
whenever diffFixtureList changes, and in render gate on a valid fixture (e.g.,
if (!fixture) return null or a placeholder) before dereferencing fixture;
reference selectedIndex, setSelectedIndex, diffFixtureList, and fixture to
locate where to add these checks.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createBlamePlugin.ts`:
- Line 97: The code in createBlamePlugin.ts builds a fake parent hash via const
parentHash = `${entry.commitHash.substring(0, 6)}0`, which misleads users;
replace this by either retrieving the real parent commit hash from the available
git/commit metadata (use the commit's parent field or call the repo API where
entry or commit object is available) and display that, or if that data is not
present, remove the parentHash display entirely (do not fabricate values).
Locate the parentHash usage in createBlamePlugin (and any UI rendering that
references entry.commitHash / parentHash) and update the logic to show the real
parent when available or hide the parent element when not.
- Around line 104-127: The tooltip currently assigns unescaped HTML via
tooltip.innerHTML in createBlamePlugin.ts using fields like entry.author and
entry.summary (and optionally initials/parentHash), which risks XSS from commit
data; replace the innerHTML construction with DOM-safe code that creates
elements (e.g., divs, spans, buttons) and sets their text via textContent (or
use a vetted sanitizer) for any user-controllable strings, then append those
nodes to tooltip—ensure ICON_* values that are trusted remain inserted as HTML
only where safe and keep copy button markup created via createElement to avoid
injecting raw commit text.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createCodeMirrorTheme.ts`:
- Around line 89-105: The theme code is appending hex alpha suffixes to OKLCH
color strings (using editorTheme.colors.deletion/addition) which produces
invalid CSS; update createCodeMirrorTheme to avoid string-concatenating alpha
hexes—either convert the color tokens to hex/hex8 in getEditorTheme (ensuring
editorTheme.colors.deletion and .addition are hex/hex8) or replace the
concatenations for the selectors that set backgroundColor/background (the rules
for ".cm-deletedChunk", "&.cm-merge-a .cm-changedText, .cm-deletedChunk
.cm-deletedText", "&.cm-merge-b .cm-changedText", "&.cm-merge-b
.cm-deletedText", and the .cm-changedLine rules) with format-agnostic mixing
like CSS color-mix(in srgb, ${editorTheme.colors.deletion} 8%) / color-mix(in
srgb, ${editorTheme.colors.addition} 8%) so OKLCH values remain valid.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/ConflictViewer.tsx`:
- Around line 215-301: The effect currently destroys and recreates the
EditorView on absoluteFilePath or activeTheme changes which discards unsaved
edits; instead preserve the editor instance and document by: 1) move theme and
syntax-related extensions (editorTheme, syntaxHighlighting, conflictTheme) into
their own Compartment(s) (e.g., themeCompartment, syntaxCompartment) so you can
call themeCompartment.reconfigure(...) and syntaxCompartment.reconfigure(...)
via editorRef.current.dispatch without destroying the view; 2) only recreate the
EditorView when absoluteFilePath truly requires a new doc (and before replacing
the view, read view.state.doc.toString() to detect unsaved edits and prompt the
user via your existing save/confirm flow); and 3) keep langCompartment behavior
for language loads but use reconfigure on that compartment instead of recreating
the whole EditorView.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/parseConflictMarkers.ts`:
- Around line 44-65: The parser must recognize diff3 base markers so
base-section lines aren't misclassified as "current": in
parseConflictMarkers.ts, when line.startsWith("|||||||") and state === "current"
set state = "base", record separatorLine for the base start, and start
collecting baseLines in a new array (like currentLines and incomingLines); when
you later hit "=======" transition from "base" to "incoming" (instead of from
"current"), and when building the region pushed into regions include baseLines
(and currentLabel/incomingLabel as before) so consumers (Accept Current/Both)
can ignore baseLines; also reset baseLines and state same as other branches.
Ensure you add the new baseLines variable and the "base" state handling
alongside existing state transitions (state, currentLines, incomingLines,
separatorLine, startLine, regions).

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx`:
- Around line 197-203: The blame overlay is fetched via
electronTrpc.changes.getGitBlame.useQuery (blameData) which only accepts
worktree/absolute paths and thus shows current-worktree authors even for
historical diffs; change the query's enabled condition to also require that
commitHash is not set (e.g., enabled: Boolean(worktreePath && absoluteFilePath
&& !commitHash)) so blame is skipped for committed diffs, and consider updating
getGitBlame to accept a revision/commitHash in the future to make it
revision-aware.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx`:
- Around line 266-279: The mutation handler generateCommitMessageMutation
unconditionally calls setCommitMessage in onSuccess which can overwrite a user
draft typed during generation; capture the current commit message when starting
the request (e.g., store a snapshot in a ref or local variable in the function
that triggers electronTrpc.changes.generateCommitMessage.useMutation) and in
onSuccess only call setCommitMessage(data.message) if the current commit message
still equals that snapshot (i.e., unchanged by the user). Update the trigger
code that calls generateCommitMessageMutation.mutate to save the snapshot and
pass or reference it so onSuccess can compare before updating the commit
message.
- Around line 819-823: The hasUncommittedChanges prop currently checks
stagedFiles, unstagedFiles, and untrackedFiles but omits conflictedFiles,
causing conflicted files to be treated as clean; update the
hasUncommittedChanges expression (the prop passed where hasUncommittedChanges is
set) to also consider conflictedFiles (e.g., include conflictedFiles.length > 0)
so unresolved merge conflicts are treated as uncommitted changes alongside
staged/unstaged/untracked files.

In `@apps/desktop/src/shared/tabs-types.ts`:
- Line 94: Update the documentation/comments that describe FileViewer modes and
diff-category to include the new "conflict" mode: locate the FileViewerMode type
and any doc comments referencing FileViewerState.viewMode or the diff-category
descriptions and add "conflict" to the listed modes and explain its semantics
(when it is used) alongside "rendered", "raw", and "diff" so the docs match the
type definition. Ensure any examples or guidance for diff-category handling
mention how "conflict" should be displayed/treated.

---

Outside diff comments:
In `@apps/desktop/src/lib/trpc/routers/ui-state/index.ts`:
- Around line 43-47: The paneSchema zod enum for pane types currently excludes
"git-graph", causing validation failures; update the z.enum([...]) in the
paneSchema declaration (the const paneSchema) to include "git-graph" among the
allowed values so persisted state containing git-graph panes passes tabs.set
validation.

---

Nitpick comments:
In `@apps/desktop/src/lib/trpc/routers/changes/git-blame.ts`:
- Around line 108-109: The catch block that currently does "return { entries: []
}" swallows errors; update that catch to log the caught error before returning
empty entries. Locate the catch that returns "{ entries: [] }" and add a
debug/error log (e.g., console.debug/console.error or the local logger if
available) that includes the caught error and context (path/commit/file) so
failures are recorded while preserving the graceful return of empty entries.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/diff-test/page.tsx`:
- Around line 1-9: The diff-test route (Route created by createFileRoute with
component DiffTestPage) should only be registered in non-production builds:
guard the route export/registration with an environment check (e.g.
import.meta.env.DEV or process.env.NODE_ENV !== "production") so Route is not
created or exported in production, and likewise hide or conditionally render the
TopBar button that links to this route (referencing the same DiffTestPage/Route
symbol) behind the same check to prevent exposing dev tooling to end users.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createBlamePlugin.ts`:
- Around line 19-41: formatTimeAgo and formatFullDate currently hardcode
Japanese strings; replace them with locale-aware formatting using
Intl.RelativeTimeFormat and Intl.DateTimeFormat. In formatTimeAgo(timestamp:
number) compute the largest appropriate unit (seconds, minutes, hours, days,
months, years), create an Intl.RelativeTimeFormat with a locale parameter
(accept locale or use navigator.language / a passed-in app locale as fallback)
and return rtf.format(value, unit). In formatFullDate(timestamp: number) create
an Intl.DateTimeFormat with the same locale and options { year: "numeric",
month: "long" or "numeric" as desired, day: "numeric", hour: "numeric", minute:
"2-digit", hour12: false/true based on locale } and return formatter.format(new
Date(timestamp * 1000)); ensure functions accept or derive locale rather than
emitting hardcoded Japanese text.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/conflict-theme.ts`:
- Around line 43-51: Replace the hard-coded hover RGB values in the
conflict-theme styles with the theme's shared tokens/CSS variables so the hover
colors follow the app's design system; specifically update the
".cm-conflict-action-btn-current" and ".cm-conflict-action-btn-incoming" hover
rules in conflict-theme.ts to use the appropriate theme token or CSS variable
(e.g., a success/positive hover token for current and an info/primary hover
token for incoming such as var(--color-success-hover) and
var(--color-info-hover) or the equivalent theme token accessor) instead of
"rgb(35, 197, 94)" and "rgb(56, 154, 230)".

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/CommitDetailsPanel.tsx`:
- Around line 26-37: The formatCommitDate function currently hardcodes "ja-JP"
and "Asia/Tokyo"; change it to use the user's system locale/timezone (or make
them configurable) by either: 1) removing the hardcoded locale and passing
undefined or navigator.language to toLocaleString/Intl.DateTimeFormat, and 2)
obtaining the user's timezone from
Intl.DateTimeFormat().resolvedOptions().timeZone or accepting an optional
timezone parameter on formatCommitDate(date, locale?, timeZone?) so callers can
override; update the function signature (formatCommitDate) and its call sites
accordingly to default to system settings when no overrides are provided.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/constants.ts`:
- Around line 7-8: MIN_COLUMN_WIDTHS is declared as a mutable number[] while
DEFAULT_COLUMN_WIDTHS is a readonly tuple; change MIN_COLUMN_WIDTHS to be a
readonly tuple by adding "as const" (or remove the explicit number[] type so TS
infers the readonly tuple) so it becomes immutable like DEFAULT_COLUMN_WIDTHS;
update any code that expects a mutable array to accept a readonly tuple if
necessary and reference MIN_COLUMN_WIDTHS and DEFAULT_COLUMN_WIDTHS when making
the change.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/GitGraphView.tsx`:
- Around line 140-143: The expression computing nextWidth uses
MIN_COLUMN_WIDTHS[index] directly which can be undefined if index is out of
bounds; fix by deriving a safe minimum first (e.g., const safeMin = (index >= 0
&& index < MIN_COLUMN_WIDTHS.length) ? MIN_COLUMN_WIDTHS[index] :
(MIN_COLUMN_WIDTHS[MIN_COLUMN_WIDTHS.length - 1] ?? 0)) and then compute
nextWidth with Math.max(safeMin, startWidth + event.clientX - startX); update
the code around the nextWidth calculation in GitGraphView.tsx to use safeMin so
Math.max never receives undefined.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/GraphRow.tsx`:
- Around line 53-57: The date formatting in GraphRow.tsx uses a hardcoded
"ja-JP" locale for formattedDate which forces Japanese formatting; update the
code that constructs formattedDate (the new
Date(node.date).toLocaleDateString(...) call) to use undefined (or a supplied
user locale preference) instead of "ja-JP" so it respects the system/user locale
or wiring a locale prop/settings value through the component if you need an
app-specific preference.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/useOrderedSections/useOrderedSections.test.tsx`:
- Around line 18-40: Add a unit test in useOrderedSections.test.tsx that
verifies the "conflicted" section is present and its count reflects
conflictedFiles; call useOrderedSections with the existing emptyArgs but set
conflictedFiles to an array of fixtures (e.g., two emptyFile() items), find the
section by section.id === "conflicted", and assert the section is defined and
its count equals the number of conflictedFiles provided (2).

In `@apps/desktop/src/renderer/stores/tabs/store.ts`:
- Around line 1666-1701: The addGitGraphTab action correctly creates and sets
the new tab/pane but misses emitting telemetry; after creating the tab/pane (use
createGitGraphTabWithPane) and after the set(...) call but before returning,
call the same telemetry capture used by other add*Tab actions (e.g.,
capture('panel_opened', { workspaceId, tabId: tab.id, paneId: pane.id, panel:
'git_graph' })) so Git Graph opens are tracked consistently; ensure you
import/use the same capture function/name as other tabs and keep the payload
keys consistent with existing panel_opened events.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b809e98a-6fb5-4116-be7f-c30c0a599ecd

📥 Commits

Reviewing files that changed from the base of the PR and between fbf90eb and c51b1c4.

📒 Files selected for processing (48)
  • README.md
  • apps/desktop/src/lib/trpc/routers/changes/git-blame.ts
  • apps/desktop/src/lib/trpc/routers/changes/index.ts
  • apps/desktop/src/lib/trpc/routers/changes/status.ts
  • apps/desktop/src/lib/trpc/routers/changes/utils/parse-status.ts
  • apps/desktop/src/lib/trpc/routers/changes/workers/git-task-handlers.ts
  • apps/desktop/src/lib/trpc/routers/changes/workers/git-task-types.ts
  • apps/desktop/src/lib/trpc/routers/ui-state/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/TopBar/TopBar.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/diff-test/DiffTestPage.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/diff-test/page.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ChangesContent/components/InfiniteScrollView/hooks/useOrderedSections/useOrderedSections.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/diff-test-fixtures.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/ConflictActionWidget.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/ConflictViewer.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/conflict-theme.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/index.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/parseConflictMarkers.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/GitGraphPane/GitGraphPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/GitGraphPane/index.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/index.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/ChangesHeader/ChangesHeader.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/CommitInput/CommitInput.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/CommitDetailsPanel.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/GitGraphView.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/GraphRow.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/constants.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/index.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/ReviewPanel/ReviewPanel.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/useOrderedSections/useOrderedSections.test.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/useOrderedSections/useOrderedSections.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/utils/computeGraphLanes.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/index.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/CodeEditor.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createBlamePlugin.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createCodeMirrorTheme.ts
  • apps/desktop/src/renderer/stores/changes/section-order.ts
  • apps/desktop/src/renderer/stores/changes/store.ts
  • apps/desktop/src/renderer/stores/editor-state/types.ts
  • apps/desktop/src/renderer/stores/tabs/store.ts
  • apps/desktop/src/renderer/stores/tabs/types.ts
  • apps/desktop/src/renderer/stores/tabs/utils.ts
  • apps/desktop/src/shared/changes-types.ts
  • apps/desktop/src/shared/tabs-types.ts

Comment thread apps/desktop/src/lib/trpc/routers/changes/status.ts Outdated
Comment thread apps/desktop/src/lib/trpc/routers/changes/utils/parse-status.ts
Comment thread apps/desktop/src/shared/tabs-types.ts
- createBlamePlugin: innerHTML → DOM構築でXSS対策、偽parentHash表示を削除
- TopBar/diff-test: 開発用テストページを本番コードから削除
- FileViewerContent: onContextMenuCapture を復元し右クリック位置情報を再取得
- ChangesView: isGitGraphOpen をタブストアから動的に取得
- useOrderedSections (ChangesContent): conflictedFiles を接続、count/content を修正
- parseConflictMarkers: ======= 判定を trimEnd() === に変更し誤検出を防止
- ChangesHeader: gitエラーメッセージに日本語キーワードを追加
- CommitDetailsPanel: ja-JP/Asia/Tokyo のハードコードを削除しシステムロケールを使用
- ui-state/index.ts: paneSchema.type に git-graph を追加(永続化失敗の防止)
- status.ts: getCommitGraph の dedupeKey に maxCount を含める
- parse-status.ts: CONFLICT_PAIRS をマーカー生成状態(AA/UU)のみに絞り込み
- constants.ts: MIN_COLUMN_WIDTHS に as const を追加
- GitGraphView.tsx: MIN_COLUMN_WIDTHS[index] の undefined フォールバック追加
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (3)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx (2)

283-296: ⚠️ Potential issue | 🟠 Major

AI-generated message can overwrite user's draft.

The onSuccess handler unconditionally calls setCommitMessage(data.message). If the user types while generation is in-flight, their draft will be overwritten when the response arrives.

Consider capturing the commit message value when the mutation is triggered and only applying the result if the field hasn't changed:

💡 Proposed approach
+const commitMessageAtRequestRef = useRef<string | null>(null);
+
 const generateCommitMessageMutation =
   electronTrpc.changes.generateCommitMessage.useMutation({
     onSuccess: (data) => {
-      if (data.message) {
+      // Only apply if user hasn't edited since request started
+      if (data.message && commitMessage === commitMessageAtRequestRef.current) {
         setCommitMessage(data.message);
+        commitMessageAtRequestRef.current = null;
       } else {
         toast.error(
           "Failed to generate commit message. Check your AI provider settings.",
         );
       }
     },

Then when triggering the mutation:

 onGenerateCommitMessage={() => {
+  commitMessageAtRequestRef.current = commitMessage;
   generateCommitMessageMutation.mutate({ worktreePath })
 }}
🤖 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/RightSidebar/ChangesView/ChangesView.tsx`
around lines 283 - 296, The onSuccess handler for
electronTrpc.changes.generateCommitMessage.useMutation currently always calls
setCommitMessage(data.message), which can clobber a user's in-progress draft;
fix by capturing the commit message field value when the mutation is triggered
(e.g., store current commitMessage in a local ref or variable inside the handler
that calls generateCommitMessageMutation.mutate) and in onSuccess only call
setCommitMessage(data.message) if the field value still matches that captured
snapshot (or is empty/unchanged), otherwise ignore the AI result; update
references around generateCommitMessageMutation, setCommitMessage, and the
mutation trigger so the response only applies when the user hasn't modified the
draft.

836-840: ⚠️ Potential issue | 🟠 Major

hasUncommittedChanges should include conflicted files.

The hasUncommittedChanges prop checks staged, unstaged, and untracked files but omits conflictedFiles. This means unresolved merge conflicts won't trigger the branch-switch warning dialog in ChangesHeader.

💡 Proposed fix
 hasUncommittedChanges={
   stagedFiles.length > 0 ||
   unstagedFiles.length > 0 ||
-  untrackedFiles.length > 0
+  untrackedFiles.length > 0 ||
+  conflictedFiles.length > 0
 }
🤖 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/RightSidebar/ChangesView/ChangesView.tsx`
around lines 836 - 840, The hasUncommittedChanges prop currently only checks
stagedFiles, unstagedFiles, and untrackedFiles; include conflictedFiles in that
condition so unresolved merge conflicts also count as uncommitted changes.
Locate the JSX where hasUncommittedChanges is set (the prop on ChangesHeader /
ChangesView) and update the boolean expression to OR in conflictedFiles.length >
0 (or check conflictedFiles.length && > 0) alongside stagedFiles, unstagedFiles,
and untrackedFiles to ensure the branch-switch warning triggers for conflicts.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx (1)

199-205: ⚠️ Potential issue | 🟠 Major

Don't reuse current-worktree blame for historical tabs.

commitHash never participates in electronTrpc.changes.getGitBlame.useQuery, but the returned blameData now feeds both CodeMirrorDiffViewer and CodeEditor. Opening the same filePath for a historical revision therefore overlays current-worktree authors onto historical content. Disable this query when commitHash is set until the procedure becomes revision-aware.

Proposed fix
 	const { data: blameData } = electronTrpc.changes.getGitBlame.useQuery(
 		{ worktreePath: worktreePath ?? "", absolutePath: absoluteFilePath },
 		{
-			enabled: Boolean(worktreePath && absoluteFilePath),
+			enabled: Boolean(worktreePath && absoluteFilePath && !commitHash),
 			staleTime: 60_000,
 		},
 	);
🤖 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/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx`
around lines 199 - 205, The current use of
electronTrpc.changes.getGitBlame.useQuery returns blameData for the live
worktree and is being used by CodeMirrorDiffViewer and CodeEditor even for
historical tabs; modify the query call
(electronTrpc.changes.getGitBlame.useQuery) so it is disabled when commitHash is
set (e.g., include !commitHash in the enabled condition) to prevent
current-worktree blame from being fetched/used for historical revisions (affects
blameData fed into CodeMirrorDiffViewer and CodeEditor that use worktreePath and
absoluteFilePath).
🧹 Nitpick comments (2)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createBlamePlugin.ts (1)

66-67: Unused constant.

_ICON_ARROW is defined but never used. If it's not planned for future use, consider removing it to reduce clutter.

🤖 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/createBlamePlugin.ts`
around lines 66 - 67, The constant _ICON_ARROW is defined but never used in
createBlamePlugin.ts; either remove the unused constant declaration
(_ICON_ARROW) to eliminate dead code or, if the arrow SVG is intended for UI,
replace existing inline SVG usages with this constant and reference _ICON_ARROW
where needed (e.g., in the plugin's render/toolbar/template code such as any
renderBlameIcon or toolbar item that expects an SVG). Ensure imports/exports are
updated accordingly and that no lint errors remain after removal or usage.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx (1)

97-113: Consider simplifying the isGitGraphOpen selector.

The current implementation uses multiple inline type assertions which reduce readability. Consider extracting a helper or using a more type-safe approach.

♻️ Suggested refactor
 const isGitGraphOpen = useTabsStore((s) =>
   worktreePath
-    ? s.tabs.some(
-        (t) =>
-          t.workspaceId === workspaceId &&
-          Object.values(s.panes).some(
-            (p) =>
-              p.tabId === t.id &&
-              "type" in p &&
-              (p as { type: string }).type === "git-graph" &&
-              "gitGraph" in p &&
-              (p as { gitGraph?: { worktreePath: string } }).gitGraph
-                ?.worktreePath === worktreePath,
-          ),
-      )
+    ? s.tabs.some((t) => {
+        if (t.workspaceId !== workspaceId) return false;
+        return Object.values(s.panes).some((p) => {
+          if (p.tabId !== t.id) return false;
+          if (!("type" in p) || p.type !== "git-graph") return false;
+          if (!("gitGraph" in p)) return false;
+          const gitGraphPane = p as { gitGraph?: { worktreePath: string } };
+          return gitGraphPane.gitGraph?.worktreePath === worktreePath;
+        });
+      })
     : false,
 );
🤖 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/RightSidebar/ChangesView/ChangesView.tsx`
around lines 97 - 113, The isGitGraphOpen selector is hard to read due to
repeated inline type assertions; refactor by extracting a type-guard/helper
function (e.g., isGitGraphPane(p): p is { type: "git-graph"; gitGraph?: {
worktreePath: string } }) and then rewrite the useTabsStore callback to use that
guard and clearer checks against tabs, panes and gitGraph.worktreePath; update
the selector logic in isGitGraphOpen to call isGitGraphPane(p) and directly
compare p.gitGraph?.worktreePath === worktreePath for readability and
type-safety while preserving the existing semantics.
🤖 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/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createBlamePlugin.ts`:
- Around line 19-29: The UI strings are hardcoded in Japanese in formatTimeAgo
and formatFullDate and the copy button title (copyBtn.title); extract all
user-facing strings ("分前","時間前","日前","ヶ月前","年前","朝","午後","コピー", etc.) into the
app's localization layer or a translation file and replace literal strings with
locale lookups (e.g., t('time.minutes_ago')), and update formatFullDate to
compute the correct English ordinal suffix for day numbers (1st, 2nd, 3rd,
4th...21st, 22nd, 23rd) instead of always appending "th". Ensure formatTimeAgo
uses localized pluralization/units via the same i18n API so it renders
appropriately for different locales and wire copyBtn.title to the translation
key.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx`:
- Around line 385-394: The code currently only updates
lastDiffLocationRef.current when getDiffLocationFromEvent(nativeEvent) returns a
location, leaving stale values when it returns null; update the handler so that
after calling getDiffLocationFromEvent(nativeEvent) you explicitly clear
lastDiffLocationRef.current (set to null/undefined) when location is falsy,
otherwise compute column via getColumnFromDiffPoint and assign
lastDiffLocationRef.current = { ...location, column } so subsequent handlers
like onEditAtLocation don't use stale coordinates.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/CommitDetailsPanel.tsx`:
- Around line 66-250: The CommitDetailsPanel component needs to be relocated
into its own folder-per-component layout: create a CommitDetailsPanel directory,
move the current CommitDetailsPanel.tsx into
CommitDetailsPanel/CommitDetailsPanel.tsx, add a CommitDetailsPanel/index.ts
that re-exports the component (export { CommitDetailsPanel } from
'./CommitDetailsPanel'), and update any imports across the codebase that
referenced the old path to import from the new barrel; ensure the component's
named export remains CommitDetailsPanel and run the project to fix any broken
import paths.
- Around line 212-223: The click handler that calls addFileViewerPane from
CommitDetailsPanel.tsx currently builds absolutePath via
toAbsoluteWorkspacePath(worktreePath, file.path) but doesn't pass the original
path for renamed files; update the payload passed to addFileViewerPane so that
when file.status === "renamed" you include an oldPath field (e.g. compute
toAbsoluteWorkspacePath(worktreePath, file.oldPath) or use file.oldPath)
alongside filePath, diffCategory, fileStatus, and commitHash so the file viewer
can render rename diffs correctly.

---

Duplicate comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx`:
- Around line 199-205: The current use of
electronTrpc.changes.getGitBlame.useQuery returns blameData for the live
worktree and is being used by CodeMirrorDiffViewer and CodeEditor even for
historical tabs; modify the query call
(electronTrpc.changes.getGitBlame.useQuery) so it is disabled when commitHash is
set (e.g., include !commitHash in the enabled condition) to prevent
current-worktree blame from being fetched/used for historical revisions (affects
blameData fed into CodeMirrorDiffViewer and CodeEditor that use worktreePath and
absoluteFilePath).

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx`:
- Around line 283-296: The onSuccess handler for
electronTrpc.changes.generateCommitMessage.useMutation currently always calls
setCommitMessage(data.message), which can clobber a user's in-progress draft;
fix by capturing the commit message field value when the mutation is triggered
(e.g., store current commitMessage in a local ref or variable inside the handler
that calls generateCommitMessageMutation.mutate) and in onSuccess only call
setCommitMessage(data.message) if the field value still matches that captured
snapshot (or is empty/unchanged), otherwise ignore the AI result; update
references around generateCommitMessageMutation, setCommitMessage, and the
mutation trigger so the response only applies when the user hasn't modified the
draft.
- Around line 836-840: The hasUncommittedChanges prop currently only checks
stagedFiles, unstagedFiles, and untrackedFiles; include conflictedFiles in that
condition so unresolved merge conflicts also count as uncommitted changes.
Locate the JSX where hasUncommittedChanges is set (the prop on ChangesHeader /
ChangesView) and update the boolean expression to OR in conflictedFiles.length >
0 (or check conflictedFiles.length && > 0) alongside stagedFiles, unstagedFiles,
and untrackedFiles to ensure the branch-switch warning triggers for conflicts.

---

Nitpick comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createBlamePlugin.ts`:
- Around line 66-67: The constant _ICON_ARROW is defined but never used in
createBlamePlugin.ts; either remove the unused constant declaration
(_ICON_ARROW) to eliminate dead code or, if the arrow SVG is intended for UI,
replace existing inline SVG usages with this constant and reference _ICON_ARROW
where needed (e.g., in the plugin's render/toolbar/template code such as any
renderBlameIcon or toolbar item that expects an SVG). Ensure imports/exports are
updated accordingly and that no lint errors remain after removal or usage.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx`:
- Around line 97-113: The isGitGraphOpen selector is hard to read due to
repeated inline type assertions; refactor by extracting a type-guard/helper
function (e.g., isGitGraphPane(p): p is { type: "git-graph"; gitGraph?: {
worktreePath: string } }) and then rewrite the useTabsStore callback to use that
guard and clearer checks against tabs, panes and gitGraph.worktreePath; update
the selector logic in isGitGraphOpen to call isGitGraphPane(p) and directly
compare p.gitGraph?.worktreePath === worktreePath for readability and
type-safety while preserving the existing semantics.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b15931da-90c7-43ed-817e-ad11c5097ba1

📥 Commits

Reviewing files that changed from the base of the PR and between c51b1c4 and 96a1f6e.

📒 Files selected for processing (8)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ChangesContent/components/InfiniteScrollView/InfiniteScrollView.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ChangesContent/components/InfiniteScrollView/hooks/useOrderedSections/useOrderedSections.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/parseConflictMarkers.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/ChangesView.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/ChangesHeader/ChangesHeader.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/CommitDetailsPanel.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/createBlamePlugin.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/ConflictViewer/parseConflictMarkers.ts

- createBlamePlugin: 日付の序数サフィックスを正しく計算(1st/2nd/3rd/th)
- FileViewerContent: diff外の右クリック時にlastDiffLocationRefをnullクリア
- CommitDetailsPanel: フォルダ構成に移動(CommitDetailsPanel/index.ts追加)
- CommitDetailsPanel: リネームファイルのoldPathをaddFileViewerPaneに渡す
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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/lib/trpc/routers/changes/status.ts`:
- Around line 121-124: The schema for the router input currently allows any
number for maxCount; update the zod schema where maxCount is defined (the
z.object with worktreePath and maxCount) to validate it as a positive integer by
using .int().min(1).optional() (following the pattern used for retryCount). This
ensures the maxCount field passed into the handler (which forwards to git
--max-count) is a valid positive integer and prevents negative/fractional values
or zero from reaching the git call.

In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/GitGraphView.tsx`:
- Around line 93-102: The ResizeObserver setup effect currently runs only once
and returns early if scrollContainerRef.current is null, so when the loading UI
is replaced by the real content the observer is never attached; change the
effect that creates the ResizeObserver (the useEffect that references
scrollContainerRef, setContainerWidth) to re-run when the container element
appears by depending on the ref (e.g., include scrollContainerRef.current or a
stateful callback ref value in the dependency array) so the observer is attached
once the DOM node exists; ensure you still call
setContainerWidth(el.clientWidth) after observing and properly disconnect the
observer in the cleanup.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 33e67a06-f31f-4974-86a6-d52f4d6924b4

📥 Commits

Reviewing files that changed from the base of the PR and between 96a1f6e and 0cfa2a5.

📒 Files selected for processing (6)
  • .gitignore
  • apps/desktop/src/lib/trpc/routers/changes/status.ts
  • apps/desktop/src/lib/trpc/routers/changes/utils/parse-status.ts
  • apps/desktop/src/lib/trpc/routers/ui-state/index.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/GitGraphView.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/constants.ts
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/GitGraphView/constants.ts

Comment thread apps/desktop/src/lib/trpc/routers/changes/status.ts
- status.ts: maxCountのバリデーションをint().min(1).max(5_000)に強化し、デフォルト500を明示
- GitGraphView: ResizeObserverをローディング完了後に設定されるよう依存配列を修正
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant