@git-diff-view/react 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/cjs/index.development.js +497 -503
  2. package/dist/cjs/index.development.js.map +1 -1
  3. package/dist/cjs/index.production.js +497 -503
  4. package/dist/cjs/index.production.js.map +1 -1
  5. package/dist/css/diff-view-pure.css +111 -0
  6. package/dist/css/diff-view.css +111 -0
  7. package/dist/esm/index.mjs +499 -506
  8. package/dist/esm/index.mjs.map +1 -1
  9. package/index.d.ts +239 -7
  10. package/package.json +2 -2
  11. package/src/_com.css +99 -0
  12. package/src/components/DiffAddWidget.tsx +4 -3
  13. package/src/components/DiffContent.tsx +6 -6
  14. package/src/components/DiffExpand.tsx +0 -2
  15. package/src/components/DiffNoNewLine.tsx +0 -2
  16. package/src/components/DiffSplitContentLineNormal.tsx +3 -4
  17. package/src/components/DiffSplitContentLineWrap.tsx +9 -10
  18. package/src/components/DiffSplitExtendLineNormal.tsx +3 -3
  19. package/src/components/DiffSplitExtendLineWrap.tsx +4 -4
  20. package/src/components/DiffSplitHunkLineNormal.tsx +0 -1
  21. package/src/components/DiffSplitHunkLineWrap.tsx +0 -1
  22. package/src/components/DiffSplitView.tsx +0 -1
  23. package/src/components/DiffSplitViewNormal.tsx +13 -12
  24. package/src/components/DiffSplitViewWrap.tsx +10 -10
  25. package/src/components/DiffSplitWidgetLineNormal.tsx +2 -2
  26. package/src/components/DiffSplitWidgetLineWrap.tsx +12 -2
  27. package/src/components/DiffUnifiedContentLine.tsx +1 -2
  28. package/src/components/DiffUnifiedExtendLine.tsx +4 -5
  29. package/src/components/DiffUnifiedHunkLine.tsx +0 -1
  30. package/src/components/DiffUnifiedView.tsx +9 -9
  31. package/src/components/DiffUnifiedWidgetLine.tsx +2 -2
  32. package/src/components/DiffView.tsx +26 -20
  33. package/src/components/DiffViewContext.ts +2 -0
  34. package/src/components/DiffViewWithMultiSelect.tsx +321 -0
  35. package/src/components/DiffWidgetContext.ts +4 -2
  36. package/src/components/tools.ts +6 -5
  37. package/src/components/v2/DiffSplitContentLineNormal_v2.tsx +4 -5
  38. package/src/components/v2/DiffSplitContentLineWrap_v2.tsx +11 -12
  39. package/src/components/v2/DiffSplitExtendLineNormal_v2.tsx +3 -3
  40. package/src/components/v2/DiffSplitExtendLineWrap_v2.tsx +4 -4
  41. package/src/components/v2/DiffSplitHunkLineNormal_v2.tsx +0 -1
  42. package/src/components/v2/DiffSplitHunkLineWrap_v2.tsx +0 -1
  43. package/src/components/v2/DiffSplitViewLineNormal_v2.tsx +0 -1
  44. package/src/components/v2/DiffSplitViewLineWrap_v2.tsx +0 -1
  45. package/src/components/v2/DiffSplitViewNormal_v2.tsx +9 -8
  46. package/src/components/v2/DiffSplitViewWrap_v2.tsx +5 -5
  47. package/src/components/v2/DiffSplitView_v2.tsx +0 -1
  48. package/src/components/v2/DiffSplitWidgetLineNormal_v2.tsx +2 -3
  49. package/src/components/v2/DiffSplitWidgetLineWrap_v2.tsx +4 -5
  50. package/src/hooks/useCallbackRef.ts +13 -0
  51. package/src/hooks/useDomWidth.ts +5 -5
  52. package/src/hooks/useIsMounted.ts +1 -0
  53. package/src/hooks/useSyncHeight.ts +3 -3
  54. package/src/hooks/useTextWidth.ts +1 -1
  55. package/src/hooks/useUnmount.ts +1 -0
  56. package/src/hooks/useUpdateEffect.ts +15 -0
  57. package/src/index.ts +1 -2
  58. package/styles/diff-view-pure.css +111 -0
  59. package/styles/diff-view.css +111 -0
