@gitkraken/gitkraken-components 13.0.0-vnext.28 → 13.0.0-vnext.29

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.
package/README.md CHANGED
@@ -16,4 +16,8 @@ This is a shared module between the GitKraken app and GitLens to share React com
16
16
  }
17
17
  ```
18
18
 
19
+ ## Guides
20
+
21
+ - [Multi-WIP rows](./docs/multi-wip-rows.md) — rendering secondary (per-worktree) WIP rows, deferred stats loading, and stale-while-revalidate invalidation.
22
+
19
23
  [CHANGELOG](./CHANGELOG.md)
@@ -5,12 +5,12 @@ import type { Sha } from '../../domain/commit/CommitTypes';
5
5
  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
- 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, OnColumnsCalculated, 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';
8
+ import type { AbsoluteNodeLeftByColumn, AvatarUrlByEmail, AvatarUrlRequestedByEmail, ChildrenBySha, ColumnColorByColumn, DownstreamsByUpstream, ExcludeByType, ExcludeRefsById, ExcludeRemotesByName, FormatCommitMessage, FormatRefShorthand, GetExternalIcon, GetMissingAvatar, GetMissingRefMetadata, GraphColumnSetting, GraphColumnsSettings, GraphContexts, GraphMarkerColors, GraphMarkerRowIndices, GraphPlatform, GraphRefGroup, GraphRow, GraphScope, GraphZoneModeConstants, GraphZonesByType, Head, HighlightedShas, IncludeOnlyRefsById, IncludeOnlyRemotesByName, IsRefShorthandValid, IsSelectedBySha, NodeOffsetByColumn, NodeOpacityByColumn, OnClearCurrentlyHoveredGraphCommit, OnClickCommit, OnClickGraphRef, OnClickGraphRow, OnClickRef, OnColumnsCalculated, 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, RowStats, RowStatsConstraints, ShaByRefId, Tag, TopAndBottomVisibleRowIndex } from '../../domain/graph/GraphTypes';
9
9
  import type { RowAdornmentProvider } from '../../domain/graph/rowAdornment/RowAdornmentTypes';
10
10
  import type { TranslationFn } from '../../domain/language/LanguageTypes';
11
11
  import type { CreateRefFormData, RefIconsPosition } from '../../domain/ref/RefTypes';
12
12
  import type { OnKeyDown, OnKeyUp, OnMouseDown, OnResizeParams } from '../../domain/ui/UiTypes';
13
- import type { WorkDirStats } from '../../domain/workdir/WorkDirTypes';
13
+ import type { OnVisibleWipShasChanged, OnWipShasMissingStats, VisibleWipShas, WipNodeMetadataBySha, WipShasMissingStats, WorkDirStats } from '../../domain/workdir/WorkDirTypes';
14
14
  import type { IGraphContainer } from './IGraphContainer';
15
15
  type ClearScrollToIndex = () => void;
16
16
  type OnZoneEnter = (e: React.MouseEvent<any>, zoneType: GraphZoneType, hoveredRefGroup?: GraphRefGroup, hoveredSha?: Sha) => void;
@@ -47,6 +47,12 @@ export interface StateProps {
47
47
  isRefShorthandValid?: IsRefShorthandValid;
48
48
  showGhostRefsOnRowHover?: boolean;
49
49
  showRemoteNamesOnRefs?: boolean;
50
+ /**
51
+ * Whether stash rows can keep otherwise-unreferenced parent commits visible.
52
+ * Defaults to true for backwards compatibility. When false, a stash is shown
53
+ * only if its parent commit would already be visible for some other reason.
54
+ */
55
+ stashesAnchorCommits?: boolean;
50
56
  enabledRefMetadataTypes?: RefMetadataType[];
51
57
  enabledScrollMarkerTypes?: GraphMarkerType[];
52
58
  scrollRowPadding?: number;
@@ -71,6 +77,19 @@ export interface StateProps {
71
77
  * If null or undefined, no branch is pinned.
72
78
  */
73
79
  pinnedBranchFullName?: string | null;
80
+ /**
81
+ * Narrow the graph to commits reachable from a specific branch, its upstream (if any), and
82
+ * a merge-target tip commit. Anchors (tips + merge target) show all their refs and never
83
+ * propagate child refs to their parents. When an anchor's real parents are hidden, a
84
+ * synthetic wavy dashed edge is drawn to its nearest visible ancestor.
85
+ */
86
+ scope?: GraphScope;
87
+ /**
88
+ * Fired when the helper determined that an anchor cannot reach a visible ancestor within the
89
+ * loaded graphRows (merge base not loaded). Consumer should fetch more data and re-render.
90
+ * Called once per processing pass, only when the set actually changes.
91
+ */
92
+ onScopeAnchorsUnreachable?: (unreachableAnchors: Set<Sha>) => void;
74
93
  shiftSelectMode?: ShiftSelectMode;
75
94
  suppressNonRefRowTooltips?: boolean;
76
95
  refMetadataById?: RefMetadataById | null;
@@ -83,8 +102,23 @@ export interface StateProps {
83
102
  useAuthorInitialsForAvatars: boolean;
84
103
  rowsStats?: Record<string, RowStats>;
85
104
  rowsStatsLoading?: boolean;
105
+ /**
106
+ * Controls when the WIP row is inserted into the graph.
107
+ * - 'auto' (default): shown only when there are pending working-tree changes.
108
+ * - 'always': shown even with a clean working tree, anchored at HEAD.
109
+ */
110
+ wipVisibility?: 'always' | 'auto';
86
111
  wipMessageEditable?: boolean;
87
112
  workDirStats?: WorkDirStats;
113
+ wipNodeMetadataBySha?: WipNodeMetadataBySha;
114
+ /**
115
+ * Settle delay (ms) for the internal scroll-settle debounce that drives both
116
+ * `onVisibleWipShasChanged` and `onWipShasMissingStats`. Each scroll/visible-range change
117
+ * resets the timer; the callbacks fire once activity stops for this long. Raise it (e.g. 350)
118
+ * to avoid firing stats requests for rows that are only momentarily visible during fast scrolls.
119
+ * Defaults to 100.
120
+ */
121
+ wipShasSettleDelayMs?: number;
88
122
  /**
89
123
  * Provider for row adornments.
90
124
  * Graph subscribes to invalidate events and calls provide/resolve as needed.
@@ -138,6 +172,8 @@ export interface DispatchProps {
138
172
  onGraphMouseLeave?: OnGraphMouseLeave;
139
173
  onEmailsMissingAvatarUrls?: OnEmailsMissingAvatarUrls;
140
174
  onRefsMissingMetadata?: OnRefsMissingMetadata;
175
+ onWipShasMissingStats?: OnWipShasMissingStats;
176
+ onVisibleWipShasChanged?: OnVisibleWipShasChanged;
141
177
  }
142
178
  interface Props extends OwnProps, StateProps, DispatchProps {
143
179
  }
@@ -223,6 +259,9 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
223
259
  reserverInfoBySha: ReserverInfoBySha;
224
260
  columnsUsed: ColumnsUsed;
225
261
  private pinnedBranchShas;
262
+ private scopeVisibility;
263
+ private syntheticEdgeChildShas;
264
+ private _lastReportedUnreachable;
226
265
  rowsStats?: Record<string, RowStats>;
227
266
  workDirStats: WorkDirStats;
228
267
  private _markerColorsCache;
@@ -248,6 +287,9 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
248
287
  pendingMissingRefsMetadata: RefsMissingMetadata;
249
288
  requestedMissingRefsMetadata: RefMetadataRequestedById;
250
289
  previousRefMetadataById: RefMetadataById | null;
290
+ requestedMissingWipStats: WipShasMissingStats;
291
+ visibleWipShasTimer?: ReturnType<typeof setTimeout>;
292
+ lastFiredVisibleWipShas: VisibleWipShas | undefined;
251
293
  branchUpstreamRowIndices: number[];
252
294
  downstreamsByUpstream: DownstreamsByUpstream;
253
295
  timelinesInterval?: ReturnType<typeof setInterval>;
@@ -462,12 +504,8 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
462
504
  initializeGraphZones(zoneSettings: GraphColumnsSettings | undefined, zoneOrdering: GraphZoneType[], width: number): GraphZone[];
463
505
  maybeSortGraphZoneOrderingFromSettings(zoneSettings?: GraphColumnsSettings): void;
464
506
  updateZonesFromSettings(zoneSettings?: GraphColumnsSettings): void;
465
- getEdgeColumnMaxes(sha: Sha): number;
466
- getEndingAndPassThroughEdgesByColumnFromPrevRow(graphRow: ProcessedGraphRow, prevRowEdges: RowEdges): RowEdges;
467
- getFinalEdgeStateForGraphAndRow(graphRow: ProcessedGraphRow, prevRowEdges: RowEdges): RowEdges;
468
507
  loadEdgesBySha(): void;
469
508
  processRows(graphRows?: GraphRow[], pinnedBranchFullName?: string | null): void;
470
- getStartingEdgesByColumn(currentRow: ProcessedGraphRow): EdgeByColumn;
471
509
  formatCommitDateTimeCallback: OnFormatCommitDateTime;
472
510
  formatCommitMessageCallback: FormatCommitMessage;
473
511
  formatRefShorthandCallback: FormatRefShorthand;
@@ -476,6 +514,7 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
476
514
  getIconCallback: GetExternalIcon;
477
515
  onMissingAvatar: GetMissingAvatar;
478
516
  onMissingRefMetadata: GetMissingRefMetadata;
517
+ scheduleVisibleWipShasUpdate: () => void;
479
518
  selectNext(shiftKey?: boolean, topological?: boolean, shiftSelectMode?: ShiftSelectMode): void;
480
519
  selectPrevious(shiftKey?: boolean, topological?: boolean, shiftSelectMode?: ShiftSelectMode): void;
481
520
  /**
@@ -530,10 +569,17 @@ declare class GraphContainer extends React.Component<Props, ComponentState> impl
530
569
  selectLast(shiftKey?: boolean, shiftSelectMode?: ShiftSelectMode): void;
531
570
  selectPageUp(shiftKey?: boolean, shiftSelectMode?: ShiftSelectMode): void;
532
571
  selectPageDown(shiftKey?: boolean, shiftSelectMode?: ShiftSelectMode): void;
533
- getColumns(graphRow: GraphRow): number;
572
+ /**
573
+ * Computes the column for a row.
574
+ *
575
+ * `parentsOverride` lets callers supply a different parent list than `graphRow.parents` —
576
+ * used by the scoped-graph feature to reserve lanes against synthetic ancestors when a
577
+ * row's real parents are hidden by scope filtering. Avoids mutating row.parents.
578
+ */
579
+ getColumns(graphRow: GraphRow, parentsOverride?: Sha[]): number;
534
580
  getFilteredHeadsForGraphRow(row: GraphRow, hasIncludes: boolean): Head[];
535
581
  getFilteredRemotesForGraphRow(row: GraphRow, hasIncludes: boolean): Remote[];
536
- getFilteredTagsForGraphRow(row: GraphRow, hasIncludes: boolean, rowHasVisibleChild: boolean): Tag[];
582
+ getBaseFilteredTagsForGraphRow(row: GraphRow, hasIncludesWithTags: boolean): Tag[];
537
583
  loadRowsbySha(graphRows: GraphRow[], pinnedBranchFullName?: string | null): void;
538
584
  updateRowStatsConstraints(): void;
539
585
  updateTimelines(): void;
@@ -2,7 +2,7 @@ import type { ReactElement } from 'react';
2
2
  import * as React from 'react';
3
3
  import type { CommitType, Sha } from '../../../domain/commit/CommitTypes';
4
4
  import type { CssVariables, Style } from '../../../domain/generic/GenericTypes';
5
- import type { ColumnColorByColumn, GetExternalIcon, GraphItemContext, GraphZoneModeConstants, OnClearCurrentlyHoveredGraphCommit, OnClickCommit, OnCommitContextMenu, OnCurrentlyHoveredGraphCommit, OnDoubleClickCommit, RowEdges } from '../../../domain/graph/GraphTypes';
5
+ import type { ColumnColorByColumn, GetExternalIcon, GraphItemContext, GraphZoneModeConstants, OnClearCurrentlyHoveredGraphCommit, OnClickCommit, OnCommitContextMenu, OnCurrentlyHoveredGraphCommit, OnDoubleClickCommit, RowEdges, ScopeBoundaryKind } from '../../../domain/graph/GraphTypes';
6
6
  import type { TranslationFn } from '../../../domain/language/LanguageTypes';
7
7
  type Props = {
8
8
  authorEmail: string;
@@ -38,6 +38,7 @@ type Props = {
38
38
  rightGutterBoxShadowAlpha: number;
39
39
  rightGutterWidth: number;
40
40
  rowContext?: GraphItemContext;
41
+ scopeBoundaryKind?: ScopeBoundaryKind;
41
42
  scrollLeft: number;
42
43
  setAsCurrentlyHoveredGraphCommit: OnCurrentlyHoveredGraphCommit;
43
44
  sha: Sha;
@@ -1,4 +1,4 @@
1
- import type { CommitType } from '../../../../domain/commit/CommitTypes';
1
+ import type { EdgeCommitType } from '../../../../domain/commit/CommitTypes';
2
2
  import type { ArcEndpointAngle, ColumnColor, SvgElement } from '../../../../domain/graph/GraphTypes';
3
- declare function getArc(color: ColumnColor, endAngle: ArcEndpointAngle, startAngle: ArcEndpointAngle, type: CommitType, x: number, y: number, strokeWidth: number, isCompact?: boolean): SvgElement;
3
+ declare function getArc(color: ColumnColor, endAngle: ArcEndpointAngle, startAngle: ArcEndpointAngle, type: EdgeCommitType, x: number, y: number, strokeWidth: number, isCompact?: boolean): SvgElement;
4
4
  export default getArc;
@@ -1,4 +1,4 @@
1
- import type { CommitType } from '../../../../domain/commit/CommitTypes';
1
+ import type { EdgeCommitType } from '../../../../domain/commit/CommitTypes';
2
2
  import type { ColumnColorByColumn, SvgElement } from '../../../../domain/graph/GraphTypes';
3
3
  export declare function clearEndingEdgeCacheCache(): void;
4
- export declare function getEndingEdge(edgeColumn: number, nodeColumn: number, type: CommitType, columnColorByColumn: ColumnColorByColumn, columnWidth: number, gutterWidth: number, strokeWidth: number, isCompact?: boolean): SvgElement;
4
+ export declare function getEndingEdge(edgeColumn: number, nodeColumn: number, type: EdgeCommitType, columnColorByColumn: ColumnColorByColumn, columnWidth: number, gutterWidth: number, strokeWidth: number, isCompact?: boolean): SvgElement;
@@ -1,4 +1,4 @@
1
- import type { CommitType } from '../../../../domain/commit/CommitTypes';
1
+ import type { EdgeCommitType } from '../../../../domain/commit/CommitTypes';
2
2
  import type { ColumnColorByColumn, SvgElement } from '../../../../domain/graph/GraphTypes';
3
3
  export declare function clearPassThroughEdgeCache(): void;
4
- export declare function getPassThroughEdge(column: number, type: CommitType, columnColorByColumn: ColumnColorByColumn, columnWidth: number, gutterWidth: number, strokeWidth: number, isCompact?: boolean): SvgElement;
4
+ export declare function getPassThroughEdge(column: number, type: EdgeCommitType, columnColorByColumn: ColumnColorByColumn, columnWidth: number, gutterWidth: number, strokeWidth: number, isCompact?: boolean): SvgElement;
@@ -1,4 +1,4 @@
1
- import type { CommitType } from '../../../../domain/commit/CommitTypes';
1
+ import type { EdgeCommitType } from '../../../../domain/commit/CommitTypes';
2
2
  import type { ColumnColorByColumn, SvgElement } from '../../../../domain/graph/GraphTypes';
3
3
  export declare function clearStartingEdgeCache(): void;
4
- export declare function getStartingEdge(edgeColumn: number, nodeColumn: number, type: CommitType, columnColorByColumn: ColumnColorByColumn, columnWidth: number, gutterWidth: number, strokeWidth: number, isCompact?: boolean): SvgElement;
4
+ export declare function getStartingEdge(edgeColumn: number, nodeColumn: number, type: EdgeCommitType, columnColorByColumn: ColumnColorByColumn, columnWidth: number, gutterWidth: number, strokeWidth: number, isCompact?: boolean): SvgElement;
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import type { CommitType, Sha } from '../../../domain/commit/CommitTypes';
4
4
  import type { Style } from '../../../domain/generic/GenericTypes';
5
5
  import type { GraphZoneType } from '../../../domain/graph/GraphConstants';
6
- import type { GetExternalIcon, GraphItemContext, OnClearCurrentlyHoveredGraphCommit, OnClickCommit, OnCommitContextMenu, OnCurrentlyHoveredGraphCommit, OnDoubleClickCommit } from '../../../domain/graph/GraphTypes';
6
+ import type { GetExternalIcon, GraphItemContext, OnClearCurrentlyHoveredGraphCommit, OnClickCommit, OnCommitContextMenu, OnCurrentlyHoveredGraphCommit, OnDoubleClickCommit, ScopeBoundaryKind } from '../../../domain/graph/GraphTypes';
7
7
  import type { ChildrenElements } from '../../../domain/ui/UiTypes';
8
8
  type Props = {
9
9
  clearCurrentlyHoveredGraphCommit: OnClearCurrentlyHoveredGraphCommit;
@@ -15,6 +15,7 @@ type Props = {
15
15
  dimRowsOfSelectedCommit?: boolean;
16
16
  getExternalIcon: GetExternalIcon;
17
17
  graphZoneType: GraphZoneType;
18
+ hasActive?: boolean;
18
19
  hasAvatars?: boolean;
19
20
  highlightRowsOnRefHover: boolean;
20
21
  isDimmedMergeCommitRow: boolean;
@@ -29,6 +30,7 @@ type Props = {
29
30
  onClickCommit: OnClickCommit;
30
31
  onDoubleClickCommit: OnDoubleClickCommit;
31
32
  rowContext?: GraphItemContext | null;
33
+ scopeBoundaryKind?: ScopeBoundaryKind;
32
34
  setAsCurrentlyHoveredGraphCommit: OnCurrentlyHoveredGraphCommit;
33
35
  sha: Sha;
34
36
  showColorStrip?: boolean;
@@ -2,7 +2,7 @@ import type { ReactElement } from 'react';
2
2
  import React from 'react';
3
3
  import type { CommitType, Sha } from '../../../domain/commit/CommitTypes';
4
4
  import type { Style } from '../../../domain/generic/GenericTypes';
5
- import type { GetExternalIcon, GetMissingRefMetadata, GraphItemContext, GraphRefGroup, IncludeOnlyRefsById, IncludeOnlyRemotesByName, OnClearCurrentlyHoveredGraphCommit, OnClickRef, OnDoubleClickRef, OnRefBeginDrag, OnRefCanDrag, OnRefCanDrop, OnRefDragEnter, OnRefDragLeave, OnRefDrop, OnRefEndDrag, OnRefNodeHovered, OnRefNodeUnhovered, OnRefZoneContextMenu, OnRefZoneHovered, OnRefZoneUnhovered, OnToggleRefNodesShown, RefGroupContexts, RefMetadataById, RefMetadataType } from '../../../domain/graph/GraphTypes';
5
+ import type { GetExternalIcon, GetMissingRefMetadata, GraphItemContext, GraphRefGroup, IncludeOnlyRefsById, IncludeOnlyRemotesByName, OnClearCurrentlyHoveredGraphCommit, OnClickRef, OnDoubleClickRef, OnRefBeginDrag, OnRefCanDrag, OnRefCanDrop, OnRefDragEnter, OnRefDragLeave, OnRefDrop, OnRefEndDrag, OnRefNodeHovered, OnRefNodeUnhovered, OnRefZoneContextMenu, OnRefZoneHovered, OnRefZoneUnhovered, OnToggleRefNodesShown, RefGroupContexts, RefMetadataById, RefMetadataType, ScopeBoundaryKind } from '../../../domain/graph/GraphTypes';
6
6
  import type { TranslationFn } from '../../../domain/language/LanguageTypes';
7
7
  import type { RefIconsPosition } from '../../../domain/ref/RefTypes';
8
8
  type RefZoneDefaultProps = {
@@ -12,6 +12,7 @@ type RefZoneProps = {
12
12
  column: number;
13
13
  columnForColoring?: number;
14
14
  context: GraphItemContext | null;
15
+ scopeBoundaryKind?: ScopeBoundaryKind;
15
16
  enableShowHideRefsOptions: boolean;
16
17
  showGhostRefsOnRowHover: boolean;
17
18
  getExternalIcon: GetExternalIcon;
@@ -2,12 +2,13 @@ import type { GridCellRenderer } from 'react-virtualized';
2
2
  import type { GraphCommitDescDisplayMode } from '../../../domain/graph/GraphConstants';
3
3
  import type { CommonGraphRowDispatchProps, CommonGraphRowProps, OnCommitMessageChange, OnWipMessageBlur, OnWipMessageFocus } from '../../../domain/graph/GraphTypes';
4
4
  import type { GetRealKeyForCellFunc } from '../../../domain/reactvirtualized/ReactVirtualizedHelpers';
5
- import type { WorkDirStats } from '../../../domain/workdir/WorkDirTypes';
5
+ import type { WipNodeMetadataBySha, WorkDirStats } from '../../../domain/workdir/WorkDirTypes';
6
6
  interface StateProps {
7
7
  graphCommitDescDisplayMode: GraphCommitDescDisplayMode;
8
8
  isCommitting: boolean;
9
9
  pendingCommitMessageSummary: string;
10
10
  workDirStats?: WorkDirStats;
11
+ wipNodeMetadataBySha?: WipNodeMetadataBySha;
11
12
  wipMessageEditable: boolean;
12
13
  }
13
14
  interface DispatchProps {
@@ -1,9 +1,11 @@
1
1
  import type { GridCellRenderer } from 'react-virtualized';
2
2
  import type { AvatarUrlByEmail, ColumnColorByColumn, CommonGraphRowDispatchProps, CommonGraphRowProps, GetMissingAvatar, GraphZoneModeConstants, NodeOffsetByColumn, NodeOpacityByColumn } from '../../../domain/graph/GraphTypes';
3
3
  import type { GetRealKeyForCellFunc } from '../../../domain/reactvirtualized/ReactVirtualizedHelpers';
4
+ import type { WipNodeMetadataBySha } from '../../../domain/workdir/WorkDirTypes';
4
5
  interface OwnProps {
5
6
  avatarUrlByEmail: AvatarUrlByEmail;
6
7
  graphZoneModeConstants: GraphZoneModeConstants;
8
+ wipNodeMetadataBySha?: WipNodeMetadataBySha;
7
9
  height: number;
8
10
  onMissingAvatar: GetMissingAvatar;
9
11
  columnColorByColumn: ColumnColorByColumn;
@@ -1,5 +1,5 @@
1
- import type { Author, CommitType, Sha, ShortSha } from './CommitTypes';
2
- export declare const hasPendingChanges: (type: CommitType) => boolean;
1
+ import type { Author, EdgeCommitType, Sha, ShortSha } from './CommitTypes';
2
+ export declare const hasPendingChanges: (type: EdgeCommitType) => boolean;
3
3
  export declare const getShortSha: (sha?: Sha | null, shaLength?: number) => ShortSha;
4
4
  export declare function getCommitMessageDescription(commitMessage: string, lineEnding?: string): string;
5
5
  export declare function getCommitMessageCoauthors(commitMessageDescription: string): Author[];
@@ -3,6 +3,7 @@ export type Sha = string;
3
3
  export type ShortSha = string;
4
4
  export type WorkDirType = typeof mergeConflictNodeType | typeof unsupportedRebaseWarningNodeType | typeof workDirType;
5
5
  export type CommitType = typeof commitNodeType | typeof mergeNodeType | typeof stashNodeType | WorkDirType;
6
+ export type EdgeCommitType = CommitType | 'synthetic-edge';
6
7
  export type Author = {
7
8
  name: string;
8
9
  email: string;
@@ -1,10 +1,10 @@
1
- import type { CommitType } from '../commit/CommitTypes';
1
+ import type { EdgeCommitType } from '../commit/CommitTypes';
2
2
  import type { ColumnColor, LineSvgProps, RowEdges, SvgElement, SvgElementName, SvgProps, SvgStyles } from '../graph/GraphTypes';
3
3
  export declare function getXValueAtColumn(column: number, columnWidth: number, gutterWidth: number, isCompact?: boolean): number;
4
4
  export declare function clearXValueAtColumnCache(): void;
5
5
  export declare function getSvgElement(element: SvgElementName, props: SvgProps): SvgElement;
6
6
  export declare function getLineElement(props: LineSvgProps): SvgElement;
7
- export declare function getSvgStyles(type: CommitType, color: ColumnColor, strokeWidth: number, isCompact?: boolean): SvgStyles;
8
- export declare function buildStartingOrEndingEdgeHash(edgeColumn: number, nodeColumn: number, type: CommitType | undefined, isCompact?: boolean): string;
9
- export declare function buildPassThroughEdgeHash(column: number, type: CommitType | undefined, isCompact?: boolean): string;
7
+ export declare function getSvgStyles(type: EdgeCommitType, color: ColumnColor, strokeWidth: number, isCompact?: boolean): SvgStyles;
8
+ export declare function buildStartingOrEndingEdgeHash(edgeColumn: number, nodeColumn: number, type: EdgeCommitType | undefined, isCompact?: boolean): string;
9
+ export declare function buildPassThroughEdgeHash(column: number, type: EdgeCommitType | undefined, isCompact?: boolean): string;
10
10
  export declare function buildEdgeHash(edges: RowEdges, edgeColumnMax: number, nodeColumn: number, isCompact?: boolean): string;
@@ -91,7 +91,7 @@ export declare enum GraphColumnMode {
91
91
  Rich = "rich",
92
92
  Text = "text"
93
93
  }
94
- export type GraphMarkerType = 'head' | 'highlights' | 'localBranches' | 'pullRequests' | 'remoteBranches' | 'selection' | 'stashes' | 'tags' | 'upstream';
94
+ export type GraphMarkerType = 'head' | 'highlights' | 'localBranches' | 'pullRequests' | 'remoteBranches' | 'selection' | 'stashes' | 'tags' | 'upstream' | 'wip';
95
95
  export declare enum GraphMarkerShape {
96
96
  Block = "block",
97
97
  FullLine = "fullLine",
@@ -0,0 +1,34 @@
1
+ import type { Sha } from '../commit/CommitTypes';
2
+ import type { EdgeByColumn, ProcessedGraphRow, RowEdges } from './GraphTypes';
3
+ /**
4
+ * Derives the ending/passThrough edges that the current row inherits from the previous row's
5
+ * edge state. For each column the previous row occupied, the inherited edge is either:
6
+ * - an ending, if its parentSha matches the current row's sha
7
+ * - a passThrough, otherwise
8
+ *
9
+ * Between a concurrent passThrough and starting in the same column (which can happen when a
10
+ * chain above passes through a row that also starts a new edge in the same lane), we prefer
11
+ * the non-pending (real commit/merge) edge so that a WIP/synthetic edge doesn't shadow a
12
+ * real commit chain mid-flight. The bare fallback still carries pending edges forward when
13
+ * that's the only thing available.
14
+ */
15
+ export declare function getEndingAndPassThroughEdgesByColumnFromPrevRow(currentSha: Sha, prevRowEdges: RowEdges): RowEdges;
16
+ /**
17
+ * Builds the set of starting edges for a row — one per parent it points to. Edges for rows
18
+ * in `syntheticEdgeChildShas` are tagged `synthetic-edge` so the renderer draws them wavy.
19
+ *
20
+ * `columnBySha` maps already-processed shas to their assigned column. This is used for the
21
+ * non-first parents of a row (which reserve lanes beside the row's own column). The first
22
+ * parent lives on the current row's own column.
23
+ */
24
+ export declare function getStartingEdgesByColumn(currentRow: ProcessedGraphRow, columnBySha: (sha: Sha) => number | undefined, syntheticEdgeChildShas: Set<Sha>): EdgeByColumn;
25
+ /**
26
+ * Composes the full edge state for a row: inherited ending/passThrough edges from the prev
27
+ * row merged with this row's own starting edges.
28
+ */
29
+ export declare function getFinalEdgeStateForGraphAndRow(currentRow: ProcessedGraphRow, prevRowEdges: RowEdges, columnBySha: (sha: Sha) => number | undefined, syntheticEdgeChildShas: Set<Sha>): RowEdges;
30
+ /**
31
+ * Returns the highest column index present in an edges map. Edges render from column 0 up
32
+ * to (inclusive) this value, so the caller uses it as a loop bound when emitting SVG.
33
+ */
34
+ export declare function getEdgeColumnMax(edges: RowEdges | undefined | null): number;
@@ -9,6 +9,11 @@ import { GraphColumnMode } from './GraphConstants';
9
9
  import type { BaseMetadata, ExternalIconKeys, GetMissingRefMetadata, GraphColumnWidthConstraints, GraphItemContext, GraphRef, GraphRefGroup, GraphRow, GraphZoneModeConstants, Head, HighlightedShas, ProcessedGraphRow, ProcessedGraphRowBySha, Ref, RefMetadata, RefMetadataById, RefMetadataType, RowStatsConstraints } from './GraphTypes';
10
10
  export type DebouncableFn = (...args: any) => void;
11
11
  export type DebouncedFn = (...args: any) => void;
12
+ export type CachedRowVisibilityState = {
13
+ hasBaseFilteredTags: boolean;
14
+ hasFilteredBranches: boolean;
15
+ };
16
+ export declare function getVisibleRowShasWithoutStashAnchoring(graphRows: GraphRow[], cachedRowVisibilityByIndex: CachedRowVisibilityState[], tagsRequireBranchOrVisibleChild: boolean): Set<Sha>;
12
17
  export declare const debounceFrame: (func: DebouncableFn) => DebouncedFn;
13
18
  export type ThrottleableFn = (...args: any) => void;
14
19
  export type ThrottledFn = (...args: any) => void;
@@ -65,6 +70,7 @@ export declare function getHeadRefShaFromGraphRows(graphRows: GraphRow[]): Sha |
65
70
  export declare function isWorkDirNodeType(nodeType: CommitType): boolean;
66
71
  export declare function isCommitNodeType(nodeType: CommitType): boolean;
67
72
  export declare function workDirStatsHaveChanges(workDirStats?: WorkDirStats): boolean;
73
+ export declare function shouldShowWorkDirRow(workDirStats: WorkDirStats | undefined, wipVisibility: 'always' | 'auto' | undefined): boolean;
68
74
  export declare function hasSingleSelectedSha(selectedShas: Set<Sha>): boolean;
69
75
  export declare function isSingleSelected(selectedShas: Set<Sha>, sha: Sha): boolean;
70
76
  export declare function getScrollToAlignment(rowHeight: number, clientHeight: number, scrollTop: number, scrollToIndex: number): Alignment;
@@ -1,7 +1,7 @@
1
1
  import type { GridCellRenderer, ScrollParams } from 'react-virtualized';
2
2
  import type { ReactElement, ReactNode } from 'react';
3
3
  import type { CommitDateTimeSources } from '../commit/CommitConstants';
4
- import type { CommitType, Sha } from '../commit/CommitTypes';
4
+ import type { CommitType, EdgeCommitType, Sha } from '../commit/CommitTypes';
5
5
  import type { CssVariables } from '../generic/GenericTypes';
6
6
  import type { HostingServiceType, IssueTrackerType } from '../hostingservice/HostingServiceTypes';
7
7
  import type { TranslationFn } from '../language/LanguageTypes';
@@ -38,6 +38,7 @@ export type EdgeCache = {
38
38
  export type ArcEndpointAngle = 0 | 90 | 180 | 270;
39
39
  export interface SvgStyles {
40
40
  fill: string;
41
+ filter?: string;
41
42
  shapeRendering: string;
42
43
  strokeLinejoin: string;
43
44
  strokeWidth: number;
@@ -57,7 +58,7 @@ export type SvgProps = ArcSvgProps | LineSvgProps;
57
58
  export type AnySvgProps = ArcSvgProps & LineSvgProps;
58
59
  export type Edge = {
59
60
  parentSha: Sha;
60
- type: CommitType;
61
+ type: EdgeCommitType;
61
62
  };
62
63
  export type EdgeByColumn = {
63
64
  [column: number]: Edge;
@@ -136,6 +137,45 @@ export type IsSelectedBySha = {
136
137
  export type HighlightedShas = {
137
138
  [sha: Sha]: boolean;
138
139
  };
140
+ export type SyntheticParentsBySha = Map<Sha, Sha[]>;
141
+ export interface GraphScope {
142
+ /** Full ref id of the specific branch to scope to (e.g. 'refs/heads/feature/x'). NOT necessarily HEAD. */
143
+ branchRef: string;
144
+ /** Full ref id of the branch's upstream (e.g. 'refs/remotes/origin/feature/x'). */
145
+ upstreamRef?: string;
146
+ /** SHA of the merge-target tip commit. Its ancestors are NOT walked — the tip is kept as a marker. */
147
+ mergeTargetTipSha?: Sha;
148
+ /**
149
+ * Additional ref ids to include in the scope. Each tip becomes an anchor (same treatment as
150
+ * branchRef — shows all refs, acts as visibility floor) and its ancestors contribute to
151
+ * visibleShas subject to the mergeTarget exclusion.
152
+ *
153
+ * Primary use case: branches stacked on top of the focal branch (e.g. F2, F3 stacked on F1).
154
+ * The helper makes no stackedness check — any refs are valid (siblings, comparisons, etc.).
155
+ */
156
+ additionalBranchRefs?: string[];
157
+ /** How to render gaps when an anchor's real parents are hidden. Defaults to 'syntheticEdge'. */
158
+ collapseStyle?: 'syntheticEdge' | 'gap';
159
+ }
160
+ export interface ScopeVisibility {
161
+ visibleShas: Set<Sha>;
162
+ anchorShas: Set<Sha>;
163
+ /** Fork points (merge bases) where an anchor's ancestor walk terminated. Visual boundary "below" the focus group. */
164
+ forkPointShas: Set<Sha>;
165
+ /** The merge-target tip sha, when scope.mergeTargetTipSha was provided. Visual boundary "above" the focus group. */
166
+ mergeTargetShas: Set<Sha>;
167
+ syntheticParentsBySha: SyntheticParentsBySha;
168
+ /** Anchors whose synthetic walk failed — merge base not in loaded rows. Consumer should fetch more data. */
169
+ unreachableAnchors: Set<Sha>;
170
+ /**
171
+ * True when scope walks terminated at identified boundaries — at least one fork point was
172
+ * found AND no anchor was left unreachable. When false, the scope may be incomplete due to
173
+ * missing data (merge base not loaded, foreign ref not yet surfaced, partial history). Callers
174
+ * should NOT suppress commit autoloading while this is false — more data could establish the
175
+ * missing boundary.
176
+ */
177
+ isBounded: boolean;
178
+ }
139
179
  export type IsExcludedBySha = {
140
180
  [id: Sha]: boolean;
141
181
  };
@@ -325,6 +365,8 @@ export interface ReadonlyGraphRow extends ReadonlyDeep<GraphRow> {
325
365
  /** Whether the row is hidden from the graph (filtered out by type or other filters). Output only */
326
366
  readonly hidden: boolean;
327
367
  }
368
+ /** Which scope-boundary role this row plays, when an active scope identifies it as one. */
369
+ export type ScopeBoundaryKind = 'forkPoint' | 'mergeTarget';
328
370
  export interface ProcessedGraphRow extends GraphRow {
329
371
  rowIndex?: number;
330
372
  column: number;
@@ -341,6 +383,7 @@ export interface ProcessedGraphRow extends GraphRow {
341
383
  timeLineEntry?: TimelineEntry;
342
384
  childRefs?: ChildRefsByType;
343
385
  refsData?: GraphRefsData;
386
+ scopeBoundaryKind?: ScopeBoundaryKind;
344
387
  }
345
388
  export type TopAndBottomVisibleRowIndex = {
346
389
  top: number;
@@ -0,0 +1,15 @@
1
+ import type { GraphRow, GraphScope, ScopeVisibility } from './GraphTypes';
2
+ export declare function computeScopeVisibility(rows: GraphRow[], scope: GraphScope): ScopeVisibility;
3
+ /**
4
+ * Returns true when a WIP row should be hidden under an active scope.
5
+ *
6
+ * Secondary (per-worktree) WIP rows are kept only when their parent is a branch anchor of the
7
+ * scope — i.e. the tip of `branchRef`, `upstreamRef`, or one of `additionalBranchRefs` (so a
8
+ * worktree checked out on a scoped branch still surfaces its WIP, even when empty). The merge
9
+ * target tip is excluded: it's a boundary, not a focused branch.
10
+ *
11
+ * The primary WIP row, merge-conflict node, and unsupported-rebase warning node are tied to
12
+ * the primary repo's HEAD. They are kept whenever at least one of their parents is in the
13
+ * scope's visible set; otherwise they would render as floating nodes with no anchoring topology.
14
+ */
15
+ export declare function shouldHideWipRowForScope(row: GraphRow, scopeVisibility: ScopeVisibility | null): boolean;
@@ -2,7 +2,7 @@ import type { ReactNode } from 'react';
2
2
  import type { IGraphContainer } from '../../../components/graph/IGraphContainer';
3
3
  import type { CommitType, Sha } from '../../commit/CommitTypes';
4
4
  import type { TimelineEntry } from '../GraphConstants';
5
- import type { ChildRefsByType, GraphRefsData, GraphRow, Head, ProcessedGraphRow, Remote, RowContexts, RowEdges, Tag } from '../GraphTypes';
5
+ import type { ChildRefsByType, GraphRefsData, GraphRow, Head, ProcessedGraphRow, Remote, RowContexts, RowEdges, ScopeBoundaryKind, Tag } from '../GraphTypes';
6
6
  export declare class ProcessedGraphRowObj implements ProcessedGraphRow {
7
7
  private _graph;
8
8
  private _message;
@@ -27,6 +27,7 @@ export declare class ProcessedGraphRowObj implements ProcessedGraphRow {
27
27
  edges: RowEdges;
28
28
  childRefs: ChildRefsByType;
29
29
  timeLineEntry?: TimelineEntry;
30
+ scopeBoundaryKind?: ScopeBoundaryKind;
30
31
  summary: string;
31
32
  body?: string;
32
33
  displaySummary: string | ReactNode;
@@ -0,0 +1,14 @@
1
+ import type { Sha } from '../../commit/CommitTypes';
2
+ /**
3
+ * Builds the CSS `<dashed-ident>` used to tie a graph row's `anchor-name`
4
+ * to the overlay's `position-anchor`. Shas are arbitrary caller-provided
5
+ * strings (e.g. `worktree-wip::/some/path` for multi-WIP rows) and may
6
+ * contain characters that aren't valid inside a CSS <custom-ident>, so
7
+ * each unsafe character is prefixed with `\` — a single-character CSS
8
+ * escape that preserves readability in DevTools while keeping the
9
+ * identifier parseable by the browser.
10
+ *
11
+ * Letters, digits, hyphen, underscore, and non-ASCII code points are
12
+ * already legal inside a CSS ident and pass through unchanged.
13
+ */
14
+ export declare function getRowAnchorName(sha: Sha): string;
@@ -0,0 +1,50 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { Sha } from '../commit/CommitTypes';
3
+ import type { VisibleWipShas, WipNodeMetadataBySha, WipShasMissingStats, WorkDirStats } from './WorkDirTypes';
4
+ /** Translation key for the WIP row's label, selected based on stats state. */
5
+ export type WipRowMessageKey = 'Graph-WorkInProgress' | 'Graph-WorkInProgress-Changes' | 'Graph-WorkInProgress-Clean';
6
+ /**
7
+ * Picks the translation key for a WIP row's label based on the stats it has (or doesn't have):
8
+ * - `Graph-WorkInProgress-Changes` when stats show any added/modified/deleted activity
9
+ * - `Graph-WorkInProgress-Clean` when stats are present but all zero (queried, clean)
10
+ * - `Graph-WorkInProgress` when stats are not yet known (neutral fallback)
11
+ */
12
+ export declare function getWipRowMessageKey(workDirStats: WorkDirStats | undefined): WipRowMessageKey;
13
+ export type ResolvedWipState = {
14
+ isPrimaryWip: boolean;
15
+ effectiveWorkDirStats: WorkDirStats | undefined;
16
+ hasWorkDirChanges: boolean;
17
+ isWipEditable: boolean;
18
+ customWipContent: ReactElement<any> | null;
19
+ needsWipStatsRequest: boolean;
20
+ };
21
+ /**
22
+ * Resolves the effective WIP state for a given row.
23
+ *
24
+ * For the primary WIP row (sha === workDirType), uses the global workDirStats.
25
+ * For secondary WIP rows, uses the per-row metadata from wipNodeMetadataBySha. There is
26
+ * NO fallback to the global workDirStats — secondary rows belong to other worktrees, and
27
+ * painting the primary repo's counts on them would be wrong. Until per-row stats arrive,
28
+ * the pill renders nothing and `needsWipStatsRequest` triggers a deferred fetch.
29
+ */
30
+ export declare function resolveWipState(sha: Sha, type: string, workDirStats: WorkDirStats | undefined, wipNodeMetadataBySha: WipNodeMetadataBySha | undefined, wipMessageEditable: boolean): ResolvedWipState;
31
+ /** Predicate for secondary-WIP rows (per-worktree WIPs), excluding the primary WIP row. */
32
+ export declare function isSecondaryWipRow(row: {
33
+ sha: Sha;
34
+ type?: string;
35
+ }): boolean;
36
+ /** Key-set equality for `VisibleWipShas` / `WipShasMissingStats`-shaped records (values are always `true`). */
37
+ export declare function areVisibleWipShasEqual(a: VisibleWipShas, b: VisibleWipShas): boolean;
38
+ /**
39
+ * Given the set of currently-visible secondary-WIP shas, the prior dedup map of shas already
40
+ * requested, and the per-sha metadata, compute (a) the pruned dedup map that only retains entries
41
+ * for shas still visible, and (b) the set of shas whose stats should be requested this tick.
42
+ *
43
+ * Shas that have left the viewport are dropped from the dedup so they can be re-asked on re-entry.
44
+ * Shas with `customContent` override or with non-stale `workDirStats` already populated are not
45
+ * added to `missing`.
46
+ */
47
+ export declare function computeWipMissingShasUpdate(current: VisibleWipShas, priorRequestedDedup: WipShasMissingStats, metadataBySha: WipNodeMetadataBySha | undefined): {
48
+ missing: WipShasMissingStats;
49
+ prunedRequestedDedup: WipShasMissingStats;
50
+ };
@@ -1,3 +1,5 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { Sha } from '../commit/CommitTypes';
1
3
  import type { GraphItemContext } from '../graph/GraphTypes';
2
4
  export type WorkDirStats = {
3
5
  added: number;
@@ -6,3 +8,52 @@ export type WorkDirStats = {
6
8
  context?: GraphItemContext;
7
9
  renamed?: number;
8
10
  };
11
+ /**
12
+ * Per-row decoration for a secondary (worktree) WIP graph row. Looked up by the row's `sha`.
13
+ *
14
+ * Not applied to the primary WIP row (`sha === workDirType`); use the `workDirStats` prop for that one.
15
+ *
16
+ * @see docs/multi-wip-rows.md
17
+ */
18
+ export type WipNodeMetadata = {
19
+ /**
20
+ * Stats for this secondary WIP row's worktree. Omit to have the library request them via
21
+ * `onWipShasMissingStats`. Until per-row stats arrive, the pill renders nothing — the library does
22
+ * NOT fall back to the global `workDirStats` (which belongs to the primary repo).
23
+ */
24
+ workDirStats?: WorkDirStats;
25
+ /** When true, the library requests fresh stats while continuing to render `workDirStats` (stale-while-revalidate). */
26
+ workDirStatsStale?: boolean;
27
+ /** If provided, renders this element in place of the default WIP stats pill. Suppresses any stats request. */
28
+ customContent?: ReactElement<any>;
29
+ /** If provided, renders this avatar URL inside the graph node circle instead of the default WIP dot. */
30
+ nodeAvatarUrl?: string;
31
+ };
32
+ /**
33
+ * Decoration map for secondary WIP rows, keyed by the row's `sha`. The library does not create rows from
34
+ * this map — provide the row in `graphRows` and this map augments it.
35
+ */
36
+ export type WipNodeMetadataBySha = Record<Sha, WipNodeMetadata>;
37
+ /**
38
+ * Batched set of secondary-WIP shas for which the library is requesting stats. Delivered to the
39
+ * `onWipShasMissingStats` callback after a 100ms debounce with dedup.
40
+ */
41
+ export type WipShasMissingStats = Record<Sha, true>;
42
+ /**
43
+ * Caller-supplied handler invoked when one or more secondary WIP rows need stats fetched.
44
+ * Handler should async-fetch and push back an updated `wipNodeMetadataBySha` reference with
45
+ * `workDirStats` populated for each requested sha.
46
+ */
47
+ export type OnWipShasMissingStats = (shas: WipShasMissingStats) => void;
48
+ /**
49
+ * Batched set of secondary-WIP shas currently rendered in the viewport. Delivered to the
50
+ * `onVisibleWipShasChanged` callback after a 100ms debounce when the set changes.
51
+ */
52
+ export type VisibleWipShas = Record<Sha, true>;
53
+ /**
54
+ * Caller-supplied handler invoked when the set of secondary WIP rows visible in the viewport changes.
55
+ * Use this to acquire/release per-worktree resources (filesystem watchers, subscriptions, etc.) as
56
+ * rows scroll in and out of view. Consumers diff the provided set against their own tracking to
57
+ * decide what to hook/unhook.
58
+ */
59
+ export type OnVisibleWipShasChanged = (shas: VisibleWipShas) => void;