@theia/scm 1.71.0-next.8 → 1.71.0

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 (80) hide show
  1. package/lib/browser/dirty-diff/dirty-diff-widget.js +1 -1
  2. package/lib/browser/dirty-diff/dirty-diff-widget.js.map +1 -1
  3. package/lib/browser/merge-editor/merge-editor-module.d.ts +6 -0
  4. package/lib/browser/merge-editor/merge-editor-module.d.ts.map +1 -1
  5. package/lib/browser/merge-editor/merge-editor-module.js +21 -2
  6. package/lib/browser/merge-editor/merge-editor-module.js.map +1 -1
  7. package/lib/browser/scm-context-key-service.d.ts +8 -0
  8. package/lib/browser/scm-context-key-service.d.ts.map +1 -1
  9. package/lib/browser/scm-context-key-service.js +16 -0
  10. package/lib/browser/scm-context-key-service.js.map +1 -1
  11. package/lib/browser/scm-contribution.d.ts.map +1 -1
  12. package/lib/browser/scm-contribution.js +143 -1
  13. package/lib/browser/scm-contribution.js.map +1 -1
  14. package/lib/browser/scm-frontend-module.d.ts.map +1 -1
  15. package/lib/browser/scm-frontend-module.js +14 -0
  16. package/lib/browser/scm-frontend-module.js.map +1 -1
  17. package/lib/browser/scm-history-graph-helpers.d.ts +39 -0
  18. package/lib/browser/scm-history-graph-helpers.d.ts.map +1 -0
  19. package/lib/browser/scm-history-graph-helpers.js +167 -0
  20. package/lib/browser/scm-history-graph-helpers.js.map +1 -0
  21. package/lib/browser/scm-history-graph-lanes.d.ts +59 -0
  22. package/lib/browser/scm-history-graph-lanes.d.ts.map +1 -0
  23. package/lib/browser/scm-history-graph-lanes.js +183 -0
  24. package/lib/browser/scm-history-graph-lanes.js.map +1 -0
  25. package/lib/browser/scm-history-graph-lanes.spec.d.ts +2 -0
  26. package/lib/browser/scm-history-graph-lanes.spec.d.ts.map +1 -0
  27. package/lib/browser/scm-history-graph-lanes.spec.js +554 -0
  28. package/lib/browser/scm-history-graph-lanes.spec.js.map +1 -0
  29. package/lib/browser/scm-history-graph-model.d.ts +46 -0
  30. package/lib/browser/scm-history-graph-model.d.ts.map +1 -0
  31. package/lib/browser/scm-history-graph-model.js +184 -0
  32. package/lib/browser/scm-history-graph-model.js.map +1 -0
  33. package/lib/browser/scm-history-graph-model.spec.d.ts +2 -0
  34. package/lib/browser/scm-history-graph-model.spec.d.ts.map +1 -0
  35. package/lib/browser/scm-history-graph-model.spec.js +131 -0
  36. package/lib/browser/scm-history-graph-model.spec.js.map +1 -0
  37. package/lib/browser/scm-history-graph-tooltip.d.ts +14 -0
  38. package/lib/browser/scm-history-graph-tooltip.d.ts.map +1 -0
  39. package/lib/browser/scm-history-graph-tooltip.js +190 -0
  40. package/lib/browser/scm-history-graph-tooltip.js.map +1 -0
  41. package/lib/browser/scm-history-graph-widget.d.ts +77 -0
  42. package/lib/browser/scm-history-graph-widget.d.ts.map +1 -0
  43. package/lib/browser/scm-history-graph-widget.js +490 -0
  44. package/lib/browser/scm-history-graph-widget.js.map +1 -0
  45. package/lib/browser/scm-provider.d.ts +61 -0
  46. package/lib/browser/scm-provider.d.ts.map +1 -1
  47. package/lib/browser/scm-provider.js.map +1 -1
  48. package/lib/browser/scm-repositories-widget.d.ts +6 -1
  49. package/lib/browser/scm-repositories-widget.d.ts.map +1 -1
  50. package/lib/browser/scm-repositories-widget.js +11 -4
  51. package/lib/browser/scm-repositories-widget.js.map +1 -1
  52. package/lib/browser/scm-repositories-widget.spec.js +1 -1
  53. package/lib/browser/scm-repositories-widget.spec.js.map +1 -1
  54. package/lib/browser/scm-service.d.ts.map +1 -1
  55. package/lib/browser/scm-service.js +4 -1
  56. package/lib/browser/scm-service.js.map +1 -1
  57. package/lib/browser/scm-service.spec.d.ts +2 -0
  58. package/lib/browser/scm-service.spec.d.ts.map +1 -0
  59. package/lib/browser/scm-service.spec.js +77 -0
  60. package/lib/browser/scm-service.spec.js.map +1 -0
  61. package/package.json +11 -11
  62. package/src/browser/dirty-diff/dirty-diff-widget.ts +1 -1
  63. package/src/browser/merge-editor/merge-editor-module.ts +24 -6
  64. package/src/browser/scm-context-key-service.ts +24 -0
  65. package/src/browser/scm-contribution.ts +157 -0
  66. package/src/browser/scm-frontend-module.ts +15 -0
  67. package/src/browser/scm-history-graph-helpers.ts +175 -0
  68. package/src/browser/scm-history-graph-lanes.spec.ts +635 -0
  69. package/src/browser/scm-history-graph-lanes.ts +258 -0
  70. package/src/browser/scm-history-graph-model.spec.ts +171 -0
  71. package/src/browser/scm-history-graph-model.ts +207 -0
  72. package/src/browser/scm-history-graph-tooltip.ts +213 -0
  73. package/src/browser/scm-history-graph-widget.tsx +712 -0
  74. package/src/browser/scm-provider.ts +68 -0
  75. package/src/browser/scm-repositories-widget.spec.ts +1 -1
  76. package/src/browser/scm-repositories-widget.tsx +10 -3
  77. package/src/browser/scm-service.spec.ts +91 -0
  78. package/src/browser/scm-service.ts +4 -1
  79. package/src/browser/style/index.css +12 -13
  80. package/src/browser/style/scm-history-graph.css +313 -0
