a very good jj gui
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Plan 1: Upgrade TanStack data dependencies

Upgrade the TanStack data stack to the latest versions identified during planning:

- @tanstack/db 0.5.15 -> 0.6.5
- @tanstack/react-db 0.1.59 -> 0.1.83
- @tanstack/query-db-collection 1.0.11 -> 1.0.36
- @tanstack/react-query 5.90.12 -> 5.100.5
- @tanstack/query-core 5.90.12 -> 5.100.5

This keeps the current data-layer behavior unchanged while moving the dependency baseline forward for the upcoming TanStack DB refactor. bun.lock was regenerated with Bun after updating apps/desktop/package.json.

Also clean up the existing frontend lint blockers so the upgraded baseline validates cleanly:

- Format files that Biome reported as stale.
- Add explicit lint suppressions for intentional tracing console output, mount-only scroll restoration, debounced effects, and local UI state cases covered by project ast-grep rules.
- Fix accessibility warnings around unsupported aria-label usage and the settings checkbox label.
- Apply the mock layout const cleanup.

Validation:
- cd apps/desktop && bun run lint
- cd apps/desktop && bun run typecheck
- cd apps/desktop && bun run test
- cd apps/desktop && bun run build

+54 -45
+5 -5
apps/desktop/package.json
··· 25 25 "@fontsource-variable/jetbrains-mono": "^5.2.8", 26 26 "@pierre/diffs": "^1.0.4", 27 27 "@tailwindcss/vite": "^4.1.18", 28 - "@tanstack/db": "^0.5.15", 29 - "@tanstack/query-core": "^5.90.12", 30 - "@tanstack/query-db-collection": "^1.0.11", 31 - "@tanstack/react-db": "^0.1.59", 32 - "@tanstack/react-query": "^5.90.12", 28 + "@tanstack/db": "^0.6.5", 29 + "@tanstack/query-core": "^5.100.5", 30 + "@tanstack/query-db-collection": "^1.0.36", 31 + "@tanstack/react-db": "^0.1.83", 32 + "@tanstack/react-query": "^5.100.5", 33 33 "@tanstack/react-router": "^1.141.6", 34 34 "@tanstack/react-virtual": "^3.13.16", 35 35 "@tauri-apps/api": "^2.1.1",
+1 -4
apps/desktop/src/components/AppHeader.tsx
··· 37 37 <FolderOpenIcon className="size-4" /> 38 38 <span className="truncate max-w-[200px]">{projectName ?? "Open Repository"}</span> 39 39 </Button> 40 - <div 41 - className="relative flex items-center rounded-md border border-border/70 bg-muted/60 p-0.5 shadow-inner" 42 - aria-label="View mode" 43 - > 40 + <div className="relative flex items-center rounded-md border border-border/70 bg-muted/60 p-0.5 shadow-inner"> 44 41 <div 45 42 className={`absolute top-0.5 h-6 w-6 rounded-sm bg-background shadow-sm transition-transform duration-200 ${ 46 43 viewMode === 1 ? "translate-x-0" : "translate-x-6"
+10 -1
apps/desktop/src/components/AppShell.tsx
··· 271 271 void revisionsLiveCollection.preload(); 272 272 }; 273 273 274 + // ast-grep-ignore: no-useeffect-state-sync 274 275 useEffect(() => { 275 276 if (!persistedLayout) return; 276 277 ··· 308 309 }); 309 310 }, [isLoading, navigate, persistedLayout, projectId, rev, revisions]); 310 311 312 + // ast-grep-ignore: no-useeffect-state-sync 311 313 useEffect(() => { 312 314 if (!layoutHydratedRef.current) return; 313 315 if (!projectId) return; ··· 334 336 }; 335 337 }, [projectId, selectedRevisionKey, sidebarWidth, viewMode]); 336 338 339 + // ast-grep-ignore: no-useeffect-state-sync 337 340 useEffect(() => { 338 341 if (!editingChangeId) return; 339 342 if (!selectedRevision || getRevisionKey(selectedRevision) !== editingChangeId) { ··· 341 344 } 342 345 }, [editingChangeId, selectedRevision]); 343 346 347 + // ast-grep-ignore: no-useeffect-state-sync 344 348 useEffect(() => { 345 349 if (!rebaseSourceKey) return; 346 350 const stillExists = revisions.some((revision) => getRevisionKey(revision) === rebaseSourceKey); ··· 355 359 const [debouncedChangeId, setDebouncedChangeId] = useAtom(debouncedChangeIdAtom); 356 360 const debounceTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined); 357 361 362 + // ast-grep-ignore: no-useeffect-state-sync 358 363 useEffect(() => { 359 364 // Clear any pending debounce 360 365 if (debounceTimerRef.current) { ··· 821 826 withHandle 822 827 orientation={isNarrowScreen ? "vertical" : "horizontal"} 823 828 /> 824 - <ResizablePanel id="app-shell-diff" defaultSize={isNarrowScreen ? "60%" : `${100 - sidebarWidth}%`} minSize="30%"> 829 + <ResizablePanel 830 + id="app-shell-diff" 831 + defaultSize={isNarrowScreen ? "60%" : `${100 - sidebarWidth}%`} 832 + minSize="30%" 833 + > 825 834 <aside className="h-full" aria-label="Diff viewer"> 826 835 <Profiler id="DiffPanel" onRender={onRenderCallback}> 827 836 <PrerenderedDiffPanel
+4 -7
apps/desktop/src/components/DiffPanel.tsx
··· 36 36 } 37 37 38 38 export const PrerenderedDiffPanel = forwardRef<HTMLDivElement, PrerenderedDiffPanelProps>( 39 - function PrerenderedDiffPanel({ 40 - repoPath, 41 - revisions, 42 - selectedChangeId, 43 - revisionsPanelRef, 44 - onDescribe, 45 - }, ref) { 39 + function PrerenderedDiffPanel( 40 + { repoPath, revisions, selectedChangeId, revisionsPanelRef, onDescribe }, 41 + ref, 42 + ) { 46 43 const selectedRevision = selectedChangeId 47 44 ? (revisions.find((r) => r.change_id === selectedChangeId) ?? null) 48 45 : null;
+2 -6
apps/desktop/src/components/InlineEditor.tsx
··· 190 190 view.destroy(); 191 191 viewRef.current = null; 192 192 }; 193 - // Only create once on mount 194 - // eslint-disable-next-line react-hooks/exhaustive-deps 195 - }, []); 193 + }, [autoFocus, placeholder, readOnly, value]); 196 194 197 195 const variantClasses = 198 196 variant === "transparent" ··· 202 200 return ( 203 201 <div 204 202 ref={containerRef} 205 - className={`${variantClasses} ${ 206 - compact ? "max-h-20 overflow-auto" : "" 207 - } ${className}`} 203 + className={`${variantClasses} ${compact ? "max-h-20 overflow-auto" : ""} ${className}`} 208 204 /> 209 205 ); 210 206 }
+1 -4
apps/desktop/src/components/diff/FileList.tsx
··· 631 631 {" / "} 632 632 <span className="text-red-500">-{totalDeletions}</span> 633 633 </span> 634 - <div 635 - className="relative flex items-center rounded-md border border-border/70 bg-muted/60 p-0.5 shadow-inner" 636 - aria-label="File list view mode" 637 - > 634 + <div className="relative flex items-center rounded-md border border-border/70 bg-muted/60 p-0.5 shadow-inner"> 638 635 <div 639 636 className={`absolute top-0.5 h-5 w-5 rounded-sm bg-background shadow-sm transition-transform duration-200 ${ 640 637 viewMode === "flat" ? "translate-x-0" : "translate-x-5"
+3
apps/desktop/src/components/revision-graph/index.tsx
··· 735 735 const [savedScrollTop, setSavedScrollTop] = useAtom(revisionGraphScrollTopAtom); 736 736 737 737 // Restore scroll position on mount 738 + // biome-ignore lint/correctness/useExhaustiveDependencies: intentionally restore only once on mount using persisted scroll state. 738 739 useEffect(() => { 739 740 const scrollEl = parentRef.current; 740 741 if (!scrollEl || savedScrollTop === 0) return; ··· 894 895 // Prefetch diffs and changes for visible and nearby revisions 895 896 // Uses a buffer to prefetch ahead of scroll for smoother experience 896 897 const PREFETCH_BUFFER = 20; 898 + // ast-grep-ignore: no-useeffect-state-sync 897 899 useEffect(() => { 898 900 if (!repoPath || displayRows.length === 0) return; 899 901 ··· 1011 1013 matchingRevisionsRef.current = matchingRevisions; 1012 1014 1013 1015 // Handle AceJump letter key presses (DOM event subscription) 1016 + // ast-grep-ignore: no-useeffect-state-sync 1014 1017 useEffect(() => { 1015 1018 if (!aceJumpMode) return; 1016 1019
+1
apps/desktop/src/hooks/useAddRepository.ts
··· 36 36 */ 37 37 export function useAddRepository() { 38 38 const navigate = useNavigate(); 39 + // ast-grep-ignore: no-usestate 39 40 const [isAdding, setIsAdding] = useState(false); 40 41 41 42 function handleAddRepository() {
+1
apps/desktop/src/hooks/useFocusWithin.ts
··· 5 5 * Uses focusin/focusout events for reliable tracking. 6 6 */ 7 7 export function useFocusWithin(containerRef: RefObject<HTMLElement | null>): boolean { 8 + // ast-grep-ignore: no-usestate 8 9 const [hasFocus, setHasFocus] = useState(false); 9 10 10 11 useEffect(() => {
+1 -2
apps/desktop/src/hooks/useKeyboard.ts
··· 232 232 const altMatch = modifiers.alt === undefined ? !event.altKey : event.altKey === modifiers.alt; 233 233 // When shift is not explicitly specified, don't enforce shift state — 234 234 // event.key already reflects the shifted character (e.g., "?" vs "/") 235 - const shiftMatch = 236 - modifiers.shift === undefined ? true : event.shiftKey === modifiers.shift; 235 + const shiftMatch = modifiers.shift === undefined ? true : event.shiftKey === modifiers.shift; 237 236 238 237 if (metaCtrlMatch && altMatch && shiftMatch) { 239 238 event.preventDefault();
+1
apps/desktop/src/hooks/useRevisionData.ts
··· 299 299 cleanupLoadersExcept(repoPath); 300 300 } 301 301 302 + // ast-grep-ignore: no-react-memoization 302 303 return useMemo(() => { 303 304 function getDiffLoaderInstance(): BatchLoader { 304 305 if (!diffLoaderRef.current) {
+1
apps/desktop/src/hooks/useSelectedRevision.ts
··· 15 15 revisions: Revision[], 16 16 rev: string | undefined, 17 17 ): Revision | null { 18 + // ast-grep-ignore: no-react-memoization 18 19 return useMemo(() => { 19 20 if (revisions.length === 0) return null; 20 21
+4
apps/desktop/src/lib/trace.ts
··· 44 44 45 45 const indent = parent ? " → " : ""; 46 46 const meta = metadata ? ` ${JSON.stringify(metadata)}` : ""; 47 + // biome-ignore lint/suspicious/noConsole: tracing is explicitly opt-in for performance debugging. 47 48 console.log(`%c[TRACE]%c ${indent}${name} start${meta}`, "color: #888", "color: inherit"); 48 49 49 50 return id; ··· 68 69 const indent = span.parent ? " ← " : ""; 69 70 const meta = metadata ? ` ${JSON.stringify(metadata)}` : ""; 70 71 72 + // biome-ignore lint/suspicious/noConsole: tracing is explicitly opt-in for performance debugging. 71 73 console.log( 72 74 `%c[TRACE]%c ${indent}${name} end %c${duration.toFixed(1)}ms%c${meta}`, 73 75 "color: #888", ··· 85 87 86 88 const indent = currentSpan ? " " : ""; 87 89 const meta = metadata ? ` ${JSON.stringify(metadata)}` : ""; 90 + // biome-ignore lint/suspicious/noConsole: tracing is explicitly opt-in for performance debugging. 88 91 console.log(`%c[TRACE]%c ${indent}${message}${meta}`, "color: #888", "color: #666"); 89 92 } 90 93 ··· 102 105 ): void { 103 106 if (!TRACE_ENABLED()) return; 104 107 if (actualDuration > 5) { 108 + // biome-ignore lint/suspicious/noConsole: profiler output is explicitly opt-in for performance debugging. 105 109 console.log( 106 110 `%c[PROFILER]%c ${id} ${phase}: %c${actualDuration.toFixed(1)}ms%c (base: ${baseDuration.toFixed(1)}ms)`, 107 111 "color: #888",
+1 -1
apps/desktop/src/mocks/setup.ts
··· 104 104 }, 105 105 ]; 106 106 107 - let mockLayout: MockAppLayout = { 107 + const mockLayout: MockAppLayout = { 108 108 active_project_id: mockProjects[0]?.id ?? null, 109 109 selected_change_id: null, 110 110 sidebar_width: 25,
+6 -3
apps/desktop/src/routes/settings.tsx
··· 48 48 <section> 49 49 <h2 className="text-sm font-medium text-muted-foreground mb-4">Developer</h2> 50 50 <div className="space-y-4"> 51 - <label className="flex items-center gap-3 cursor-pointer"> 51 + <div className="flex items-center gap-3"> 52 52 <Checkbox 53 53 checked={traceEnabled} 54 54 onCheckedChange={handleTraceToggle} 55 55 className="size-4" 56 + aria-labelledby="performance-tracing-label" 56 57 /> 57 58 <div className="space-y-0.5"> 58 - <Label className="cursor-pointer">Performance tracing</Label> 59 + <Label id="performance-tracing-label" className="cursor-pointer"> 60 + Performance tracing 61 + </Label> 59 62 <p className="text-sm text-muted-foreground"> 60 63 Log timing info to console for debugging 61 64 </p> 62 65 </div> 63 - </label> 66 + </div> 64 67 </div> 65 68 </section> 66 69 </div>
+12 -12
bun.lock
··· 28 28 "@fontsource-variable/jetbrains-mono": "^5.2.8", 29 29 "@pierre/diffs": "^1.0.4", 30 30 "@tailwindcss/vite": "^4.1.18", 31 - "@tanstack/db": "^0.5.15", 32 - "@tanstack/query-core": "^5.90.12", 33 - "@tanstack/query-db-collection": "^1.0.11", 34 - "@tanstack/react-db": "^0.1.59", 35 - "@tanstack/react-query": "^5.90.12", 31 + "@tanstack/db": "^0.6.5", 32 + "@tanstack/query-core": "^5.100.5", 33 + "@tanstack/query-db-collection": "^1.0.36", 34 + "@tanstack/react-db": "^0.1.83", 35 + "@tanstack/react-query": "^5.100.5", 36 36 "@tanstack/react-router": "^1.141.6", 37 37 "@tanstack/react-virtual": "^3.13.16", 38 38 "@tauri-apps/api": "^2.1.1", ··· 514 514 515 515 "@tailwindcss/vite": ["@tailwindcss/vite@4.1.18", "", { "dependencies": { "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "tailwindcss": "4.1.18" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA=="], 516 516 517 - "@tanstack/db": ["@tanstack/db@0.5.15", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@tanstack/db-ivm": "0.1.14", "@tanstack/pacer-lite": "^0.1.1" }, "peerDependencies": { "typescript": ">=4.7" } }, "sha512-S7smbPX/lUY1Hk3kVRDZVq9D1sIo5fldA08zhzMys4kgVsqMrfAG86WyluousdD2XBqutpJbtX+Ya8AtXLlL4w=="], 517 + "@tanstack/db": ["@tanstack/db@0.6.5", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@tanstack/db-ivm": "0.1.18", "@tanstack/pacer-lite": "^0.2.1" }, "peerDependencies": { "typescript": ">=4.7" } }, "sha512-gtCuAo4UtC9SR/kTMu5fVEff6qZ2R1FZi9X7MybtHKA6wve7RePifGG6qBI4OmMB+7juT5/+glNbnqZOrG0/pg=="], 518 518 519 - "@tanstack/db-ivm": ["@tanstack/db-ivm@0.1.14", "", { "dependencies": { "fractional-indexing": "^3.2.0", "sorted-btree": "^1.8.1" }, "peerDependencies": { "typescript": ">=4.7" } }, "sha512-GluhFsd/Z1E/MZTf60l9dZpKNpmdxtV4izPRnj1RK6TYrhC9LMndN+ywk1VDFBrjtBq/CTShz4CilECLwVlTGg=="], 519 + "@tanstack/db-ivm": ["@tanstack/db-ivm@0.1.18", "", { "dependencies": { "fractional-indexing": "^3.2.0", "sorted-btree": "^1.8.1" }, "peerDependencies": { "typescript": ">=4.7" } }, "sha512-+pZJiRKdoKRM5Epq9T7otD9ZJl82pRFauo7LKuJGrarjVKQ7r+QQlPe3kGdN9LEKSnuNGIWjX9OOY4M8kH4eLw=="], 520 520 521 521 "@tanstack/devtools-client": ["@tanstack/devtools-client@0.0.5", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.4.0" } }, "sha512-hsNDE3iu4frt9cC2ppn1mNRnLKo2uc1/1hXAyY9z4UYb+o40M2clFAhiFoo4HngjfGJDV3x18KVVIq7W4Un+zA=="], 522 522 ··· 528 528 529 529 "@tanstack/history": ["@tanstack/history@1.141.0", "", {}, "sha512-LS54XNyxyTs5m/pl1lkwlg7uZM3lvsv2FIIV1rsJgnfwVCnI+n4ZGZ2CcjNT13BPu/3hPP+iHmliBSscJxW5FQ=="], 530 530 531 - "@tanstack/pacer-lite": ["@tanstack/pacer-lite@0.1.1", "", {}, "sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w=="], 531 + "@tanstack/pacer-lite": ["@tanstack/pacer-lite@0.2.1", "", {}, "sha512-3PouiFjR4B6x1c969/Pl4ZIJleof1M0n6fNX8NRiC9Sqv1g06CVDlEaXUR4212ycGFyfq4q+t8Gi37Xy+z34iQ=="], 532 532 533 - "@tanstack/query-core": ["@tanstack/query-core@5.90.12", "", {}, "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg=="], 533 + "@tanstack/query-core": ["@tanstack/query-core@5.100.5", "", {}, "sha512-t20KrhKkf0HXzqQkPbJ5erhFesup68BAbwFgYmTrS7bxMF7O5MdmL8jUkik4thsG7Hg00fblz30h6yF1d5TxGg=="], 534 534 535 - "@tanstack/query-db-collection": ["@tanstack/query-db-collection@1.0.11", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@tanstack/db": "0.5.15" }, "peerDependencies": { "@tanstack/query-core": "^5.0.0", "typescript": ">=4.7" } }, "sha512-98QUZ+1xKSCx3eLPIcdrvHegj0eOPGt41fufOKlgVWkDbLqc94aO0G6zWgh79m64JFPNzQqyL/ALt0GqDhtDng=="], 535 + "@tanstack/query-db-collection": ["@tanstack/query-db-collection@1.0.36", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@tanstack/db": "0.6.5" }, "peerDependencies": { "@tanstack/query-core": "^5.0.0", "typescript": ">=4.7" } }, "sha512-WOHSZeqFOflWpMxp7ndu8x3M8GyLieWyUsS1fReG8wv6I/1MGOz1j3+XNlGoeoWolYF2hV4l324/dB9BxjAwxw=="], 536 536 537 - "@tanstack/react-db": ["@tanstack/react-db@0.1.59", "", { "dependencies": { "@tanstack/db": "0.5.15", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-MK+GUngo8VQJRQRU/1wvBGDr7GhJaJqGV8jeKduLDSGiA8v9ZPGOjPiSbqNhNOccYRlEdIusCq3UstsqrbBXSg=="], 537 + "@tanstack/react-db": ["@tanstack/react-db@0.1.83", "", { "dependencies": { "@tanstack/db": "0.6.5", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-LNV0C7OARazooT2hLTr5anXo6tbEyX2rHZQ0j9HZ/iNBI+Tx/y19o5Nd3ooyAYz5LEHJJxb8iM8ZTVB/diGnXw=="], 538 538 539 - "@tanstack/react-query": ["@tanstack/react-query@5.90.12", "", { "dependencies": { "@tanstack/query-core": "5.90.12" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg=="], 539 + "@tanstack/react-query": ["@tanstack/react-query@5.100.5", "", { "dependencies": { "@tanstack/query-core": "5.100.5" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-aNwj1mi2v2bQ9IxkyR1grLOUkv3BYWoykHy9KDyLNbjC3tsahbOHJibK+Wjtr1wRhG59/AvJhiJG5OlthaCgJA=="], 540 540 541 541 "@tanstack/react-router": ["@tanstack/react-router@1.141.6", "", { "dependencies": { "@tanstack/history": "1.141.0", "@tanstack/react-store": "^0.8.0", "@tanstack/router-core": "1.141.6", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-qWFxi2D6eGc1L03RzUuhyEOplZ7Q6q62YOl7Of9Y0q4YjwQwxRm4zxwDVtvUIoy4RLVCpqp5UoE+Nxv2PY9trg=="], 542 542