@pierre/diffs 1.1.0-beta.17 → 1.1.0-beta.19

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 (65) hide show
  1. package/dist/components/File.d.ts +3 -5
  2. package/dist/components/File.d.ts.map +1 -1
  3. package/dist/components/File.js +12 -21
  4. package/dist/components/File.js.map +1 -1
  5. package/dist/components/FileDiff.d.ts +5 -6
  6. package/dist/components/FileDiff.d.ts.map +1 -1
  7. package/dist/components/FileDiff.js +21 -22
  8. package/dist/components/FileDiff.js.map +1 -1
  9. package/dist/components/VirtualizedFileDiff.d.ts +1 -0
  10. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  11. package/dist/components/VirtualizedFileDiff.js +6 -0
  12. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  13. package/dist/index.d.ts +3 -4
  14. package/dist/index.js +2 -3
  15. package/dist/managers/InteractionManager.d.ts +140 -0
  16. package/dist/managers/InteractionManager.d.ts.map +1 -0
  17. package/dist/managers/InteractionManager.js +789 -0
  18. package/dist/managers/InteractionManager.js.map +1 -0
  19. package/dist/managers/ResizeManager.d.ts +0 -2
  20. package/dist/managers/ResizeManager.d.ts.map +1 -1
  21. package/dist/managers/ResizeManager.js +43 -32
  22. package/dist/managers/ResizeManager.js.map +1 -1
  23. package/dist/react/index.d.ts +2 -2
  24. package/dist/react/jsx.d.ts.map +1 -1
  25. package/dist/react/types.d.ts +1 -2
  26. package/dist/react/types.d.ts.map +1 -1
  27. package/dist/react/utils/renderDiffChildren.d.ts +1 -1
  28. package/dist/react/utils/renderDiffChildren.d.ts.map +1 -1
  29. package/dist/react/utils/renderDiffChildren.js.map +1 -1
  30. package/dist/react/utils/renderFileChildren.d.ts +1 -1
  31. package/dist/react/utils/renderFileChildren.d.ts.map +1 -1
  32. package/dist/react/utils/renderFileChildren.js.map +1 -1
  33. package/dist/react/utils/useFileDiffInstance.d.ts +1 -2
  34. package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
  35. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  36. package/dist/react/utils/useFileInstance.d.ts +1 -2
  37. package/dist/react/utils/useFileInstance.d.ts.map +1 -1
  38. package/dist/react/utils/useFileInstance.js.map +1 -1
  39. package/dist/renderers/DiffHunksRenderer.d.ts +1 -0
  40. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  41. package/dist/renderers/DiffHunksRenderer.js +7 -0
  42. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  43. package/dist/ssr/index.d.ts +2 -2
  44. package/dist/style.js +1 -1
  45. package/dist/style.js.map +1 -1
  46. package/dist/types.d.ts +5 -1
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/utils/areSelectionPointsEqual.d.ts +7 -0
  49. package/dist/utils/areSelectionPointsEqual.d.ts.map +1 -0
  50. package/dist/utils/areSelectionPointsEqual.js +8 -0
  51. package/dist/utils/areSelectionPointsEqual.js.map +1 -0
  52. package/dist/utils/areSelectionsEqual.d.ts +1 -1
  53. package/dist/utils/areSelectionsEqual.d.ts.map +1 -1
  54. package/dist/utils/areSelectionsEqual.js.map +1 -1
  55. package/dist/worker/worker-portable.js +9 -6
  56. package/dist/worker/worker-portable.js.map +1 -1
  57. package/package.json +1 -1
  58. package/dist/managers/LineSelectionManager.d.ts +0 -64
  59. package/dist/managers/LineSelectionManager.d.ts.map +0 -1
  60. package/dist/managers/LineSelectionManager.js +0 -270
  61. package/dist/managers/LineSelectionManager.js.map +0 -1
  62. package/dist/managers/MouseEventManager.d.ts +0 -86
  63. package/dist/managers/MouseEventManager.d.ts.map +0 -1
  64. package/dist/managers/MouseEventManager.js +0 -424
  65. package/dist/managers/MouseEventManager.js.map +0 -1