@@ -0,0 +1,213 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 EclipseSource GmbH and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer';
18
+ import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering/markdown-string';
19
+ import { codicon } from '@theia/core/lib/browser/widgets/widget';
20
+ import { nls } from '@theia/core/lib/common/nls';
21
+ import { ScmHistoryItemRef } from './scm-provider';
22
+ import { HistoryGraphEntry } from './scm-history-graph-model';
23
+ import { laneColor, getRefBadgeClass, deduplicateRefs, isTagRef, isRemoteRef } from './scm-history-graph-helpers';
24
+
25
+ export function formatRelativeTime(ms: number): string {
26
+ const now = Date.now();
27
+ const diffMs = now - ms;
28
+ const diffSec = Math.floor(diffMs / 1000);
29
+ const diffMin = Math.floor(diffSec / 60);
30
+ const diffHour = Math.floor(diffMin / 60);
31
+ const diffDay = Math.floor(diffHour / 24);
32
+
33
+ if (diffDay > 30) {
34
+ return new Date(ms).toLocaleDateString();
35
+ }
36
+ if (diffDay >= 1) {
37
+ return diffDay === 1
38
+ ? nls.localizeByDefault('{0} day ago', diffDay)
39
+ : nls.localizeByDefault('{0} days ago', diffDay);
40
+ }
41
+ if (diffHour >= 1) {
42
+ return diffHour === 1
43
+ ? nls.localize('theia/scm/1HourAgo', '1 hour ago')
44
+ : nls.localizeByDefault('{0} hours ago', diffHour);
45
+ }
46
+ if (diffMin >= 1) {
47
+ return diffMin === 1
48
+ ? nls.localize('theia/scm/1MinuteAgo', '1 minute ago')
49
+ : nls.localizeByDefault('{0} minutes ago', diffMin);
50
+ }
51
+ return nls.localize('theia/scm/justNow', 'just now');
52
+ }
53
+
54
+ export function formatAbsoluteDate(ms: number): string {
55
+ return new Date(ms).toLocaleString(undefined, {
56
+ year: 'numeric',
57
+ month: 'long',
58
+ day: 'numeric',
59
+ hour: 'numeric',
60
+ minute: '2-digit',
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Appends a theme icon `<i>` element followed by a text node into a container.
66
+ */
67
+ export function appendIconText(container: HTMLElement, iconName: string, text: string): void {
68
+ const icon = document.createElement('i');
69
+ icon.className = codicon(iconName) + ' icon-inline';
70
+ container.appendChild(icon);
71
+ container.appendChild(document.createTextNode(` ${text}`));
72
+ }
73
+
74
+ export function createHoverHr(): HTMLElement {
75
+ return document.createElement('hr');
76
+ }
77
+
78
+ /** Creates a ref badge element for the HTML tooltip. */
79
+ export function buildTooltipRefBadge(
80
+ ref: ScmHistoryItemRef,
81
+ iconClass: string,
82
+ showText: boolean,
83
+ bgColor: string,
84
+ extraClass?: string
85
+ ): HTMLElement {
86
+ const badge = document.createElement('span');
87
+ badge.className = `scm-history-ref-badge ${getRefBadgeClass(ref)} tooltip-badge${extraClass ? ' ' + extraClass : ''}`;
88
+ badge.title = ref.description ?? ref.name;
89
+ badge.style.backgroundColor = bgColor;
90
+ badge.style.color = 'var(--theia-scmGraph-historyItemRefForeground, var(--theia-badge-foreground))';
91
+ const icon = document.createElement('i');
92
+ icon.className = `codicon ${iconClass} scm-history-ref-icon`;
93
+ badge.appendChild(icon);
94
+ if (showText) {
95
+ const text = document.createElement('span');
96
+ text.className = 'scm-history-ref-text';
97
+ text.textContent = ref.name;
98
+ badge.appendChild(text);
99
+ }
100
+ return badge;
101
+ }
102
+
103
+ export function buildHtmlTooltip(entry: HistoryGraphEntry, markdownRenderer: MarkdownRenderer): HTMLElement {
104
+ const { item } = entry;
105
+ const badgeColor = laneColor(entry.graphRow.color);
106
+ const container = document.createElement('div');
107
+ container.className = 'scm-history-tooltip';
108
+
109
+ // Header
110
+ if (item.author || item.timestamp !== undefined) {
111
+ const header = document.createElement('div');
112
+ header.className = 'scm-history-tooltip-header';
113
+ header.style.fontWeight = 'bold';
114
+ header.style.marginBottom = '4px';
115
+
116
+ if (item.author) {
117
+ appendIconText(header, 'account', item.author);
118
+ }
119
+ if (item.timestamp !== undefined) {
120
+ if (item.author) {
121
+ header.appendChild(document.createTextNode('\u00a0\u00a0'));
122
+ }
123
+ const timeSpan = document.createElement('span');
124
+ appendIconText(timeSpan, 'clock', `${formatRelativeTime(item.timestamp)} (${formatAbsoluteDate(item.timestamp)})`);
125
+ header.appendChild(timeSpan);
126
+ }
127
+
128
+ container.appendChild(header);
129
+ container.appendChild(createHoverHr());
130
+ }
131
+
132
+ // Body
133
+ const bodyText = (item.message && item.message.trim() !== item.subject.trim())
134
+ ? item.message.trim()
135
+ : item.subject;
136
+ const bodyMd = new MarkdownStringImpl(bodyText);
137
+ const rendered = markdownRenderer.render(bodyMd);
138
+ container.appendChild(rendered.element);
139
+
140
+ // Stats
141
+ if (item.statistics) {
142
+ const s = item.statistics;
143
+ container.appendChild(createHoverHr());
144
+
145
+ const stats = document.createElement('div');
146
+ stats.className = 'scm-history-tooltip-stats';
147
+
148
+ const fileCount = s.files === 1 ? '1 file changed' : `${s.files} files changed`;
149
+ const filesStrong = document.createElement('strong');
150
+ filesStrong.textContent = fileCount;
151
+ stats.appendChild(filesStrong);
152
+
153
+ if (s.insertions > 0) {
154
+ stats.appendChild(document.createTextNode(', '));
155
+ const ins = document.createElement('span');
156
+ ins.className = 'scm-history-stat-added';
157
+ ins.textContent = `${s.insertions} insertion${s.insertions === 1 ? '' : 's'}(+)`;
158
+ stats.appendChild(ins);
159
+ }
160
+
161
+ if (s.deletions > 0) {
162
+ stats.appendChild(document.createTextNode(', '));
163
+ const del = document.createElement('span');
164
+ del.className = 'scm-history-stat-deleted';
165
+ del.textContent = `${s.deletions} deletion${s.deletions === 1 ? '' : 's'}(-)`;
166
+ stats.appendChild(del);
167
+ }
168
+
169
+ container.appendChild(stats);
170
+ }
171
+
172
+ // Refs + hash
173
+ const hasRefs = item.references && item.references.length > 0;
174
+ if (hasRefs || item.displayId) {
175
+ container.appendChild(createHoverHr());
176
+
177
+ const refsRow = document.createElement('div');
178
+ refsRow.className = 'scm-history-tooltip-refs';
179
+ refsRow.style.display = 'flex';
180
+ refsRow.style.flexWrap = 'wrap';
181
+ refsRow.style.gap = '4px';
182
+ refsRow.style.alignItems = 'center';
183
+
184
+ if (hasRefs) {
185
+ const deduplicated = deduplicateRefs(item.references!);
186
+ for (const { ref, hasBoth } of deduplicated) {
187
+ const isTag = isTagRef(ref);
188
+ const isRemote = isRemoteRef(ref);
189
+
190
+ if (isTag) {
191
+ refsRow.appendChild(buildTooltipRefBadge(ref, 'codicon-tag', true, badgeColor));
192
+ } else if (isRemote) {
193
+ refsRow.appendChild(buildTooltipRefBadge(ref, 'codicon-cloud', true, badgeColor));
194
+ } else {
195
+ refsRow.appendChild(buildTooltipRefBadge(ref, 'codicon-git-branch', true, badgeColor));
196
+ if (hasBoth) {
197
+ refsRow.appendChild(buildTooltipRefBadge(ref, 'codicon-cloud', false, badgeColor, 'scm-history-ref-badge-cloud'));
198
+ }
199
+ }
200
+ }
201
+ }
202
+
203
+ if (item.displayId) {
204
+ const hash = document.createElement('code');
205
+ hash.textContent = item.displayId;
206
+ refsRow.appendChild(hash);
207
+ }
208
+
209
+ container.appendChild(refsRow);
210
+ }
211
+
212
+ return container;
213
+ }