@theia/scm 1.75.0-next.3 → 1.75.0-next.35

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 (83) hide show
  1. package/lib/browser/decorations/scm-navigator-decorator.d.ts.map +1 -1
  2. package/lib/browser/decorations/scm-navigator-decorator.js +1 -0
  3. package/lib/browser/decorations/scm-navigator-decorator.js.map +1 -1
  4. package/lib/browser/scm-context-key-service.d.ts +2 -0
  5. package/lib/browser/scm-context-key-service.d.ts.map +1 -1
  6. package/lib/browser/scm-context-key-service.js +4 -0
  7. package/lib/browser/scm-context-key-service.js.map +1 -1
  8. package/lib/browser/scm-context-key-service.spec.js +7 -0
  9. package/lib/browser/scm-context-key-service.spec.js.map +1 -1
  10. package/lib/browser/scm-frontend-module.d.ts.map +1 -1
  11. package/lib/browser/scm-frontend-module.js +6 -0
  12. package/lib/browser/scm-frontend-module.js.map +1 -1
  13. package/lib/browser/scm-history-graph-contribution.d.ts +44 -0
  14. package/lib/browser/scm-history-graph-contribution.d.ts.map +1 -0
  15. package/lib/browser/scm-history-graph-contribution.js +226 -0
  16. package/lib/browser/scm-history-graph-contribution.js.map +1 -0
  17. package/lib/browser/scm-history-graph-contribution.spec.d.ts +2 -0
  18. package/lib/browser/scm-history-graph-contribution.spec.d.ts.map +1 -0
  19. package/lib/browser/scm-history-graph-contribution.spec.js +66 -0
  20. package/lib/browser/scm-history-graph-contribution.spec.js.map +1 -0
  21. package/lib/browser/scm-history-graph-helpers.d.ts +34 -6
  22. package/lib/browser/scm-history-graph-helpers.d.ts.map +1 -1
  23. package/lib/browser/scm-history-graph-helpers.js +71 -5
  24. package/lib/browser/scm-history-graph-helpers.js.map +1 -1
  25. package/lib/browser/scm-history-graph-helpers.spec.d.ts +2 -0
  26. package/lib/browser/scm-history-graph-helpers.spec.d.ts.map +1 -0
  27. package/lib/browser/scm-history-graph-helpers.spec.js +96 -0
  28. package/lib/browser/scm-history-graph-helpers.spec.js.map +1 -0
  29. package/lib/browser/scm-history-graph-lanes.d.ts +19 -5
  30. package/lib/browser/scm-history-graph-lanes.d.ts.map +1 -1
  31. package/lib/browser/scm-history-graph-lanes.js +25 -9
  32. package/lib/browser/scm-history-graph-lanes.js.map +1 -1
  33. package/lib/browser/scm-history-graph-lanes.spec.js +62 -25
  34. package/lib/browser/scm-history-graph-lanes.spec.js.map +1 -1
  35. package/lib/browser/scm-history-graph-model.d.ts +52 -5
  36. package/lib/browser/scm-history-graph-model.d.ts.map +1 -1
  37. package/lib/browser/scm-history-graph-model.js +108 -10
  38. package/lib/browser/scm-history-graph-model.js.map +1 -1
  39. package/lib/browser/scm-history-graph-model.spec.js +197 -4
  40. package/lib/browser/scm-history-graph-model.spec.js.map +1 -1
  41. package/lib/browser/scm-history-graph-tooltip.d.ts +27 -2
  42. package/lib/browser/scm-history-graph-tooltip.d.ts.map +1 -1
  43. package/lib/browser/scm-history-graph-tooltip.js +113 -22
  44. package/lib/browser/scm-history-graph-tooltip.js.map +1 -1
  45. package/lib/browser/scm-history-graph-tooltip.spec.d.ts +2 -0
  46. package/lib/browser/scm-history-graph-tooltip.spec.d.ts.map +1 -0
  47. package/lib/browser/scm-history-graph-tooltip.spec.js +213 -0
  48. package/lib/browser/scm-history-graph-tooltip.spec.js.map +1 -0
  49. package/lib/browser/scm-history-graph-widget.d.ts +23 -1
  50. package/lib/browser/scm-history-graph-widget.d.ts.map +1 -1
  51. package/lib/browser/scm-history-graph-widget.js +93 -24
  52. package/lib/browser/scm-history-graph-widget.js.map +1 -1
  53. package/lib/browser/scm-history-graph-widget.spec.d.ts +2 -0
  54. package/lib/browser/scm-history-graph-widget.spec.d.ts.map +1 -0
  55. package/lib/browser/scm-history-graph-widget.spec.js +100 -0
  56. package/lib/browser/scm-history-graph-widget.spec.js.map +1 -0
  57. package/lib/browser/scm-provider.d.ts +9 -1
  58. package/lib/browser/scm-provider.d.ts.map +1 -1
  59. package/lib/browser/scm-provider.js.map +1 -1
  60. package/lib/common/scm-preferences.d.ts +3 -0
  61. package/lib/common/scm-preferences.d.ts.map +1 -1
  62. package/lib/common/scm-preferences.js +24 -0
  63. package/lib/common/scm-preferences.js.map +1 -1
  64. package/package.json +6 -6
  65. package/src/browser/decorations/scm-navigator-decorator.ts +3 -2
  66. package/src/browser/scm-context-key-service.spec.ts +9 -0
  67. package/src/browser/scm-context-key-service.ts +6 -0
  68. package/src/browser/scm-frontend-module.ts +7 -1
  69. package/src/browser/scm-history-graph-contribution.spec.ts +83 -0
  70. package/src/browser/scm-history-graph-contribution.ts +222 -0
  71. package/src/browser/scm-history-graph-helpers.spec.ts +118 -0
  72. package/src/browser/scm-history-graph-helpers.ts +81 -6
  73. package/src/browser/scm-history-graph-lanes.spec.ts +70 -25
  74. package/src/browser/scm-history-graph-lanes.ts +48 -11
  75. package/src/browser/scm-history-graph-model.spec.ts +247 -12
  76. package/src/browser/scm-history-graph-model.ts +125 -12
  77. package/src/browser/scm-history-graph-tooltip.spec.ts +254 -0
  78. package/src/browser/scm-history-graph-tooltip.ts +144 -24
  79. package/src/browser/scm-history-graph-widget.spec.ts +112 -0
  80. package/src/browser/scm-history-graph-widget.tsx +104 -35
  81. package/src/browser/scm-provider.ts +9 -1
  82. package/src/browser/style/scm-history-graph.css +43 -2
  83. package/src/common/scm-preferences.ts +27 -0
