-
Notifications
You must be signed in to change notification settings - Fork 8
Add GitHub-style diff viewer with infinite scroll #168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR introduces a comprehensive diff viewer feature for the desktop application. It adds Git diff parsing utilities, a TRPC diff router, React hooks for syntax highlighting and theme management, a Zustand-based diff store for UI state, and multiple React components for displaying and navigating changes with virtualization support. Three npm dependencies are added (react-virtualized-auto-sizer, react-window, shiki). Changes
Sequence DiagramsequenceDiagram
participant User as User
participant SidebarCV as ChangesView<br/>(Sidebar)
participant ContentCV as ChangesContent<br/>(Main)
participant Store as Diff Store
participant TRPC as TRPC Router
participant Git as Git Utils
participant AllDiffs as AllDiffsViewer<br/>(with virtualization)
User->>SidebarCV: Selects workspace
SidebarCV->>Store: Set mode (unstaged/all-changes/range)
SidebarCV->>TRPC: getChangedFiles(worktreePath, mode)
TRPC->>Git: Parse git status & numstat
Git-->>TRPC: Changed files with additions/deletions
TRPC-->>SidebarCV: File list
SidebarCV->>SidebarCV: Build file tree
SidebarCV-->>User: Display file tree
rect rgb(200, 220, 240)
Note over User,AllDiffs: User clicks file or changes mode
User->>SidebarCV: Click file or select commit range
SidebarCV->>Store: scrollToFile(path) / setCommitRange(range)
end
SidebarCV->>ContentCV: Trigger diffs fetch
ContentCV->>TRPC: getAllDiffs(worktreePath, mode, range)
TRPC->>Git: Get diffs for all files
Git-->>TRPC: Raw diff content per file
TRPC->>TRPC: parseGitDiff() for each file
TRPC-->>ContentCV: FileDiff[] with hunks & lines
ContentCV->>Store: Get scrollToFilePath
ContentCV->>AllDiffs: Render with virtualization
AllDiffs->>AllDiffs: Flatten hunks into items
AllDiffs->>AllDiffs: Compute item heights (header/line/spacer)
AllDiffs-->>User: Display virtualized diffs
User->>AllDiffs: Scroll/interact
AllDiffs->>AllDiffs: VariableSizeList renders visible items only
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas requiring extra attention:
Possibly Related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 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 |
Implement a comprehensive diff viewing system for the Changes tab: - Add tRPC router for git diff operations (getChangedFiles, getAllDiffs, etc.) - Create Zustand store for diff UI state (mode, commit range, scroll-to) - Build sidebar components: DiffModeSelector, FileTree, FileNode, FolderNode - Build content components: AllDiffsViewer with react-window virtualization - Add theme utilities for diff colors derived from terminal ANSI colors - Add Shiki highlighter hook for syntax highlighting - Support three diff modes: Unstaged, All Changes (vs parent), and Range Uses infinite scroll approach where all diffs are shown in a single virtualized list, and clicking a file in the sidebar scrolls to it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
9097620 to
9e33026
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Nitpick comments (12)
apps/desktop/src/lib/trpc/routers/diff/utils/parse-diff.ts (1)
144-152: Edge case: Empty lines at EOF may be misclassified.The condition
line === ""handles trailing empty strings fromsplit("\n")at EOF, but these aren't actual diff content. They'll be added as context lines with both line counters incremented, which could cause line number drift if the diff has trailing newlines.Consider filtering out trailing empty strings before processing:
export function parseGitDiff(rawDiff: string, filePath: string): FileDiff { - const lines = rawDiff.split("\n"); + const lines = rawDiff.split("\n"); + // Remove trailing empty string from split + if (lines.length > 0 && lines[lines.length - 1] === "") { + lines.pop(); + }apps/desktop/src/renderer/hooks/useDiffColors.ts (1)
1-32: Hook implementation looks solid; consider alias imports for storesThe default colors and
useMemooverthemecorrectly fall back when no theme is loaded and recompute only on theme changes. The mapping throughcreateDiffColorsmatches theDiffColorscontract.To align with the desktop alias convention, consider switching the relative store imports to aliases, e.g. something along the lines of:
// Example sketch – adjust to your actual aliases import { useTheme } from "renderer/stores/theme"; import { createDiffColors, type DiffColors } from "renderer/stores/theme/utils";This keeps imports consistent across the renderer codebase.
As per coding guidelines.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/ChangesContent.tsx (1)
31-40: Guard diff query for range mode when no commit range is selectedIf the diff router requires a
rangewhenmode === "range", callingdiff.getAllDiffswithmode: "range"butrange: undefinedwill surface as an avoidable error in the UI.You can make the query enable condition stricter, e.g.:
const isRangeMode = mode === "range"; const hasRange = !!commitRange; const { data: allDiffs, isLoading, error, } = trpc.diff.getAllDiffs.useQuery( { worktreePath: worktreePath ?? "", mode, range: commitRange || undefined, }, { enabled: !!worktreePath && (!isRangeMode || hasRange), }, );so the range-mode query only fires once a valid range is configured.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/FileNode.tsx (2)
7-26: Hardcoded colors break theme consistency.The modified (
#d29922) and renamed/copied (#58a6ff) status colors are hardcoded and won't adapt to theme changes, unlike other status colors that usecolors.addedIndicator,colors.deletedIndicator, etc. Consider extending theDiffColorsinterface to include colors for all status types.As per coding guidelines, follow existing patterns and match the codebase style when writing new code.
69-69: Consider safer filename extraction.Using
.split("/").pop()could theoretically returnundefinedfor edge cases (empty path). While file paths should always have a filename in practice, consider adding a fallback for robustness.-<span className="truncate flex-1">{file.path.split("/").pop()}</span> +<span className="truncate flex-1">{file.path.split("/").pop() || file.path}</span>apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsx (1)
9-11: Consider importing DiffViewerProps from types.The
DiffViewerPropsinterface is defined locally here, but according to the AI summary, it's also defined in../../types.ts. Consider importing it from the types file to avoid duplication and maintain a single source of truth.As per coding guidelines, keep diffs minimal and avoid code duplication.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsx (1)
86-92: Potential unnecessary effect re-runs due tofileTreedependency.
fileTreeis derived fromchangedFilesviauseMemo, so including bothchangedFilesandfileTreein the dependency array is redundant. SincefileTreechanges wheneverchangedFileschanges, the effect will run correctly with just[changedFiles, expandAllFolders]. Additionally,getAllFolderPathsis a pure function call, not a dependency.useEffect(() => { if (changedFiles && changedFiles.length > 0) { const allPaths = getAllFolderPaths(fileTree); expandAllFolders(allPaths); } - }, [changedFiles, fileTree, expandAllFolders]); + }, [changedFiles, fileTree, expandAllFolders]); // fileTree is derived from changedFilesAlternatively, if the linter doesn't complain, you could simplify to
[changedFiles, expandAllFolders]sincefileTreeis a stable derivation ofchangedFiles.apps/desktop/src/lib/trpc/routers/diff/diff.ts (2)
364-369:Promise.allwill fail entirely if any single file diff fails.If one file's diff fails (e.g., binary file, permission issue), the entire
getAllDiffscall fails. Consider usingPromise.allSettledor adding individual error handling to provide partial results.// Get all diffs in parallel - const diffs = await Promise.all( + const results = await Promise.allSettled( files.map(async (file) => { - const rawDiff = await git.raw([...diffBase, "--", file.path]); - return parseGitDiff(rawDiff, file.path); + try { + const rawDiff = await git.raw([...diffBase, "--", file.path]); + return parseGitDiff(rawDiff, file.path); + } catch { + // Return empty diff on failure + return { path: file.path, isBinary: false, language: "", hunks: [] }; + } }), ); + const diffs = results + .filter((r): r is PromiseFulfilledResult<FileDiff> => r.status === "fulfilled") + .map((r) => r.value); return diffs;
119-142: Consider extracting duplicated file parsing logic.The logic for parsing
--name-statusoutput and merging numstat data is duplicated acrossall-changesandrangemodes (and partially ingetAllDiffs). Extracting this into a helper function would improve maintainability.For example:
function parseNameStatus(output: string): ChangedFile[] { const files: ChangedFile[] = []; const lines = output.trim().split("\n").filter(Boolean); for (const line of lines) { const parts = line.split("\t"); if (parts.length >= 2) { const statusChar = parts[0][0] as FileStatus; const path = parts[parts.length - 1]; const oldPath = parts[0].startsWith("R") && parts.length === 3 ? parts[1] : undefined; files.push({ path, status: statusChar, oldPath, additions: 0, deletions: 0 }); } } return files; }Also applies to: 165-215, 217-272
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/AllDiffsViewer.tsx (1)
89-102: Consider callingresetAfterIndexwhen diffs change.When
diffschange, the flatteneditemsarray changes, butVariableSizeListcaches item sizes internally. Without resetting the cache, stale sizes may cause layout issues.+ // Reset list size cache when items change + useEffect(() => { + if (listRef.current) { + listRef.current.resetAfterIndex(0); + } + }, [items]); + // Pre-calculate changes for each file (for headers) const fileChanges = useMemo(() => {apps/desktop/src/renderer/hooks/useHighlighter.ts (1)
91-130: Consider cleanup for async effect.The
initOrUpdateasync function runs without cleanup handling. If the component unmounts orshikiThemechanges rapidly while initialization is in progress, the stale callback may still callsetHighlighter/setErroron an unmounted component.useEffect(() => { if (!shikiTheme) return; const themeJson = JSON.stringify(shikiTheme); + let cancelled = false; const initOrUpdate = async () => { try { // Dynamically import shiki to enable code splitting const { createHighlighter } = await import("shiki"); // Initialize highlighter if needed if (!highlighterPromise) { setIsLoading(true); highlighterPromise = createHighlighter({ themes: [shikiTheme], langs: PRELOAD_LANGUAGES, }); highlighterInstance = await highlighterPromise; currentThemeJson = themeJson; } else if (currentThemeJson !== themeJson) { // Theme changed, load the new theme const hl = await highlighterPromise; await hl.loadTheme(shikiTheme); currentThemeJson = themeJson; // Clear cache when theme changes lineCache.clear(); } + if (cancelled) return; setHighlighter(highlighterInstance); setError(null); } catch (err) { + if (cancelled) return; console.error("Failed to initialize highlighter:", err); setError(err instanceof Error ? err : new Error(String(err))); } finally { + if (cancelled) return; setIsLoading(false); } }; initOrUpdate(); + + return () => { + cancelled = true; + }; }, [shikiTheme]);apps/desktop/src/renderer/stores/theme/utils/diff-theme.ts (1)
30-39: MakehexToRgbaslightly more defensive without changing behaviorThe implementation is fine given controlled inputs, but you can cheaply harden it by only stripping a leading
#(rather than the first#anywhere), which avoids surprises if a malformed value ever sneaks in. This keeps the change minimal and behaviorally equivalent for valid color strings (e.g.#RRGGBB). Based on learnings, this keeps the diff tight while improving robustness.-function hexToRgba(hex: string, alpha: number): string { - // Remove # if present - const cleanHex = hex.replace("#", ""); +function hexToRgba(hex: string, alpha: number): string { + // Remove leading # if present + const cleanHex = hex.replace(/^#/, "");
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (44)
apps/desktop/package.json(2 hunks)apps/desktop/src/lib/trpc/routers/diff/diff.ts(1 hunks)apps/desktop/src/lib/trpc/routers/diff/index.ts(1 hunks)apps/desktop/src/lib/trpc/routers/diff/types.ts(1 hunks)apps/desktop/src/lib/trpc/routers/diff/utils/parent-branch.ts(1 hunks)apps/desktop/src/lib/trpc/routers/diff/utils/parse-diff.ts(1 hunks)apps/desktop/src/lib/trpc/routers/index.ts(2 hunks)apps/desktop/src/renderer/hooks/index.ts(1 hunks)apps/desktop/src/renderer/hooks/useDiffColors.ts(1 hunks)apps/desktop/src/renderer/hooks/useHighlighter.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent.tsx(0 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/ChangesContent.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/AllDiffsViewer.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/DiffHeader.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/DiffHunkHeader.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/types.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView.tsx(0 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/DiffModeSelector.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/FileNode.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/FileTree.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/FolderNode.tsx(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/hooks/useFileTree.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/types.ts(1 hunks)apps/desktop/src/renderer/stores/diff/hooks.ts(1 hunks)apps/desktop/src/renderer/stores/diff/index.ts(1 hunks)apps/desktop/src/renderer/stores/diff/store.ts(1 hunks)apps/desktop/src/renderer/stores/diff/types.ts(1 hunks)apps/desktop/src/renderer/stores/index.ts(1 hunks)apps/desktop/src/renderer/stores/theme/utils/diff-theme.ts(1 hunks)apps/desktop/src/renderer/stores/theme/utils/index.ts(1 hunks)
💤 Files with no reviewable changes (2)
- apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView.tsx
- apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent.tsx
🧰 Additional context used
📓 Path-based instructions (7)
apps/desktop/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
For Electron interprocess communication, ALWAYS use tRPC as defined in
src/lib/trpc
Files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/index.tsapps/desktop/src/lib/trpc/routers/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/ChangesContent.tsxapps/desktop/src/renderer/stores/theme/utils/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/index.tsapps/desktop/src/lib/trpc/routers/diff/utils/parent-branch.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/index.tsapps/desktop/src/renderer/stores/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/hooks/useFileTree.tsapps/desktop/src/lib/trpc/routers/diff/diff.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/index.tsapps/desktop/src/renderer/stores/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/DiffHeader.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/FileNode.tsxapps/desktop/src/renderer/hooks/useDiffColors.tsapps/desktop/src/renderer/hooks/useHighlighter.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/types.tsapps/desktop/src/renderer/stores/diff/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/AllDiffsViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsxapps/desktop/src/renderer/stores/theme/utils/diff-theme.tsapps/desktop/src/renderer/stores/diff/store.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/FolderNode.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsxapps/desktop/src/lib/trpc/routers/diff/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/DiffModeSelector.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/FileTree.tsxapps/desktop/src/renderer/hooks/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/index.tsapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/index.tsapps/desktop/src/lib/trpc/routers/diff/utils/parse-diff.tsapps/desktop/src/renderer/stores/diff/hooks.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/DiffHunkHeader.tsx
apps/desktop/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
apps/desktop/**/*.{ts,tsx}: Please use alias as defined intsconfig.jsonwhen possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary
Files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/index.tsapps/desktop/src/lib/trpc/routers/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/ChangesContent.tsxapps/desktop/src/renderer/stores/theme/utils/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/index.tsapps/desktop/src/lib/trpc/routers/diff/utils/parent-branch.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/index.tsapps/desktop/src/renderer/stores/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/hooks/useFileTree.tsapps/desktop/src/lib/trpc/routers/diff/diff.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/index.tsapps/desktop/src/renderer/stores/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/DiffHeader.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/FileNode.tsxapps/desktop/src/renderer/hooks/useDiffColors.tsapps/desktop/src/renderer/hooks/useHighlighter.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/types.tsapps/desktop/src/renderer/stores/diff/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/AllDiffsViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsxapps/desktop/src/renderer/stores/theme/utils/diff-theme.tsapps/desktop/src/renderer/stores/diff/store.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/FolderNode.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsxapps/desktop/src/lib/trpc/routers/diff/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/DiffModeSelector.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/FileTree.tsxapps/desktop/src/renderer/hooks/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/index.tsapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/index.tsapps/desktop/src/lib/trpc/routers/diff/utils/parse-diff.tsapps/desktop/src/renderer/stores/diff/hooks.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/DiffHunkHeader.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid usinganytype - use explicit types instead for type safety
Use camelCase for variable and function names following existing codebase patterns
Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications
Follow existing patterns and match the codebase style when writing new code
Files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/index.tsapps/desktop/src/lib/trpc/routers/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/ChangesContent.tsxapps/desktop/src/renderer/stores/theme/utils/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/index.tsapps/desktop/src/lib/trpc/routers/diff/utils/parent-branch.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/index.tsapps/desktop/src/renderer/stores/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/hooks/useFileTree.tsapps/desktop/src/lib/trpc/routers/diff/diff.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/index.tsapps/desktop/src/renderer/stores/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/DiffHeader.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/FileNode.tsxapps/desktop/src/renderer/hooks/useDiffColors.tsapps/desktop/src/renderer/hooks/useHighlighter.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/types.tsapps/desktop/src/renderer/stores/diff/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/AllDiffsViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsxapps/desktop/src/renderer/stores/theme/utils/diff-theme.tsapps/desktop/src/renderer/stores/diff/store.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/FolderNode.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsxapps/desktop/src/lib/trpc/routers/diff/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/DiffModeSelector.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/FileTree.tsxapps/desktop/src/renderer/hooks/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/index.tsapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/index.tsapps/desktop/src/lib/trpc/routers/diff/utils/parse-diff.tsapps/desktop/src/renderer/stores/diff/hooks.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/DiffHunkHeader.tsx
apps/desktop/src/renderer/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never import Node.js modules (fs, path, os, net, etc.) in renderer process code - browser environment only
Files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/ChangesContent.tsxapps/desktop/src/renderer/stores/theme/utils/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/index.tsapps/desktop/src/renderer/stores/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/hooks/useFileTree.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/index.tsapps/desktop/src/renderer/stores/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/DiffHeader.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/FileNode.tsxapps/desktop/src/renderer/hooks/useDiffColors.tsapps/desktop/src/renderer/hooks/useHighlighter.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/types.tsapps/desktop/src/renderer/stores/diff/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/AllDiffsViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsxapps/desktop/src/renderer/stores/theme/utils/diff-theme.tsapps/desktop/src/renderer/stores/diff/store.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/FolderNode.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/DiffModeSelector.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/FileTree.tsxapps/desktop/src/renderer/hooks/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/index.tsapps/desktop/src/renderer/stores/diff/hooks.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/DiffHunkHeader.tsx
apps/desktop/src/lib/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Never import Node.js modules in shared code like
src/lib/electron-router-dom.ts- this code runs in both main and renderer processes
Files:
apps/desktop/src/lib/trpc/routers/diff/index.tsapps/desktop/src/lib/trpc/routers/diff/utils/parent-branch.tsapps/desktop/src/lib/trpc/routers/diff/diff.tsapps/desktop/src/lib/trpc/routers/diff/types.tsapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/lib/trpc/routers/diff/utils/parse-diff.ts
**/components/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
**/components/**/*.tsx: Create one folder per component with structure:ComponentName/ComponentName.tsx+index.tsfor barrel export
Co-locate tests next to the component file they test (e.g.,ComponentName.test.tsx)
Co-locate dependencies (utils, hooks, constants, config, stories) next to the file using them
Use nestedcomponents/subdirectory within a parent component only if a sub-component is used 2+ times within that parent; otherwise keep it in the parent'scomponents/folder
One component per file - avoid multi-component files
Files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/ChangesContent.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/DiffHeader.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/FileNode.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/AllDiffsViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/FolderNode.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/DiffModeSelector.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/FileTree.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/DiffHunkHeader.tsx
apps/desktop/src/renderer/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Call IPC methods using
window.ipcRenderer.invoke()with object parameters - TypeScript will infer the exact response type automatically
Files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/ChangesContent.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/DiffHeader.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/FileNode.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/AllDiffsViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/FolderNode.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/DiffModeSelector.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/FileTree.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/DiffHunkHeader.tsx
🧠 Learnings (10)
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to **/*.{ts,tsx} : Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications
Applied to files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/index.tsapps/desktop/src/lib/trpc/routers/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/ChangesContent.tsxapps/desktop/src/renderer/stores/theme/utils/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/index.tsapps/desktop/src/lib/trpc/routers/diff/utils/parent-branch.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/index.tsapps/desktop/src/renderer/stores/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsxapps/desktop/src/lib/trpc/routers/diff/diff.tsapps/desktop/src/renderer/stores/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/DiffHeader.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/FileNode.tsxapps/desktop/src/renderer/hooks/useDiffColors.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/types.tsapps/desktop/src/renderer/stores/diff/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/AllDiffsViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/DiffViewer.tsxapps/desktop/src/renderer/stores/theme/utils/diff-theme.tsapps/desktop/src/renderer/stores/diff/store.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/ChangesView.tsxapps/desktop/src/lib/trpc/routers/diff/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/DiffModeSelector.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/FileTree.tsxapps/desktop/src/renderer/hooks/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/index.tsapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffViewer/index.tsapps/desktop/src/lib/trpc/routers/diff/utils/parse-diff.tsapps/desktop/src/renderer/stores/diff/hooks.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/DiffHunkHeader.tsx
📚 Learning: 2025-11-24T21:33:13.244Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.244Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Please use alias as defined in `tsconfig.json` when possible
Applied to files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/index.tsapps/desktop/src/renderer/stores/diff/index.tsapps/desktop/src/renderer/stores/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/index.tsapps/desktop/src/renderer/hooks/useHighlighter.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/types.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/types.tsapps/desktop/src/renderer/hooks/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/index.ts
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to apps/desktop/src/lib/**/*.ts : Never import Node.js modules in shared code like `src/lib/electron-router-dom.ts` - this code runs in both main and renderer processes
Applied to files:
apps/desktop/src/lib/trpc/routers/diff/index.tsapps/desktop/src/renderer/stores/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/index.tsapps/desktop/src/renderer/hooks/index.tsapps/desktop/src/lib/trpc/routers/index.ts
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to **/components/**/*.tsx : Create one folder per component with structure: `ComponentName/ComponentName.tsx` + `index.ts` for barrel export
Applied to files:
apps/desktop/src/lib/trpc/routers/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/FolderNode.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/FileTree.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/index.ts
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to **/components/**/*.tsx : Co-locate dependencies (utils, hooks, constants, config, stories) next to the file using them
Applied to files:
apps/desktop/src/renderer/stores/diff/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsxapps/desktop/src/renderer/hooks/index.tsapps/desktop/src/renderer/stores/diff/hooks.ts
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to packages/ui/src/**/*.tsx : Use shadcn/ui components and TailwindCSS v4 for all UI component styling in the shared UI package
Applied to files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsxapps/desktop/package.json
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net, etc.) in renderer process code - browser environment only
Applied to files:
apps/desktop/src/renderer/stores/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/index.tsapps/desktop/src/renderer/hooks/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/index.ts
📚 Learning: 2025-11-24T21:33:13.244Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.244Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary
Applied to files:
apps/desktop/src/renderer/stores/index.tsapps/desktop/src/renderer/stores/diff/store.ts
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to apps/desktop/src/main/**/*.{ts,tsx} : Node.js modules (fs, path, os, net, etc.) can be used in main process code only
Applied to files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/index.ts
📚 Learning: 2025-11-28T01:03:47.951Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.951Z
Learning: Applies to **/components/**/*.tsx : One component per file - avoid multi-component files
Applied to files:
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileTree/FileTree.tsx
🧬 Code graph analysis (15)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsx (4)
apps/desktop/src/renderer/hooks/useHighlighter.ts (1)
useHighlighter(71-204)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/types.ts (2)
DiffLineProps(12-16)DiffLine(3-3)apps/desktop/src/renderer/hooks/useDiffColors.ts (1)
useDiffColors(23-33)apps/desktop/src/lib/trpc/routers/diff/types.ts (1)
DiffLine(22-31)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/hooks/useFileTree.ts (2)
apps/desktop/src/lib/trpc/routers/diff/types.ts (1)
ChangedFile(7-18)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/types.ts (1)
FileTreeNode(5-16)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/FileNode.tsx (4)
apps/desktop/src/renderer/hooks/index.ts (1)
useDiffColors(2-2)apps/desktop/src/renderer/hooks/useDiffColors.ts (1)
useDiffColors(23-33)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FileNode/index.ts (1)
FileNode(1-1)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/types.ts (1)
FileNodeProps(29-36)
apps/desktop/src/renderer/hooks/useDiffColors.ts (3)
apps/desktop/src/renderer/stores/theme/utils/diff-theme.ts (2)
DiffColors(6-25)createDiffColors(44-63)apps/desktop/src/renderer/stores/theme/utils/index.ts (2)
DiffColors(10-10)createDiffColors(7-7)apps/desktop/src/renderer/hooks/index.ts (1)
useDiffColors(2-2)
apps/desktop/src/renderer/hooks/useHighlighter.ts (3)
apps/desktop/src/renderer/hooks/index.ts (1)
useHighlighter(3-3)apps/desktop/src/renderer/stores/theme/utils/diff-theme.ts (1)
createShikiTheme(108-241)apps/desktop/src/renderer/stores/theme/utils/index.ts (1)
createShikiTheme(8-8)
apps/desktop/src/renderer/stores/diff/types.ts (1)
apps/desktop/src/lib/trpc/routers/diff/types.ts (1)
DiffMode(74-74)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/AllDiffsViewer/AllDiffsViewer.tsx (5)
apps/desktop/src/lib/trpc/routers/diff/types.ts (4)
FileDiff(48-59)DiffLineType(20-20)DiffHunk(33-46)DiffLine(22-31)apps/desktop/src/renderer/stores/diff/hooks.ts (2)
useScrollToFilePath(6-7)useClearScrollTarget(17-18)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHeader/DiffHeader.tsx (1)
DiffHeader(5-32)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffHunkHeader/DiffHunkHeader.tsx (1)
DiffHunkHeader(27-27)apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsx (1)
DiffLine(92-92)
apps/desktop/src/renderer/stores/theme/utils/diff-theme.ts (1)
apps/desktop/src/renderer/stores/theme/utils/index.ts (5)
DiffColors(10-10)createDiffColors(7-7)ShikiThemeTokenColors(11-11)createShikiTokenColors(9-9)createShikiTheme(8-8)
apps/desktop/src/renderer/stores/diff/store.ts (3)
apps/desktop/src/renderer/stores/diff/index.ts (1)
useDiffStore(2-2)apps/desktop/src/renderer/stores/diff/types.ts (1)
DiffStore(52-52)apps/cli/src/lib/storage/lowdb-adapter.ts (1)
set(162-172)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/FolderNode.tsx (2)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/FolderNode/index.ts (1)
FolderNode(1-1)apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/types.ts (1)
FolderNodeProps(38-49)
apps/desktop/src/lib/trpc/routers/diff/types.ts (1)
apps/desktop/src/renderer/stores/diff/types.ts (1)
DiffMode(5-5)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/DiffModeSelector.tsx (4)
apps/desktop/src/lib/trpc/routers/diff/types.ts (2)
DiffMode(74-74)Commit(61-72)apps/desktop/src/renderer/stores/diff/types.ts (2)
DiffMode(5-5)CommitRange(7-10)packages/ui/src/components/button.tsx (1)
Button(61-61)packages/ui/src/components/dropdown-menu.tsx (5)
DropdownMenu(245-245)DropdownMenuTrigger(247-247)DropdownMenuContent(248-248)DropdownMenuItem(251-251)DropdownMenuSeparator(255-255)
apps/desktop/src/lib/trpc/routers/index.ts (2)
apps/desktop/src/lib/trpc/routers/diff/diff.ts (1)
createDiffRouter(101-500)apps/desktop/src/lib/trpc/routers/diff/index.ts (1)
createDiffRouter(1-1)
apps/desktop/src/lib/trpc/routers/diff/utils/parse-diff.ts (1)
apps/desktop/src/lib/trpc/routers/diff/types.ts (3)
FileDiff(48-59)DiffHunk(33-46)DiffLine(22-31)
apps/desktop/src/renderer/stores/diff/hooks.ts (2)
apps/desktop/src/renderer/stores/diff/index.ts (1)
useDiffStore(2-2)apps/desktop/src/renderer/stores/diff/store.ts (1)
useDiffStore(12-72)
🪛 ast-grep (0.40.0)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsx
[warning] 27-27: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
| // Handle renamed files (format: "old => new" or "{prefix => suffix}") | ||
| let normalizedPath = filePath; | ||
| if (filePath.includes(" => ")) { | ||
| // Extract the new path from rename notation | ||
| normalizedPath = filePath.replace(/.*\{.*? => (.*?)\}.*/, "$1"); | ||
| if (normalizedPath === filePath) { | ||
| // Simple rename without braces | ||
| normalizedPath = filePath.split(" => ")[1] || filePath; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regex for renamed file path extraction may not handle all cases.
The regex .*\{.*? => (.*?)\}.* extracts only the new portion but doesn't reconstruct the full path when the rename uses brace notation like src/{old.ts => new.ts}. The result would be new.ts instead of src/new.ts.
Consider a more robust approach:
// Handle renamed files (format: "old => new" or "{prefix => suffix}")
let normalizedPath = filePath;
if (filePath.includes(" => ")) {
- // Extract the new path from rename notation
- normalizedPath = filePath.replace(/.*\{.*? => (.*?)\}.*/, "$1");
- if (normalizedPath === filePath) {
- // Simple rename without braces
- normalizedPath = filePath.split(" => ")[1] || filePath;
- }
+ // Handle brace notation: path/{old => new}/file.ts -> path/new/file.ts
+ if (filePath.includes("{") && filePath.includes("}")) {
+ normalizedPath = filePath.replace(/\{[^}]* => ([^}]*)\}/g, "$1");
+ } else {
+ // Simple rename: old => new
+ normalizedPath = filePath.split(" => ")[1] || filePath;
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Handle renamed files (format: "old => new" or "{prefix => suffix}") | |
| let normalizedPath = filePath; | |
| if (filePath.includes(" => ")) { | |
| // Extract the new path from rename notation | |
| normalizedPath = filePath.replace(/.*\{.*? => (.*?)\}.*/, "$1"); | |
| if (normalizedPath === filePath) { | |
| // Simple rename without braces | |
| normalizedPath = filePath.split(" => ")[1] || filePath; | |
| } | |
| } | |
| // Handle renamed files (format: "old => new" or "{prefix => suffix}") | |
| let normalizedPath = filePath; | |
| if (filePath.includes(" => ")) { | |
| // Handle brace notation: path/{old => new}/file.ts -> path/new/file.ts | |
| if (filePath.includes("{") && filePath.includes("}")) { | |
| normalizedPath = filePath.replace(/\{[^}]* => ([^}]*)\}/g, "$1"); | |
| } else { | |
| // Simple rename: old => new | |
| normalizedPath = filePath.split(" => ")[1] || filePath; | |
| } | |
| } |
🤖 Prompt for AI Agents
In apps/desktop/src/lib/trpc/routers/diff/diff.ts around lines 83-92, the
current regex extracts only the inner "new" token from brace-style renames (e.g.
"src/{old.ts => new.ts}") producing "new.ts" instead of "src/new.ts"; change the
logic to detect brace notation, capture the parts before the brace, the new
inner path, and any suffix after the brace, then reconstruct the full path by
concatenating prefix + newPart + suffix; keep a fallback for simple "old => new"
notations by splitting on " => " and using the right-hand side when brace
parsing fails.
| date: string; | ||
| } | ||
|
|
||
| export type DiffMode = "unstaged" | "staged" | "all-changes" | "range"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent DiffMode type definition across files.
This file defines DiffMode as "unstaged" | "staged" | "all-changes" | "range", but the store types at apps/desktop/src/renderer/stores/diff/types.ts (line 4) defines it as "unstaged" | "all-changes" | "range" (missing "staged").
This inconsistency could cause type mismatches when the UI state interacts with the tRPC API.
Either:
- Add
"staged"to the store'sDiffModetype if staged mode is supported in the UI - Remove
"staged"from this file if it's not a supported UI mode - Consider having the store import and re-export the type from this file to maintain a single source of truth
#!/bin/bash
# Check all DiffMode usages to understand intended behavior
rg -n "DiffMode" --type ts -B 2 -A 2| export async function getCurrentBranch(worktreePath: string): Promise<string> { | ||
| const git = simpleGit(worktreePath); | ||
| const branch = await git.revparse(["--abbrev-ref", "HEAD"]); | ||
| return branch.trim(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for getCurrentBranch.
Unlike detectParentBranch, this function has no try/catch. If the repository is in a detached HEAD state or the revparse fails for other reasons, the error will propagate unhandled.
export async function getCurrentBranch(worktreePath: string): Promise<string> {
const git = simpleGit(worktreePath);
- const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
- return branch.trim();
+ try {
+ const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
+ return branch.trim();
+ } catch {
+ // Detached HEAD or other error - return HEAD as fallback
+ return "HEAD";
+ }
}🤖 Prompt for AI Agents
In apps/desktop/src/lib/trpc/routers/diff/utils/parent-branch.ts around lines 51
to 55, getCurrentBranch lacks error handling and will throw if revparse fails
(e.g., detached HEAD); wrap the git.revparse call in a try/catch, handle
expected failure modes by returning a sensible fallback (such as an empty string
or null) or rethrowing a more descriptive error, and ensure the returned
type/signature reflects the fallback (update Promise<string> to Promise<string |
null> if returning null) and add a clear processLogger or debug log inside the
catch with the original error message.
| export function ChangesContent() { | ||
| const { data: activeWorkspace } = trpc.workspaces.getActive.useQuery(); | ||
| const { data: workspaceGroups } = trpc.workspaces.getAllGrouped.useQuery(); | ||
|
|
||
| // Diff store state | ||
| const mode = useDiffMode(); | ||
| const commitRange = useCommitRange(); | ||
|
|
||
| // Get worktree path from active workspace | ||
| const worktreePath = useMemo(() => { | ||
| if (!activeWorkspace || !workspaceGroups) return null; | ||
| for (const group of workspaceGroups) { | ||
| const ws = group.workspaces.find((w) => w.id === activeWorkspace.id); | ||
| if (ws) return ws.worktreePath; | ||
| } | ||
| return null; | ||
| }, [activeWorkspace, workspaceGroups]); | ||
|
|
||
| // Query for all diffs | ||
| const { | ||
| data: allDiffs, | ||
| isLoading, | ||
| error, | ||
| } = trpc.diff.getAllDiffs.useQuery( | ||
| { | ||
| worktreePath: worktreePath ?? "", | ||
| mode: mode, | ||
| range: commitRange || undefined, | ||
| }, | ||
| { | ||
| enabled: !!worktreePath, | ||
| }, | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loading/empty state conflates “no changes” with “still resolving workspace”
Right now the loading spinner is tied only to the diff query’s isLoading. While the workspace queries are still in-flight (or if they error), worktreePath is null, the diff query is disabled, and the component falls through to the “No changes to display” state. That can briefly show an empty state while things are still loading, or mask workspace-level failures.
Consider:
- Tracking
isLoading/errorfromgetActiveandgetAllGrouped, and - Using a combined loading flag (e.g.
isWorkspaceLoading || isDiffLoading) before rendering “no changes”, - Optionally differentiating “no workspace / failed to load workspace” from “no diffs for this workspace”.
This will make the user-facing states more accurate.
Also applies to: 70-82
🤖 Prompt for AI Agents
In
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent.tsx
around lines 8-40 (and similarly update 70-82), capture the loading and error
flags from trpc.workspaces.getActive.useQuery and
trpc.workspaces.getAllGrouped.useQuery, derive a combined isLoadingWorkspace (or
isWorkspaceError) and use that to: 1) show a global loading spinner while either
workspace query is still loading (i.e. isWorkspaceLoading || isDiffLoading), 2)
surface workspace-level errors when either workspace query errored, and 3) only
render the “No changes to display” empty state when worktreePath is non-null,
workspace queries are settled without error, and the diff query is finished and
returned an empty list; ensure the diff query remains enabled only when
worktreePath is present.
| // biome-ignore lint/security/noDangerouslySetInnerHtml: Shiki output is trusted | ||
| dangerouslySetInnerHTML={{ __html: html }} | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the file under review and understand its context
find . -path "*DiffLine.tsx" -type f 2>/dev/null | head -5Repository: superset-sh/superset
Length of output: 189
🏁 Script executed:
#!/bin/bash
# Search for escapeHtml function definition
ast-grep --pattern 'function escapeHtml($_) {
$$$
}'
echo "---"
# Also search for it as a named export or const
rg -n "escapeHtml" --type ts --type tsx -A 3 | head -50Repository: superset-sh/superset
Length of output: 112
🏁 Script executed:
#!/bin/bash
# Find all usages of dangerouslySetInnerHTML in the codebase
rg -n "dangerouslySetInnerHTML" --type tsx --type tsRepository: superset-sh/superset
Length of output: 91
🏁 Script executed:
#!/bin/bash
# Read the DiffLine.tsx file to understand context around lines 27-29
cat -n ./apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/components/DiffLine/DiffLine.tsx | head -50Repository: superset-sh/superset
Length of output: 1597
🏁 Script executed:
#!/bin/bash
# Search for escapeHtml without type filter
rg -n "escapeHtml" -A 3 -B 1 2>/dev/null | head -60Repository: superset-sh/superset
Length of output: 1775
🏁 Script executed:
#!/bin/bash
# Search for dangerouslySetInnerHTML without type filter
rg -n "dangerouslySetInnerHTML" -B 2 -A 2 2>/dev/nullRepository: superset-sh/superset
Length of output: 867
🏁 Script executed:
#!/bin/bash
# Search for useHighlighter hook
rg -n "useHighlighter" -A 5 -B 1 2>/dev/null | head -40Repository: superset-sh/superset
Length of output: 2996
🏁 Script executed:
#!/bin/bash
# Read the complete useHighlighter.ts file to understand escapeHtml and highlightLine
cat -n ./apps/desktop/src/renderer/hooks/useHighlighter.tsRepository: superset-sh/superset
Length of output: 6650
Review comment contains valid concerns but misses a critical vulnerability.
After verifying the implementation, the escapeHtml function is comprehensive and properly escapes all five critical HTML entities (&, <, >, ", '). The token content is correctly escaped at line 162 of useHighlighter.ts:
`<span style="color:${token.color}">${escapeHtml(token.content)}</span>`
However, there's a separate XSS/CSS injection vulnerability: token.color is directly interpolated into the style attribute without validation or sanitization. While Shiki is a trusted library and colors come from the app's theme configuration, this pattern is still risky:
- An attacker could exploit Shiki bugs or malicious theme data to inject CSS expressions or values
- Better approach: Use CSS classes with predefined colors instead of inline
style="color:..."attributes, or validate/sanitize the color value to ensure it matches expected format (e.g., hex, rgb, named colors)
The original review comment's focus on escapeHtml coverage is verified as correct, but the real risk is the unvalidated token.color in the style attribute, not the escaped token content.
🧰 Tools
🪛 ast-grep (0.40.0)
[warning] 27-27: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
| export interface DiffLineProps { | ||
| line: DiffLine; | ||
| language: string; | ||
| style?: React.CSSProperties; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing React import for React.CSSProperties.
React.CSSProperties is used on line 15, but React is not imported. This will cause a TypeScript/runtime error.
+import type React from "react";
import type { DiffHunk, DiffLine, FileDiff } from "lib/trpc/routers/diff/types";Alternatively, import the type directly:
+import type { CSSProperties } from "react";
import type { DiffHunk, DiffLine, FileDiff } from "lib/trpc/routers/diff/types";
export interface DiffLineProps {
line: DiffLine;
language: string;
- style?: React.CSSProperties;
+ style?: CSSProperties;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export interface DiffLineProps { | |
| line: DiffLine; | |
| language: string; | |
| style?: React.CSSProperties; | |
| } | |
| import type React from "react"; | |
| import type { DiffHunk, DiffLine, FileDiff } from "lib/trpc/routers/diff/types"; | |
| export interface DiffLineProps { | |
| line: DiffLine; | |
| language: string; | |
| style?: React.CSSProperties; | |
| } |
🤖 Prompt for AI Agents
In
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/ChangesContent/types.ts
around lines 12 to 16, the file references React.CSSProperties but does not
import React, causing a TypeScript error; fix by either importing React (e.g.
import React from 'react') so React.CSSProperties is available, or preferably
import the type directly (e.g. import type { CSSProperties } from 'react' and
change the prop to style?: CSSProperties) and update the type usage accordingly.
| import type { Commit } from "lib/trpc/routers/diff/types"; | ||
| import { HiChevronDown } from "react-icons/hi2"; | ||
| import type { CommitRange, DiffMode } from "renderer/stores/diff/types"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all DiffMode type definitions
rg -n "type DiffMode\s*=" --type tsRepository: superset-sh/superset
Length of output: 296
🏁 Script executed:
#!/bin/bash
# Search for usage of "staged" mode
rg -n '"staged"' --type ts -C 2Repository: superset-sh/superset
Length of output: 2696
🏁 Script executed:
#!/bin/bash
# Check DiffModeSelector component to see how it uses DiffMode
cat -n apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/DiffModeSelector/DiffModeSelector.tsxRepository: superset-sh/superset
Length of output: 4752
🏁 Script executed:
#!/bin/bash
# Search for other DiffMode usage in renderer components
rg -n "DiffMode\|mode === \"staged\"" apps/desktop/src/renderer --type tsRepository: superset-sh/superset
Length of output: 46
🏁 Script executed:
#!/bin/bash
# Check the diff store implementation
fd -t f "diff" apps/desktop/src/renderer/stores/ --exec cat -n {} \;Repository: superset-sh/superset
Length of output: 7439
🏁 Script executed:
#!/bin/bash
# Check the diff types file directly
cat -n apps/desktop/src/renderer/stores/diff/types.tsRepository: superset-sh/superset
Length of output: 1654
Type mismatch confirms "staged" mode is unsupported in renderer store.
The review comment is accurate. Two DiffMode definitions exist with a critical inconsistency:
lib/trpc/routers/diff/types.ts(backend):"unstaged" | "staged" | "all-changes" | "range"renderer/stores/diff/types.ts(frontend):"unstaged" | "all-changes" | "range"
The backend tRPC router fully implements "staged" mode (three case handlers in diff.ts), but the renderer store's DiffMode type omits it. This prevents the frontend from ever handling "staged" mode due to type incompatibility—DiffModeSelector cannot pass "staged" to setMode() without a type error.
Fix: Add "staged" to the DiffMode type definition in renderer/stores/diff/types.ts line 5.
🤖 Prompt for AI Agents
In apps/desktop/src/renderer/stores/diff/types.ts around line 5, the DiffMode
union type omits "staged", causing a type mismatch with the backend and
preventing the frontend from passing staged to setMode(); update the DiffMode
definition to include the "staged" variant so it matches the backend ("unstaged"
| "staged" | "all-changes" | "range"), then run type checks to ensure no other
places need adjustments.
Implement a comprehensive diff viewing system for the Changes tab:
Uses infinite scroll approach where all diffs are shown in a single
virtualized list, and clicking a file in the sidebar scrolls to it.
🤖 Generated with Claude Code
Co-Authored-By: Claude [email protected]
Description
Related Issues
Type of Change
Testing
Screenshots (if applicable)
Additional Notes
Summary by CodeRabbit
New Features
✏️ Tip: You can customize this high-level summary in your review settings.