Conversation
…ep-alive, multi-select, findInPage) - Clone: stream git progress via tRPC observable, add cancel + UI with phase/bar/log - Diff viewer: reuse CodeEditorSearchOverlay at top-right with center-scroll on navigate - Tabs: keep file-viewer tabs mounted when inactive so scroll/search/cursor survive switching - Git sidebar: Shift/Cmd click multi-select + BulkActionBar for bulk stage/unstage - Browser: Cmd+F find-in-page via webContents.findInPage with right-top overlay - CodeEditor: diagnostic mousedown logging for drag-autoscroll investigation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughブラウザペインのページ内検索(UI・IPC・tRPC)、クローン進捗のストリーミングとキャンセル、複数選択と一括操作UI、CodeMirror の検索オーバーレイ統合、タブ永続化対象の拡張、およびエディタ向けドラッグ診断ロガーが追加されました。 Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant UI as BrowserPane
participant TRPCClient as tRPC Client
participant TRPCServer as tRPC Server
participant Manager as BrowserManager
participant Web as WebContents
User->>UI: Cmd/Ctrl+F 押下
UI->>TRPCClient: サブスクライブ (onFindRequested)
TRPCServer->>Manager: emit find-requested:paneId
Manager->>Web: preventDefault / emit find-requested
User->>UI: 検索語入力 -> UI: findInPage mutation
TRPCClient->>TRPCServer: findInPage({paneId,text,...})
TRPCServer->>Manager: RPC -> Manager.findInPage -> Web.findInPage
Web->>Manager: found-in-page イベント (matches)
Manager->>TRPCServer: emit found-in-page:paneId
TRPCServer->>TRPCClient: onFoundInPage イベント配信
TRPCClient->>UI: UI 更新 (matchCount/activeOrdinal)
sequenceDiagram
actor User
participant UI as CloneRepoTab
participant TRPCClient as tRPC Client
participant TRPCServer as tRPC Server
participant Git as simple-git
User->>UI: Clone ボタン(生成 cloneId)
UI->>TRPCClient: cloneRepo.mutate({ cloneId })
TRPCServer->>Git: start clone with progress & abort hooks
Git->>TRPCServer: progress/log callbacks
TRPCServer->>TRPCClient: cloneProgress サブスクリプションでイベント送出
TRPCClient->>UI: 進捗/ログ到着 -> UI 更新
User->>UI: Cancel -> cancelClone({ cloneId })
TRPCClient->>TRPCServer: cancelClone -> abort controller -> emit "canceled"
TRPCClient->>UI: terminal event 到着(done/error/canceled)
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 00314640c5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 7
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/main/lib/browser/browser-manager.ts (1)
55-60:⚠️ Potential issue | 🟠 Major再登録時に
findリスナーが掃除されません。
prevId !== webContentsIdの掃除対象にthis.findListenersが入っていないので、pane が新しいwebContentsに張り替わると古いbefore-input-event/found-in-pageハンドラが残ります。結果として find 系イベントが二重発火し得ます。🩹 最小修正案
for (const map of [ this.consoleListeners, this.contextMenuListeners, this.fullscreenListeners, this.popupListeners, + this.findListeners, ]) {Also applies to: 107-107
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/lib/browser/browser-manager.ts` around lines 55 - 60, 修正対象は再登録時のリスナー掃除ループで、現在 this.findListeners が含まれておらず古い before-input-event / found-in-page ハンドラが残るため、for ループの配列(this.consoleListeners, this.contextMenuListeners, this.fullscreenListeners, this.popupListeners)に this.findListeners を追加して、prevId !== webContentsId の分岐で find リスナーも正しく remove/cleanup されるようにする(該当箇所は browser-manager.ts の該当ループと同様の別箇所(行 ~107)にも同じ変更を適用すること)。
🧹 Nitpick comments (4)
apps/desktop/src/renderer/globals.css (1)
432-434:prefers-reduced-motion対応を追加してください実装は問題ありませんが、無限アニメーションなので OS の reduced motion 設定時に停止できるようにしておくとアクセシビリティが向上します。
差分案
.animate-clone-indeterminate { animation: clone-indeterminate 1.4s ease-in-out infinite; } + +@media (prefers-reduced-motion: reduce) { + .animate-clone-indeterminate { + animation: none; + } +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/globals.css` around lines 432 - 434, The infinite animation defined by the CSS rule .animate-clone-indeterminate (animation: clone-indeterminate 1.4s ease-in-out infinite) must respect users' OS reduced motion setting; add a prefers-reduced-motion: reduce media query that targets .animate-clone-indeterminate and disables the animation (e.g., set animation: none and animation-duration: 0 or similar, optionally adding !important to override) so the animation is turned off for users who prefer reduced motion.apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/MultiSelectContext/MultiSelectContext.tsx (1)
66-77: Shift+クリック時にアンカーがない場合の動作を検討
anchorPathがnullの状態で Shift+クリックすると、条件をスキップして Cmd/Ctrl の分岐もスキップし、最終的に通常クリックとして処理されます。結果としてanchorPathが設定され、次回の Shift+クリックで範囲選択が機能しますが、初回の Shift+クリックでは単一選択ではなくクリアされる動作になります。VS Code の動作と同様にするなら、アンカーがない状態での Shift+クリックはクリックした項目を選択しつつアンカーを設定する方が直感的かもしれません。
♻️ 提案: アンカーがない場合の Shift+クリック処理
const handleClick = useCallback<MultiSelectApi["handleClick"]>( (path, event) => { - if (event.shiftKey && anchorPath) { + if (event.shiftKey) { + if (!anchorPath) { + // No anchor yet: select clicked item and set as anchor + setSelectedPaths(new Set([path])); + setAnchorPath(path); + return "multi"; + } const paths = filesRef.current.map((f) => f.path); const fromIdx = paths.indexOf(anchorPath); const toIdx = paths.indexOf(path);🤖 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/MultiSelectContext/MultiSelectContext.tsx` around lines 66 - 77, When Shift+click occurs but anchorPath is null, modify the handleClick (MultiSelectApi["handleClick"]) logic so it doesn't fall through to clearing selection; instead, set the clicked item as selected and initialize the anchor. Concretely, inside the shiftKey branch detect if anchorPath is falsy and then call setSelectedPaths(new Set([path])) and setAnchorPath(path) (or the component's equivalent) and return the appropriate action string (e.g., "single" or "multi") so subsequent Shift+clicks will perform range selection; keep the existing range-selection behavior when anchorPath is present.apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/BulkActionBar/BulkActionBar.tsx (1)
33-46: アクセシビリティ: アクションボタンにaria-labelの追加を検討クリア選択ボタン(Line 82)には
aria-labelがありますが、Stage/Unstage/Discard ボタンにはtitle属性のみです。スクリーンリーダーの一貫性のためにaria-labelの追加を検討してください。♻️ 例: Stage ボタン
<button type="button" onClick={() => { onStageSelected(files); ctx.clear(); }} disabled={isActioning} title="Stage selected" + aria-label="Stage selected" className="..." >🤖 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/BulkActionBar/BulkActionBar.tsx` around lines 33 - 46, The Stage button (JSX using VscAdd and calling onStageSelected(files) then ctx.clear()) only has a title attribute but lacks an aria-label; add an aria-label attribute (e.g., aria-label="Stage selected") to this button and do the same for the Unstage and Discard action buttons in the BulkActionBar component so screen readers get consistent labels—ensure the aria-label text matches the visible title/tooltips and update the corresponding JSX for each action button.apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserFindOverlay/BrowserFindOverlay.tsx (1)
29-37:Match Caseトグルに押下状態を公開してください。見た目の active 状態はありますが、支援技術には現在状態が伝わりません。
aria-pressedと明示的なラベルを付けておくと操作性が上がります。♿ 例
<button type="button" onClick={onClick} title={title} + aria-label={title} + aria-pressed={active} className={`inline-flex h-5 w-5 items-center justify-center rounded text-[11px] font-medium leading-none transition-colors ${Also applies to: 101-107
🤖 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/BrowserPane/components/BrowserFindOverlay/BrowserFindOverlay.tsx` around lines 29 - 37, The Match Case toggle in BrowserFindOverlay renders a button with visual active styling but doesn't expose its pressed state to assistive tech; update the button (in BrowserFindOverlay) to include aria-pressed={active} and an explicit aria-label (e.g., `${title}, ${active ? "pressed" : "not pressed"}` or a localized equivalent) so screen readers receive state + purpose, and apply the same change to the other similar toggle buttons referenced around lines 101-107 (use the relevant title/label and their active state props).
🤖 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/projects/projects.ts`:
- Around line 353-359: 現在の cloneEventBus/emitCloneEvent 実装は購読開始前に発生した
log/progress/即時 error を取りこぼすため、cloneId ごとに最新の progress と terminal
event(error/complete)をバッファして購読開始時に再送するか、購読確立ハンドシェイクを導入してから clone
を開始するよう修正してください。具体的には cloneEventBus と emitCloneEvent の振る舞いを変更して(1)内部 Map で
cloneId → {lastProgress?, terminalEvent?} を保持し(2)購読用の subscribe/onceSubscribe
API を追加してサブスクライブ時にバッファを即時再送し、さらに必要なら clone を開始する cloneRepo 呼び出し側(例: CloneRepoTab
の cloneId
設定フロー)を購読確立後にトリガーするよう調整してください。同様の修正をファイル内の該当箇所(コメントで指摘の別ブロック)にも適用してください。
- Around line 1401-1453: The code leaves a partial clone directory at clonePath
on abort/failure which causes subsequent attempts to be skipped by the "existing
folder" check; update the streaming branch (the try/catch around
gitWithProgress.clone) to remove the created clonePath directory on error/abort
before emitting the error and rethrowing (use cloneId, abortController,
gitWithProgress, emitCloneEvent to locate where to add cleanup), and similarly
wrap the non-streaming branch (where getSimpleGitWithShellPath().clone is
called) in a try/catch that deletes clonePath on failure before rethrowing;
ensure the cleanup only deletes the intended directory (check existence) and
that cloneAbortControllers.delete(cloneId) remains in finally.
In
`@apps/desktop/src/renderer/routes/_authenticated/_onboarding/new-project/components/CloneRepoTab/CloneRepoTab.tsx`:
- Around line 212-217: handleCancel currently sets wasCanceledRef.current = true
and setStatus("canceled") immediately before calling cancelClone.mutate, causing
the UI to show canceled even if the cancel RPC fails; change handleCancel so it
only calls cancelClone.mutate({ cloneId }, { onSuccess: () => {
wasCanceledRef.current = true; setStatus("canceled"); }, onError: () => { /*
keep status "cloning" and surface an error if needed */ } }) and remove the
premature assignments from the top of handleCancel so the state is updated only
when cancelClone succeeds.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/CodeEditor.tsx`:
- Around line 795-806: The dragDiagnosticHandler currently enables
logDragAutoscrollDiagnostics on every left mousedown (registered via
EditorView.domEventHandlers) which causes ancestor walks and layout reads in
production; change it so logDragAutoscrollDiagnostics is only attached when a
debug flag or dev build is active (e.g., guard creation of dragDiagnosticHandler
with a runtime DEBUG or process.env.NODE_ENV !== 'production' check, or add a
boolean prop like enableDragDiagnostics) and ensure EditorState.create uses the
conditional extension only when enabled; reference the symbols
dragDiagnosticHandler, logDragAutoscrollDiagnostics,
EditorView.domEventHandlers, and EditorState.create when making the change.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserFindOverlay/BrowserFindOverlay.tsx`:
- Around line 58-63: The current useEffect in BrowserFindOverlay only
focuses/selects inputRef when isOpen changes, so pressing Cmd/Ctrl+F again while
the overlay is already open does not refocus the input; add a keydown listener
(registered when isOpen is true and removed on cleanup) inside the
BrowserFindOverlay component that listens for the Cmd/Ctrl+F key combo and calls
inputRef.current?.focus() and inputRef.current?.select(), or extract that logic
into a named handler (e.g., handleFocusFindInput) and invoke it both from the
existing useEffect and from the keydown listener to ensure repeated Cmd/Ctrl+F
always returns focus to the input.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsx`:
- Around line 467-470: Focus ハンドラで activeEditorRef.current
のみ更新しているためペイン切替時にオーバーレイの matchCount / activeMatchIndex
が更新されません。EditorView.domEventHandlers の focus ハンドラ(focusTracker の定義箇所)内で
activeEditorRef.current = view の直後に syncSearchOverlayStateRef.current?.()
を呼び出して検索オーバーレイの状態を同期するようにしてください(関数が存在するかを optional call で確認すること)。
- Around line 274-284: MergeView rebuilds leave stale search state and overlays
pointing at the old EditorView; update the teardown/create logic so that when
MergeView is destroyed or recreated you reset search-related refs/state (clear
activeEditorRef.current, set isSearchOpenRef.current = false, set
syncSearchOverlayStateRef.current = null, reset
searchMatchCount/activeSearchMatchIndex) and/or immediately reapply the current
search query into the newly created EditorView by invoking the search overlay
sync (use the existing syncSearchOverlayStateRef callback or a new
applySearchToEditor function right after MergeView creation) to ensure overlays
and F3/Mod-g target the new EditorView (affects activeEditorRef,
isSearchOpenRef, syncSearchOverlayStateRef and search state handling around
MergeView construction/destruction).
---
Outside diff comments:
In `@apps/desktop/src/main/lib/browser/browser-manager.ts`:
- Around line 55-60: 修正対象は再登録時のリスナー掃除ループで、現在 this.findListeners が含まれておらず古い
before-input-event / found-in-page ハンドラが残るため、for ループの配列(this.consoleListeners,
this.contextMenuListeners, this.fullscreenListeners, this.popupListeners)に
this.findListeners を追加して、prevId !== webContentsId の分岐で find リスナーも正しく
remove/cleanup されるようにする(該当箇所は browser-manager.ts の該当ループと同様の別箇所(行
~107)にも同じ変更を適用すること)。
---
Nitpick comments:
In `@apps/desktop/src/renderer/globals.css`:
- Around line 432-434: The infinite animation defined by the CSS rule
.animate-clone-indeterminate (animation: clone-indeterminate 1.4s ease-in-out
infinite) must respect users' OS reduced motion setting; add a
prefers-reduced-motion: reduce media query that targets
.animate-clone-indeterminate and disables the animation (e.g., set animation:
none and animation-duration: 0 or similar, optionally adding !important to
override) so the animation is turned off for users who prefer reduced motion.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserFindOverlay/BrowserFindOverlay.tsx`:
- Around line 29-37: The Match Case toggle in BrowserFindOverlay renders a
button with visual active styling but doesn't expose its pressed state to
assistive tech; update the button (in BrowserFindOverlay) to include
aria-pressed={active} and an explicit aria-label (e.g., `${title}, ${active ?
"pressed" : "not pressed"}` or a localized equivalent) so screen readers receive
state + purpose, and apply the same change to the other similar toggle buttons
referenced around lines 101-107 (use the relevant title/label and their active
state props).
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/BulkActionBar/BulkActionBar.tsx`:
- Around line 33-46: The Stage button (JSX using VscAdd and calling
onStageSelected(files) then ctx.clear()) only has a title attribute but lacks an
aria-label; add an aria-label attribute (e.g., aria-label="Stage selected") to
this button and do the same for the Unstage and Discard action buttons in the
BulkActionBar component so screen readers get consistent labels—ensure the
aria-label text matches the visible title/tooltips and update the corresponding
JSX for each action button.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/MultiSelectContext/MultiSelectContext.tsx`:
- Around line 66-77: When Shift+click occurs but anchorPath is null, modify the
handleClick (MultiSelectApi["handleClick"]) logic so it doesn't fall through to
clearing selection; instead, set the clicked item as selected and initialize the
anchor. Concretely, inside the shiftKey branch detect if anchorPath is falsy and
then call setSelectedPaths(new Set([path])) and setAnchorPath(path) (or the
component's equivalent) and return the appropriate action string (e.g., "single"
or "multi") so subsequent Shift+clicks will perform range selection; keep the
existing range-selection behavior when anchorPath is present.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a01a6471-c1fa-4668-a91e-6f6cae53adb8
📒 Files selected for processing (17)
apps/desktop/src/lib/trpc/routers/browser/browser.tsapps/desktop/src/lib/trpc/routers/projects/projects.tsapps/desktop/src/main/lib/browser/browser-manager.tsapps/desktop/src/renderer/globals.cssapps/desktop/src/renderer/routes/_authenticated/_onboarding/new-project/components/CloneRepoTab/CloneRepoTab.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/PersistentTabRenderer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/BrowserPane.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserFindOverlay/BrowserFindOverlay.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserFindOverlay/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/BulkActionBar/BulkActionBar.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/BulkActionBar/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/FileItem/FileItem.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/MultiSelectContext/MultiSelectContext.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/components/MultiSelectContext/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/ChangesView/hooks/useOrderedSections/useOrderedSections.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/CodeEditor.tsx
P1: - projects: cleanup partial clonePath on git clone abort/failure so retry is not blocked by the existing-folder guard - projects: buffer clone progress events per cloneId with monotonic seq so subscribers established after the mutation starts do not miss early events; dedupe replay via seq - CodeMirrorDiffViewer: reset search state/refs on MergeView teardown to avoid pointing at a destroyed EditorView after original/modified/language change - CodeEditor: gate drag-autoscroll diagnostic logging behind localStorage.debug:code-editor-drag flag P2: - BrowserPane: hoist Cmd+F keydown capture to the pane root so the shortcut also fires when focus is in the toolbar / URL bar - BrowserFindOverlay: expose imperative focusInput handle; re-Cmd+F now refocuses the input even while overlay is already open - CloneRepoTab: only transition to "canceled" after cancelClone mutation confirms, otherwise leave status as cloning - CodeMirrorDiffViewer: sync overlay match count when focus moves between a/b sides
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsx (2)
421-421: biome-ignore コメントの説明が実際の動作と若干異なります。コメントでは「MergeView is created once and destroyed on unmount」と記載されていますが、依存配列
[original, modified, language, viewMode]により、これらの値が変更されると MergeView は再作成されます。lint 抑制自体は正当ですが、コメントを「MergeView is recreated when content or view mode changes」などに更新すると、コードの意図がより明確になります。📝 コメントの修正案
- // biome-ignore lint/correctness/useExhaustiveDependencies: MergeView is created once and destroyed on unmount + // biome-ignore lint/correctness/useExhaustiveDependencies: Helper functions use refs to avoid stale closures; MergeView is recreated when deps change🤖 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/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsx` at line 421, Update the biome-ignore comment above the MergeView creation to accurately reflect behavior: change the message from "MergeView is created once and destroyed on unmount" to something like "MergeView is recreated when original, modified, language, or viewMode change (cleanup on unmount)" so it matches the dependency array [original, modified, language, viewMode] and the actual lifecycle of the MergeView instance in CodeMirrorDiffViewer (look for the MergeView initialization and the lint suppression comment).
321-343: 検索マッチの列挙処理は許容範囲内のパフォーマンスです。
syncSearchOverlayStateはSEARCH_MATCH_LIMIT(10,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/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsx` around lines 321 - 343, syncSearchOverlayState currently re-enumerates all matches on every selection/document change (inside syncSearchOverlayState using getActiveEditor(), getSearchQuery(), and the cursor loop up to SEARCH_MATCH_LIMIT), which can cause UI jank with many matches; debounce calls to syncSearchOverlayState (e.g., wrap invocations in a short debounce) and implement a simple cache keyed by the editor document version + query (store computed matches, match count, and active index) so you only re-enumerate when the document version or query changes; ensure you still compute active match index from cached matches using getActiveSearchMatchIndex and preserve behavior when query.valid is false.
🤖 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/projects/projects.ts`:
- Around line 1495-1521: The code currently emits and logs input.url (which can
include credentials) via emitCloneLog and emitCloneEvent; change it to parse
input.url and produce a sanitizedUrl with the userinfo removed/ redacted, use
sanitizedUrl in all calls to emitCloneLog and emitCloneEvent (and any error
messages) while still passing the original input.url only to
gitWithProgress.clone; update the catch block where cloneError and message are
emitted to use the same sanitizedUrl; apply the same sanitization and
replacement in the other similar block around the emitCloneEvent usage (the one
at the second location noted) so no renderer-bound logs include userinfo.
In
`@apps/desktop/src/renderer/routes/_authenticated/_onboarding/new-project/components/CloneRepoTab/CloneRepoTab.tsx`:
- Around line 63-68: The subscription is being disabled based on status which
can flip before final events arrive, losing terminal log entries; change the
electronTrpc.projects.cloneProgress.useSubscription calls (e.g., the one
currently using isActive = status === "cloning" and its enabled arg) to base the
subscription lifetime on cloneId presence instead (enabled: cloneId !== null) so
the subscription stays open until cloneId is cleared, and ensure you still pass
a valid param shape (use { cloneId } when cloneId exists, otherwise undefined)
for all occurrences (also update the other two useSubscription sites referenced
around the later blocks).
---
Nitpick comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsx`:
- Line 421: Update the biome-ignore comment above the MergeView creation to
accurately reflect behavior: change the message from "MergeView is created once
and destroyed on unmount" to something like "MergeView is recreated when
original, modified, language, or viewMode change (cleanup on unmount)" so it
matches the dependency array [original, modified, language, viewMode] and the
actual lifecycle of the MergeView instance in CodeMirrorDiffViewer (look for the
MergeView initialization and the lint suppression comment).
- Around line 321-343: syncSearchOverlayState currently re-enumerates all
matches on every selection/document change (inside syncSearchOverlayState using
getActiveEditor(), getSearchQuery(), and the cursor loop up to
SEARCH_MATCH_LIMIT), which can cause UI jank with many matches; debounce calls
to syncSearchOverlayState (e.g., wrap invocations in a short debounce) and
implement a simple cache keyed by the editor document version + query (store
computed matches, match count, and active index) so you only re-enumerate when
the document version or query changes; ensure you still compute active match
index from cached matches using getActiveSearchMatchIndex and preserve behavior
when query.valid is false.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f8c5d733-7553-4747-9271-68a981b479f2
📒 Files selected for processing (7)
apps/desktop/src/lib/trpc/routers/projects/projects.tsapps/desktop/src/renderer/routes/_authenticated/_onboarding/new-project/components/CloneRepoTab/CloneRepoTab.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/BrowserPane.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserFindOverlay/BrowserFindOverlay.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserFindOverlay/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/CodeEditor.tsx
✅ Files skipped from review due to trivial changes (2)
- apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserFindOverlay/index.ts
- apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/CodeEditor.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/BrowserPane.tsx
- apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/BrowserPane/components/BrowserFindOverlay/BrowserFindOverlay.tsx
- projects: redact credentials from git URLs before logging so PATs / basic auth tokens embedded in the clone URL never reach the renderer via progress logs or error messages - CloneRepoTab: base the cloneProgress subscription lifetime on cloneId presence instead of status, so terminal log entries arriving after status flips in cloneRepo/cancelClone callbacks are still delivered
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/projects/projects.ts`:
- Around line 1510-1514: emitCloneEvent({type: "done", cloneId, time:
Date.now()}) を現行の位置から移動して、プロジェクト作成とワークスペース初期化が確実に成功した直後にのみ送るようにしてください: 具体的には
getDefaultBranch の取得、DB insert(プロジェクト保存処理)、ensureProjectGitHubOwner と
ensureMainWorkspace のすべてが成功した後に emitCloneEvent
を呼び出すように順序を変更し、失敗時はエラーイベントを送るか例外を伝搬させて renderer に誤って「Clone
complete」を記録させないようにしてください(参照シンボル: emitCloneEvent, getDefaultBranch,
ensureProjectGitHubOwner, ensureMainWorkspace, cloneId)。
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2974defa-0f6b-4c7b-abc8-8d6f1620fcaf
📒 Files selected for processing (2)
apps/desktop/src/lib/trpc/routers/projects/projects.tsapps/desktop/src/renderer/routes/_authenticated/_onboarding/new-project/components/CloneRepoTab/CloneRepoTab.tsx
Previously `{type: "done"}` was emitted right after `git clone` returned,
so the renderer logged "Clone complete" even if getDefaultBranch / DB
insert / ensureProjectGitHubOwner / ensureMainWorkspace subsequently
failed. Move the terminal success event to after the project row and
workspace are fully created, and have the outer catch emit an error
event (when no terminal event has been produced yet) so post-clone
failures are still reported through the streaming channel.
- projects: use distributive Omit so CloneEventInput preserves the discriminated union shape; the previous Omit<Union, 'seq'> collapsed into the common intersection and broke emitCloneEvent callers that passed type-specific fields (message/stage/etc) - MultiSelectContext: re-order barrel exports per biome organizeImports
- CodeEditor: unconditionally log drag-autoscroll scrollableParents walk on mousedown (removed localStorage gate) so the race is visible in any session without setup - CodeEditor: add scrollSearchMatchToCenter to the overlay find handlers — the file viewer was missing center-scroll behaviour entirely (only the DiffViewer had it). Log head / scrollTop / matchCoordsY before + after the dispatch so we can see whether the effect is applied and where it lands - CodeMirrorDiffViewer: same before/after logging around the existing scrollIntoView center dispatch to investigate why navigation is not visibly centering
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/CodeEditor.tsx (1)
858-922:⚠️ Potential issue | 🟠 Major
searchMode切り替え時に CodeMirror の検索実装が再構成されていません。
search()拡張と overlay 用の keymap はEditorState.create()で固定されており、searchMode変更時に reconfigure されていません。他の機能(theme、editable、language など)は Compartment パターンで適切に再構成されていますが、search 機能はこのパターンに従っていません。結果として、native-panel⇄overlayを切り替えても UI の表示のみ更新され、CodeMirror 側のプラグインと keymap は元の状態のまま残ります。
searchCompartmentを作成して search 拡張と keymap を Compartment でラップし、useEffect で searchMode 変更時に reconfigure するか、あるいは editor 全体を作り直す必要があります。🤖 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/CodeEditor.tsx` around lines 858 - 922, The search extension and overlay-specific keymap are currently created statically inside the editor init and aren’t reconfigured when searchMode changes; create a new Compartment (e.g., searchCompartment) and move the search(...) extension plus the conditional overlay keymap entries into that compartment, then in a useEffect that depends on searchMode call editorView.dispatch({ effects: searchCompartment.reconfigure(...) }) (or editor.reconfigure if using EditorState) to swap between the overlay config and the native-panel config; ensure you reference the existing symbols search(), searchMode, ensureOverlaySearchOpen/handleOverlayFindNext/handleOverlaySearchClose, and the keymap block so the overlay keys are added/removed when reconfiguring.
♻️ Duplicate comments (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/CodeEditor.tsx (1)
831-846:⚠️ Potential issue | 🟠 Majorドラッグ診断ハンドラを常時登録しないでください。
ここは全ユーザーの左クリックごとに ancestor walk、
getComputedStyle、getBoundingClientRect()、console.logを実行します。テキスト選択のホットパスなので、調査用なら dev build か明示フラグでのみ extension を追加する形に戻した方が安全です。🤖 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/CodeEditor.tsx` around lines 831 - 846, The dragDiagnosticHandler (created via EditorView.domEventHandlers and calling logDragAutoscrollDiagnostics) is always added into the EditorState.create extensions, causing expensive ancestor walks and geometry/style reads on every left-click; only add this extension when running under a dev/debug mode or an explicit diagnostic flag. Modify the code that builds the extensions array passed to EditorState.create to conditionally push dragDiagnosticHandler (or call a helper like isDragDiagnosticsEnabled()) only when a dev build or feature-flag evaluates true (e.g., NODE_ENV === 'development' or a dedicated runtime flag), leaving production builds to omit the handler entirely.
🤖 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/CodeEditor.tsx`:
- Around line 723-756: The debug console.logs and expensive measurements inside
scrollSearchMatchToCenter are always executed on every search; guard them behind
a debug flag (e.g., isDebugSearch or a logger.debug check) so normal F3/Mod-g
searches don't call coordsAtPos/getBoundingClientRect or log. In function
scrollSearchMatchToCenter, wrap the initial coordsAtPos/getBoundingClientRect
and the two console.log blocks (the "before scrollIntoView" and the
queueMicrotask "after scrollIntoView") in a conditional that checks the debug
flag (or use an existing debug logger method) so these calculations and logs run
only when debugging is enabled. Ensure the scrollIntoView dispatch remains
unconditional.
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsx`:
- Around line 383-417: The search-navigation diagnostic logs and expensive
layout measurements inside scrollActiveSelectionToCenter (calls to
view.coordsAtPos, scroller.getBoundingClientRect(), and the two console.log
statements) must be removed or gated behind a debug flag; update the function so
these measurements and console.logs only run when a debug/dev mode is enabled
(e.g. check a DEBUG or isDev boolean) or use the existing logging facility
instead of console.log; keep the actual scrollIntoView dispatch always, but
avoid calling coordsAtPos/getBoundingClientRect and avoid logging in
production—reference scrollActiveSelectionToCenter, view.coordsAtPos,
scroller.getBoundingClientRect, and mergeViewRef to locate and guard/remove the
diagnostic code.
---
Outside diff comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/CodeEditor.tsx`:
- Around line 858-922: The search extension and overlay-specific keymap are
currently created statically inside the editor init and aren’t reconfigured when
searchMode changes; create a new Compartment (e.g., searchCompartment) and move
the search(...) extension plus the conditional overlay keymap entries into that
compartment, then in a useEffect that depends on searchMode call
editorView.dispatch({ effects: searchCompartment.reconfigure(...) }) (or
editor.reconfigure if using EditorState) to swap between the overlay config and
the native-panel config; ensure you reference the existing symbols search(),
searchMode,
ensureOverlaySearchOpen/handleOverlayFindNext/handleOverlaySearchClose, and the
keymap block so the overlay keys are added/removed when reconfiguring.
---
Duplicate comments:
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/CodeEditor.tsx`:
- Around line 831-846: The dragDiagnosticHandler (created via
EditorView.domEventHandlers and calling logDragAutoscrollDiagnostics) is always
added into the EditorState.create extensions, causing expensive ancestor walks
and geometry/style reads on every left-click; only add this extension when
running under a dev/debug mode or an explicit diagnostic flag. Modify the code
that builds the extensions array passed to EditorState.create to conditionally
push dragDiagnosticHandler (or call a helper like isDragDiagnosticsEnabled())
only when a dev build or feature-flag evaluates true (e.g., NODE_ENV ===
'development' or a dedicated runtime flag), leaving production builds to omit
the handler entirely.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ebde9dfb-a6be-4781-99f5-50ee047d3e13
📒 Files selected for processing (2)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/CodeMirrorDiffViewer/CodeMirrorDiffViewer.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/components/CodeEditor/CodeEditor.tsx
CM6's find commands dispatch `scrollIntoView: true` (y: "nearest") synchronously as part of the selection update. Dispatching a second `y: "center"` effect in the same tick causes CM to coalesce them and the nearest scroll wins — which is exactly what the diagnostic logs (matchCoordsY unchanged, small scrollTop deltas) showed for both the CodeEditor and CodeMirrorDiffViewer navigation paths. Schedule the center scroll in requestAnimationFrame so runFindNext's scroll has settled first, then dispatch `y: "center"` from the updated head position. Log scrollTop and coordsAtPos before + after in both frames so we can verify the effect applied and whether centering lands at the expected coordinate. Also widen the drag-autoscroll diagnostic: mousedown now attaches a scoped mousemove + scrollDOM scroll listener (both released on mouseup) so we can capture spurious scrolls that happen while dragging — the initial mousedown position consistently reports `nearEdgeAtStart: false` so the trigger must be in a subsequent mousemove inside the 6px edge band.
## drag-autoscroll spurious triggering
Diagnostic logs showed CM firing drag-select autoscroll at +8px/tick
(dragScrollSpeed's baseline) even while the pointer was hundreds of
pixels from any edge. Tracked the cause to the hidden search panel:
- `@codemirror/search` defaults `top: false`, so the hidden search panel
lives in the BOTTOM PanelGroup
- `PanelGroup.scrollMargin()` for a bottom group is
`min(innerHeight, scrollDOM.bottom) - panelDom.top`
- The previous `createHiddenSearchPanel` hid the container with
`display: none`, which makes `getBoundingClientRect()` return
`{top: 0, bottom: 0, ...}`
- That leaves `scrollMargin = scrollDOM.bottom - 0 ≈ scroller height`,
i.e. the drag autoscroll's "bottom edge" effectively collapses to
y=0 and every mousemove inside the viewport satisfies the edge check
Replace `display: none` with a zero-height in-flow collapse (height:0,
visibility:hidden, overflow:hidden). The panel stays at scroller.bottom
so scrollMargin resolves to ~0, and drag autoscroll only fires when
the pointer is genuinely within 6px of the scroller edge.
Same fix mirrored in CodeMirrorDiffViewer's local copy of
createHiddenSearchPanel.
## search center scroll
Follow-up `scrollIntoView({y: "center"})` dispatches remained flaky on
huge virtualized files — logs showed `delta: 43` and the match still at
clientY=-63 after the dispatch. `view.coordsAtPos(head)` returns
estimated coords for un-rendered lines, and CM's internal scroll math
uses those estimates, producing a near-noop instead of the large jump
needed to center a line tens of thousands of pixels away.
Replace the `scrollIntoView` effect with a manual `scrollDOM.scrollTop`
write computed from `view.lineBlockAt(head)`, which is backed by CM's
own doc-relative line-height cache (maintained by the measure cycle and
always accurate for `scrollTop` math). Same refactor in DiffViewer.
The root causes are now fixed (hidden search panel no longer uses display:none so PanelGroup.scrollMargin resolves correctly, and search navigation uses view.lineBlockAt + manual scrollTop to sidestep CM's virtualized-content coordsAtPos estimation). Strip the mousedown/ mousemove/scroll telemetry and before/after scrollIntoView logging — the functional fixes stay in place.
Each FileList variant re-sorts files internally — compact sorts by filename, grouped sorts by folder+name, tree builds a DFS-ordered tree with folders first. The MultiSelectProvider, however, was wrapping the list with the raw unordered `files` array, so `indexOf(anchor)` / `indexOf(clicked)` for shift-click range selection sliced the wrong positions and produced the "sparse / flickering" selections the user reported. Extract `orderFilesForViewMode` that mirrors each variant's sort (and flattens tree DFS) and thread the ordered arrays through `useOrderedSections` to the provider. FileList variants keep their own internal sort; the helper is the single source of truth for the visual order used by range selection. Tree mode approximates "all folders expanded" DFS order — collapsed folders still contribute their files to the range, which is acceptable until expand/collapse state is threaded through the context.
1. Hover / context-menu actions on a selected file apply to the whole
multi-selection.
- MultiSelectProvider now accepts onStageSelected / onUnstageSelected
/ onDiscardSelected and exposes them through the context.
- FileItem reads the context at action-invocation time: if the file
is part of a selection of 2+, it runs the bulk handler with
`ctx.selectedFiles` and clears the selection; otherwise it runs
the single-file handler. Clicking an action on a non-selected
file still targets only that file, matching VS Code.
- Context menu and hover labels reflect the bulk count
("Stage 5 files" etc.) when applicable.
- The standalone BulkActionBar component is removed — the
VS Code-native hover / context flow replaces it.
2. Shift-click without a prior anchor picks the first visible file as
the anchor so the range extends from the top of the list down to
the clicked item (VS Code fallback).
3. Range selection order is DOM-driven: FileItem carries a
`data-multi-select-path` attribute, and MultiSelectProvider wraps
children in a `display: contents` div that scopes
`querySelectorAll('[data-multi-select-path]')` to its subtree.
DOM order naturally reflects what the user sees — including tree
view's collapsed-folder filtering, which the previous static
`orderFilesForViewMode` helper couldn't handle. For virtualized
lists where the anchor has scrolled out of the DOM the resolver
falls back to the precomputed flat order so shift-click still
works across large change sets.
Adds the first batch of VS Code Git settings — `git.enableSmartCommit` and `git.smartCommitChanges` — under Settings → Git & Worktrees. When enabled and the staging area is empty, the Commit button auto- stages all unstaged changes (`git add -A`) or tracked-only changes (`git add -u`) before the commit runs. The button label flips to `Commit All (N)` and the tooltip explains which files will be staged, so the auto-stage path is visually distinct from a regular commit. No confirmation dialog is shown — the setting itself is opt-in. Changes: - schema: `enableSmartCommit`, `smartCommitChanges` columns on settings (migration 0045) - zod: `SMART_COMMIT_CHANGES_MODES = ["all", "tracked"]` + type (the VS Code `"none"` option is dropped since it is redundant with the top-level toggle) - security/git-commands: new `gitStageTracked` helper (`git add -u`) - staging router: `stageTracked` mutation - settings router: `getSmartCommit` / `setSmartCommit` - CommitInput: reads setting, relaxes `canCommit`, runs stage mutation before commit mutation in the smart path, overrides primary button label + tooltip, includes stage mutations in `isPending` - GitSettings: Switch + Select UI; settings-search entry for discovery
Adds `git.autoStash` to Settings → Git & Worktrees. When enabled, the pull and sync actions in CommitInput orchestrate: stashIncludeUntracked → pull/sync → stashPop around the network op whenever the working tree has local changes (staged, unstaged, or untracked). A clean tree skips the stash step. Failure handling favours data preservation: - If the network op fails, the stash is left intact on the stack and the user sees a `auto-stash-pull-failed` Japanese info dialog telling them to run `git stash pop` manually once they're ready. - If stash pop fails (usually a conflict), an `auto-stash-pop-conflict` Japanese dialog appears with the same guidance. The default pull/sync error handlers bail out via an `autoStashInFlight` ref so the built-in pull error dialog doesn't fire on top of the auto-stash dialogs. Also: - schema: `autoStash` boolean column on settings (migration 0046) - settings router: `getAutoStash` / `setAutoStash` - GitSettings: Switch with description - settings-search: `GIT_AUTO_STASH` entry for in-settings search - `isPending` now includes stashIncludeUntracked / stashPop so the primary button correctly disables during the orchestration The existing manual `stashAndRetry` action on the pull error dialog is kept for the auto-stash=off case.
Adds `git.branchSortOrder` + `git.pinDefaultBranch` settings under
Settings → Git & Worktrees. The base branch picker now orders
branches by committer date (newest first) or alphabetically (case
insensitive), and optionally pins the repo's default branch at the
top regardless of sort order.
Remote-only branches are included too — `host-service.git.listBranches`
reads `git branch -r --sort=...` in addition to local refs, dedupes
remote entries whose name already exists locally, and flags the rest
with `isRemote: true`. BaseBranchSelector displays them as
`origin/<name>` with a muted "remote" tag so they're distinguishable
from locals while `branch.name` stays in short-name form (the same
shape `git.listCommits` / `git.getStatus` already consume as a
baseBranch input).
Changes:
- schema: `branchSortOrder` + `pinDefaultBranch` columns on settings
(migration 0047)
- zod: `BRANCH_SORT_ORDERS = ["committerdate", "alphabetical"]`
- settings router: `getBranchSortOrder` / `setBranchSortOrder`
- host-service: `listBranches` accepts `sortOrder` + `pinDefault`,
returns local + remote-only branches each with `isRemote`; uses
`git branch --sort=-committerdate` for the committerdate case and a
JS `localeCompare({ sensitivity: "base" })` pass for alphabetical;
pin step moves the default branch to the head of the list
- Branch type: new `isRemote: boolean` field; buildBranch accepts an
`isRemote` option and skips the upstream probe for remote-tracking
entries, reading commit metadata via `origin/<name>`
- useChangesTab: reads the setting via electronTrpc and threads it
into the listBranches query key
- BaseBranchSelector: displays `origin/<name>` for remote entries and
labels them; dedup key includes the local/remote flag
- GitSettings: Select (committerdate / alphabetical) + Switch for pin
- settings-search: `GIT_BRANCH_SORT_ORDER` entry
Adds `git.postCommitCommand` to Settings → Git & Worktrees. When set to `push` or `sync`, a successful commit automatically chains into the corresponding network op. Chaining goes through the existing push / handleSync paths so all the established UX pieces stay intact: - pushMutation's onSuccess → toast + warning dialog + PR follow-ups - pushMutation's onError → retry / pullRebaseAndRetryPush dialog - handleSync routes through the auto-stash orchestrator, so `postCommitCommand: "sync"` composes correctly with `git.autoStash` (commit → stash → sync → pop) - Smart Commit's stage → commit path also triggers post-commit because the chain is wired on the commit mutation's mutate-side onSuccess (so it fires after both the smart and plain commit flows) Changes: - schema: `postCommitCommand` text column (migration 0048) - zod: `POST_COMMIT_COMMANDS = ["none", "push", "sync"]` - settings router: `getPostCommitCommand` / `setPostCommitCommand` - CommitInput: query the setting, mirror into a ref, and invoke push / handleSync from a new `runPostCommitCommand` helper inside the commit mutation's onSuccess options - GitSettings: Select (None / Push / Sync) with description - settings-search: `GIT_POST_COMMIT_COMMAND` entry
The `commit-identity-missing` dialog that PR #153 introduced told the user to run `git config` in a terminal, which meant leaving the app mid-commit. Replace that instruction with an inline form inside the existing unified GitOperationDialog so the user can set user.name / user.email and retry the commit without context switching. - `getGitAuthorEmail` helper added next to the existing `getGitAuthorName`; `settings.getGitInfo` now returns it too. - `settings.setGlobalGitUserConfig` mutation: writes `user.name` and `user.email` to the global git config via `simple-git.addConfig(..., "global")`. - `MissingGitUserConfigForm` component: two inputs + a "保存して再試行" button. On success it fires the caller-provided `onSaved` callback so the commit retry runs immediately with the new identity. - `gitErrorDialog` is now a `.tsx` so it can render the form as the dialog's `extraContent`. The old "設定後に再試行" primary button is removed — the form's own save button owns the retry flow to avoid a stale retry that would hit the same error again. - Dialog is closed before firing retry so that any subsequent failure (`commit-identity-missing` classifier hit a second time, network error, etc) can open a fresh dialog from its own onError.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7058fb7a2d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
B (修正必須) CommitInput: handleCommitAndPush / handleCommitPushAndCreatePR
Extract the Smart Commit staging branch into runCommitWithCallback so
that Commit+Push and Commit+Push+PR use the same smart-commit staging
path as the primary Commit button. Previously willSmartCommit=true with
an empty index caused nothing-to-commit errors from the variant buttons.
handleCommit is now a one-liner calling runCommitWithCallback.
C (修正必須) CommitInput: handleFetchAndPull bypassed Auto Stash
Thread Fetch & Pull through runPullOrSyncWithAutoStash("pull") so the
auto-stash orchestration runs on the pull step, matching the plain Pull
button behaviour.
D (修正推奨) browser-manager: findListeners missing from re-registration cleanup
Add this.findListeners to the cleanup loop in register() so that when a
pane is re-registered with a new webContentsId (e.g. after a page
reload) the previous before-input-event and found-in-page listeners are
torn down before new ones are added, preventing listener accumulation
and double-fire of find-requested events.
A (修正推奨) CloneRepoTab: remove `undefined as unknown as` type cast
Pass { cloneId: cloneId ?? "" } so the required input field is always
a string; the enabled:false guard ensures the subscription never fires
with an empty string.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dcf967791f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
1. host-service listBranches: filter non-origin remotes git branch -r includes refs from all configured remotes (upstream/*, fork/*, etc). The readRefs helper was stripping the "origin/" prefix from matching lines but leaving non-origin lines unchanged, so "upstream/main" survived as-is and then buildBranch assembled the invalid ref "origin/upstream/main" for metadata lookups. Add an explicit filter so only entries that start with the given prefix are kept, dropping all non-origin remote-tracking refs. 2. CommitInput: guard "tracked" smart-commit against untracked-only changes smartCommitMode "tracked" uses `git add -u`, which ignores untracked files. If the only changes were new/untracked files, the Commit button was enabled by willSmartCommit but the commit would always fail with "nothing to commit". Pass unstagedTrackedCount (status.unstaged.length, excludes untracked) from ChangesView and require at least one tracked unstaged change in "tracked" mode; "all" mode still only needs any unstagedChangeCount > 0.
Summary
デスクトップの UX 改善を 5 トピックまとめて対応。
1. Clone 進捗ストリーミング (New Project → Clone)
projects.cloneRepoをcloneIdを受け取れる形に拡張し、simpleGit({ progress, abort })で進捗イベントと中断制御を付与projects.cloneProgressとprojects.cancelClonemutation を新設CloneRepoTabに phase / progress bar / streaming log / Cancel ボタンの UI を追加。モックはclone.html参照2. Diff Viewer の検索を Files タブと統一 (#2 + #4)
CodeMirrorDiffViewerにsearch({ createPanel: createHiddenSearchPanel })+CodeEditorSearchOverlayを組み込み、右上top-1 right-1の overlay に統一runFindNext/runFindPrevious直後にEditorView.scrollIntoView(head, { y: \"center\", yMargin: 48 })を重ねて dispatch し 検索結果を画面中央にスクロール3. タブ切替で editor state がリセットされる問題
PersistentTabRendererがwebview/vscode-extension/reference-graphの 3 タイプのみ keep-alive していた →file-viewerタブは非アクティブ時に unmount されていたfile-viewerも persistent 判定に追加。これで非アクティブタブがposition: fixed; left: -9999pxで keep-alive され、scroll / search / cursor / ハイライトが全て保持される4. Git サイドバーの複数選択 stage/unstage
MultiSelectContextを新設: セクション単位で選択状態を保持、Shift+Click (range) / Cmd+Click (toggle) / 通常 click (clear) を処理BulkActionBarを選択中に表示 (Stage / Unstage / × clear)<MultiSelectProvider>でラップ。既存のstageFiles/unstageFilesmutation (バックエンド対応済み) をそのまま利用5. 内蔵ブラウザの Cmd+F (find in page)
browser-managerにfindInPage/stopFindInPageメソッドとsetupFindInPagehelper を追加。webview のbefore-input-eventで Cmd+F を検知し renderer に通知、found-in-pageイベントを購読してマッチ数/ordinal を emitbrowser.findInPage/stopFindInPagemutation、onFoundInPage/onFindRequestedsubscription を新設BrowserFindOverlay(CodeEditorSearchOverlay と同じ右上配置・Match Case トグル・prev/next/close)、BrowserPane 内で購読と keydown capture を wire6. drag autoscroll 診断ログ (#3 調査用)
CodeEditorの mousedown で CM6 のscrollableParentswalk を再現してコンソールに出力 (scroller rect / scrollHeight / clientHeight / 親チェーン / pointer との距離)scrollParents.yが意図しない祖先に解決されているかを確認するのが目的。修正自体はログ結果を踏まえて別 PRTest plan
[CodeEditor drag-diagnostic]が出ること (修正用)Summary by CodeRabbit
リリースノート
新機能
スタイル
ドキュメント