@@ -16,10 +16,11 @@
16
16
 
17
17
  import * as React from '@theia/core/shared/react';
18
18
  import { Virtuoso } from '@theia/core/shared/react-virtuoso';
19
- import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
19
+ import { injectable, inject, postConstruct, named } from '@theia/core/shared/inversify';
20
20
  import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
21
21
  import { LabelProvider } from '@theia/core/lib/browser/label-provider';
22
22
  import { HoverService } from '@theia/core/lib/browser/hover-service';
23
+ import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
23
24
  import { MarkdownRenderer, MarkdownRendererFactory } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer';
24
25
  import { ScmHistoryGraphModel, HistoryGraphEntry } from './scm-history-graph-model';
25
26
  import { ScmHistoryItemRef, ScmHistoryItemChange } from './scm-provider';
@@ -28,16 +29,21 @@ import { GraphRow } from './scm-history-graph-lanes';
28
29
  import URI from '@theia/core/lib/common/uri';
29
30
  import { nls } from '@theia/core/lib/common/nls';
30
31
  import { CancellationTokenSource } from '@theia/core/lib/common/cancellation';
32
+ import { DisposableCollection } from '@theia/core/lib/common/disposable';
33
+ import { Emitter, Event } from '@theia/core/lib/common/event';
34
+ import { DynamicToolbarWidget } from '@theia/core/lib/browser/view-container';
31
35
  import { MenuPath } from '@theia/core/lib/common/menu/menu-types';
32
36
  import { ContextMenuRenderer } from '@theia/core/lib/browser/context-menu-renderer';