package/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
3
  import { Root } from 'hast';
4
- import { CSSProperties, ForwardedRef, ReactNode, RefObject } from 'react';
4
+ import { CSSProperties, ForwardedRef, JSX, ReactNode, RefObject } from 'react';
5
5
 
6
6
  declare class Cache$1<K, V> extends Map<K, V> {
7
- name: string;
7
+ name?: string;
8
8
  get maxLength(): number;
9
9
  setMaxLength(length: number): void;
10
10
  set(key: K, value: V): this;
@@ -277,6 +277,9 @@ export declare class DiffFile {
277
277
  theme: "light" | "dark";
278
278
  };
279
279
  _mergeFullBundle: (data: ReturnType<DiffFile["_getFullBundle"]>, notifyUpdate?: boolean) => void;
280
+ _getAllListener: () => ((() => void) & {
281
+ isSyncExternal?: boolean;
282
+ })[];
280
283
  _destroy: () => void;
281
284
  clear: () => void;
282
285
  }
@@ -334,6 +337,48 @@ export declare class DiffLine {
334
337
  equals(other: DiffLine): boolean;
335
338
  clone(text: string): DiffLine;
336
339
  }
340
+ /**
341
+ * Framework-agnostic multi-select manager for diff views
342
+ * Handles mouse events and selection state management
343
+ */
344
+ export declare class DiffMultiSelectManager {
345
+ constructor(container: HTMLElement, diffFile: DiffFileForMultiSelect, options?: MultiSelectOptions);
346
+ /**
347
+ * Get the current selection result with line data from DiffFile
348
+ */
349
+ getSelectionResult(): MultiSelectResult | null;
350
+ /**
351
+ * Get the current selection state
352
+ */
353
+ getState(): MultiSelectState;
354
+ /**
355
+ * Set preselected lines (e.g., from existing comments)
356
+ */
357
+ setPreselectedLines(lines: {
358
+ old?: number[];
359
+ new?: number[];
360
+ }): void;
361
+ /**
362
+ * Clear the current selection
363
+ */
364
+ clearSelection(): void;
365
+ /**
366
+ * Update options
367
+ */
368
+ updateOptions(options: Partial<MultiSelectOptions>): void;
369
+ /**
370
+ * Update the DiffFile instance
371
+ */
372
+ updateDiffFile(diffFile: DiffFileForMultiSelect): void;
373
+ /**
374
+ * Update the container element
375
+ */
376
+ updateContainer(container: HTMLElement): void;
377
+ /**
378
+ * Destroy the manager and cleanup event listeners
379
+ */
380
+ destroy(): void;
381
+ }
337
382
  /**
338
383
  * A parser for the GNU unified diff format
339
384
  *
@@ -462,6 +507,7 @@ export declare class DiffParser {
462
507
  */
463
508
  parse(text: string): IRawDiff;
464
509
  }
510
+ export declare const DEFAULT_SELECTED_CLASS = "diff-multi-select-active";
465
511
  /** How many new lines will be added to a diff hunk by default. */
466
512
  export declare const DefaultDiffExpansionStep = 40;
467
513
  export declare const _cacheMap: Cache$1<string, File$1>;
@@ -478,6 +524,7 @@ export declare const checkDiffLineIncludeChange: (diffLine?: DiffLine) => boolea
478
524
  export declare const defaultTransform: (content: string) => string;
479
525
  export declare const disableCache: () => void;
480
526
  export declare const getCurrentComposeLength: () => number;
527
+ export declare const getEnableBuildTemplate: () => boolean;
481
528
  export declare const getEnableFastDiffTemplate: () => boolean;
482
529
  export declare const getLang: (fileName: string) => string;
483
530
  export declare const getMaxLengthToIgnoreLineDiff: () => number;
