From 2da57923ded38eb0b84d6f5f13c0cb47d809805f Mon Sep 17 00:00:00 2001 From: Azhad Syed Date: Wed, 2 Sep 2026 16:23:13 -0400 Subject: [PATCH] Add a commentOnLineClick setting to keep line clicks from opening comment drafts Co-Authored-By: Claude Fable 5.1 --- README.md | 4 ++ config/defaults.json | 1 + core/App.tsx | 1 + core/__tests__/App-render.test.tsx | 1 + core/__tests__/ReviewCodeView-scroll.test.tsx | 43 +++++++++++++++++++ core/__tests__/config-defaults.test.ts | 10 +++++ core/app/components/ReviewCodeView.tsx | 9 ++-- core/config/codiff-config.schema.json | 5 +++ core/config/types.ts | 1 + core/types.ts | 1 + electron/config.cjs | 4 ++ 11 files changed, 77 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 36f21d6a..661d7b63 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,9 @@ is running so changes apply to open windows. Set `settings.showWhitespace` to `true` to show whitespace-only changes in diffs and file line counts; when it is `false`, Codiff hides those changes from the working-tree review state. +Set `settings.commentOnLineClick` to `false` to stop clicks and drags on diff lines from opening a +review comment draft. The comment button in the gutter still opens one. + ```jsonc { "$schema": "https://raw.githubusercontent.com/nkzw-tech/codiff/main/core/config/codiff-config.schema.json", @@ -141,6 +144,7 @@ counts; when it is `false`, Codiff hides those changes from the working-tree rev "claudeModel": "claude-sonnet-4-6", "codeFontFamily": "", "codeFontSize": 13, + "commentOnLineClick": true, "copyCommentsOnClose": false, "diffStyle": "split", "editorCommand": "", diff --git a/config/defaults.json b/config/defaults.json index b091814e..0a806103 100644 --- a/config/defaults.json +++ b/config/defaults.json @@ -5,6 +5,7 @@ "claudeModel": "claude-sonnet-4-6", "codeFontFamily": "", "codeFontSize": 13, + "commentOnLineClick": true, "copyCommentsOnClose": false, "diffStyle": "split", "editorCommand": "", diff --git a/core/App.tsx b/core/App.tsx index 6e2fa347..69ce7289 100644 --- a/core/App.tsx +++ b/core/App.tsx @@ -1714,6 +1714,7 @@ export default function App() { agentLabel, codeQualityFindings: state.codeQualityFindings, collapsed, + commentOnLineClick: preferences.commentOnLineClick, comments: visibleReviewComments, commitMetadata, diffLineHeight, diff --git a/core/__tests__/App-render.test.tsx b/core/__tests__/App-render.test.tsx index c1894bc8..0c259679 100644 --- a/core/__tests__/App-render.test.tsx +++ b/core/__tests__/App-render.test.tsx @@ -178,6 +178,7 @@ const createCodiffMock = (overrides: Partial = {}): Window['co claudeModel: defaultSettings.claudeModel, codeFontFamily: defaultSettings.codeFontFamily, codeFontSize: defaultSettings.codeFontSize, + commentOnLineClick: true, copyCommentsOnClose: true, diffStyle: 'split' as const, editorCommand: '', diff --git a/core/__tests__/ReviewCodeView-scroll.test.tsx b/core/__tests__/ReviewCodeView-scroll.test.tsx index bced2b18..5425b520 100644 --- a/core/__tests__/ReviewCodeView-scroll.test.tsx +++ b/core/__tests__/ReviewCodeView-scroll.test.tsx @@ -2510,3 +2510,46 @@ test('line content clicks create review comments unless text is selected', async }); expect(onCreateComment).toHaveBeenCalledTimes(3); }); + +test('commentOnLineClick=false leaves comment creation to the gutter button', async () => { + const onCreateComment = vi.fn(); + const file = createChangedFileWithPatch( + 'src/click.ts', + 'diff --git a/src/click.ts b/src/click.ts\n@@ -1 +1 @@\n-old\n+new\n', + ); + await using _view = await renderReact( + , + ); + const { item, onGutterUtilityClick, onLineClick, onLineSelectionEnd } = + getReviewCodeViewHandlers(); + const range = { end: 1, side: 'additions' as const, start: 1 }; + await act(async () => { + onLineClick( + { + annotationSide: 'additions', + event: nonInteractivePointerEvent, + lineNumber: 1, + }, + { item }, + ); + onLineSelectionEnd(range, { item }); + await new Promise((resolve) => setTimeout(resolve, 0)); + }); + expect(onCreateComment).not.toHaveBeenCalled(); + expect(codeViewMock.lastOptions?.enableLineSelection).toBe(false); + await act(async () => { + onGutterUtilityClick(range, { item }); + await new Promise((resolve) => setTimeout(resolve, 0)); + }); + expect(onCreateComment).toHaveBeenCalledTimes(1); + expect(onCreateComment).toHaveBeenLastCalledWith({ + filePath: 'src/click.ts', + lineNumber: 1, + sectionId: 'src/click.ts:unstaged', + side: 'additions', + }); +}); diff --git a/core/__tests__/config-defaults.test.ts b/core/__tests__/config-defaults.test.ts index cff379d6..74a43c17 100644 --- a/core/__tests__/config-defaults.test.ts +++ b/core/__tests__/config-defaults.test.ts @@ -88,6 +88,16 @@ test('electron config normalizes sidebar position', () => { ).toBe('left'); }); +test('electron config keeps commentOnLineClick only when it is a boolean', () => { + expect(readElectronConfig({}).settings.commentOnLineClick).toBe(true); + expect( + readElectronConfig({ settings: { commentOnLineClick: false } }).settings.commentOnLineClick, + ).toBe(false); + expect( + readElectronConfig({ settings: { commentOnLineClick: 'no' } }).settings.commentOnLineClick, + ).toBe(true); +}); + test('electron config keeps custom walkthrough prompt text only when it is a string', () => { expect( readElectronConfig({ diff --git a/core/app/components/ReviewCodeView.tsx b/core/app/components/ReviewCodeView.tsx index 337c6607..91c4f605 100644 --- a/core/app/components/ReviewCodeView.tsx +++ b/core/app/components/ReviewCodeView.tsx @@ -2499,6 +2499,7 @@ export function ReviewCodeView({ bottomInset = codeViewLayout.paddingBottom, codeQualityFindings = [], collapsed, + commentOnLineClick = true, comments, commitMetadata, diffLineHeight = DIFF_LINE_HEIGHT, @@ -2561,6 +2562,7 @@ export function ReviewCodeView({ bottomInset?: number; codeQualityFindings?: ReadonlyArray; collapsed: ReadonlySet; + commentOnLineClick?: boolean; comments: ReadonlyArray; commitMetadata: CommitMetadata | null; diffLineHeight?: number; @@ -3286,7 +3288,7 @@ export function ReviewCodeView({ diffIndicators: 'bars', diffStyle, enableGutterUtility: !isReadOnly, - enableLineSelection: !isReadOnly, + enableLineSelection: !isReadOnly && commentOnLineClick, expandUnchanged: false, expansionLineCount: diffContextExpansionLineCount, hunkSeparators: 'line-info-basic', @@ -3377,7 +3379,7 @@ export function ReviewCodeView({ return; } - if (hasActiveTextSelection()) { + if (!commentOnLineClick || hasActiveTextSelection()) { return; } @@ -3403,7 +3405,7 @@ export function ReviewCodeView({ return; } - if (!range) { + if (!range || !commentOnLineClick) { return; } @@ -3455,6 +3457,7 @@ export function ReviewCodeView({ [ bottomInset, cancelPendingEmptyCommentDeletes, + commentOnLineClick, createCommentForRange, diffStyle, isReadOnly, diff --git a/core/config/codiff-config.schema.json b/core/config/codiff-config.schema.json index ae15cb7b..402e8ab8 100644 --- a/core/config/codiff-config.schema.json +++ b/core/config/codiff-config.schema.json @@ -42,6 +42,11 @@ "default": 13, "description": "Font size in pixels used for diff and code rendering." }, + "commentOnLineClick": { + "type": "boolean", + "default": true, + "description": "Open a review comment draft when clicking or dragging across diff lines. The gutter comment button keeps working when this is false." + }, "copyCommentsOnClose": { "type": "boolean", "default": false, diff --git a/core/config/types.ts b/core/config/types.ts index 5a725979..f3f82cfc 100644 --- a/core/config/types.ts +++ b/core/config/types.ts @@ -8,6 +8,7 @@ export type CodiffSettings = { claudeModel: string; codeFontFamily: string; codeFontSize: number; + commentOnLineClick: boolean; copyCommentsOnClose: boolean; diffStyle: CodiffDiffStyle; editorCommand: string; diff --git a/core/types.ts b/core/types.ts index e5ca564a..15bf27c3 100644 --- a/core/types.ts +++ b/core/types.ts @@ -780,6 +780,7 @@ export type CodiffPreferences = { claudeModel: string; codeFontFamily: string; codeFontSize: number; + commentOnLineClick: boolean; copyCommentsOnClose: boolean; diffStyle: CodiffDiffStyle; editorCommand: string; diff --git a/electron/config.cjs b/electron/config.cjs index faf2f932..cf5579de 100644 --- a/electron/config.cjs +++ b/electron/config.cjs @@ -285,6 +285,10 @@ const mergeConfig = (raw) => { : defaults.settings.claudeModel, codeFontFamily: normalizeCodeFontFamily(rawSettings.codeFontFamily), codeFontSize: normalizeCodeFontSize(rawSettings.codeFontSize), + commentOnLineClick: + typeof rawSettings.commentOnLineClick === 'boolean' + ? rawSettings.commentOnLineClick + : defaults.settings.commentOnLineClick, copyCommentsOnClose: typeof rawSettings.copyCommentsOnClose === 'boolean' ? rawSettings.copyCommentsOnClose