@gitkraken/gitkraken-components 13.0.0-vnext.19 → 13.0.0-vnext.26

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.
@@ -20,7 +20,7 @@ type GraphColumnProps = {
20
20
  graphZones: GraphZone[];
21
21
  hasMoreCommits: boolean;
22
22
  horizontalScrollHeight: number;
23
- isCommitListFiltered: boolean;
23
+ isFilteredSearch: boolean;
24
24
  isLoadingRows: boolean;
25
25
  markerColors: GraphMarkerColors;
26
26
  markerRowIndices: GraphMarkerRowIndices;
@@ -42,5 +42,5 @@ type GraphColumnProps = {
42
42
  translate: TranslationFn;
43
43
  verticalScrollWidth: number;
44
44
  };
45
- export declare function GraphColumn({ branchUpstreamRowIndices, rowCount, cellRenderersByIds, columnIndex, customFooterRow, enabledScrollMarkerTypes, enableResizer, getExternalIcon, getKeyForCell, graphHeight, graphWidth, graphZoneType, graphZones, hasMoreCommits, horizontalScrollHeight, isCommitListFiltered, isLoadingRows, markerColors, markerRowIndices, onResize, onResizeEnd, onResizeFromPropChange, onScroll, onScrollToRowCausedUpdate, onZoneEnter, processedRows, scrollLeft, scrollToAlignment, scrollToIndex, scrollTop, smartCellRangeRenderer, stickyTimeline, translate, verticalScrollWidth, }: GraphColumnProps): ReactElement<any> | null;
45
+ export declare function GraphColumn({ branchUpstreamRowIndices, rowCount, cellRenderersByIds, columnIndex, customFooterRow, enabledScrollMarkerTypes, enableResizer, getExternalIcon, getKeyForCell, graphHeight, graphWidth, graphZoneType, graphZones, hasMoreCommits, horizontalScrollHeight, isFilteredSearch, isLoadingRows, markerColors, markerRowIndices, onResize, onResizeEnd, onResizeFromPropChange, onScroll, onScrollToRowCausedUpdate, onZoneEnter, processedRows, scrollLeft, scrollToAlignment, scrollToIndex, scrollTop, smartCellRangeRenderer, stickyTimeline, translate, verticalScrollWidth, }: GraphColumnProps): ReactElement<any> | null;
46
46
  export {};
@@ -6,14 +6,24 @@ import type { CssVariables } from '../../domain/generic/GenericTypes';
6
6
  import type { GraphCommitDescDisplayMode, GraphMarkerType, GraphSearchMode, GraphZone, GraphZoneType, ShiftSelectMode } from '../../domain/graph/GraphConstants';
7
7
  import type { ThrottledFn } from '../../domain/graph/GraphHelpers';
8
8
  import type { AbsoluteNodeLeftByColumn, AvatarUrlByEmail, AvatarUrlRequestedByEmail, ChildrenBySha, ColumnColorByColumn, DownstreamsByUpstream, EdgeByColumn, ExcludeByType, ExcludeRefsById, ExcludeRemotesByName, FormatCommitMessage, FormatRefShorthand, GetExternalIcon, GetMissingAvatar, GetMissingRefMetadata, GraphColumnSetting, GraphColumnsSettings, GraphContexts, GraphMarkerColors, GraphMarkerRowIndices, GraphPlatform, GraphRefGroup, GraphRow, GraphZoneModeConstants, GraphZonesByType, Head, HighlightedShas, IncludeOnlyRefsById, IncludeOnlyRemotesByName, IsRefShorthandValid, IsSelectedBySha, NodeOffsetByColumn, NodeOpacityByColumn, OnClearCurrentlyHoveredGraphCommit, OnClickCommit, OnClickGraphRef, OnClickGraphRow, OnClickRef, OnCommitContextMenu, OnCommitMessageChange, OnCurrentlyHoveredGraphCommit, OnDoubleClickCommit, OnDoubleClickGraphRef, OnDoubleClickGraphRow, OnDoubleClickRef, OnEmailsMissingAvatarUrls, OnFilterColumnClick, OnFormatCommitDateTime, OnGraphColumnReOrdered, OnGraphColumnResized, OnGraphColumnsReOrdered, OnGraphMouseEnter, OnGraphMouseLeave, OnGraphRefNodeHovered, OnGraphRefNodeUnhovered, OnGraphResized, OnGraphRowHovered, OnGraphRowUnhovered, OnGraphScrollForZone, OnGraphVisibleRowsChanged, OnGraphZoneResize, OnGraphZoneResizeEnd, OnLoadSelectedGraphRow, OnPopupGraphHeaderContextMenu, OnRefBeginDrag, OnRefCanDrag, OnRefCanDrop, OnRefContextMenu, OnRefCreate, OnRefCreateCancel, OnRefCreateContextMenu, OnRefDragEnter, OnRefDragLeave, OnRefDrop, OnRefEndDrag, OnRefNodeHovered, OnRefNodeUnhovered, OnRefShorthandChange, OnRefsMissingMetadata, OnRefZoneContextMenu, OnRefZoneHovered, OnRefZoneUnhovered, OnRowContextMenu, OnScrollForZone, OnSelectGraphRows, OnSettingsClick, OnShowMoreCommits, OnToggleRefNodesShown, OnToggleRefsVisibilityClick, OnWipMessageBlur, OnWipMessageFocus, ProcessedGraphRow, ProcessedGraphRowBySha, ReadonlyGraphRow, RefMetadataById, RefMetadataRequestedById, RefMetadataType, RefsMissingMetadata, Remote, ReserverInfoBySha, RowEdges, RowStats, RowStatsConstraints, ShaByRefId, Tag, TopAndBottomVisibleRowIndex } from '../../domain/graph/GraphTypes';
9
+ import type { RowAdornmentProvider } from '../../domain/graph/rowAdornment/RowAdornmentTypes';
9
10
  import type { TranslationFn } from '../../domain/language/LanguageTypes';
10
11
  import type { CreateRefFormData, RefIconsPosition } from '../../domain/ref/RefTypes';
11
- import type { OnBlur, OnKeyDown, OnKeyUp, OnMouseDown, OnResizeParams } from '../../domain/ui/UiTypes';
12
+ import type { OnKeyDown, OnKeyUp, OnMouseDown, OnResizeParams } from '../../domain/ui/UiTypes';
12
13
  import type { WorkDirStats } from '../../domain/workdir/WorkDirTypes';
13
14
  import type { IGraphContainer } from './IGraphContainer';
14
15
  type ClearScrollToIndex = () => void;
15
16
  type OnZoneEnter = (e: React.MouseEvent<any>, zoneType: GraphZoneType, hoveredRefGroup?: GraphRefGroup, hoveredSha?: Sha) => void;
16
17
  type OnGraphContainerBlurred = (event: React.FocusEvent<any>, hoveredRefGroup?: GraphRefGroup, hoveredSha?: Sha) => void;
18
+ /**
19
+ * Options for the public selectCommits method.
20
+ */
21
+ export interface SelectCommitsOptions {
22
+ /** If true, toggle selection; if false, replace selection. Default: false */
23
+ toggle?: boolean;
24
+ /** If true, scroll to ensure the focused commit is visible. Default: false */
25
+ ensureVisible?: boolean;
26
+ }
17
27
  interface OwnProps {
18
28
  formatCommitDateTime?: OnFormatCommitDateTime;
19
29
  formatCommitMessage?: FormatCommitMessage;
@@ -69,6 +79,11 @@ export interface StateProps {
69
79
  rowsStatsLoading?: boolean;
70
80
  wipMessageEditable?: boolean;
71
81
  workDirStats?: WorkDirStats;
82
+ /**
83
+ * Provider for row adornments.
84
+ * Graph subscribes to invalidate events and calls provide/resolve as needed.
85
+ */
86
+ rowAdornmentProvider?: RowAdornmentProvider;
72
87
  }
73
88
  export interface DispatchProps {
74
89
  onDoubleClickGraphRef?: OnDoubleClickGraphRef;
@@ -219,6 +234,7 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
219
234
  missingRefsMetadataTimer?: ReturnType<typeof setTimeout>;
220
235
  pendingMissingRefsMetadata: RefsMissingMetadata;
221
236
  requestedMissingRefsMetadata: RefMetadataRequestedById;
237
+ previousRefMetadataById: RefMetadataById | null;
222
238
  branchUpstreamRowIndices: number[];
223
239
  downstreamsByUpstream: DownstreamsByUpstream;
224
240
  timelinesInterval?: ReturnType<typeof setInterval>;
@@ -251,8 +267,49 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
251
267
  onRefDragLeave: OnRefDragLeave;
252
268
  onRefDrop: OnRefDrop;
253
269
  onRefEndDrag: OnRefEndDrag;
270
+ private performRangeSelection;
254
271
  onZoneRowClick(event: React.MouseEvent<any>, graphZoneType: GraphZoneType, sha: Sha): void;
272
+ private handleToggleSelection;
255
273
  getShiftSelectRange(currentSha: Sha, shiftSelectMode: ShiftSelectMode): Sha[] | undefined;
274
+ /**
275
+ * Check if a commit can extend the current topological selection via Ctrl+Click.
276
+ *
277
+ * In topological mode, we maintain a contiguous path through the git graph.
278
+ * This method determines if adding a commit would maintain that contiguity.
279
+ *
280
+ * Graph orientation:
281
+ * - Lower row index = NEWER commits (children, at top of graph)
282
+ * - Higher row index = OLDER commits (parents, at bottom of graph)
283
+ * - `getDirectTopologicalPathBetween(A, B)` finds path from A to B following parent pointers,
284
+ * meaning B must be an ancestor (older) of A
285
+ *
286
+ * Three cases based on clicked commit's position relative to selection bounds:
287
+ *
288
+ * 1. NEWER than newest selected (clickedIndex < newestIndex):
289
+ * - Clicked commit would extend the TOP of the selection
290
+ * - Check: clicked → newest (is newest an ancestor of clicked?)
291
+ * - Example: Selection is rows 5-10, clicking row 3
292
+ * Must verify row 3 can reach row 5 by following parents
293
+ *
294
+ * 2. OLDER than oldest selected (clickedIndex > oldestIndex):
295
+ * - Clicked commit would extend the BOTTOM of the selection
296
+ * - Check: oldest → clicked (is clicked an ancestor of oldest?)
297
+ * - Example: Selection is rows 5-10, clicking row 12
298
+ * Must verify row 10 can reach row 12 by following parents
299
+ *
300
+ * 3. BETWEEN newest and oldest (newestIndex < clickedIndex < oldestIndex):
301
+ * - Clicked commit must be ON the existing path, not a parallel branch
302
+ * - Check BOTH directions:
303
+ * - newest → clicked (is clicked an ancestor of newest?)
304
+ * - clicked → oldest (is oldest an ancestor of clicked?)
305
+ * - Example: Selection is rows 5-10, clicking row 7
306
+ * Must verify row 5 → row 7 → row 10 forms a valid path
307
+ * - This prevents selecting commits on parallel branches that happen
308
+ * to merge into the same ancestor
309
+ *
310
+ * @returns true if the commit can be added while maintaining topological contiguity
311
+ */
312
+ private canExtendTopologicalSelection;
256
313
  private getTopologicalPathBetween;
257
314
  private getDirectTopologicalPathBetween;
258
315
  /**
@@ -267,12 +324,19 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
267
324
  * (excluding the explicit start and end points).
268
325
  */
269
326
  private hasOnlyTwoEndpoints;
327
+ /**
328
+ * Checks if a set of rows form a topologically contiguous set
329
+ * Rows are contiguous if all rows are connected via parent-child relationships (i.e., they form a single connected subgraph)
330
+ */
331
+ areRowsContiguous(rows: ReadonlyGraphRow[]): boolean;
270
332
  onClickRef: OnClickRef;
271
333
  onClickCommit: OnClickCommit;
272
334
  onDoubleClickCommit: OnDoubleClickCommit;
273
- onWindowKeyUp: OnKeyUp;
274
- onWindowBlur: OnBlur;
275
- onWindowKeyDown: OnKeyDown;
335
+ onWindowBlur: (e: FocusEvent) => void;
336
+ onWindowKeyUp: () => void;
337
+ onWindowKeyDown: (e: KeyboardEvent) => void;
338
+ onWindowMouseDown: () => void;
339
+ private isKeyboardNavigating;
276
340
  onKeyUp: OnKeyUp;
277
341
  onKeyDown: OnKeyDown;
278
342
  isNavigationKey(key: string): boolean;
@@ -301,9 +365,18 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
301
365
  * Deep equality check for isSelectedBySha objects to avoid unnecessary scroll-to-selection
302
366
  * when object reference changes but content is the same (e.g., after postMessage serialization)
303
367
  */
304
- private isSelectedByShaEqual;
368
+ private areSelectedByShaEqual;
305
369
  private _pendingRowsUpdate;
306
370
  private _selectionVersion;
371
+ private _cachedSmartCellRangeRenderer;
372
+ private _cachedScrollLeftForRenderer;
373
+ private _cachedForceCache;
374
+ /**
375
+ * Gets or creates a cached smartCellRangeRenderer.
376
+ * Only recreates when scrollLeft or forceCache changes.
377
+ * This prevents react-virtualized from invalidating its cache on every render.
378
+ */
379
+ private getOrCreateSmartCellRangeRenderer;
307
380
  /**
308
381
  * Gets commits rows by SHA
309
382
  *
@@ -313,24 +386,31 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
313
386
  getCommits(shas: Sha[]): ReadonlyGraphRow[];
314
387
  /**
315
388
  * Selects commits in the graph
316
- *
317
389
  * @param shas - Array of commit SHAs to select
318
- * @param includeToPrevSel - If true, toggle selection; if false, replace selection
319
- * @param isAutoOrKeyScroll - Whether this is an auto-scroll or keyboard navigation
390
+ * @param options - Selection options (toggle, ensureVisible)
320
391
  * @returns Array of selected ReadonlyGraphRow objects
321
392
  */
322
- selectCommits(shas: Sha[], includeToPrevSel: boolean, isAutoOrKeyScroll: boolean): ReadonlyGraphRow[];
393
+ selectCommits(shas: Sha[], options?: SelectCommitsOptions): ReadonlyGraphRow[];
394
+ /**
395
+ * Internal method to select commits with full control over selection behavior
396
+ * @param shas - Array of commit SHAs to select
397
+ * @param options - Selection options including toggle, ensureVisible, isKeyboardNav, isTopological, isContiguous
398
+ * @returns Array of selected ReadonlyGraphRow objects
399
+ */
400
+ private selectCommitsCore;
401
+ private predictScrollTop;
323
402
  getCommitZoneContentWidthBetweenGutters(): number;
324
403
  getExcludeRemotesByName(): ExcludeRemotesByName;
325
404
  getIncludeOnlyRemotesByName(): IncludeOnlyRemotesByName;
326
405
  clearScrollToIndex: ClearScrollToIndex;
406
+ getVisibleRows: () => ProcessedGraphRow[];
327
407
  getTopAndBottomVisibleRowIndex(): TopAndBottomVisibleRowIndex;
328
- getOffsetSha(sha: Sha, scrollDirection: 'up' | 'down' | 'none', lastScrollDirection: 'up' | 'down' | 'none' | undefined, isAutoOrKeyScroll: boolean): Sha;
408
+ getOffsetSha(sha: Sha, scrollDirection: 'up' | 'down' | 'none', lastScrollDirection: 'up' | 'down' | 'none' | undefined, isKeyboardNav: boolean): Sha;
329
409
  private _pendingScrollUpdate;
330
410
  private _lastSelectedRowIndex;
331
411
  private _lastScrollDirection;
332
412
  private _scrollToAlignmentOverride;
333
- selectSha(sha: Sha, isAutoOrKeyScroll?: boolean): void;
413
+ selectSha(sha: Sha, isKeyboardNav?: boolean): void;
334
414
  updateWidthByZone(widthToAssignForZone: number, graphZoneType: GraphZoneType, updatePreferredWidth?: boolean): void;
335
415
  updateCommitZoneContentWidthFromChange(): void;
336
416
  getCurrentWidthByZone(graphZoneType: GraphZoneType, withoutVerticalScrollWidth?: boolean): number;
@@ -383,13 +463,60 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
383
463
  getIconCallback: GetExternalIcon;
384
464
  onMissingAvatar: GetMissingAvatar;
385
465
  onMissingRefMetadata: GetMissingRefMetadata;
386
- selectNext(topological?: boolean): void;
387
- selectPrevious(topological?: boolean): void;
466
+ selectNext(shiftKey?: boolean, topological?: boolean, shiftSelectMode?: ShiftSelectMode): void;
467
+ selectPrevious(shiftKey?: boolean, topological?: boolean, shiftSelectMode?: ShiftSelectMode): void;
468
+ /**
469
+ * Navigate selection in the specified direction.
470
+ * @param direction - 'up' (towards newer/children) or 'down' (towards older/parents)
471
+ */
472
+ private selectInDirection;
473
+ /**
474
+ * Determine the next SHA to select based on direction and mode.
475
+ */
476
+ private getNextShaInDirection;
477
+ private getNextTopologicalSha;
478
+ private getPreviousTopologicalSha;
479
+ /**
480
+ * Find the next commit in the given direction that is topologically reachable from the anchor.
481
+ * Uses a hybrid approach: fast path for common cases, BFS fallback for complex graphs.
482
+ * @param preferSameColumn - If true, tries same-column navigation first (faster, used for ctrl+shift+arrow)
483
+ */
484
+ private findNextReachableFromAnchor;
485
+ /**
486
+ * Fast reachability check - determines if targetSha is reachable from startSha.
487
+ * Uses BFS with visit limit for performance.
488
+ */
489
+ private isReachableFrom;
490
+ /**
491
+ * Get the same-column child of a commit.
492
+ * Prefers children on the same column, then falls back to children where this commit is the first parent.
493
+ */
494
+ private getSameColumnChild;
495
+ /**
496
+ * Jump to the previous branching point (towards newer commits), or tip if none.
497
+ * A branching point is a commit with visible children on a different column.
498
+ */
499
+ private selectPreviousBranchingPoint;
500
+ /**
501
+ * Check if a commit is a branching point (has visible children on a different column).
502
+ */
503
+ private isBranchingPoint;
504
+ /**
505
+ * Get the same-column parent of a commit.
506
+ */
507
+ private getSameColumnParent;
508
+ /**
509
+ * Jump to the next branching point (towards older commits), or root if none.
510
+ * A branching point is a commit with visible children on a different column.
511
+ */
512
+ private selectNextBranchingPoint;
513
+ selectNextRef(): void;
514
+ selectPreviousRef(): void;
388
515
  selectHead(upstream?: boolean): void;
389
- selectFirst(shiftKey?: boolean): void;
390
- selectLast(shiftKey?: boolean): void;
391
- selectPageUp(shiftKey?: boolean): void;
392
- selectPageDown(shiftKey?: boolean): void;
516
+ selectFirst(shiftKey?: boolean, shiftSelectMode?: ShiftSelectMode): void;
517
+ selectLast(shiftKey?: boolean, shiftSelectMode?: ShiftSelectMode): void;
518
+ selectPageUp(shiftKey?: boolean, shiftSelectMode?: ShiftSelectMode): void;
519
+ selectPageDown(shiftKey?: boolean, shiftSelectMode?: ShiftSelectMode): void;
393
520
  getAvailableColumnAndUseIt(): number;
394
521
  getColumns(graphRow: GraphRow): number;
395
522
  getFilteredHeadsForGraphRow(row: GraphRow, hasIncludes: boolean): Head[];
@@ -428,7 +555,7 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
428
555
  onGraphVisibleRowsUpdated(): void;
429
556
  getCurrentPlatform(): string;
430
557
  isMacOSPlatform(): boolean;
431
- isCommitListFiltered(): boolean;
558
+ isFilteredSearch(): boolean;
432
559
  getGraphColumnSettingFromZoneType(graphZone: GraphZone): GraphColumnSetting;
433
560
  decorateWithHelmet: (container: ReactElement<"div">, cssVariables: CssVariables, nonce?: string) => React.JSX.Element;
434
561
  render(): React.JSX.Element;
@@ -33,6 +33,7 @@ export type RefNodeProps = {
33
33
  refGroupContext: GraphItemContext | null;
34
34
  refIconsPosition: RefIconsPosition;
35
35
  refMetadata: RefMetadataById | null;
36
+ fallbackRefMetadata?: RefMetadataById | null;
36
37
  sha: Sha;
37
38
  showRemoteNamesOnRefs: boolean;
38
39
  enabledRefMetadataTypes: RefMetadataType[];
@@ -26,6 +26,7 @@ type RefNodesProps = {
26
26
  refGroupsByName: RefGroup[];
27
27
  refIconsPosition: RefIconsPosition;
28
28
  refMetadata: RefMetadataById | null;
29
+ fallbackRefMetadata?: RefMetadataById | null;
29
30
  refNodeHovered: OnRefNodeHovered;
30
31
  onToggleRefNodesShown: OnToggleRefNodesShown;
31
32
  onRefBeginDrag: OnRefBeginDrag;
@@ -42,5 +43,5 @@ type RefNodesProps = {
42
43
  type: CommitType;
43
44
  useColumnHeaderIcons: boolean;
44
45
  };
45
- declare function RefNodes({ enabledRefMetadataTypes, column, columnForColoring, context, enableShowHideRefsOptions, targetRef, hasActive, hasRefs, getExternalIcon, includeOnlyRefsById, includeOnlyRemotesByName, isGhostRef, isHovered, isInUnsupportedRebase, numGraphColumns, onMissingRefMetadata, refGroupContexts, refGroupsByName, refIconsPosition, refMetadata, refNodeHovered, sha, showRemoteNamesOnRefs, showContextMenuForGroupedRef, onClickRef, onDoubleClickRef, onToggleRefNodesShown, onRefBeginDrag, onRefCanDrag, onRefCanDrop, onRefDragEnter, onRefDragLeave, onRefDrop, onRefEndDrag, translate, type, useColumnHeaderIcons, }: RefNodesProps): ReactElement<'span' | 'div'>;
46
+ declare function RefNodes({ enabledRefMetadataTypes, column, columnForColoring, context, enableShowHideRefsOptions, targetRef, hasActive, hasRefs, getExternalIcon, includeOnlyRefsById, includeOnlyRemotesByName, isGhostRef, isHovered, isInUnsupportedRebase, numGraphColumns, onMissingRefMetadata, refGroupContexts, refGroupsByName, refIconsPosition, refMetadata, fallbackRefMetadata, refNodeHovered, sha, showRemoteNamesOnRefs, showContextMenuForGroupedRef, onClickRef, onDoubleClickRef, onToggleRefNodesShown, onRefBeginDrag, onRefCanDrag, onRefCanDrop, onRefDragEnter, onRefDragLeave, onRefDrop, onRefEndDrag, translate, type, useColumnHeaderIcons, }: RefNodesProps): ReactElement<'span' | 'div'>;
46
47
  export default RefNodes;
@@ -40,6 +40,7 @@ type RefZoneProps = {
40
40
  refGroupsByName: GraphRefGroup[];
41
41
  refIconsPosition: RefIconsPosition;
42
42
  refMetadata: RefMetadataById | null;
43
+ fallbackRefMetadata?: RefMetadataById | null;
43
44
  refNodeHovered: OnRefNodeHovered;
44
45
  refNodeUnhovered: OnRefNodeUnhovered;
45
46
  refZoneHovered: OnRefZoneHovered;
@@ -0,0 +1,52 @@
1
+ import * as React from 'react';
2
+ import type { Sha } from '../../../domain/commit/CommitTypes';
3
+ import type { ProcessedGraphRow } from '../../../domain/graph/GraphTypes';
4
+ import type { RowAdornmentProvider } from '../../../domain/graph/rowAdornment/RowAdornmentTypes';
5
+ interface RowAdornmentLayerProps {
6
+ /** The row adornment provider */
7
+ provider: RowAdornmentProvider;
8
+ /** Lazily get currently visible rows - only called when needed */
9
+ getVisibleRows: () => ProcessedGraphRow[];
10
+ /** All processed rows for index lookups */
11
+ allRows: ProcessedGraphRow[];
12
+ /** Set of selected SHAs */
13
+ selectedShas: Set<Sha>;
14
+ /** Currently hovered commit SHA */
15
+ hoveredSha?: Sha;
16
+ /** Currently focused commit SHA */
17
+ focusedSha?: Sha;
18
+ /** Whether vertical scrollbar is visible (to offset adornments) */
19
+ hasVerticalScrollbar?: boolean;
20
+ }
21
+ interface RowAdornmentLayerState {
22
+ /** Trigger re-render when cache changes */
23
+ cacheVersion: number;
24
+ }
25
+ /**
26
+ * Manages row adornment state and rendering.
27
+ * Encapsulates all adornment caching, provide/resolve flows, and overlay rendering.
28
+ */
29
+ declare class RowAdornmentLayer extends React.Component<RowAdornmentLayerProps, RowAdornmentLayerState> {
30
+ /** Cached adornment info per SHA */
31
+ private adornments;
32
+ /** Tracks pending async resolve calls to cancel stale ones */
33
+ private pendingResolves;
34
+ /** Abort controller for canceling in-flight provide calls */
35
+ private provideAbortController?;
36
+ /** Counter for generating unique resolve IDs */
37
+ private resolveIdCounter;
38
+ state: RowAdornmentLayerState;
39
+ componentDidMount(): void;
40
+ componentDidUpdate(prevProps: RowAdornmentLayerProps): void;
41
+ componentWillUnmount(): void;
42
+ private setupProvider;
43
+ private teardownProvider;
44
+ private onInvalidate;
45
+ private provideForVisibleRows;
46
+ private applyProvideResults;
47
+ private evictDistantAdornments;
48
+ private shouldShowAdornment;
49
+ private getOrResolveContent;
50
+ render(): React.ReactNode;
51
+ }
52
+ export default RowAdornmentLayer;
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+ import * as React from 'react';
3
+ import type { Sha } from '../../../domain/commit/CommitTypes';
4
+ interface RowAdornmentOverlayProps {
5
+ /** The SHA of the row this adornment is for */
6
+ sha: Sha;
7
+ /** The content to render */
8
+ content: ReactNode;
9
+ /** Whether the row is hovered */
10
+ isHovered?: boolean;
11
+ /** Whether the row is selected */
12
+ isSelected?: boolean;
13
+ }
14
+ /**
15
+ * Renders a row adornment overlay positioned at the right edge of a row.
16
+ * Uses CSS anchor positioning to attach to the row element.
17
+ */
18
+ declare function RowAdornmentOverlay({ sha, content, isHovered, isSelected, }: RowAdornmentOverlayProps): React.ReactElement | null;
19
+ export default RowAdornmentOverlay;
@@ -17,6 +17,7 @@ interface StateProps {
17
17
  hoveredRefZoneSha?: Sha;
18
18
  refIconsPosition: RefIconsPosition;
19
19
  refMetadataById?: RefMetadataById | null;
20
+ fallbackRefMetadataById?: RefMetadataById | null;
20
21
  shouldShowRefLine: boolean;
21
22
  }
22
23
  interface DispatchProps {
@@ -15,6 +15,8 @@ export type GitRemoteUrl = GitHttpsUrl | GitSshUrl;
15
15
  export type Platform = 'darwin' | 'linux' | 'win32';
16
16
  export declare enum NamedKeys {
17
17
  arrowDownKey = "ArrowDown",
18
+ arrowLeftKey = "ArrowLeft",
19
+ arrowRightKey = "ArrowRight",
18
20
  arrowUpKey = "ArrowUp",
19
21
  controlKey = "Control",
20
22
  endKey = "End",
@@ -17,7 +17,7 @@ export declare function hasScrolledToBottom(htmlElement: HTMLElement): boolean;
17
17
  export declare function hasScrolledToRight(htmlElement: HTMLElement): boolean;
18
18
  export declare function parseContext(context?: GraphItemContext | null): string | null;
19
19
  export declare function getRowStatsConstraints(stats: number[]): RowStatsConstraints;
20
- export declare function getOrAskForRefMetadata(ref: GraphRef, refMetadata: RefMetadataById | undefined, onMissingRefMetadata: GetMissingRefMetadata, refMetadataType?: RefMetadataType): RefMetadata | BaseMetadata | BaseMetadata[] | null | undefined;
20
+ export declare function getOrAskForRefMetadata(ref: GraphRef, refMetadata: RefMetadataById | undefined, onMissingRefMetadata: GetMissingRefMetadata, refMetadataType?: RefMetadataType, fallbackRefMetadata?: RefMetadataById | null): RefMetadata | BaseMetadata | BaseMetadata[] | null | undefined;
21
21
  export declare function getTimelineLabelAndValue(diffMs: number): {
22
22
  label: string;
23
23
  value: number;
@@ -317,6 +317,7 @@ type ReadonlyDeep<T> = T extends Record<string, unknown> ? {
317
317
  readonly [K in keyof T]: ReadonlyDeep<T[K]>;
318
318
  } : T extends Array<infer U> ? ReadonlyArray<ReadonlyDeep<U>> : T;
319
319
  export interface ReadonlyGraphRow extends ReadonlyDeep<GraphRow> {
320
+ rowIndex?: number;
320
321
  readonly hasRefs?: boolean;
321
322
  /** Whether the row is hidden from the graph (filtered out by type or other filters). Output only */
322
323
  readonly hidden: boolean;
@@ -475,7 +476,13 @@ export type OnDoubleClickGraphRef = (event: React.MouseEvent<any>, refGroup: Gra
475
476
  export type OnRowContextMenu = (event: React.MouseEvent<any>, graphZoneType: GraphZoneType, graphRow: GraphRow) => void;
476
477
  export type OnRefContextMenu = (event: React.MouseEvent<any>, refGroup: GraphRefGroup, graphRow: GraphRow) => void;
477
478
  export type OnToggleRefsVisibilityClick = (event: React.MouseEvent<any>, refs: GraphRefOptData[], visible: boolean, graphRow?: GraphRow) => void;
478
- export type OnSelectGraphRows = (selectedRows: ReadonlyGraphRow[]) => void;
479
+ export interface GraphSelectionState {
480
+ /** Whether the selected rows form a topologically contiguous selection (connected via parent-child relationships) */
481
+ isContiguous: boolean;
482
+ /** Whether the selection was made topologically (via shift+click following graph topology) */
483
+ isTopological: boolean;
484
+ }
485
+ export type OnSelectGraphRows = (rows: ReadonlyGraphRow[], focusedRow: ReadonlyGraphRow | undefined, state: GraphSelectionState) => void;
479
486
  export type OnGraphRowHovered = (event: React.MouseEvent<any>, graphZoneType: GraphZoneType, graphRow: GraphRow) => void;
480
487
  export type OnGraphRowUnhovered = (event: React.MouseEvent<any>, graphZoneType: GraphZoneType, graphRow: GraphRow) => void;
481
488
  export type OnGraphRefNodeHovered = (event: React.MouseEvent<any>, refGroup: GraphRefGroup, graphRow: GraphRow) => void;
@@ -0,0 +1,97 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { Sha } from '../../commit/CommitTypes';
3
+ import type { ReadonlyGraphRow } from '../GraphTypes';
4
+ /**
5
+ * When the adornment should be visible:
6
+ * - `true` = always visible
7
+ * - Array of triggers = visible when ANY of the specified triggers are active
8
+ */
9
+ export type AdornmentVisibility = true | readonly ('hover' | 'focus' | 'selected')[];
10
+ /**
11
+ * Adornment configuration returned from provideAdornments for each row.
12
+ * @template TContext - Type of context data passed from provide to resolve
13
+ */
14
+ export interface RowAdornment<TContext = unknown> {
15
+ /** When the adornment is visible */
16
+ visibility?: AdornmentVisibility;
17
+ /**
18
+ * Optional context data to pass to resolveAdornment.
19
+ * Use for passing batch-fetched data (e.g., PR status) to the resolve phase.
20
+ */
21
+ context?: TContext;
22
+ /**
23
+ * If true, resolve() is called on every render (content not cached).
24
+ * Use for adornments whose content depends on external state.
25
+ * Default: false (content is cached until invalidated)
26
+ */
27
+ dynamic?: boolean;
28
+ }
29
+ /**
30
+ * Invalidation level
31
+ */
32
+ export type InvalidationType = 'all' | 'content';
33
+ /**
34
+ * Custom event dispatched when adornments need re-evaluation
35
+ */
36
+ export declare class RowAdornmentInvalidateEvent extends CustomEvent<{
37
+ shas?: Set<Sha>;
38
+ type: InvalidationType;
39
+ }> {
40
+ static readonly type = "invalidate";
41
+ constructor(type: InvalidationType, shas?: Iterable<Sha>);
42
+ }
43
+ /**
44
+ * Provider interface - consumer implements, graph consumes
45
+ * @template TContext - Type of context data passed from provide to resolve
46
+ */
47
+ export interface RowAdornmentProvider<TContext = unknown> {
48
+ /**
49
+ * Batch evaluation: which rows should have adornments?
50
+ *
51
+ * Called for:
52
+ * - Newly visible rows (scrolled into view)
53
+ * - Rows with 'all' invalidation
54
+ *
55
+ * Can return sync or async:
56
+ * - Sync: Result applied immediately
57
+ * - Async: Rows render without adornments until resolved
58
+ * (AbortSignal can be used to cancel in-flight requests)
59
+ *
60
+ * @param rows - Rows needing evaluation
61
+ * @param signal - Abort signal (check before applying async results)
62
+ * @returns Map of SHA → adornment config. Rows not in result have no adornment.
63
+ */
64
+ provideAdornments(rows: readonly ReadonlyGraphRow[], signal: AbortSignal): Record<Sha, RowAdornment<TContext>> | Promise<Record<Sha, RowAdornment<TContext>>>;
65
+ /**
66
+ * Render content for a specific row.
67
+ *
68
+ * Called when:
69
+ * - Row becomes active (visible for 'always', hovered/focused for triggers)
70
+ * - Row has 'content' invalidation and is active
71
+ * - Row is dynamic (every render when active)
72
+ *
73
+ * Can return sync or async:
74
+ * - Sync (ReactNode): Content rendered immediately
75
+ * - Async (Promise<ReactNode>): Nothing shown until resolved
76
+ *
77
+ * @param row - The row to render adornment for
78
+ * @param context - Context data from provideAdornments (if any)
79
+ * @returns Content to render, or null/Promise for no/pending content
80
+ */
81
+ resolveAdornment(row: ReadonlyGraphRow, context?: TContext): ReactNode | null | Promise<ReactNode | null>;
82
+ /**
83
+ * EventTarget for invalidation.
84
+ * - Graph subscribes to 'invalidate' events
85
+ * - Consumer dispatches RowAdornmentInvalidateEvent to trigger re-evaluation
86
+ */
87
+ invalidate?: EventTarget;
88
+ }
89
+ /**
90
+ * Cached adornment state per row. Single map contains everything.
91
+ */
92
+ export interface CachedAdornment {
93
+ visibility: AdornmentVisibility;
94
+ dynamic: boolean;
95
+ context?: unknown;
96
+ content?: ReactNode;
97
+ }
@@ -8,4 +8,4 @@ export type CustomCellCacheValue = {
8
8
  export type CustomCellCache = {
9
9
  [key: string]: CustomCellCacheValue;
10
10
  };
11
- export declare const makeSmartCellRangeRenderer: (getRealKeyForCell: GetRealKeyForCellFunc, scrollLeft?: number) => GridCellRangeRenderer;
11
+ export declare const makeSmartCellRangeRenderer: (getRealKeyForCell: GetRealKeyForCellFunc, scrollLeft?: number, forceCache?: boolean) => GridCellRangeRenderer;
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export * from './domain/generic/GenericTypes';
8
8
  export * from './domain/commit/CommitTypes';
9
9
  export * from './domain/graph/GraphConstants';
10
10
  export * from './domain/graph/GraphTypes';
11
+ export * from './domain/graph/rowAdornment/RowAdornmentTypes';
11
12
  export * from './domain/hostingservice/HostingServiceTypes';
12
13
  export * from './domain/ref/RefConstants';
13
14
  export * from './domain/ref/RefTypes';