@@ -0,0 +1,789 @@
1
+ import { areSelectionPointsEqual } from "../utils/areSelectionPointsEqual.js";
2
+ import { areSelectionsEqual } from "../utils/areSelectionsEqual.js";
3
+ import { createGutterUtilityElement } from "../utils/createGutterUtilityElement.js";
4
+ import { toHtml } from "hast-util-to-html";
5
+
6
+ //#region src/managers/InteractionManager.ts
7
+ var InteractionManager = class {
8
+ hoveredLine;
9
+ pre;
10
+ gutterUtilityContainer;
11
+ gutterUtilityButton;
12
+ gutterUtilitySlot;
13
+ interactiveLinesAttr = false;
14
+ interactiveLineNumbersAttr = false;
15
+ hasPointerListeners = false;
16
+ hasDocumentPointerListeners = false;
17
+ selectedRange = null;
18
+ renderedSelectionRange;
19
+ selectionAnchor;
20
+ queuedSelectionRender;
21
+ pointerSession = { mode: "idle" };
22
+ constructor(mode, options) {
23
+ this.mode = mode;
24
+ this.options = options;
25
+ }
26
+ setOptions(options) {
27
+ this.options = options;
28
+ }
29
+ cleanUp() {
30
+ this.pre?.removeEventListener("click", this.handlePointerClick);
31
+ this.pre?.removeEventListener("pointerdown", this.handlePointerDown);
32
+ this.pre?.removeEventListener("pointermove", this.handlePointerMove);
33
+ this.pre?.removeEventListener("pointerleave", this.handlePointerLeave);
34
+ this.pre?.removeAttribute("data-interactive-lines");
35
+ this.pre?.removeAttribute("data-interactive-line-numbers");
36
+ this.pre = void 0;
37
+ this.gutterUtilityContainer?.remove();
38
+ this.gutterUtilityContainer = void 0;
39
+ this.gutterUtilityButton = void 0;
40
+ this.gutterUtilitySlot = void 0;
41
+ this.clearHoveredLine();
42
+ this.detachDocumentPointerListeners();
43
+ this.clearPointerSession();
44
+ if (this.queuedSelectionRender != null) {
45
+ cancelAnimationFrame(this.queuedSelectionRender);
46
+ this.queuedSelectionRender = void 0;
47
+ }
48
+ this.interactiveLinesAttr = false;
49
+ this.interactiveLineNumbersAttr = false;
50
+ this.hasPointerListeners = false;
51
+ }
52
+ setup(pre) {
53
+ this.setSelectionDirty();
54
+ const { usesCustomGutterUtility = false, enableGutterUtility = false } = this.options;
55
+ if (this.pre !== pre) {
56
+ this.cleanUp();
57
+ this.pre = pre;
58
+ }
59
+ if (enableGutterUtility) this.ensureGutterUtilityNode(usesCustomGutterUtility);
60
+ else if (this.gutterUtilityContainer != null) {
61
+ this.gutterUtilityContainer.remove();
62
+ this.gutterUtilityContainer = void 0;
63
+ this.gutterUtilityButton = void 0;
64
+ this.gutterUtilitySlot = void 0;
65
+ if (this.pointerSession.mode === "gutterSelecting") {
66
+ this.clearPointerSession();
67
+ this.detachDocumentPointerListeners();
68
+ }
69
+ }
70
+ this.syncPointerListeners(pre);
71
+ this.updateInteractiveLineAttributes();
72
+ this.renderSelection();
73
+ }
74
+ setSelectionDirty() {
75
+ this.renderedSelectionRange = void 0;
76
+ }
77
+ isSelectionDirty() {
78
+ return this.renderedSelectionRange === null;
79
+ }
80
+ setSelection(range) {
81
+ const isRangeChange = !(range === this.selectedRange || areSelectionsEqual(range ?? void 0, this.selectedRange ?? void 0));
82
+ if (!this.isSelectionDirty() && !isRangeChange) return;
83
+ this.selectedRange = range;
84
+ this.renderSelection();
85
+ if (isRangeChange) this.notifySelectionCommitted();
86
+ }
87
+ getSelection() {
88
+ return this.selectedRange;
89
+ }
90
+ getHoveredLine = () => {
91
+ if (this.hoveredLine != null) {
92
+ if (this.mode === "diff" && this.hoveredLine.type === "diff-line") return {
93
+ lineNumber: this.hoveredLine.lineNumber,
94
+ side: this.hoveredLine.annotationSide
95
+ };
96
+ if (this.mode === "file" && this.hoveredLine.type === "line") return { lineNumber: this.hoveredLine.lineNumber };
97
+ }
98
+ };
99
+ handlePointerClick = (event) => {
100
+ const { onHunkExpand, onLineClick, onLineNumberClick } = this.options;
101
+ if (onHunkExpand == null && onLineClick == null && onLineNumberClick == null) return;
102
+ if (this.options.onGutterUtilityClick != null && isGutterUtilityPointerPath(event.composedPath())) return;
103
+ debugLogIfEnabled(this.options.__debugPointerEvents, "click", "FileDiff.DEBUG.handlePointerClick:", event);
104
+ this.handlePointerEvent({
105
+ eventType: "click",
106
+ event
107
+ });
108
+ };
109
+ handlePointerMove = (event) => {
110
+ const { lineHoverHighlight = "disabled", onLineEnter, onLineLeave, enableGutterUtility = false } = this.options;
111
+ if (lineHoverHighlight === "disabled" && !enableGutterUtility && onLineEnter == null && onLineLeave == null) return;
112
+ debugLogIfEnabled(this.options.__debugPointerEvents, "move", "FileDiff.DEBUG.handlePointerMove:", event);
113
+ this.handlePointerEvent({
114
+ eventType: "move",
115
+ event
116
+ });
117
+ };
118
+ handlePointerLeave = (event) => {
119
+ const { __debugPointerEvents } = this.options;
120
+ debugLogIfEnabled(__debugPointerEvents, "move", "FileDiff.DEBUG.handlePointerLeave: no event");
121
+ if (this.hoveredLine == null) {
122
+ debugLogIfEnabled(__debugPointerEvents, "move", "FileDiff.DEBUG.handlePointerLeave: returned early, no .hoveredLine");
123
+ return;
124
+ }
125
+ this.gutterUtilityContainer?.remove();
126
+ this.options.onLineLeave?.({
127
+ ...this.hoveredLine,
128
+ event
129
+ });
130
+ this.clearHoveredLine();
131
+ };
132
+ handlePointerEvent({ eventType, event }) {
133
+ const { __debugPointerEvents } = this.options;
134
+ const composedPath = event.composedPath();
135
+ debugLogIfEnabled(__debugPointerEvents, eventType, "FileDiff.DEBUG.handlePointerEvent:", {
136
+ eventType,
137
+ composedPath
138
+ });
139
+ const target = this.resolvePointerTarget(composedPath);
140
+ debugLogIfEnabled(__debugPointerEvents, eventType, "FileDiff.DEBUG.handlePointerEvent: resolvePointerTarget result:", target);
141
+ const { onLineClick, onLineNumberClick, onLineEnter, onLineLeave, onHunkExpand } = this.options;
142
+ switch (eventType) {
143
+ case "move":
144
+ if (isLinePointerTarget(target) && this.hoveredLine?.lineElement === target.lineElement) break;
145
+ if (this.hoveredLine != null) {
146
+ this.gutterUtilityContainer?.remove();
147
+ onLineLeave?.({
148
+ ...this.hoveredLine,
149
+ event
150
+ });
151
+ this.clearHoveredLine();
152
+ }
153
+ if (isLinePointerTarget(target)) {
154
+ this.setHoveredLine(this.toEventBaseProps(target));
155
+ if (this.gutterUtilityContainer != null) target.numberElement.appendChild(this.gutterUtilityContainer);
156
+ onLineEnter?.({
157
+ ...this.hoveredLine,
158
+ event
159
+ });
160
+ }
161
+ break;
162
+ case "click": {
163
+ if (target == null) break;
164
+ if (isExpandoPointerTarget(target) && onHunkExpand != null) {
165
+ onHunkExpand(target.hunkIndex, target.direction, event.shiftKey);
166
+ break;
167
+ }
168
+ if (!isLinePointerTarget(target)) break;
169
+ const eventBase = this.toEventBaseProps(target);
170
+ if (onLineNumberClick != null && target.numberColumn) onLineNumberClick({
171
+ ...eventBase,
172
+ event
173
+ });
174
+ else if (onLineClick != null) onLineClick({
175
+ ...eventBase,
176
+ event
177
+ });
178
+ break;
179
+ }
180
+ }
181
+ }
182
+ syncPointerListeners(pre) {
183
+ const { __debugPointerEvents, lineHoverHighlight = "disabled", onLineClick, onLineNumberClick, onLineEnter, onLineLeave, onHunkExpand, enableGutterUtility = false, enableLineSelection = false, onGutterUtilityClick } = this.options;
184
+ const enableGutterSelection = onGutterUtilityClick != null;
185
+ const shouldAttachPointerListeners = lineHoverHighlight !== "disabled" || onLineClick != null || onLineNumberClick != null || onHunkExpand != null || onLineEnter != null || onLineLeave != null || enableGutterUtility || enableLineSelection || enableGutterSelection;
186
+ if (shouldAttachPointerListeners && !this.hasPointerListeners) {
187
+ pre.addEventListener("click", this.handlePointerClick);
188
+ pre.addEventListener("pointerdown", this.handlePointerDown);
189
+ pre.addEventListener("pointermove", this.handlePointerMove);
190
+ pre.addEventListener("pointerleave", this.handlePointerLeave);
191
+ this.hasPointerListeners = true;
192
+ debugLogIfEnabled(__debugPointerEvents, "click", "FileDiff.DEBUG.attachEventListeners: Attaching click events for:", (() => {
193
+ const reasons = [];
194
+ if (__debugPointerEvents === "both" || __debugPointerEvents === "click") {
195
+ if (onLineClick != null) reasons.push("onLineClick");
196
+ if (onLineNumberClick != null) reasons.push("onLineNumberClick");
197
+ if (onHunkExpand != null) reasons.push("expandable hunk separators");
198
+ }
199
+ return reasons;
200
+ })());
201
+ debugLogIfEnabled(__debugPointerEvents, "move", "FileDiff.DEBUG.attachEventListeners: Attaching pointer move event");
202
+ debugLogIfEnabled(__debugPointerEvents, "move", "FileDiff.DEBUG.attachEventListeners: Attaching pointer leave event");
203
+ } else if (!shouldAttachPointerListeners && this.hasPointerListeners) {
204
+ pre.removeEventListener("click", this.handlePointerClick);
205
+ pre.removeEventListener("pointerdown", this.handlePointerDown);
206
+ pre.removeEventListener("pointermove", this.handlePointerMove);
207
+ pre.removeEventListener("pointerleave", this.handlePointerLeave);
208
+ this.hasPointerListeners = false;
209
+ }
210
+ const hasActiveLineSelectionSession = this.pointerSession.mode === "selecting" || this.pointerSession.mode === "pendingSingleLineUnselect";
211
+ const hasActiveGutterSelectionSession = this.pointerSession.mode === "gutterSelecting";
212
+ if (!enableLineSelection && hasActiveLineSelectionSession || !enableGutterSelection && hasActiveGutterSelectionSession) {
213
+ this.clearPointerSession();
214
+ this.detachDocumentPointerListeners();
215
+ this.selectionAnchor = void 0;
216
+ this.clearPendingSingleLineState();
217
+ }
218
+ }
219
+ updateInteractiveLineAttributes() {
220
+ if (this.pre == null) return;
221
+ const { onLineClick, onLineNumberClick, enableLineSelection = false } = this.options;
222
+ const shouldHaveInteractiveLines = onLineClick != null;
223
+ const shouldHaveInteractiveLineNumbers = onLineNumberClick != null || enableLineSelection;
224
+ if (shouldHaveInteractiveLines && !this.interactiveLinesAttr) {
225
+ this.pre.setAttribute("data-interactive-lines", "");
226
+ this.interactiveLinesAttr = true;
227
+ } else if (!shouldHaveInteractiveLines && this.interactiveLinesAttr) {
228
+ this.pre.removeAttribute("data-interactive-lines");
229
+ this.interactiveLinesAttr = false;
230
+ }
231
+ if (shouldHaveInteractiveLineNumbers && !this.interactiveLineNumbersAttr) {
232
+ this.pre.setAttribute("data-interactive-line-numbers", "");
233
+ this.interactiveLineNumbersAttr = true;
234
+ } else if (!shouldHaveInteractiveLineNumbers && this.interactiveLineNumbersAttr) {
235
+ this.pre.removeAttribute("data-interactive-line-numbers");
236
+ this.interactiveLineNumbersAttr = false;
237
+ }
238
+ }
239
+ handlePointerDown = (event) => {
240
+ if (event.pointerType === "mouse" && event.button !== 0 || this.pre == null || this.pointerSession.mode !== "idle") return;
241
+ const path = event.composedPath();
242
+ if (isGutterUtilityPointerPath(path) && this.options.onGutterUtilityClick != null) this.startGutterSelectionFromPointerDown(event, path);
243
+ else this.startLineSelectionFromPointerDown(event, path);
244
+ };
245
+ startLineSelectionFromPointerDown(event, path) {
246
+ const { enableLineSelection = false } = this.options;
247
+ if (!enableLineSelection) return;
248
+ const pointerInfo = this.getSelectionPointerInfo(path, true);
249
+ if (pointerInfo == null) return;
250
+ const { pre } = this;
251
+ if (pre == null) return;
252
+ event.preventDefault();
253
+ const { lineNumber, eventSide, lineIndex } = pointerInfo;
254
+ if (event.shiftKey && this.selectedRange != null) {
255
+ const rowRange = this.getIndexesFromSelection(this.selectedRange, pre.getAttribute("data-diff-type") === "split");
256
+ if (rowRange == null) return;
257
+ const useStart = rowRange.start <= rowRange.end ? lineIndex >= rowRange.start : lineIndex <= rowRange.end;
258
+ this.selectionAnchor = {
259
+ lineNumber: useStart ? this.selectedRange.start : this.selectedRange.end,
260
+ side: useStart ? this.selectedRange.side : this.selectedRange.endSide ?? this.selectedRange.side
261
+ };
262
+ this.updateSelection(lineNumber, eventSide, false);
263
+ this.notifySelectionStart(this.selectedRange);
264
+ this.pointerSession = {
265
+ mode: "selecting",
266
+ pointerId: event.pointerId
267
+ };
268
+ this.attachDocumentPointerListeners();
269
+ return;
270
+ }
271
+ if (this.selectedRange?.start === lineNumber && this.selectedRange?.end === lineNumber) {
272
+ const point = {
273
+ lineNumber,
274
+ side: eventSide
275
+ };
276
+ this.selectionAnchor = point;
277
+ this.pointerSession = {
278
+ mode: "pendingSingleLineUnselect",
279
+ pointerId: event.pointerId,
280
+ anchor: point,
281
+ pending: point
282
+ };
283
+ this.attachDocumentPointerListeners();
284
+ return;
285
+ }
286
+ this.selectedRange = null;
287
+ this.selectionAnchor = {
288
+ lineNumber,
289
+ side: eventSide
290
+ };
291
+ this.updateSelection(lineNumber, eventSide, false);
292
+ this.notifySelectionStart(this.selectedRange);
293
+ this.pointerSession = {
294
+ mode: "selecting",
295
+ pointerId: event.pointerId
296
+ };
297
+ this.attachDocumentPointerListeners();
298
+ }
299
+ startGutterSelectionFromPointerDown(event, path) {
300
+ const { enableLineSelection = false, onGutterUtilityClick } = this.options;
301
+ if (onGutterUtilityClick == null) return;
302
+ const point = this.getSelectionPointFromPath(path);
303
+ if (point == null) return;
304
+ event.preventDefault();
305
+ event.stopPropagation();
306
+ this.pointerSession = {
307
+ mode: "gutterSelecting",
308
+ pointerId: event.pointerId,
309
+ anchor: point,
310
+ current: point
311
+ };
312
+ if (enableLineSelection) {
313
+ this.selectionAnchor = {
314
+ lineNumber: point.lineNumber,
315
+ side: point.side
316
+ };
317
+ this.updateSelection(point.lineNumber, point.side, false);
318
+ this.notifySelectionStart(this.selectedRange);
319
+ }
320
+ this.attachDocumentPointerListeners();
321
+ }
322
+ handleDocumentPointerMove = (event) => {
323
+ const { enableLineSelection = false } = this.options;
324
+ switch (this.pointerSession.mode) {
325
+ case "idle": return;
326
+ case "gutterSelecting": {
327
+ if (event.pointerId !== this.pointerSession.pointerId) return;
328
+ const point = this.getSelectionPointFromPath(event.composedPath());
329
+ if (point == null) return;
330
+ this.pointerSession.current = point;
331
+ if (enableLineSelection === true) this.updateSelection(point.lineNumber, point.side);
332
+ return;
333
+ }
334
+ case "selecting": {
335
+ if (event.pointerId !== this.pointerSession.pointerId) return;
336
+ const pointerInfo = this.getSelectionPointerInfo(event.composedPath(), false);
337
+ if (pointerInfo == null || this.selectionAnchor == null) return;
338
+ this.updateSelection(pointerInfo.lineNumber, pointerInfo.eventSide);
339
+ return;
340
+ }
341
+ case "pendingSingleLineUnselect": {
342
+ if (event.pointerId !== this.pointerSession.pointerId) return;
343
+ const pointerInfo = this.getSelectionPointerInfo(event.composedPath(), false);
344
+ if (pointerInfo == null || this.selectionAnchor == null) return;
345
+ const point = {
346
+ lineNumber: pointerInfo.lineNumber,
347
+ side: pointerInfo.eventSide
348
+ };
349
+ if (areSelectionPointsEqual(this.pointerSession.pending, point)) return;
350
+ this.updateSelection(pointerInfo.lineNumber, pointerInfo.eventSide, false);
351
+ this.notifySelectionStart(this.selectedRange);
352
+ this.notifySelectionChangeDelta();
353
+ this.pointerSession = {
354
+ mode: "selecting",
355
+ pointerId: event.pointerId
356
+ };
357
+ return;
358
+ }
359
+ }
360
+ };
361
+ handleDocumentPointerUp = (event) => {
362
+ const { enableLineSelection = false, onGutterUtilityClick } = this.options;
363
+ switch (this.pointerSession.mode) {
364
+ case "idle": return;
365
+ case "gutterSelecting": {
366
+ if (event.pointerId !== this.pointerSession.pointerId) return;
367
+ const point = this.getSelectionPointFromPath(event.composedPath());
368
+ if (point != null) {
369
+ this.pointerSession.current = point;
370
+ if (enableLineSelection) this.updateSelection(point.lineNumber, point.side);
371
+ }
372
+ onGutterUtilityClick?.(this.buildSelectedLineRange(this.pointerSession.anchor, this.pointerSession.current));
373
+ this.selectionAnchor = void 0;
374
+ if (enableLineSelection) {
375
+ this.notifySelectionEnd(this.selectedRange);
376
+ this.notifySelectionCommitted();
377
+ }
378
+ this.clearPointerSession();
379
+ this.detachDocumentPointerListeners();
380
+ return;
381
+ }
382
+ case "pendingSingleLineUnselect":
383
+ if (event.pointerId !== this.pointerSession.pointerId) return;
384
+ this.updateSelection(null, void 0, false);
385
+ this.selectionAnchor = void 0;
386
+ this.clearPendingSingleLineState();
387
+ this.detachDocumentPointerListeners();
388
+ this.notifySelectionEnd(this.selectedRange);
389
+ this.notifySelectionCommitted();
390
+ return;
391
+ case "selecting":
392
+ if (event.pointerId !== this.pointerSession.pointerId) return;
393
+ this.selectionAnchor = void 0;
394
+ this.detachDocumentPointerListeners();
395
+ this.clearPointerSession();
396
+ this.notifySelectionEnd(this.selectedRange);
397
+ this.notifySelectionCommitted();
398
+ }
399
+ };
400
+ handleDocumentPointerCancel = (event) => {
401
+ switch (this.pointerSession.mode) {
402
+ case "idle": return;
403
+ case "gutterSelecting":
404
+ case "selecting":
405
+ case "pendingSingleLineUnselect":
406
+ if ("pointerId" in this.pointerSession) {
407
+ if (event.pointerId !== this.pointerSession.pointerId) return;
408
+ }
409
+ this.selectionAnchor = void 0;
410
+ this.clearPendingSingleLineState();
411
+ this.clearPointerSession();
412
+ this.detachDocumentPointerListeners();
413
+ }
414
+ };
415
+ clearHoveredLine() {
416
+ if (this.hoveredLine == null) return;
417
+ this.hoveredLine.lineElement.removeAttribute("data-hovered");
418
+ this.hoveredLine.numberElement.removeAttribute("data-hovered");
419
+ this.hoveredLine = void 0;
420
+ }
421
+ setHoveredLine(hoveredLine) {
422
+ const { lineHoverHighlight = "disabled" } = this.options;
423
+ if (this.hoveredLine != null) this.clearHoveredLine();
424
+ this.hoveredLine = hoveredLine;
425
+ if (lineHoverHighlight !== "disabled") {
426
+ if (lineHoverHighlight === "both" || lineHoverHighlight === "line") this.hoveredLine.lineElement.setAttribute("data-hovered", "");
427
+ if (lineHoverHighlight === "both" || lineHoverHighlight === "number") this.hoveredLine.numberElement.setAttribute("data-hovered", "");
428
+ }
429
+ }
430
+ ensureGutterUtilityNode(useCustomGutterUtility) {
431
+ if (this.gutterUtilityContainer == null) {
432
+ this.gutterUtilityContainer = document.createElement("div");
433
+ this.gutterUtilityContainer.setAttribute("data-gutter-utility-slot", "");
434
+ }
435
+ if (useCustomGutterUtility) {
436
+ if (this.gutterUtilityButton != null) {
437
+ this.gutterUtilityButton.remove();
438
+ this.gutterUtilityButton = void 0;
439
+ }
440
+ if (this.gutterUtilitySlot == null) {
441
+ this.gutterUtilitySlot = document.createElement("slot");
442
+ this.gutterUtilitySlot.name = "gutter-utility-slot";
443
+ }
444
+ if (this.gutterUtilitySlot.parentNode !== this.gutterUtilityContainer) this.gutterUtilityContainer.replaceChildren(this.gutterUtilitySlot);
445
+ } else {
446
+ this.gutterUtilitySlot?.remove();
447
+ this.gutterUtilitySlot = void 0;
448
+ if (this.gutterUtilityButton == null) {
449
+ const tempDiv = document.createElement("div");
450
+ tempDiv.innerHTML = toHtml(createGutterUtilityElement());
451
+ const utilityButton = tempDiv.firstElementChild;
452
+ if (!(utilityButton instanceof HTMLButtonElement)) throw new Error("InteractionManager.ensureGutterUtilityNode: Node element should be a button");
453
+ utilityButton.remove();
454
+ this.gutterUtilityButton = utilityButton;
455
+ }
456
+ if (this.gutterUtilityButton.parentNode !== this.gutterUtilityContainer) this.gutterUtilityContainer.replaceChildren(this.gutterUtilityButton);
457
+ }
458
+ }
459
+ attachDocumentPointerListeners() {
460
+ if (this.hasDocumentPointerListeners) return;
461
+ document.addEventListener("pointermove", this.handleDocumentPointerMove);
462
+ document.addEventListener("pointerup", this.handleDocumentPointerUp);
463
+ document.addEventListener("pointercancel", this.handleDocumentPointerCancel);
464
+ this.hasDocumentPointerListeners = true;
465
+ }
466
+ detachDocumentPointerListeners() {
467
+ if (!this.hasDocumentPointerListeners) return;
468
+ document.removeEventListener("pointermove", this.handleDocumentPointerMove);
469
+ document.removeEventListener("pointerup", this.handleDocumentPointerUp);
470
+ document.removeEventListener("pointercancel", this.handleDocumentPointerCancel);
471
+ this.hasDocumentPointerListeners = false;
472
+ }
473
+ clearPointerSession() {
474
+ this.pointerSession = { mode: "idle" };
475
+ }
476
+ clearPendingSingleLineState() {
477
+ if (this.pointerSession.mode === "pendingSingleLineUnselect") this.pointerSession = { mode: "idle" };
478
+ }
479
+ getSelectionPointerInfo(path, requireNumberColumn) {
480
+ const target = this.resolvePointerTarget(path);
481
+ if (!isLinePointerTarget(target)) return;
482
+ if (requireNumberColumn && !target.numberColumn) return;
483
+ if (target.splitLineIndex == null) return;
484
+ return {
485
+ lineIndex: target.splitLineIndex,
486
+ lineNumber: target.lineNumber,
487
+ eventSide: this.mode === "diff" ? target.side : void 0
488
+ };
489
+ }
490
+ getSelectionPointFromPath(path) {
491
+ const target = this.resolvePointerTarget(path);
492
+ if (!isLinePointerTarget(target)) return;
493
+ return {
494
+ lineNumber: target.lineNumber,
495
+ side: this.mode === "diff" ? target.side : void 0
496
+ };
497
+ }
498
+ getLineIndex(lineNumber, side) {
499
+ const { getLineIndex } = this.options;
500
+ return getLineIndex != null ? getLineIndex(lineNumber, side) : [lineNumber - 1, lineNumber - 1];
501
+ }
502
+ updateSelection(currentLine, side, emitChange = true) {
503
+ const { selectedRange: previousRange } = this;
504
+ let nextRange;
505
+ if (currentLine == null) nextRange = null;
506
+ else {
507
+ const anchorSide = this.selectionAnchor?.side ?? side;
508
+ const anchorLine = this.selectionAnchor?.lineNumber ?? currentLine;
509
+ nextRange = this.buildSelectionRange(anchorLine, currentLine, anchorSide, side);
510
+ }
511
+ if (areSelectionsEqual(previousRange ?? void 0, nextRange ?? void 0)) return;
512
+ this.selectedRange = nextRange;
513
+ if (emitChange) this.notifySelectionChangeDelta();
514
+ this.queuedSelectionRender ??= requestAnimationFrame(this.renderSelection);
515
+ }
516
+ getIndexesFromSelection(selectedRange, split) {
517
+ if (this.pre == null) return;
518
+ const startIndexes = this.getLineIndex(selectedRange.start, selectedRange.side);
519
+ const finalIndexes = this.getLineIndex(selectedRange.end, selectedRange.endSide ?? selectedRange.side);
520
+ return startIndexes != null && finalIndexes != null ? {
521
+ start: split ? startIndexes[1] : startIndexes[0],
522
+ end: split ? finalIndexes[1] : finalIndexes[0]
523
+ } : void 0;
524
+ }
525
+ renderSelection = () => {
526
+ if (this.queuedSelectionRender != null) {
527
+ cancelAnimationFrame(this.queuedSelectionRender);
528
+ this.queuedSelectionRender = void 0;
529
+ }
530
+ if (this.pre == null || this.renderedSelectionRange === this.selectedRange) return;
531
+ const allSelected = this.pre.querySelectorAll("[data-selected-line]");
532
+ for (const element of allSelected) element.removeAttribute("data-selected-line");
533
+ this.renderedSelectionRange = this.selectedRange;
534
+ if (this.selectedRange == null) return;
535
+ const { children: codeElements } = this.pre;
536
+ if (codeElements.length === 0) return;
537
+ if (codeElements.length > 2) {
538
+ console.error(codeElements);
539
+ throw new Error("InteractionManager.renderSelection: Somehow there are more than 2 code elements...");
540
+ }
541
+ const split = this.pre.getAttribute("data-diff-type") === "split";
542
+ const rowRange = this.getIndexesFromSelection(this.selectedRange, split);
543
+ if (rowRange == null) {
544
+ console.error({
545
+ rowRange,
546
+ selectedRange: this.selectedRange
547
+ });
548
+ throw new Error("InteractionManager.renderSelection: No valid rowRange");
549
+ }
550
+ const isSingle = rowRange.start === rowRange.end;
551
+ const first = Math.min(rowRange.start, rowRange.end);
552
+ const last = Math.max(rowRange.start, rowRange.end);
553
+ for (const code of codeElements) {
554
+ const [gutter, content] = code.children;
555
+ const len = content.children.length;
556
+ if (len !== gutter.children.length) throw new Error("InteractionManager.renderSelection: gutter and content children dont match, something is wrong");
557
+ for (let i = 0; i < len; i++) {
558
+ const contentElement = content.children[i];
559
+ const gutterElement = gutter.children[i];
560
+ if (!(contentElement instanceof HTMLElement) || !(gutterElement instanceof HTMLElement)) continue;
561
+ const lineIndex = this.parseLineIndex(contentElement, split);
562
+ if ((lineIndex ?? 0) > last) break;
563
+ if (lineIndex == null || lineIndex < first) continue;
564
+ let attributeValue = isSingle ? "single" : lineIndex === first ? "first" : lineIndex === last ? "last" : "";
565
+ contentElement.setAttribute("data-selected-line", attributeValue);
566
+ gutterElement.setAttribute("data-selected-line", attributeValue);
567
+ if (gutterElement.nextSibling instanceof HTMLElement && contentElement.nextSibling instanceof HTMLElement && contentElement.nextSibling.hasAttribute("data-line-annotation")) {
568
+ if (isSingle) {
569
+ attributeValue = "last";
570
+ contentElement.setAttribute("data-selected-line", "first");
571
+ } else if (lineIndex === first) attributeValue = "";
572
+ else if (lineIndex === last) contentElement.setAttribute("data-selected-line", "");
573
+ contentElement.nextSibling.setAttribute("data-selected-line", attributeValue);
574
+ gutterElement.nextSibling.setAttribute("data-selected-line", attributeValue);
575
+ }
576
+ }
577
+ }
578
+ };
579
+ notifySelectionCommitted() {
580
+ this.options.onLineSelected?.(this.selectedRange ?? null);
581
+ }
582
+ notifySelectionChangeDelta() {
583
+ this.options.onLineSelectionChange?.(this.selectedRange ?? null);
584
+ }
585
+ notifySelectionStart(range) {
586
+ this.options.onLineSelectionStart?.(range);
587
+ }
588
+ notifySelectionEnd(range) {
589
+ this.options.onLineSelectionEnd?.(range);
590
+ }
591
+ toEventBaseProps(target) {
592
+ if (this.mode === "file") return {
593
+ type: "line",
594
+ lineElement: target.lineElement,
595
+ lineNumber: target.lineNumber,
596
+ numberColumn: target.numberColumn,
597
+ numberElement: target.numberElement
598
+ };
599
+ return {
600
+ type: "diff-line",
601
+ annotationSide: target.side,
602
+ lineType: target.lineType,
603
+ lineElement: target.lineElement,
604
+ numberElement: target.numberElement,
605
+ lineNumber: target.lineNumber,
606
+ numberColumn: target.numberColumn
607
+ };
608
+ }
609
+ buildSelectedLineRange(anchor, current) {
610
+ return this.buildSelectionRange(anchor.lineNumber, current.lineNumber, anchor.side, current.side);
611
+ }
612
+ buildSelectionRange(start, end, side, endSide) {
613
+ return {
614
+ start,
615
+ end,
616
+ ...side != null ? { side } : {},
617
+ ...side !== endSide && endSide != null ? { endSide } : {}
618
+ };
619
+ }
620
+ resolvePointerTarget(path) {
621
+ let numberColumn = false;
622
+ let lineType;
623
+ let codeElement;
624
+ let lineElement;
625
+ let lineIndexValue;
626
+ let numberElement;
627
+ let expandInfo;
628
+ let lineNumber;
629
+ for (const element of path) {
630
+ if (!(element instanceof HTMLElement)) continue;
631
+ const columnNumber = numberElement == null ? element.getAttribute("data-column-number") ?? void 0 : void 0;
632
+ if (columnNumber != null) {
633
+ numberElement = element;
634
+ lineNumber = Number.parseInt(columnNumber, 10);
635
+ numberColumn = true;
636
+ lineType = getLineTypeFromElement(element);
637
+ lineIndexValue = element.getAttribute("data-line-index") ?? void 0;
638
+ continue;
639
+ }
640
+ const lineAttr = lineElement == null ? element.getAttribute("data-line") ?? void 0 : void 0;
641
+ if (lineAttr != null) {
642
+ lineElement = element;
643
+ lineNumber = Number.parseInt(lineAttr, 10);
644
+ lineType = getLineTypeFromElement(element);
645
+ lineIndexValue = element.getAttribute("data-line-index") ?? void 0;
646
+ continue;
647
+ }
648
+ if (expandInfo == null && element.hasAttribute("data-expand-button")) {
649
+ expandInfo = {
650
+ hunkIndex: void 0,
651
+ direction: (() => {
652
+ if (element.hasAttribute("data-expand-up")) return "up";
653
+ if (element.hasAttribute("data-expand-down")) return "down";
654
+ return "both";
655
+ })()
656
+ };
657
+ continue;
658
+ }
659
+ const expandIndexValue = expandInfo != null ? element.getAttribute("data-expand-index") ?? void 0 : void 0;
660
+ if (expandInfo != null && expandIndexValue != null) {
661
+ const expandIndex = Number.parseInt(expandIndexValue, 10);
662
+ if (!Number.isNaN(expandIndex)) expandInfo.hunkIndex = expandIndex;
663
+ continue;
664
+ }
665
+ if (codeElement == null && element.hasAttribute("data-code")) {
666
+ codeElement = element;
667
+ break;
668
+ }
669
+ }
670
+ if (expandInfo?.hunkIndex != null) return {
671
+ type: "line-info",
672
+ hunkIndex: expandInfo.hunkIndex,
673
+ direction: expandInfo.direction
674
+ };
675
+ lineElement ??= lineIndexValue != null ? queryHTMLElement(codeElement, `[data-line][data-line-index="${lineIndexValue}"]`) : void 0;
676
+ numberElement ??= lineIndexValue != null ? queryHTMLElement(codeElement, `[data-column-number][data-line-index="${lineIndexValue}"]`) : void 0;
677
+ if (codeElement == null || lineElement == null || numberElement == null || lineType == null || lineNumber == null || Number.isNaN(lineNumber)) return;
678
+ const splitLineIndex = this.parseLineIndex(lineElement, this.isSplitDiff());
679
+ if (this.mode === "file") return {
680
+ kind: "line",
681
+ lineType,
682
+ lineElement,
683
+ lineNumber,
684
+ numberColumn,
685
+ numberElement,
686
+ side: void 0,
687
+ splitLineIndex
688
+ };
689
+ const annotationSide = (() => {
690
+ switch (lineType) {
691
+ case "change-deletion": return "deletions";
692
+ case "change-addition": return "additions";
693
+ default: return codeElement.hasAttribute("data-deletions") ? "deletions" : "additions";
694
+ }
695
+ })();
696
+ return {
697
+ kind: "line",
698
+ lineType,
699
+ lineElement,
700
+ lineNumber,
701
+ numberColumn,
702
+ numberElement,
703
+ side: annotationSide,
704
+ splitLineIndex
705
+ };
706
+ }
707
+ isSplitDiff() {
708
+ return this.pre?.getAttribute("data-diff-type") === "split";
709
+ }
710
+ parseLineIndex(element, split) {
711
+ const lineIndexes = (element.getAttribute("data-line-index") ?? "").split(",").map((value) => Number.parseInt(value, 10)).filter((value) => !Number.isNaN(value));
712
+ if (split && lineIndexes.length === 2) return lineIndexes[1];
713
+ if (!split) return lineIndexes[0];
714
+ }
715
+ };
716
+ function pluckInteractionOptions({ enableGutterUtility, enableHoverUtility, lineHoverHighlight, onGutterUtilityClick, onLineClick, onLineEnter, onLineLeave, onLineNumberClick, renderGutterUtility, renderHoverUtility, __debugPointerEvents, enableLineSelection, onLineSelected, onLineSelectionStart, onLineSelectionChange, onLineSelectionEnd }, onHunkExpand, getLineIndex) {
717
+ return {
718
+ enableGutterUtility: resolveEnableGutterUtilityOption({
719
+ enableGutterUtility,
720
+ enableHoverUtility,
721
+ renderGutterUtility,
722
+ renderHoverUtility,
723
+ onGutterUtilityClick
724
+ }),
725
+ usesCustomGutterUtility: renderGutterUtility != null || renderHoverUtility != null,
726
+ lineHoverHighlight,
727
+ onGutterUtilityClick,
728
+ onHunkExpand,
729
+ onLineClick,
730
+ onLineEnter,
731
+ onLineLeave,
732
+ onLineNumberClick,
733
+ __debugPointerEvents,
734
+ enableLineSelection,
735
+ onLineSelected,
736
+ onLineSelectionStart,
737
+ onLineSelectionChange,
738
+ onLineSelectionEnd,
739
+ getLineIndex
740
+ };
741
+ }
742
+ function resolveEnableGutterUtilityOption({ enableGutterUtility, enableHoverUtility, renderGutterUtility, renderHoverUtility, onGutterUtilityClick }) {
743
+ if (enableGutterUtility !== void 0 && enableHoverUtility !== void 0) throw new Error("Cannot use both 'enableGutterUtility' and deprecated 'enableHoverUtility'. Use only 'enableGutterUtility'.");
744
+ if (renderGutterUtility != null && renderHoverUtility != null) throw new Error("Cannot use both 'renderGutterUtility' and deprecated 'renderHoverUtility'. Use only 'renderGutterUtility'.");
745
+ if (onGutterUtilityClick != null && (renderGutterUtility != null || renderHoverUtility != null)) throw new Error("Cannot use both 'onGutterUtilityClick' and render utility callbacks ('renderGutterUtility'/'renderHoverUtility'). Use only one gutter utility API.");
746
+ return enableGutterUtility ?? enableHoverUtility ?? false;
747
+ }
748
+ function isLinePointerTarget(target) {
749
+ return target != null && "kind" in target && target.kind === "line";
750
+ }
751
+ function isExpandoPointerTarget(target) {
752
+ return "type" in target && target.type === "line-info";
753
+ }
754
+ function queryHTMLElement(parent, query) {
755
+ const element = parent?.querySelector(query);
756
+ return element instanceof HTMLElement ? element : void 0;
757
+ }
758
+ function getLineTypeFromElement(element) {
759
+ const lineType = element.getAttribute("data-line-type");
760
+ if (lineType == null) return;
761
+ switch (lineType) {
762
+ case "change-deletion":
763
+ case "change-addition":
764
+ case "context":
765
+ case "context-expanded": return lineType;
766
+ default: return;
767
+ }
768
+ }
769
+ function isGutterUtilityPointerPath(path) {
770
+ for (const element of path) if (element instanceof HTMLElement && element.hasAttribute("data-utility-button")) return true;
771
+ return false;
772
+ }
773
+ function debugLogIfEnabled(debugLogType = "none", logIfType, ...args) {
774
+ switch (debugLogType) {
775
+ case "none": return;
776
+ case "both": break;
777
+ case "click":
778
+ if (logIfType !== "click") return;
779
+ break;
780
+ case "move":
781
+ if (logIfType !== "move") return;
782
+ break;
783
+ }
784
+ console.log(...args);
785
+ }
786
+
787
+ //#endregion
788
+ export { InteractionManager, pluckInteractionOptions };
789
+ //# sourceMappingURL=InteractionManager.js.map