@@ -497,13 +544,13 @@ export declare const getSplitLines: (diffFile: DiffFile) => DiffSplitLineItem[];
497
544
  export declare const getSyntaxDiffTemplate: ({ diffFile, diffLine, syntaxLine, operator, }: {
498
545
  diffFile: DiffFile;
499
546
  diffLine: DiffLine;
500
- syntaxLine: SyntaxLine;
547
+ syntaxLine: SyntaxLine | null;
501
548
  operator: "add" | "del";
502
549
  }) => void;
503
550
  export declare const getSyntaxDiffTemplateByFastDiff: ({ diffFile, diffLine, syntaxLine, operator, }: {
504
551
  diffFile: DiffFile;
505
552
  diffLine: DiffLine;
506
- syntaxLine: SyntaxLine;
553
+ syntaxLine: SyntaxLine | null;
507
554
  operator: "add" | "del";
508
555
  }) => void;
509
556
  export declare const getSyntaxLineTemplate: (line: SyntaxLine) => string;
@@ -523,6 +570,13 @@ export declare const highlighter: DiffHighlighter;
523
570
  * ```
524
571
  */
525
572
  export declare const isTransformEnabled: () => boolean;
573
+ /**
574
+ * CSS class names used for multi-select styling
575
+ */
576
+ export declare const multiSelectClassNames: {
577
+ readonly selected: "diff-multi-select-active";
578
+ readonly selecting: "diff-multi-selecting";
579
+ };
526
580
  export declare const parseInstance: DiffParser;
527
581
  export declare const processAST: (ast: DiffAST) => {
528
582
  syntaxFileObject: Record<number, SyntaxLine>;
@@ -553,6 +607,7 @@ export declare const processTransformForFile: (content: string) => string;
553
607
  */
554
608
  export declare const processTransformTemplateContent: (content: string) => string;
555
609
  export declare const resetDefaultComposeLength: () => void;
610
+ export declare const resetEnableBuildTemplate: () => void;
556
611
  export declare const resetEnableFastDiffTemplate: () => void;
557
612
  export declare const resetMaxLengthToIgnoreLineDiff: () => void;
558
613
  /**
@@ -564,6 +619,7 @@ export declare const resetMaxLengthToIgnoreLineDiff: () => void;
564
619
  * ```
565
620
  */
566
621
  export declare const resetTransform: () => void;
622
+ export declare const setEnableBuildTemplate: (enable: boolean) => void;
567
623
  export declare const setEnableFastDiffTemplate: (enable: boolean) => void;
568
624
  /**
569
625
  * ⚠️ **WARNING: DANGEROUS OPERATION** ⚠️
@@ -635,18 +691,81 @@ export declare enum SplitSide {
635
691
  }
636
692
  export declare function _getAST(raw: string, fileName?: string, lang?: DiffHighlighterLang, theme?: "light" | "dark"): DiffAST;
637
693
  export declare function _getAST(raw: string, fileName?: string, lang?: string, theme?: "light" | "dark"): DiffAST;
694
+ /**
695
+ * Factory function to create a multi-select manager
696
+ */
697
+ export declare function createDiffMultiSelectManager(container: HTMLElement, diffFile: DiffFileForMultiSelect, options?: MultiSelectOptions): DiffMultiSelectManager;
638
698
  export declare function diffChanges(addition: DiffLine, deletion: DiffLine): {
639
699
  addRange: DiffRange;
640
700
  delRange: DiffRange;
641
701
  };
642
702
  export declare function escapeHtml(string: unknown): string;
703
+ /**
704
+ * Convert extendData to preselected lines format
705
+ * Use this when you have existing comments/annotations on lines
706
+ */
707
+ export declare function extendDataToPreselectedLines<T>(extendData?: {
708
+ oldFile?: Record<string, {
709
+ data: T;
710
+ fromLine?: number;
711
+ }>;
712
+ newFile?: Record<string, {
713
+ data: T;
714
+ fromLine?: number;
715
+ }>;
716
+ }): {
717
+ old: number[] | undefined;
718
+ new: number[] | undefined;
719
+ };
643
720
  export declare function getFile(raw: string, lang: DiffHighlighterLang, theme: "light" | "dark", fileName?: string, uuid?: string): File$1;
644
721
  export declare function getFile(raw: string, lang: string, theme: "light" | "dark", fileName?: string, uuid?: string): File$1;