33
37
  import { OpenerService, open } from '@theia/core/lib/browser/opener-service';
34
38
  import { DiffUris } from '@theia/core/lib/browser/diff-uris';
35
39
  import { ScmContextKeyService } from './scm-context-key-service';
40
+ import { ScmPreferences } from '../common/scm-preferences';
36
41
  import {
37
- laneColor, getChangeStatus, getFileName, getFilePath, getRepoRelativePath,
38
- getRefBadgeClass, isTagRef, isRemoteRef, deduplicateRefs, DeduplicatedRef
42
+ laneColor, filterRefsForBadges, getChangeStatus, getFileName, getFilePath, getRepoRelativePath,
43
+ getRefBadgeClass, getRefBadgePresentation, isTagRef, isRemoteRef, deduplicateRefs, DeduplicatedRef
39
44
  } from './scm-history-graph-helpers';
40
- import { buildHtmlTooltip } from './scm-history-graph-tooltip';
45
+ import { ILogger } from '@theia/core';
46
+ import { buildHtmlTooltip, buildProviderTooltip } from './scm-history-graph-tooltip';
41
47
 
42
48
  /** Menu path matching the VS Code 'scm/history/title' contribution point (graph section toolbar). */
43
49
  export const SCM_HISTORY_TITLE_MENU: MenuPath = ['plugin_scm/history/title'];