722
+ /**
723
+ * Get line number from a split mode line number cell element
724
+ */
725
+ export declare function getLineNumberFromElement_Split(el: HTMLElement | null): number | null;
726
+ /**
727
+ * Get line numbers from a unified mode element
728
+ */
729
+ export declare function getLineNumbersFromElement_Unified(el: HTMLElement | null): {
730
+ old?: number;
731
+ new?: number;
732
+ } | null;
733
+ /**
734
+ * Find the number holder element from a target element in split mode
735
+ */
736
+ export declare function getNumberHolderElement_Split(el: HTMLElement | null, inMouseDown?: boolean): HTMLElement | null;
737
+ /**
738
+ * Get selected lines data from DiffFile for split mode
739
+ */
740
+ export declare function getSelectedLinesFromDiffFile_Split(diffFile: DiffFileForMultiSelect, range: LineRange): SelectedLine[];
741
+ /**
742
+ * Get selected lines data from DiffFile for unified mode
743
+ */
744
+ export declare function getSelectedLinesFromDiffFile_Unified(diffFile: DiffFileForMultiSelect, range: LineRange): SelectedLine[];
745
+ /**
746
+ * Get side (old/new) from a split mode element
747
+ */
748
+ export declare function getSideFromElement_Split(el: HTMLElement | null): MultiSelectSide | null;
749
+ /**
750
+ * Ensure start <= end in a range
751
+ */
752
+ export declare function normalizeRange<T extends {
753
+ startLineNumber: number;
754
+ endLineNumber: number;
755
+ }>(range: T): T;
645
756
  /** Get the changed ranges in the strings, relative to each other. */
646
757
  export declare function relativeChanges(addition: DiffLine, deletion: DiffLine): {
647
758
  addRange: IRange;
648
759
  delRange: IRange;
649
760
  };
761
+ /**
762
+ * Update visual selection state for split mode
763
+ */
764
+ export declare function updateSelectionVisual_Split(container: HTMLElement | null, selectedRange: LineRange | null, diffFile: DiffFile | null, preselectedLines?: PreselectedLineType, className?: string): void;
765
+ /**
766
+ * Update visual selection state for unified mode
767
+ */
768
+ export declare function updateSelectionVisual_Unified(container: HTMLElement | null, selectedRange: LineRange | null, diffFile: DiffFile | null, preselectedLines?: PreselectedLineType, className?: string): void;
650
769
  export declare let composeLen: number;