@@ -80,19 +86,24 @@ function renderJsxRefBadge(
80
86
  // ── Widget ──────────────────────────────────────────────────────────────────
81
87
 
82
88
  @injectable()
83
- export class ScmHistoryGraphWidget extends ReactWidget {
89
+ export class ScmHistoryGraphWidget extends ReactWidget implements DynamicToolbarWidget {
84
90
 
85
91
  static readonly ID = 'scm-history-graph-widget';
86
92
  static readonly LABEL = nls.localizeByDefault('Graph');
87
93
 
88
94
  @inject(ScmHistoryGraphModel) protected readonly model: ScmHistoryGraphModel;
89
95
  @inject(HoverService) protected readonly hoverService: HoverService;
96
+ @inject(ClipboardService) protected readonly clipboardService: ClipboardService;
90
97
  @inject(MarkdownRendererFactory) protected readonly markdownRendererFactory: MarkdownRendererFactory;
91
98
  @inject(ScmService) protected readonly scmService: ScmService;
92
99
  @inject(LabelProvider) protected readonly labelProvider: LabelProvider;
93
100
  @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer;
94
101
  @inject(OpenerService) protected readonly openerService: OpenerService;
95
102
  @inject(ScmContextKeyService) protected readonly scmContextKeys: ScmContextKeyService;
103
+ @inject(ScmPreferences) protected readonly scmPreferences: ScmPreferences;
104
+
105
+ @inject(ILogger) @named('scm:ScmHistoryGraphWidget')
106
+ protected readonly logger: ILogger;
96
107
 
97
108
  protected selectedIndex = -1;
98
109
  /** Currently selected change row key (`${itemId}-${ci}`), or undefined. */
@@ -103,6 +114,12 @@ export class ScmHistoryGraphWidget extends ReactWidget {
103
114
  protected expandedIds = new Set<string>();
104
115
  /** Map from commit id → in-flight CancellationTokenSource for loadChanges. */
105
116
  protected loadChangesCts = new Map<string, CancellationTokenSource>();
117
+ /** Cleanup for the content of the hover currently being shown. */
118
+ protected readonly toDisposeOnHover = new DisposableCollection();
119
+
120
+ protected readonly onDidChangeToolbarItemsEmitter = new Emitter<void>();
121
+ /** Re-renders the part toolbar on model changes, e.g. to reflect the toggled state of the ref filter picker. */
122
+ readonly onDidChangeToolbarItems: Event<void> = this.onDidChangeToolbarItemsEmitter.event;
106
123
 
107
124
  constructor() {
108
125
  super();
@@ -116,10 +133,19 @@ export class ScmHistoryGraphWidget extends ReactWidget {
116
133
 
117
134
  @postConstruct()
118
135
  protected init(): void {
136
+ this.toDispose.push(this.onDidChangeToolbarItemsEmitter);
119
137
  this.toDispose.push(
120
138
  this.model.onDidChange(() => {
121
139
  this.updateContextKeys();
122
140
  this.update();
141
+ this.onDidChangeToolbarItemsEmitter.fire();
142
+ })
143
+ );
144
+ this.toDispose.push(
145
+ this.scmPreferences.onPreferenceChanged(e => {
146
+ if (e.preferenceName.startsWith('scm.graph.')) {
147
+ this.update();
148
+ }
123
149
  })
124
150
  );
125
151
  this.toDispose.push({
@@ -131,6 +157,7 @@ export class ScmHistoryGraphWidget extends ReactWidget {
131
157
  this.loadChangesCts.clear();
132
158
  }
133
159
  });
160
+ this.toDispose.push(this.toDisposeOnHover);
134
161
  this.update();
135
162
  }
136
163
 
@@ -138,6 +165,8 @@ export class ScmHistoryGraphWidget extends ReactWidget {
138
165
  const provider = this.model.provider;
139
166
  this.scmContextKeys.scmCurrentHistoryItemRefHasRemote.set(!!provider?.currentHistoryItemRemoteRef);
140
167
  this.scmContextKeys.scmCurrentHistoryItemRefHasBase.set(!!provider?.currentHistoryItemBaseRef);
168
+ // Commands like git.pullRef/git.pushRef gate their enablement on this key.
169
+ this.scmContextKeys.scmCurrentHistoryItemRefInFilter.set(!!provider?.currentHistoryItemRef && this.model.isCurrentRefInFilter());
141
170
  }
142
171
 
143
172
  protected render(): React.ReactNode {
@@ -189,7 +218,7 @@ export class ScmHistoryGraphWidget extends ReactWidget {
189
218
  className='scm-history-graph-list'
190
219
  data={entries as HistoryGraphEntry[]}
191
220
  itemContent={(idx, entry) => this.renderRow(entry, idx, svgWidth)}
192
- endReached={hasMore && !loading ? this.handleEndReached : undefined}
221
+ endReached={hasMore && !loading && this.scmPreferences['scm.graph.pageOnScroll'] !== false ? this.handleEndReached : undefined}
193
222
  overscan={500}
194
223
  components={footer ? { Footer: footer } : {}}
195
224
  style={{ overflowX: 'hidden' }}
@@ -208,8 +237,7 @@ export class ScmHistoryGraphWidget extends ReactWidget {
208
237
  const isSelected = idx === this.selectedIndex;
209
238
  const isExpanded = this.expandedIds.has(item.id);
210
239
  const changes = this.expandedChanges.get(item.id);
211
- // The first commit (idx === 0) on lane 0 is treated as HEAD/current
212
- const isHead = idx === 0 && graphRow.lane === 0;
240
+ const isHead = entry.isCurrent;
213
241
 
214
242
  return (
215
243
  <React.Fragment key={item.id}>
@@ -244,14 +272,49 @@ export class ScmHistoryGraphWidget extends ReactWidget {
244
272
  }
245
273
 
246
274
  protected handleRowMouseEnter = (e: React.MouseEvent<HTMLDivElement>, entry: HistoryGraphEntry): void => {
275
+ this.toDisposeOnHover.dispose();
247
276
  this.hoverService.requestHover({
248
- content: buildHtmlTooltip(entry, this.markdownRenderer),
277
+ content: this.buildTooltip(entry),
249
278
  target: e.currentTarget,
250
279
  position: 'right',
251
280
  interactive: true,
252
281
  });
253
282
  };
254
283
 
284
+ /**
285
+ * Prefers the hover content supplied by the provider, so that its own links and commands
286
+ * (author `mailto:`, "Open Commit", remote actions) stay intact, and falls back to the hover
287
+ * derived from the history item's fields for providers that supply none.
288
+ */
289
+ protected buildTooltip(entry: HistoryGraphEntry): HTMLElement {
290
+ const { tooltip } = entry.item;
291
+ if (tooltip) {
292
+ const provided = buildProviderTooltip(tooltip, this.markdownRenderer, this.openerService, this.toDisposeOnHover);
293
+ if (provided) {
294
+ return provided;
295
+ }
296
+ }
297
+ return buildHtmlTooltip(entry, this.markdownRenderer, this.model.provider, {
298
+ openCommit: () => this.openCommitFromTooltip(entry),
299
+ copyCommitHash: () => this.clipboardService.writeText(entry.item.id),
300
+ });
301
+ }
302
+
303
+ /** Selects the commit and expands its changes, closing the hover first. */
304
+ protected openCommitFromTooltip(entry: HistoryGraphEntry): void {
305
+ this.hoverService.cancelHover();
306
+ const idx = this.model.entries.indexOf(entry);
307
+ if (idx < 0) {
308
+ return;
309
+ }
310
+ if (this.expandedIds.has(entry.item.id)) {
311
+ this.selectedIndex = idx;
312
+ this.update();
313
+ } else {
314
+ this.handleRowClick(idx, entry);
315
+ }
316
+ }
317
+
255
318
  protected renderChangesRows(
256
319
  itemId: string,
257
320
  svgWidth: number,
@@ -347,7 +410,7 @@ export class ScmHistoryGraphWidget extends ReactWidget {
347
410
  open(this.openerService, new URI(change.uri));
348
411
  }
349
412
  } catch (err) {
350
- console.error('ScmHistoryGraphWidget: failed to open change', err);
413
+ this.logger.error('Failed to open change', err);
351
414
  }
352
415
  }
353
416
 
@@ -462,7 +525,9 @@ export class ScmHistoryGraphWidget extends ReactWidget {
462
525
 
463
526
  // Commit lane lines — split at cy=11
464
527
  // Top segment: only drawn if there is an incoming line from above
465
- // (i.e. this commit was referenced as a parent by an earlier row)
528
+ // (i.e. this commit was referenced as a parent by an earlier row).
529
+ // It keeps the color inherited from the chain above, which may differ
530
+ // from the commit's own (ref-based) color.
466
531
  if (row.hasTopLine) {
467
532
  elements.push(
468
533
  <path
@@ -471,7 +536,7 @@ export class ScmHistoryGraphWidget extends ReactWidget {
471
536
  strokeWidth='1px'
472
537
  strokeLinecap='round'
473
538
  d={`M ${commitX} 0 V ${cy}`}
474
- style={{ stroke: commitColor }}
539
+ style={{ stroke: laneColor(row.topColor) }}
475
540
  />
476
541
  );
477
542
  }
@@ -560,11 +625,14 @@ export class ScmHistoryGraphWidget extends ReactWidget {
560
625
  return undefined;
561
626
  }
562
627
 
628
+ const provider = this.model.provider;
629
+ const visibleRefs = filterRefsForBadges(refs, provider, this.scmPreferences['scm.graph.badges'] ?? 'filter', this.model.historyItemRefFilter);
630
+ if (visibleRefs.length === 0) {
631
+ return undefined;
632
+ }
563
633
  const laneColorValue = entry ? laneColor(entry.graphRow.color) : undefined;
564
- const deduplicated = deduplicateRefs(refs);
565
- const style: React.CSSProperties = laneColorValue
566
- ? { backgroundColor: laneColorValue, color: 'var(--theia-scmGraph-historyItemRefForeground, var(--theia-badge-foreground))' }
567
- : {};
634
+ const deduplicated = deduplicateRefs(visibleRefs);
635
+ const badgeForeground = 'var(--theia-scmGraph-historyItemRefForeground, var(--theia-badge-foreground))';
568
636
 
569
637
  const badges: React.ReactElement[] = [];
570
638
  for (const info of deduplicated) {
@@ -576,24 +644,25 @@ export class ScmHistoryGraphWidget extends ReactWidget {
576
644
  const isRemote = isRemoteRef(ref);
577
645
  const onContextMenu = entry ? (e: React.MouseEvent) => this.handleRefBadgeContextMenu(e, entry, ref) : undefined;
578
646
 
579
- if (isTag) {
580
- badges.push(renderJsxRefBadge(ref, 'codicon-tag', false, style, ref.id, onContextMenu));
581
- } else if (isRemote) {
582
- badges.push(renderJsxRefBadge(ref, 'codicon-cloud', true, style, ref.id, onContextMenu));
583
- } else {
584
- badges.push(renderJsxRefBadge(ref, 'codicon-git-branch', true, style, ref.id, onContextMenu));
585
- if (hasBoth && badges.length < 3) {
586
- badges.push(
587
- <span
588
- key={`${ref.id}-cloud`}
589
- className='scm-history-ref-badge scm-history-ref-badge-cloud'
590
- title={ref.description ?? ref.name}
591
- style={style}
592
- >
593
- <i className='codicon codicon-cloud scm-history-ref-icon' />
594
- </span>
595
- );
596
- }
647
+ // Current/remote/base refs are colored by role, others by the row's lane color
648
+ const { iconClass, colorIndex } = getRefBadgePresentation(ref, provider);
649
+ const backgroundColor = colorIndex !== undefined ? laneColor(colorIndex) : laneColorValue;
650
+ const style: React.CSSProperties = backgroundColor
651
+ ? { backgroundColor, color: badgeForeground }
652
+ : {};
653
+
654
+ badges.push(renderJsxRefBadge(ref, iconClass, !isTag, style, ref.id, onContextMenu));
655
+ if (!isTag && !isRemote && hasBoth && badges.length < 3) {
656
+ badges.push(
657
+ <span
658
+ key={`${ref.id}-cloud`}
659
+ className='scm-history-ref-badge scm-history-ref-badge-cloud'
660
+ title={ref.description ?? ref.name}
661
+ style={style}
662
+ >
663
+ <i className='codicon codicon-cloud scm-history-ref-icon' />
664
+ </span>
665
+ );
597
666
  }
598
667
  }
599
668
  return badges;
@@ -690,7 +759,7 @@ export class ScmHistoryGraphWidget extends ReactWidget {
690
759
  }
691
760
  } catch (err) {
692
761
  if (!cts.token.isCancellationRequested) {
693
- console.error('ScmHistoryGraphWidget: failed to load changes', err);
762
+ this.logger.error('Failed to load changes', err);
694
763
  this.expandedChanges.set(item.id, []);
695
764
  this.update();
696
765
  }
@@ -19,6 +19,7 @@
19
19
  import { Disposable, Event } from '@theia/core/lib/common';
20
20
  import URI from '@theia/core/lib/common/uri';
21
21
  import { CancellationToken } from '@theia/core/lib/common/cancellation';
22
+ import { MarkdownString } from '@theia/core/lib/common/markdown-rendering/markdown-string';
22
23
 
23
24
  export interface ScmProvider extends Disposable {
24
25
  readonly id: string;
@@ -145,7 +146,14 @@ export interface ScmHistoryItem {
145
146
  readonly authorIcon?: string;
146
147
  readonly displayId?: string;
147
148
  readonly timestamp?: number;
148
- readonly tooltip?: string;
149
+ /**
150
+ * Hover content supplied by the provider. When present, it is rendered instead of the
151
+ * hover the history graph derives from the other fields, so that providers stay in control
152
+ * of their own links and commands. May be split into sections, which are rendered separated
153
+ * by horizontal rules. Note that `command:` links are only executed if the containing
154
+ * {@link MarkdownString} declares them via {@link MarkdownString.isTrusted}.
155
+ */
156
+ readonly tooltip?: string | MarkdownString | readonly MarkdownString[];
149
157
  readonly statistics?: ScmHistoryItemStatistics;
150
158
  readonly references?: readonly ScmHistoryItemRef[];
151
159
  }
@@ -286,18 +286,59 @@
286
286
  opacity: 0.6;
287
287
  }
288
288
 
289
+ /* Markdown rendered into the tooltip keeps the browser's default paragraph margins,
290
+ which spaces the commit hover out far more than the git blame hover it mirrors. */
291
+ .scm-history-tooltip p {
292
+ margin: 0;
293
+ }
294
+
295
+ .scm-history-tooltip p + p {
296
+ margin-top: 4px;
297
+ }
298
+
289
299
  /* ── Tooltip header icon alignment ──────────────────────────────────────── */
300
+ .scm-history-tooltip-header {
301
+ margin-bottom: 4px;
302
+ }
303
+
290
304
  .scm-history-tooltip-header .icon-inline {
291
305
  vertical-align: middle;
292
306
  }
293
307
 
308
+ .scm-history-tooltip-header a {
309
+ color: var(--theia-textLink-foreground);
310
+ text-decoration: none;
311
+ }
312
+
313
+ .scm-history-tooltip-header a:hover {
314
+ text-decoration: underline;
315
+ }
316
+
317
+ .scm-history-tooltip-avatar {
318
+ width: 20px;
319
+ height: 20px;
320
+ border-radius: 50%;
321
+ vertical-align: middle;
322
+ }
323
+
324
+ /* ── Tooltip actions (aligned with the git blame hover command links) ───── */
325
+ .scm-history-tooltip-actions a.scm-history-tooltip-action {
326
+ color: var(--theia-textLink-foreground);
327
+ cursor: pointer;
328
+ text-decoration: none;
329
+ }
330
+
331
+ .scm-history-tooltip-actions a.scm-history-tooltip-action:hover {
332
+ text-decoration: underline;
333
+ }
334
+
294
335
  /* ── Tooltip stat colors (used inside .theia-hover) ─────────────────────── */
295
336
  .scm-history-stat-added {
296
- color: var(--theia-gitDecoration-addedResourceForeground, #388a34);
337
+ color: var(--theia-scmGraph-historyItemHoverAdditionsForeground, var(--theia-gitDecoration-addedResourceForeground, #388a34));
297
338
  }
298
339
 
299
340
  .scm-history-stat-deleted {
300
- color: var(--theia-gitDecoration-deletedResourceForeground, #a1260d);
341
+ color: var(--theia-scmGraph-historyItemHoverDeletionsForeground, var(--theia-gitDecoration-deletedResourceForeground, #a1260d));
301
342
  }
302
343
 
303
344
  /* ── Empty state ─────────────────────────────────────────────────────────── */
@@ -35,12 +35,39 @@ export const scmPreferenceSchema: PreferenceSchema = {
35
35
  ],
36
36
  description: nls.localizeByDefault('Controls the default Source Control repository view mode.'),
37
37
  default: 'list'
38
+ },
39
+ 'scm.graph.badges': {
40
+ type: 'string',
41
+ enum: ['all', 'filter'],
42
+ enumDescriptions: [
43
+ nls.localizeByDefault('Show badges of all history item groups in the Source Control Graph view.'),
44
+ nls.localizeByDefault('Show only the badges of history item groups used as a filter in the Source Control Graph view.')
45
+ ],
46
+ description: nls.localizeByDefault(
47
+ // eslint-disable-next-line max-len
48
+ 'Controls which badges are shown in the Source Control Graph view. The badges are shown on the right side of the graph indicating the names of history item groups.'),
49
+ default: 'filter'
50
+ },
51
+ 'scm.graph.pageOnScroll': {
52
+ type: 'boolean',
53
+ description: nls.localizeByDefault('Controls whether the Source Control Graph view will load the next page of items when you scroll to the end of the list.'),
54
+ default: true
55
+ },
56
+ 'scm.graph.pageSize': {
57
+ type: 'number',
58
+ minimum: 1,
59
+ maximum: 1000,
60
+ description: nls.localizeByDefault('The number of items to show in the Source Control Graph view by default and when loading more items.'),
61
+ default: 50
38
62
  }
39
63
  }
40
64
  };
41
65
 
42
66
  export interface ScmConfiguration {
43
67
  'scm.defaultViewMode': 'tree' | 'list'
68
+ 'scm.graph.badges': 'all' | 'filter'
69
+ 'scm.graph.pageOnScroll': boolean
70
+ 'scm.graph.pageSize': number
44
71
  }
45
72
 
46
73
  export const ScmPreferenceContribution = Symbol('ScmPreferenceContribution');