651
770
  export interface DiffHunkItem extends DiffLineItem {
652
771
  isFirst: boolean;
@@ -714,6 +833,63 @@ export interface IRawDiff {
714
833
  /** Whether or not the diff has invisible bidi characters */
715
834
  readonly hasHiddenBidiChars: boolean;
716
835
  }
836
+ export interface LineRange {
837
+ side: MultiSelectSide;
838
+ startLineNumber: number;
839
+ endLineNumber: number;
840
+ }
841
+ export interface MultiSelectOptions {
842
+ /**
843
+ * Enable multi-select feature
844
+ * @default true
845
+ */
846
+ enabled?: boolean;
847
+ /**
848
+ * Callback when selection changes (for visual updates)
849
+ */
850
+ onSelectionChange?: (range: LineRange | null, state: MultiSelectState) => void;
851
+ /**
852
+ * Callback when selection is complete (mouseup)
853
+ * Provides the selected range and line data from DiffFile
854
+ */
855
+ onSelectionComplete?: (result: MultiSelectResult | null) => void;
856
+ /**
857
+ * Custom function to scope selection to one hunk
858
+ * Return the scoped range or null to cancel selection
859
+ */
860
+ scopeToHunk?: (range: LineRange) => LineRange | null;
861
+ /**
862
+ * CSS class to add to selected cells
863
+ * @default "diff-multi-select-active"
864
+ */
865
+ selectedClassName?: string;
866
+ /**
867
+ * Whether it's unified mode
868
+ * @default false
869
+ */
870
+ isUnifiedMode?: boolean;
871
+ }
872
+ export interface MultiSelectResult {
873
+ range: LineRange;
874
+ lines: SelectedLine[];
875
+ }
876
+ export interface MultiSelectState {
877
+ isSelecting: boolean;
878
+ startInfo: {
879
+ lineNumber: number;
880
+ side: MultiSelectSide;
881
+ } | null;
882
+ currentRange: LineRange | null;
883
+ }
884
+ export interface SelectedLine {
885
+ index: number;
886
+ lineNumber: number;
887
+ value?: string;
888
+ isHide?: boolean;
889
+ isDelete?: boolean;
890
+ isAdd?: boolean;
891
+ isContext?: boolean;
892
+ }
717
893
  export interface SplitLineItem {
718
894
  lineNumber?: number;
719
895
  value?: string;
@@ -730,6 +906,10 @@ export interface UnifiedLineItem {
730
906
  _isHidden?: boolean;
731
907
  }
732
908
  export type DiffAST = Root;
909
+ /**
910
+ * Type alias for DiffFile used by multi-select
911
+ */
912
+ export type DiffFileForMultiSelect = DiffFile;
733
913
  export type DiffFileHighlighter = Omit<DiffHighlighter, "getHighlighterEngine">;
734
914
  export type DiffHighlighter = {
735
915
  name: string;
@@ -790,6 +970,11 @@ export type HunkLineInfo = {
790
970
  _endHiddenIndex: number;
791
971
  _plainText: string;
792
972
  };
973
+ export type MultiSelectSide = "old" | "new";
974
+ export type PreselectedLineType = {
975
+ old?: number[];
976
+ new?: number[];
977
+ };
793
978
  export type SyntaxLine = {
794
979
  value: string;
795
980
  lineNumber: number;
@@ -873,7 +1058,7 @@ declare const createDiffConfigStore: (props: DiffViewProps<any> & {
873
1058
  };
874
1059
  };
875
1060
  }>;
876
- setExtendData: (_extendData: DiffViewProps<any>["extendData"]) => void;
1061
+ setExtendData: (__extendData: DiffViewProps<any>["extendData"]) => void;
877
1062
  renderWidgetLine: import("reactivity-store").Ref<({ diffFile, side, lineNumber, onClose, }: {
878
1063
  lineNumber: number;
879
1064
  side: SplitSide;
@@ -1023,16 +1208,63 @@ export type DiffViewProps_2<T> = Omit<DiffViewProps<T>, "data"> & {
1023
1208
  };
1024
1209
  declare function ReactDiffView<T>(props: DiffViewProps_1<T> & {
1025
1210
  ref?: ForwardedRef<{
1026
- getDiffFileInstance: () => DiffFile;
1211
+ getDiffFileInstance: () => DiffFile | null;
1027
1212
  }>;
1028
1213
  }): JSX.Element;
1029
1214
  declare function ReactDiffView<T>(props: DiffViewProps_2<T> & {
1030
1215
  ref?: ForwardedRef<{
1031
- getDiffFileInstance: () => DiffFile;
1216
+ getDiffFileInstance: () => DiffFile | null;
1032
1217
  }>;
1033
1218
  }): JSX.Element;
1034
1219
  export declare const DiffView: typeof ReactDiffView;
1035
1220
  export declare const version: string;
1221
+ export interface DiffViewWithMultiSelectProps<T = unknown> extends Omit<DiffViewProps<T>, "renderWidgetLine" | "onAddWidgetClick"> {
1222
+ /**
1223
+ * Enable multi-select feature
1224
+ * @default true
1225
+ */
1226
+ enableMultiSelect?: boolean;
1227
+ /**
1228
+ * Callback when multi-line selection is complete
1229
+ * Use this to open a comment dialog or handle the selection
1230
+ */
1231
+ onMultiSelectComplete?: (result: MultiSelectResult) => void;
1232
+ /**
1233
+ * Callback when selection changes (during drag)
1234
+ */
1235
+ onMultiSelectChange?: (range: LineRange | null, state: MultiSelectState) => void;
1236
+ /**
1237
+ * Custom function to scope selection to one hunk
1238
+ * Return the scoped range or null to cancel selection
1239
+ */
1240
+ scopeMultiSelectToHunk?: (range: LineRange) => LineRange | null;
1241
+ onAddWidgetClick?: (props: {
1242
+ lineNumber: number;
1243
+ fromLineNumber?: number;
1244
+ side: SplitSide;
1245
+ }) => void;
1246
+ renderWidgetLine?: (props: {
1247
+ lineNumber: number;
1248
+ fromLineNumber: number;
1249
+ side: SplitSide;
1250
+ diffFile: DiffFile;
1251
+ onClose: () => void;
1252
+ }) => ReactNode;
1253
+ }
1254
+ export interface DiffViewWithMultiSelectRef {
1255
+ getDiffFileInstance: () => DiffFile | null;
1256
+ getSelectionResult: () => MultiSelectResult | null;
1257
+ getSelectionState: () => MultiSelectState;
1258
+ clearSelection: () => void;
1259
+ setPreselectedLines: (lines: {
1260
+ old: number[];
1261
+ new: number[];
1262
+ }) => void;
1263
+ }
1264
+ declare function ReactDiffView$1<T>(_props: DiffViewWithMultiSelectProps<T> & {
1265
+ ref?: ForwardedRef<DiffViewWithMultiSelectRef>;
1266
+ }): import("react/jsx-runtime").JSX.Element;
1267
+ export declare const DiffViewWithMultiSelect: typeof ReactDiffView$1;
1036
1268
 
1037
1269
  export {
1038
1270
  File$1 as File,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "@git-diff-view/react",
4
4
  "author": "MrWangJustToDo",
5
5
  "license": "MIT",
6
- "version": "0.1.3",
6
+ "version": "0.1.5",
7
7
  "main": "index.js",
8
8
  "types": "index.d.ts",
9
9
  "files": [
@@ -61,7 +61,7 @@
61
61
  "react diff component"
62
62
  ],
63
63
  "dependencies": {
64
- "@git-diff-view/core": "^0.1.3",
64
+ "@git-diff-view/core": "^0.1.5",
65
65
  "@types/hast": "^3.0.0",
66
66
  "fast-diff": "^1.3.0",
67
67
  "highlight.js": "^11.11.0",
package/src/_com.css CHANGED
@@ -167,6 +167,11 @@ td {
167
167
  display: block;
168
168
  }
169
169
 
170
+ .diff-line-extend-wrapper,
171
+ .diff-line-widget-wrapper {
172
+ display: flow-root;
173
+ }
174
+
170
175
  .diff-line-extend-wrapper * {
171
176
  color: initial;
172
177
  }
@@ -174,3 +179,97 @@ td {
174
179
  .diff-line-widget-wrapper * {
175
180
  color: initial;
176
181
  }
182
+
183
+ /* Multi-select styles for line range selection */
184
+ .diff-multi-select-active.diff-line-new-num,
185
+ .diff-multi-select-active.diff-line-old-num,
186
+ .diff-multi-select-active.diff-line-num {
187
+ z-index: 2;
188
+ }
189
+
190
+ .diff-multi-select-active.diff-line-content,
191
+ .diff-multi-select-active.diff-line-new-content,
192
+ .diff-multi-select-active.diff-line-old-content {
193
+ position: relative;
194
+ }
195
+
196
+ .diff-multi-select-active.diff-line-new-num::after,
197
+ .diff-multi-select-active.diff-line-old-num::after,
198
+ .diff-multi-select-active.diff-line-num::after {
199
+ content: "";
200
+ position: absolute;
201
+ z-index: 1;
202
+ inset: 0;
203
+ opacity: 0.15;
204
+ background-color: var(--diff-multi-select-bg, #f0c000);
205
+ pointer-events: none;
206
+ }
207
+
208
+ .diff-multi-select-active.diff-line-new-num::before,
209
+ .diff-multi-select-active.diff-line-old-num::before,
210
+ .diff-multi-select-active.diff-line-num::before {
211
+ content: "";
212
+ z-index: 2;
213
+ position: absolute;
214
+ top: 0;
215
+ bottom: 0;
216
+ right: -2px;
217
+ width: 4px;
218
+ background-color: var(--diff-multi-select-border, #2588fa);
219
+ }
220
+
221
+ .diff-multi-select-active.diff-line-new-content::after,
222
+ .diff-multi-select-active.diff-line-old-content::after,
223
+ .diff-multi-select-active.diff-line-content::after {
224
+ content: "";
225
+ position: absolute;
226
+ z-index: 1;
227
+ inset: 0;
228
+ opacity: 0.15;
229
+ background-color: var(--diff-multi-select-bg, #f0c000);
230
+ pointer-events: none;
231
+ }
232
+
233
+ /* Multi-select: ensure proper positioning for line number cells */
234
+ .diff-multi-selecting .diff-line-old-num,
235
+ .diff-multi-selecting .diff-line-new-num,
236
+ .diff-multi-selecting .diff-line-num {
237
+ user-select: none;
238
+ }
239
+
240
+ .diff-multi-selecting .diff-line-old-content,
241
+ .diff-multi-selecting .diff-line-new-content,
242
+ .diff-multi-selecting .diff-line-content {
243
+ user-select: none;
244
+ }
245
+
246
+ /* Prevent text selection during multi-line selection */
247
+ .diff-multi-selecting {
248
+ user-select: none;
249
+ }
250
+
251
+ .diff-multi-selecting * {
252
+ user-select: none;
253
+ }
254
+
255
+ /* Hide addWidget button during active dragging selection */
256
+ .diff-multi-selecting .diff-add-widget-wrapper {
257
+ display: none;
258
+ }
259
+
260
+ /* Ensure addWidget button is above selection overlay */
261
+ .diff-multiselect-wrapper .diff-add-widget-wrapper {
262
+ z-index: 10 !important;
263
+ }
264
+
265
+ /* Line number span should not interfere with click events */
266
+ .diff-line-new-num span[data-line-num],
267
+ .diff-line-old-num span[data-line-num] {
268
+ pointer-events: none;
269
+ }
270
+
271
+ .diff-multiselect-wrapper .diff-line-old-num,
272
+ .diff-multiselect-wrapper .diff-line-new-num,
273
+ .diff-multiselect-wrapper .diff-line-num {
274
+ cursor: pointer;
275
+ }
@@ -1,5 +1,4 @@
1
1
  import { addWidgetBGName, addWidgetColorName, diffFontSizeName } from "@git-diff-view/utils";
2
- import * as React from "react";
3
2
 
4
3
  import { SplitSide } from "./DiffView";
5
4
 
@@ -39,7 +38,8 @@ export const DiffSplitAddWidget = ({
39
38
  color: `var(${addWidgetColorName})`,
40
39
  backgroundColor: `var(${addWidgetBGName})`,
41
40
  }}
42
- onClick={() => {
41
+ onMouseDown={(e) => {
42
+ e.stopPropagation();
43
43
  onOpenAddWidget(lineNumber, side);
44
44
  onWidgetClick?.(lineNumber, side);
45
45
  }}
@@ -79,7 +79,8 @@ export const DiffUnifiedAddWidget = ({
79
79
  color: `var(${addWidgetColorName})`,
80
80
  backgroundColor: `var(${addWidgetBGName})`,
81
81
  }}
82
- onClick={() => {
82
+ onMouseDown={(e) => {
83
+ e.stopPropagation();
83
84
  onOpenAddWidget(lineNumber, side);
84
85
  onWidgetClick?.(lineNumber, side);
85
86
  }}
@@ -1,4 +1,3 @@
1
- /* eslint-disable max-lines */
2
1
  import {
3
2
  DiffLineType,
4
3
  getSyntaxDiffTemplate,
@@ -7,7 +6,6 @@ import {
7
6
  getPlainLineTemplate,
8
7
  } from "@git-diff-view/core";
9
8
  import { memoFunc, diffFontSizeName, NewLineSymbol } from "@git-diff-view/utils";
10
- import * as React from "react";
11
9
 
12
10
  import { DiffNoNewLine } from "./DiffNoNewLine";
13
11
 
@@ -30,7 +28,7 @@ const formatStringToCamelCase = (str: string) => {
30
28
 
31
29
  export const getStyleObjectFromString = memoFunc((str: string) => {
32
30
  if (!str) return temp;
33
- const style = {};
31
+ const style: Record<string, string> = {};
34
32
  str.split(";").forEach((el) => {
35
33
  const [property, value] = el.split(":");
36
34
  if (!property) return;
@@ -60,7 +58,7 @@ const DiffString = ({
60
58
  const isNewLineSymbolChanged = changes.newLineSymbol;
61
59
 
62
60
  if (!diffLine?.plainTemplate && typeof getPlainDiffTemplate === "function") {
63
- getPlainDiffTemplate({ diffLine, rawLine, operator });
61
+ getPlainDiffTemplate({ diffLine: diffLine!, rawLine, operator: operator! });
64
62
  }
65
63
 
66
64
  if (diffLine?.plainTemplate) {
@@ -85,6 +83,7 @@ const DiffString = ({
85
83
  }
86
84
 
87
85
  if (plainLine && !plainLine?.template) {
86
+ // eslint-disable-next-line react-hooks/immutability
88
87
  plainLine.template = getPlainLineTemplate(plainLine.value);
89
88
  }
90
89
 
@@ -124,7 +123,7 @@ const DiffSyntax = ({
124
123
  const isNewLineSymbolChanged = changes.newLineSymbol;
125
124
 
126
125
  if (!diffLine?.syntaxTemplate && typeof getSyntaxDiffTemplate === "function") {
127
- getSyntaxDiffTemplate({ diffFile, diffLine, syntaxLine, operator });
126
+ getSyntaxDiffTemplate({ diffFile, diffLine: diffLine!, syntaxLine, operator: operator! });
128
127
  }
129
128
 
130
129
  if (diffLine?.syntaxTemplate) {
@@ -149,6 +148,7 @@ const DiffSyntax = ({
149
148
  }
150
149
 
151
150
  if (!syntaxLine.template) {
151
+ // eslint-disable-next-line react-hooks/immutability
152
152
  syntaxLine.template = getSyntaxLineTemplate(syntaxLine);
153
153
  }
154
154
 
@@ -198,7 +198,7 @@ export const DiffContent = ({
198
198
 
199
199
  const isDelete = diffLine?.type === DiffLineType.Delete;
200
200
 
201
- const isMaxLineLengthToIgnoreSyntax = syntaxLine?.nodeList?.length > 150;
201
+ const isMaxLineLengthToIgnoreSyntax = syntaxLine && syntaxLine?.nodeList?.length > 150;
202
202
 
203
203
  return (
204
204
  <div
@@ -1,5 +1,3 @@
1
- import * as React from "react";
2
-
3
1
  export const ExpandDown = ({ className }: { className?: string }) => {
4
2
  return (
5
3
  <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" className={className}>
@@ -1,5 +1,3 @@
1
- import * as React from "react";
2
-
3
1
  export const DiffNoNewLine = () => {
4
2
  return (
5
3
  <svg aria-label="No newline at end of file" role="img" viewBox="0 0 16 16" version="1.1" fill="currentColor">
@@ -7,7 +7,6 @@ import {
7
7
  emptyBGName,
8
8
  expandLineNumberColorName,
9
9
  } from "@git-diff-view/utils";
10
- import * as React from "react";
11
10
 
12
11
  import { SplitSide } from "..";
13
12
 
@@ -63,9 +62,9 @@ const InternalDiffSplitLine = ({
63
62
 
64
63
  const lineNumberBG = getLineNumberBG(isAdded, isDelete, hasDiff);
65
64
 
66
- const syntaxLine = getCurrentSyntaxLine(currentLine.lineNumber);
65
+ const syntaxLine = getCurrentSyntaxLine(currentLine.lineNumber ?? -1);
67
66
 
68
- const plainLine = getCurrentPlainLine(currentLine.lineNumber);
67
+ const plainLine = getCurrentPlainLine(currentLine.lineNumber ?? -1);
69
68
 
70
69
  return (
71
70
  <tr
@@ -89,7 +88,7 @@ const InternalDiffSplitLine = ({
89
88
  {hasDiff && enableAddWidget && (
90
89
  <DiffSplitAddWidget
91
90
  index={index}
92
- lineNumber={currentLine.lineNumber}
91
+ lineNumber={currentLine.lineNumber ?? -1}
93
92
  side={side}
94
93
  diffFile={diffFile}
95
94
  onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}