@pierre/diffs 1.3.0-beta.6 → 1.3.0-beta.8

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 (180) hide show
  1. package/dist/components/CodeView.d.ts +90 -5
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +291 -38
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts +1 -0
  6. package/dist/components/File.d.ts.map +1 -1
  7. package/dist/components/File.js +20 -6
  8. package/dist/components/File.js.map +1 -1
  9. package/dist/components/FileDiff.d.ts +38 -23
  10. package/dist/components/FileDiff.d.ts.map +1 -1
  11. package/dist/components/FileDiff.js +144 -61
  12. package/dist/components/FileDiff.js.map +1 -1
  13. package/dist/components/UnresolvedFile.d.ts +3 -2
  14. package/dist/components/UnresolvedFile.d.ts.map +1 -1
  15. package/dist/components/UnresolvedFile.js +4 -2
  16. package/dist/components/UnresolvedFile.js.map +1 -1
  17. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  18. package/dist/components/VirtualizedFile.js +6 -8
  19. package/dist/components/VirtualizedFile.js.map +1 -1
  20. package/dist/components/VirtualizedFileDiff.d.ts +13 -5
  21. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  22. package/dist/components/VirtualizedFileDiff.js +192 -55
  23. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  24. package/dist/components/Virtualizer.d.ts +2 -0
  25. package/dist/components/Virtualizer.d.ts.map +1 -1
  26. package/dist/components/Virtualizer.js +16 -3
  27. package/dist/components/Virtualizer.js.map +1 -1
  28. package/dist/constants.d.ts +3 -1
  29. package/dist/constants.d.ts.map +1 -1
  30. package/dist/constants.js +3 -1
  31. package/dist/constants.js.map +1 -1
  32. package/dist/editor/command.d.ts +1 -1
  33. package/dist/editor/command.d.ts.map +1 -1
  34. package/dist/editor/command.js +8 -0
  35. package/dist/editor/command.js.map +1 -1
  36. package/dist/editor/editor.d.ts +10 -3
  37. package/dist/editor/editor.d.ts.map +1 -1
  38. package/dist/editor/editor.js +325 -135
  39. package/dist/editor/editor.js.map +1 -1
  40. package/dist/editor/editor2.js +1 -1
  41. package/dist/editor/lineAnnotations.d.ts.map +1 -1
  42. package/dist/editor/lineAnnotations.js +84 -12
  43. package/dist/editor/lineAnnotations.js.map +1 -1
  44. package/dist/editor/marker.d.ts +5 -3
  45. package/dist/editor/marker.d.ts.map +1 -1
  46. package/dist/editor/marker.js +35 -12
  47. package/dist/editor/marker.js.map +1 -1
  48. package/dist/editor/matchBrackets.d.ts +8 -0
  49. package/dist/editor/matchBrackets.d.ts.map +1 -0
  50. package/dist/editor/matchBrackets.js +146 -0
  51. package/dist/editor/matchBrackets.js.map +1 -0
  52. package/dist/editor/popover.d.ts +45 -0
  53. package/dist/editor/popover.d.ts.map +1 -0
  54. package/dist/editor/popover.js +145 -0
  55. package/dist/editor/popover.js.map +1 -0
  56. package/dist/editor/selection.d.ts +14 -2
  57. package/dist/editor/selection.d.ts.map +1 -1
  58. package/dist/editor/selection.js +142 -41
  59. package/dist/editor/selection.js.map +1 -1
  60. package/dist/editor/selectionAction.d.ts +20 -3
  61. package/dist/editor/selectionAction.d.ts.map +1 -1
  62. package/dist/editor/selectionAction.js +41 -10
  63. package/dist/editor/selectionAction.js.map +1 -1
  64. package/dist/editor/textDocument.d.ts +2 -0
  65. package/dist/editor/textDocument.d.ts.map +1 -1
  66. package/dist/editor/textDocument.js +22 -6
  67. package/dist/editor/textDocument.js.map +1 -1
  68. package/dist/editor/textMeasure.d.ts.map +1 -1
  69. package/dist/editor/textMeasure.js +25 -7
  70. package/dist/editor/textMeasure.js.map +1 -1
  71. package/dist/editor/tokenzier.d.ts +6 -7
  72. package/dist/editor/tokenzier.d.ts.map +1 -1
  73. package/dist/editor/tokenzier.js +83 -34
  74. package/dist/editor/tokenzier.js.map +1 -1
  75. package/dist/editor/utils.d.ts +3 -1
  76. package/dist/editor/utils.d.ts.map +1 -1
  77. package/dist/editor/utils.js +14 -1
  78. package/dist/editor/utils.js.map +1 -1
  79. package/dist/index.d.ts +9 -7
  80. package/dist/index.js +6 -4
  81. package/dist/managers/InteractionManager.d.ts.map +1 -1
  82. package/dist/managers/InteractionManager.js +0 -1
  83. package/dist/managers/InteractionManager.js.map +1 -1
  84. package/dist/managers/ResizeManager.d.ts +10 -2
  85. package/dist/managers/ResizeManager.d.ts.map +1 -1
  86. package/dist/managers/ResizeManager.js +33 -13
  87. package/dist/managers/ResizeManager.js.map +1 -1
  88. package/dist/react/CodeView.d.ts +25 -1
  89. package/dist/react/CodeView.d.ts.map +1 -1
  90. package/dist/react/CodeView.js +76 -17
  91. package/dist/react/CodeView.js.map +1 -1
  92. package/dist/react/EditorContext.d.ts +2 -2
  93. package/dist/react/EditorContext.d.ts.map +1 -1
  94. package/dist/react/EditorContext.js.map +1 -1
  95. package/dist/react/MultiFileDiff.d.ts +3 -4
  96. package/dist/react/MultiFileDiff.d.ts.map +1 -1
  97. package/dist/react/MultiFileDiff.js.map +1 -1
  98. package/dist/react/index.d.ts +2 -2
  99. package/dist/react/utils/useFileDiffInstance.js +14 -15
  100. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  101. package/dist/renderers/DiffHunksRenderer.d.ts +2 -2
  102. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  103. package/dist/renderers/DiffHunksRenderer.js +45 -23
  104. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  105. package/dist/renderers/FileRenderer.d.ts +2 -1
  106. package/dist/renderers/FileRenderer.d.ts.map +1 -1
  107. package/dist/renderers/FileRenderer.js +58 -38
  108. package/dist/renderers/FileRenderer.js.map +1 -1
  109. package/dist/ssr/index.d.ts +2 -2
  110. package/dist/ssr/preloadDiffs.d.ts +11 -10
  111. package/dist/ssr/preloadDiffs.d.ts.map +1 -1
  112. package/dist/ssr/preloadDiffs.js +14 -6
  113. package/dist/ssr/preloadDiffs.js.map +1 -1
  114. package/dist/types.d.ts +100 -6
  115. package/dist/types.d.ts.map +1 -1
  116. package/dist/utils/applyLineTextWithNewline.d.ts +5 -0
  117. package/dist/utils/applyLineTextWithNewline.d.ts.map +1 -0
  118. package/dist/utils/applyLineTextWithNewline.js +11 -0
  119. package/dist/utils/applyLineTextWithNewline.js.map +1 -0
  120. package/dist/utils/areHunkDataEqual.js +1 -1
  121. package/dist/utils/areHunkDataEqual.js.map +1 -1
  122. package/dist/utils/areManagedSnapshotsEqual.d.ts +2 -2
  123. package/dist/utils/areManagedSnapshotsEqual.d.ts.map +1 -1
  124. package/dist/utils/areManagedSnapshotsEqual.js +5 -0
  125. package/dist/utils/areManagedSnapshotsEqual.js.map +1 -1
  126. package/dist/utils/awaitWithTimeout.d.ts +5 -0
  127. package/dist/utils/awaitWithTimeout.d.ts.map +1 -0
  128. package/dist/utils/awaitWithTimeout.js +15 -0
  129. package/dist/utils/awaitWithTimeout.js.map +1 -0
  130. package/dist/utils/cloneFileDiffMetadata.d.ts +7 -0
  131. package/dist/utils/cloneFileDiffMetadata.d.ts.map +1 -0
  132. package/dist/utils/cloneFileDiffMetadata.js +16 -0
  133. package/dist/utils/cloneFileDiffMetadata.js.map +1 -0
  134. package/dist/utils/computeEstimatedDiffHeights.d.ts +3 -1
  135. package/dist/utils/computeEstimatedDiffHeights.d.ts.map +1 -1
  136. package/dist/utils/computeEstimatedDiffHeights.js +8 -1
  137. package/dist/utils/computeEstimatedDiffHeights.js.map +1 -1
  138. package/dist/utils/createCodeViewHeaderFooterHostElement.d.ts +5 -0
  139. package/dist/utils/createCodeViewHeaderFooterHostElement.d.ts.map +1 -0
  140. package/dist/utils/createCodeViewHeaderFooterHostElement.js +19 -0
  141. package/dist/utils/createCodeViewHeaderFooterHostElement.js.map +1 -0
  142. package/dist/utils/createPreElement.js +0 -1
  143. package/dist/utils/createPreElement.js.map +1 -1
  144. package/dist/utils/getDiffFileInput.d.ts +14 -0
  145. package/dist/utils/getDiffFileInput.d.ts.map +1 -0
  146. package/dist/utils/getDiffFileInput.js +24 -0
  147. package/dist/utils/getDiffFileInput.js.map +1 -0
  148. package/dist/utils/getDiffHunksRendererOptions.js +1 -0
  149. package/dist/utils/getDiffHunksRendererOptions.js.map +1 -1
  150. package/dist/utils/getFiletypeFromFileName.d.ts.map +1 -1
  151. package/dist/utils/getFiletypeFromFileName.js +2 -0
  152. package/dist/utils/getFiletypeFromFileName.js.map +1 -1
  153. package/dist/utils/hydratePartialDiff.d.ts +10 -0
  154. package/dist/utils/hydratePartialDiff.d.ts.map +1 -0
  155. package/dist/utils/hydratePartialDiff.js +140 -0
  156. package/dist/utils/hydratePartialDiff.js.map +1 -0
  157. package/dist/utils/iterateOverDiff.js +3 -3
  158. package/dist/utils/iterateOverDiff.js.map +1 -1
  159. package/dist/utils/parseDiffFromFile.d.ts +1 -1
  160. package/dist/utils/parseDiffFromFile.d.ts.map +1 -1
  161. package/dist/utils/parseDiffFromFile.js +26 -5
  162. package/dist/utils/parseDiffFromFile.js.map +1 -1
  163. package/dist/utils/setWrapperNodeProps.js +0 -1
  164. package/dist/utils/setWrapperNodeProps.js.map +1 -1
  165. package/dist/utils/updateDiffHunks.d.ts +5 -1
  166. package/dist/utils/updateDiffHunks.d.ts.map +1 -1
  167. package/dist/utils/updateDiffHunks.js +48 -4
  168. package/dist/utils/updateDiffHunks.js.map +1 -1
  169. package/dist/worker/WorkerPoolManager.d.ts +7 -2
  170. package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
  171. package/dist/worker/WorkerPoolManager.js +78 -15
  172. package/dist/worker/WorkerPoolManager.js.map +1 -1
  173. package/dist/worker/index.d.ts +2 -2
  174. package/dist/worker/types.d.ts +7 -1
  175. package/dist/worker/types.d.ts.map +1 -1
  176. package/dist/worker/worker-portable.js +5 -3
  177. package/dist/worker/worker-portable.js.map +1 -1
  178. package/dist/worker/worker.js +5 -3
  179. package/dist/worker/worker.js.map +1 -1
  180. package/package.json +1 -1
@@ -1,9 +1,11 @@
1
1
  import { DEFAULT_CODE_VIEW_FILE_METRICS, DEFAULT_CODE_VIEW_LAYOUT, DEFAULT_SMOOTH_SCROLL_SETTINGS, DEFAULT_THEMES, DIFFS_DEVELOPMENT_BUILD, DIFFS_TAG_NAME } from "../constants.js";
2
2
  import { dequeueRender, queueRender } from "../managers/UniversalRenderingManager.js";
3
+ import { areManagedSnapshotsEqual } from "../utils/areManagedSnapshotsEqual.js";
3
4
  import { areObjectsEqual } from "../utils/areObjectsEqual.js";
4
5
  import { areThemesEqual } from "../utils/areThemesEqual.js";
5
6
  import { areOptionsEqual } from "../utils/areOptionsEqual.js";
6
7
  import { areSelectionsEqual } from "../utils/areSelectionsEqual.js";
8
+ import { createCodeViewHeaderFooterHostElement } from "../utils/createCodeViewHeaderFooterHostElement.js";
7
9
  import { createWindowFromScrollPosition } from "../utils/createWindowFromScrollPosition.js";
8
10
  import { isStyleNode } from "../utils/isStyleNode.js";
9
11
  import { prefersReducedMotion } from "../utils/prefersReducedMotion.js";
@@ -28,6 +30,7 @@ const CODE_VIEW_DIFF_OPTION_KEYS = [
28
30
  "diffIndicators",
29
31
  "disableBackground",
30
32
  "expandUnchanged",
33
+ "loadDiffFiles",
31
34
  "collapsedContextThreshold",
32
35
  "lineDiffType",
33
36
  "maxLineDiffLength",
@@ -61,6 +64,13 @@ const CODE_VIEW_FILE_OPTION_KEYS = [
61
64
  "controlledSelection",
62
65
  "disableErrorHandling"
63
66
  ];
67
+ const CODE_VIEW_EDIT_FORCED_OPTION_KEYS = /* @__PURE__ */ new Set([
68
+ "useTokenTransformer",
69
+ "enableGutterUtility",
70
+ "enableLineSelection",
71
+ "lineHoverHighlight",
72
+ "expandUnchanged"
73
+ ]);
64
74
  const CODE_VIEW_SHARED_CALLBACK_KEYS = [
65
75
  "renderCustomHeader",
66
76
  "renderHeaderPrefix",
@@ -130,6 +140,8 @@ var CodeView = class CodeView {
130
140
  items = [];
131
141
  idToItem = /* @__PURE__ */ new Map();
132
142
  selectedLines = null;
143
+ itemEditors = /* @__PURE__ */ new Map();
144
+ attachedEditors = /* @__PURE__ */ new Set();
133
145
  instanceToItem = /* @__PURE__ */ new Map();
134
146
  layoutDirtyIndex;
135
147
  pendingLayoutReset;
@@ -171,12 +183,23 @@ var CodeView = class CodeView {
171
183
  container = document.createElement("div");
172
184
  stickyContainer = document.createElement("div");
173
185
  stickyOffset = document.createElement("div");
186
+ header = {
187
+ element: void 0,
188
+ render: void 0,
189
+ height: 0
190
+ };
191
+ footer = {
192
+ element: void 0,
193
+ render: void 0,
194
+ height: 0
195
+ };
174
196
  elementPool = [];
175
197
  elementPoolVersion = 0;
176
198
  elementPoolTracker = /* @__PURE__ */ new WeakMap();
177
199
  pendingElementPool = [];
178
200
  options;
179
201
  workerManager;
202
+ isReadySubscription;
180
203
  isContainerManaged;
181
204
  constructor(options = { theme: DEFAULT_THEMES }, workerManager, isContainerManaged = false) {
182
205
  this.options = options;
@@ -196,6 +219,9 @@ var CodeView = class CodeView {
196
219
  getLayout() {
197
220
  return this.options.layout ?? DEFAULT_CODE_VIEW_LAYOUT;
198
221
  }
222
+ getItemTopOffset() {
223
+ return this.getLayout().paddingTop + this.header.height;
224
+ }
199
225
  computeMetricsCache(itemMetrics) {
200
226
  this.itemMetricsCache = {
201
227
  hunkLineCount: itemMetrics?.hunkLineCount ?? DEFAULT_CODE_VIEW_FILE_METRICS.hunkLineCount,
@@ -298,6 +324,43 @@ var CodeView = class CodeView {
298
324
  this.container?.style.setProperty("margin-top", `${paddingTop}px`);
299
325
  this.container?.style.setProperty("margin-bottom", `${paddingBottom}px`);
300
326
  }
327
+ reconcileHeaderFooterHosts() {
328
+ const headerChanged = this.reconcileHost("header");
329
+ const footerChanged = this.reconcileHost("footer");
330
+ return headerChanged || footerChanged;
331
+ }
332
+ reconcileHost(type) {
333
+ const { root, container } = this;
334
+ if (root == null || container == null) return false;
335
+ const host = type === "header" ? this.header : this.footer;
336
+ const render = type === "header" ? this.options.renderCodeViewHeader : this.options.renderCodeViewFooter;
337
+ if (render == null) {
338
+ if (host.element == null) return false;
339
+ this.resizeObserver?.unobserve(host.element);
340
+ host.element.remove();
341
+ host.element = void 0;
342
+ host.render = void 0;
343
+ this.setHostHeight(host, 0);
344
+ return false;
345
+ }
346
+ if (host.element != null && render === host.render) return false;
347
+ const element = host.element ?? createCodeViewHeaderFooterHostElement(type, container, this.resizeObserver);
348
+ host.element = element;
349
+ const content = render();
350
+ if (content != null) element.replaceChildren(content);
351
+ else if (!this.isContainerManaged && element.children.length > 0) element.textContent = "";
352
+ host.render = render;
353
+ return true;
354
+ }
355
+ setHostHeight(host, height) {
356
+ if (host.height === height) return;
357
+ host.height = height;
358
+ this.scrollDirty = true;
359
+ }
360
+ measureMountedHosts() {
361
+ if (this.header.element != null) this.setHostHeight(this.header, this.header.element.getBoundingClientRect().height);
362
+ if (this.footer.element != null) this.setHostHeight(this.footer, this.footer.element.getBoundingClientRect().height);
363
+ }
301
364
  setup(root) {
302
365
  if (this.root != null) throw new Error("CodeView.setup: already setup");
303
366
  this.workerManager?.subscribeToThemeChanges(this);
@@ -337,8 +400,12 @@ var CodeView = class CodeView {
337
400
  };
338
401
  }
339
402
  reset() {
403
+ this.clearReadySubscription();
340
404
  this.restoreScrollInteractions();
341
405
  this.cleanAllRenderedItems();
406
+ for (const record of this.itemEditors.values()) record.editor.cleanUp();
407
+ this.itemEditors.clear();
408
+ this.attachedEditors.clear();
342
409
  this.selectedLines = null;
343
410
  this.items.length = 0;
344
411
  this.idToItem.clear();
@@ -381,6 +448,14 @@ var CodeView = class CodeView {
381
448
  this.stickyOffset.remove();
382
449
  this.stickyContainer.remove();
383
450
  this.stickyContainer.textContent = "";
451
+ this.header.element?.remove();
452
+ this.header.element = void 0;
453
+ this.header.render = void 0;
454
+ this.header.height = 0;
455
+ this.footer.element?.remove();
456
+ this.footer.element = void 0;
457
+ this.footer.render = void 0;
458
+ this.footer.height = 0;
384
459
  this.root = void 0;
385
460
  this.container = void 0;
386
461
  }
@@ -415,6 +490,7 @@ var CodeView = class CodeView {
415
490
  const { element } = item;
416
491
  if (element != null && this.renderedItemOwnsFocus(element)) this.shouldFixContainerFocus = true;
417
492
  item.instance.cleanUp(true);
493
+ this.attachedEditors.delete(item.item.id);
418
494
  item.element = void 0;
419
495
  if (element == null) return;
420
496
  element.remove();
@@ -507,6 +583,14 @@ var CodeView = class CodeView {
507
583
  getItem(itemId) {
508
584
  return this.idToItem.get(itemId)?.item;
509
585
  }
586
+ /**
587
+ * Get the live editor for an item currently in edit mode. Use this to drive
588
+ * editor APIs CodeView does not wrap (applyEdits, undo, setMarkers, …).
589
+ * Returns undefined once the item leaves edit mode.
590
+ */
591
+ getEditor(itemId) {
592
+ return this.itemEditors.get(itemId)?.editor;
593
+ }
510
594
  updateItem(input) {
511
595
  const item = this.idToItem.get(input.id);
512
596
  if (item == null) {
@@ -518,6 +602,7 @@ var CodeView = class CodeView {
518
602
  this.scrollDirty = true;
519
603
  this.render();
520
604
  this.syncSelection();
605
+ this.syncItemEditors();
521
606
  return true;
522
607
  }
523
608
  updateItemId(oldId, newId) {
@@ -542,6 +627,13 @@ var CodeView = class CodeView {
542
627
  };
543
628
  this.options.onSelectedLinesChange?.(this.selectedLines);
544
629
  }
630
+ const editorRecord = this.itemEditors.get(oldId);
631
+ if (editorRecord != null) {
632
+ editorRecord.state.id = newId;
633
+ this.itemEditors.delete(oldId);
634
+ this.itemEditors.set(newId, editorRecord);
635
+ if (this.attachedEditors.delete(oldId)) this.attachedEditors.add(newId);
636
+ }
545
637
  this.renamePendingScrollTarget(oldId, newId);
546
638
  this.renamePendingLayoutAnchor(oldId, newId);
547
639
  this.render();
@@ -549,17 +641,25 @@ var CodeView = class CodeView {
549
641
  }
550
642
  addItem(input) {
551
643
  this.addItems([input]);
552
- this.syncSelection();
553
644
  }
554
645
  addItems(inputs) {
555
646
  this.appendItemsInternal(inputs);
556
647
  this.syncSelection();
648
+ this.syncItemEditors();
557
649
  }
558
650
  setItems(items) {
559
- if (items.length === 0) this.reset();
560
- else if (this.items.length === 0) this.appendItemsInternal(items);
651
+ if (items.length === 0) {
652
+ const completions = [];
653
+ for (const record of this.itemEditors.values()) {
654
+ const { lastChange } = record.state;
655
+ if (lastChange != null) completions.push(lastChange);
656
+ }
657
+ this.reset();
658
+ for (const { item, file, lineAnnotations } of completions) this.options.onItemEditComplete?.(item, file, lineAnnotations);
659
+ } else if (this.items.length === 0) this.appendItemsInternal(items);
561
660
  else if (!this.tryAppendItems(items)) this.reconcileItems(items);
562
661
  this.syncSelection();
662
+ this.syncItemEditors();
563
663
  }
564
664
  /**
565
665
  * Append new records to the viewer while preserving existing layout state.
@@ -621,7 +721,9 @@ var CodeView = class CodeView {
621
721
  this.scrollDirty = true;
622
722
  }
623
723
  if (!areOptionsEqual(prevOptions, options)) this.renderOptionsRevision++;
624
- if (!this.isContainerManaged && this.items.length > 0) this.render();
724
+ this.syncItemEditors();
725
+ const headerFooterChanged = prevOptions.renderCodeViewHeader !== options.renderCodeViewHeader || prevOptions.renderCodeViewFooter !== options.renderCodeViewFooter;
726
+ if (!this.isContainerManaged && (this.items.length > 0 || headerFooterChanged)) this.render();
625
727
  }
626
728
  capturePendingLayoutAnchor() {
627
729
  if (this.root == null || this.items.length === 0 || this.pendingScrollTarget != null) return;
@@ -634,6 +736,25 @@ var CodeView = class CodeView {
634
736
  this.computeRenderRangeAndEmit();
635
737
  } else queueRender(this.computeRenderRangeAndEmit);
636
738
  }
739
+ isReady() {
740
+ const { workerManager } = this;
741
+ if (workerManager == null || workerManager.isInitialized() || workerManager.getStats().workersFailed) {
742
+ this.clearReadySubscription();
743
+ return true;
744
+ }
745
+ this.isReadySubscription ??= workerManager.subscribeToStatChanges((stats) => {
746
+ if (stats.managerState !== "initialized" && !stats.workersFailed) return;
747
+ this.clearReadySubscription();
748
+ this.render(true);
749
+ });
750
+ if (workerManager.getStats().managerState === "waiting") workerManager.initialize().catch(() => {});
751
+ return false;
752
+ }
753
+ clearReadySubscription() {
754
+ if (this.isReadySubscription == null) return;
755
+ this.isReadySubscription();
756
+ this.isReadySubscription = void 0;
757
+ }
637
758
  instanceChanged(instance, layoutDirty) {
638
759
  const item = this.instanceToItem.get(instance);
639
760
  if (item == null) throw new Error("CodeView.instanceChanged: An instance has changed that is not registered");
@@ -646,6 +767,12 @@ var CodeView = class CodeView {
646
767
  getContainerElement() {
647
768
  return this.root;
648
769
  }
770
+ getHeaderElement() {
771
+ return this.header.element;
772
+ }
773
+ getFooterElement() {
774
+ return this.footer.element;
775
+ }
649
776
  getRenderedItems() {
650
777
  const { firstIndex, lastIndex } = this.renderState;
651
778
  if (firstIndex === -1 || lastIndex === -1 || lastIndex < firstIndex) return [];
@@ -679,7 +806,18 @@ var CodeView = class CodeView {
679
806
  return true;
680
807
  }
681
808
  getSlotSnapshot(coordinator) {
682
- return getSlotSnapshot(this.getRenderedItems(), coordinator);
809
+ return this.buildSlotSnapshot(coordinator);
810
+ }
811
+ buildSlotSnapshot(coordinator) {
812
+ const items = getSlotItems(this.getRenderedItems(), coordinator);
813
+ const { element: header } = this.header;
814
+ const { element: footer } = this.footer;
815
+ if (items == null && header == null && footer == null) return;
816
+ return {
817
+ items,
818
+ header,
819
+ footer
820
+ };
683
821
  }
684
822
  subscribeToScroll(listener) {
685
823
  this.scrollListeners.add(listener);
@@ -695,7 +833,7 @@ var CodeView = class CodeView {
695
833
  getTopForItem(id) {
696
834
  const item = this.idToItem.get(id);
697
835
  if (item == null) return;
698
- return item.top + this.getLayout().paddingTop;
836
+ return item.top + this.getItemTopOffset();
699
837
  }
700
838
  createItem(input, index, top) {
701
839
  const { itemMetricsCache: itemMetrics } = this;
@@ -742,6 +880,75 @@ var CodeView = class CodeView {
742
880
  }
743
881
  item.instance.setSelectedLines(this.selectedLines.range, { notify: false });
744
882
  }
883
+ isItemInEditMode(item) {
884
+ return this.options.createEditor != null && item.item.edit === true && item.item.collapsed !== true;
885
+ }
886
+ isReceiverEdited(receiver, mode) {
887
+ const state = getItemOptionsState(receiver);
888
+ if (state == null) return false;
889
+ const item = this.getItemOptions(state, mode);
890
+ return item != null && this.isItemInEditMode(item);
891
+ }
892
+ /**
893
+ * Attach (or lazily create) the editor for a mounted edit-mode item. Called
894
+ * from the render loop so every mounted item passes through it: fresh
895
+ * mounts, remounts after virtualization released the item, and items whose
896
+ * edit flag was just turned on. Editors persist across unmounts, so a
897
+ * remounted item re-attaches its existing editor and resumes the retained
898
+ * document; the renderers keep the host's file/diff data in sync with the
899
+ * session so the remount paints the edited text.
900
+ */
901
+ attachItemEditor(item) {
902
+ const { id } = item.item;
903
+ const { createEditor } = this.options;
904
+ if (createEditor == null || item.element == null || this.attachedEditors.has(id) || !this.isItemInEditMode(item)) return;
905
+ let record = this.itemEditors.get(id);
906
+ if (record == null) {
907
+ const state = { id };
908
+ const editor = createEditor({ onChange: (file, lineAnnotations) => {
909
+ const latest = this.idToItem.get(state.id);
910
+ if (latest == null) return;
911
+ state.lastChange = {
912
+ item: latest.item,
913
+ file,
914
+ lineAnnotations
915
+ };
916
+ this.options.onItemEditChange?.(latest.item, file, lineAnnotations);
917
+ } });
918
+ if (editor == null) return;
919
+ record = {
920
+ editor,
921
+ state
922
+ };
923
+ this.itemEditors.set(id, record);
924
+ }
925
+ if (this.selectedLines?.id === id) this.applySelectedLines(null, { notify: false });
926
+ record.editor.edit(item.instance);
927
+ this.attachedEditors.add(id);
928
+ }
929
+ /**
930
+ * Drop editors for items that can no longer be edited: removed, edit turned
931
+ * off, collapsed, or the createEditor option was unset. Attachment happens
932
+ * in the render loop via attachItemEditor, so this only reconciles editors
933
+ * CodeView is already holding.
934
+ */
935
+ syncItemEditors() {
936
+ if (this.itemEditors.size === 0) return;
937
+ const completions = [];
938
+ for (const [id, record] of this.itemEditors) {
939
+ const item = this.idToItem.get(id);
940
+ if (item != null && this.isItemInEditMode(item)) continue;
941
+ record.editor.cleanUp();
942
+ this.itemEditors.delete(id);
943
+ this.attachedEditors.delete(id);
944
+ const { lastChange } = record.state;
945
+ if (lastChange != null) completions.push(item == null ? lastChange : {
946
+ ...lastChange,
947
+ item: item.item
948
+ });
949
+ }
950
+ for (const { item, file, lineAnnotations } of completions) this.options.onItemEditComplete?.(item, file, lineAnnotations);
951
+ }
745
952
  renamePendingScrollTarget(oldId, newId) {
746
953
  const { pendingScrollTarget } = this;
747
954
  if (pendingScrollTarget == null || pendingScrollTarget.type === "position" || pendingScrollTarget.id !== oldId) return;
@@ -755,19 +962,42 @@ var CodeView = class CodeView {
755
962
  }
756
963
  createFileOptionsPrototype() {
757
964
  const prototype = {};
758
- for (const key of CODE_VIEW_FILE_OPTION_KEYS) defineItemOption(prototype, key, () => this.options[key]);
965
+ for (const key of CODE_VIEW_FILE_OPTION_KEYS) {
966
+ if (CODE_VIEW_EDIT_FORCED_OPTION_KEYS.has(key)) continue;
967
+ defineItemOption(prototype, key, () => this.options[key]);
968
+ }
969
+ defineItemOption(prototype, "useTokenTransformer", (receiver) => this.isReceiverEdited(receiver, "file") ? true : this.options.useTokenTransformer);
970
+ defineItemOption(prototype, "enableGutterUtility", (receiver) => this.isReceiverEdited(receiver, "file") ? false : this.options.enableGutterUtility);
971
+ defineItemOption(prototype, "enableLineSelection", (receiver) => this.isReceiverEdited(receiver, "file") ? false : this.options.enableLineSelection);
972
+ defineItemOption(prototype, "lineHoverHighlight", (receiver) => this.isReceiverEdited(receiver, "file") ? "disabled" : this.options.lineHoverHighlight);
759
973
  defineItemOption(prototype, "stickyHeader", () => this.options.stickyHeaders);
760
- defineItemOption(prototype, "collapsed", (receiver) => this.getItemOptions(getItemOptionsState(receiver), "file")?.item.collapsed === true);
974
+ defineItemOption(prototype, "collapsed", (receiver) => {
975
+ const state = getItemOptionsState(receiver);
976
+ if (state == null) return;
977
+ return this.getItemOptions(state, "file")?.item.collapsed;
978
+ });
761
979
  for (const key of CODE_VIEW_SHARED_CALLBACK_KEYS) this.defineItemSharedCallback(prototype, "file", key);
762
980
  for (const key of CODE_VIEW_SELECTION_CALLBACK_KEYS) this.defineItemSelectionCallback(prototype, "file", key);
763
981
  return prototype;
764
982
  }
765
983
  createDiffOptionsPrototype() {
766
984
  const prototype = {};
767
- for (const key of CODE_VIEW_DIFF_OPTION_KEYS) defineItemOption(prototype, key, () => this.options[key]);
985
+ for (const key of CODE_VIEW_DIFF_OPTION_KEYS) {
986
+ if (CODE_VIEW_EDIT_FORCED_OPTION_KEYS.has(key)) continue;
987
+ defineItemOption(prototype, key, () => this.options[key]);
988
+ }
989
+ defineItemOption(prototype, "useTokenTransformer", (receiver) => this.isReceiverEdited(receiver, "diff") ? true : this.options.useTokenTransformer);
990
+ defineItemOption(prototype, "enableGutterUtility", (receiver) => this.isReceiverEdited(receiver, "diff") ? false : this.options.enableGutterUtility);
991
+ defineItemOption(prototype, "enableLineSelection", (receiver) => this.isReceiverEdited(receiver, "diff") ? false : this.options.enableLineSelection);
992
+ defineItemOption(prototype, "lineHoverHighlight", (receiver) => this.isReceiverEdited(receiver, "diff") ? "disabled" : this.options.lineHoverHighlight);
993
+ defineItemOption(prototype, "expandUnchanged", (receiver) => this.isReceiverEdited(receiver, "diff") ? true : this.options.expandUnchanged);
768
994
  defineItemOption(prototype, "stickyHeader", () => this.options.stickyHeaders);
769
995
  defineItemOption(prototype, "hunkSeparators", () => this.options.hunkSeparators);
770
- defineItemOption(prototype, "collapsed", (receiver) => this.getItemOptions(getItemOptionsState(receiver), "diff")?.item.collapsed === true);
996
+ defineItemOption(prototype, "collapsed", (receiver) => {
997
+ const state = getItemOptionsState(receiver);
998
+ if (state == null) return;
999
+ return this.getItemOptions(state, "diff")?.item.collapsed;
1000
+ });
771
1001
  for (const key of CODE_VIEW_SHARED_CALLBACK_KEYS) this.defineItemSharedCallback(prototype, "diff", key);
772
1002
  for (const key of CODE_VIEW_SELECTION_CALLBACK_KEYS) this.defineItemSelectionCallback(prototype, "diff", key);
773
1003
  return prototype;
@@ -783,7 +1013,9 @@ var CodeView = class CodeView {
783
1013
  return options;
784
1014
  }
785
1015
  updateItemOptionsId(options, id) {
786
- getItemOptionsState(options).id = id;
1016
+ const state = getItemOptionsState(options);
1017
+ if (state == null) throw new Error(`CodeView.updateItemOptionsId: No valid state`);
1018
+ state.id = id;
787
1019
  }
788
1020
  getItemOptions(state, mode) {
789
1021
  const item = this.idToItem.get(state.id);
@@ -794,6 +1026,7 @@ var CodeView = class CodeView {
794
1026
  defineItemOption(options, key, (receiver) => {
795
1027
  if (this.options[key] == null) return;
796
1028
  const state = getItemOptionsState(receiver);
1029
+ if (state == null) return;
797
1030
  const callbackCache = state.callbackCache ??= {};
798
1031
  let wrapped = callbackCache[key];
799
1032
  if (wrapped == null) {
@@ -812,6 +1045,9 @@ var CodeView = class CodeView {
812
1045
  defineItemOption(options, key, (receiver) => {
813
1046
  if (this.options.enableLineSelection !== true) return;
814
1047
  const state = getItemOptionsState(receiver);
1048
+ if (state == null) return;
1049
+ const item = this.getItemOptions(state, mode);
1050
+ if (item != null && this.isItemInEditMode(item)) return;
815
1051
  const callbackCache = state.callbackCache ??= {};
816
1052
  let wrapped = callbackCache[key];
817
1053
  if (wrapped == null) {
@@ -939,7 +1175,7 @@ var CodeView = class CodeView {
939
1175
  }
940
1176
  getMaxScrollTopForHeight(scrollHeight) {
941
1177
  const { paddingBottom, paddingTop } = this.getLayout();
942
- return Math.max(paddingTop + scrollHeight + paddingBottom - this.getHeight(), 0);
1178
+ return Math.max(paddingTop + this.header.height + scrollHeight + this.footer.height + paddingBottom - this.getHeight(), 0);
943
1179
  }
944
1180
  getMaxScrollTop() {
945
1181
  return this.getMaxScrollTopForHeight(this.getScrollHeight());
@@ -1051,7 +1287,7 @@ var CodeView = class CodeView {
1051
1287
  const rect = this.getScrollTargetRect(target);
1052
1288
  if (rect == null) return;
1053
1289
  const offset = target.offset ?? 0;
1054
- const targetTop = this.getLayout().paddingTop + rect.top;
1290
+ const targetTop = this.getItemTopOffset() + rect.top;
1055
1291
  const targetBottom = targetTop + rect.height;
1056
1292
  const currentTop = this.getScrollTop();
1057
1293
  const visibleTop = currentTop + (target.type === "line" || target.type === "range" ? this.getStickyHeaderOffset() : 0);
@@ -1103,7 +1339,7 @@ var CodeView = class CodeView {
1103
1339
  * and stickyOffset if necessary
1104
1340
  */
1105
1341
  resolveAlignedScrollPosition(targetTop, targetHeight, align, offset = 0, stickyOffset = 0) {
1106
- targetTop += this.getLayout().paddingTop;
1342
+ targetTop += this.getItemTopOffset();
1107
1343
  const viewportHeight = this.getHeight();
1108
1344
  if (align === "center" && targetHeight + offset < viewportHeight) return targetTop - (viewportHeight - targetHeight) / 2 + offset;
1109
1345
  if (align === "end") return targetTop - (viewportHeight - targetHeight) + offset;
@@ -1207,6 +1443,7 @@ var CodeView = class CodeView {
1207
1443
  }
1208
1444
  computeRenderRangeAndEmit = (timestamp = performance.now()) => {
1209
1445
  if (CodeView.__STOP || this.container == null) return;
1446
+ if (!this.isReady()) return;
1210
1447
  const viewportHeight = this.getHeight();
1211
1448
  const initialScrollTop = this.getScrollTop();
1212
1449
  let scrollTopAfterLayout = initialScrollTop;
@@ -1234,7 +1471,7 @@ var CodeView = class CodeView {
1234
1471
  const fitPerfectly = !computeScrollCorrection && (this.renderState.scrollTop === -1 || Math.abs(targetScrollTop - this.renderState.scrollTop) > viewportHeight + this.config.overscrollSize * 2);
1235
1472
  if (fitPerfectly) scrollAnchor = void 0;
1236
1473
  this.windowSpecs = createWindowFromScrollPosition({
1237
- scrollTop: targetScrollTop,
1474
+ scrollTop: targetScrollTop - this.header.height,
1238
1475
  height: viewportHeight,
1239
1476
  scrollHeight: this.getScrollHeight(),
1240
1477
  fitPerfectly,
@@ -1253,6 +1490,7 @@ var CodeView = class CodeView {
1253
1490
  if (item == null) throw new Error(`CodeView.computeRenderRangeAndEmit: No item at index: ${index}`);
1254
1491
  if (!(item.top > top - item.height && item.top <= bottom)) this.releaseRenderedItem(item);
1255
1492
  }
1493
+ const hostsChanged = this.reconcileHeaderFooterHosts();
1256
1494
  let prevElement;
1257
1495
  const updatedItems = /* @__PURE__ */ new Set();
1258
1496
  const startingIndex = this.findFirstVisibleIndex(top);
@@ -1278,10 +1516,13 @@ var CodeView = class CodeView {
1278
1516
  }
1279
1517
  prevElement = item.element;
1280
1518
  }
1519
+ if (item.item.edit === true) this.attachItemEditor(item);
1281
1520
  }
1282
1521
  this.renderState.firstIndex = startingIndex <= lastRenderedIndex ? startingIndex : -1;
1283
1522
  this.renderState.lastIndex = lastRenderedIndex;
1284
1523
  this.flushSlotCoordinator();
1524
+ this.flushManagers(updatedItems);
1525
+ if (hostsChanged) this.measureMountedHosts();
1285
1526
  this.reconcileRenderedItems(updatedItems);
1286
1527
  this.syncContainerHeight();
1287
1528
  this.updateStickyPositioning();
@@ -1306,7 +1547,7 @@ var CodeView = class CodeView {
1306
1547
  this.scrollAnimation = void 0;
1307
1548
  }
1308
1549
  this.renderState.scrollTop = roundToDevicePixel(syncedScrollTop);
1309
- this.flushManagers(updatedItems);
1550
+ this.updateStickyPositioning();
1310
1551
  this.validateStickyContainerHeight();
1311
1552
  this.fixContainerFocus();
1312
1553
  if (fitPerfectly || this.scrollAnimation != null) this.render();
@@ -1420,6 +1661,25 @@ var CodeView = class CodeView {
1420
1661
  this.scrollAnimation = void 0;
1421
1662
  }
1422
1663
  }
1664
+ } else if (entry.target === this.header.element || entry.target === this.footer.element) {
1665
+ const host = entry.target === this.header.element ? this.header : this.footer;
1666
+ const blockSize = entry.borderBoxSize[0].blockSize;
1667
+ if (blockSize !== host.height) {
1668
+ const currentScrollTop = this.getScrollTop();
1669
+ const anchor = this.getScrollAnchor(currentScrollTop);
1670
+ this.setHostHeight(host, blockSize);
1671
+ const anchoredScrollTop = anchor != null ? this.resolveAnchoredScrollTop(anchor) : void 0;
1672
+ if (anchoredScrollTop != null) {
1673
+ const resizeAnchorDelta = anchoredScrollTop - currentScrollTop;
1674
+ this.applyScrollFix(anchoredScrollTop, currentScrollTop, this.windowSpecs);
1675
+ if (this.scrollAnimation != null) this.scrollAnimation.position += resizeAnchorDelta;
1676
+ }
1677
+ if (this.pendingScrollTarget != null && this.isPendingTargetSettled(this.pendingScrollTarget)) {
1678
+ this.pendingScrollTarget = void 0;
1679
+ this.scrollAnimation = void 0;
1680
+ }
1681
+ this.render();
1682
+ }
1423
1683
  } else {
1424
1684
  this.scrollDirty = true;
1425
1685
  this.heightDirty = true;
@@ -1442,6 +1702,7 @@ var CodeView = class CodeView {
1442
1702
  */
1443
1703
  getScrollAnchor(scrollTop) {
1444
1704
  if (this.pendingLayoutAnchor != null) return this.pendingLayoutAnchor;
1705
+ if (scrollTop <= 0) return;
1445
1706
  const { firstIndex, lastIndex, stickyTop, stickyBottom } = this.renderState;
1446
1707
  if (firstIndex === -1 || lastIndex === -1) return;
1447
1708
  const viewportHeight = this.getHeight();
@@ -1449,7 +1710,7 @@ var CodeView = class CodeView {
1449
1710
  for (let index = firstIndex; index <= lastIndex; index++) {
1450
1711
  const item = this.items[index];
1451
1712
  if (item == null) continue;
1452
- const absoluteItemTop = this.getLayout().paddingTop + item.top;
1713
+ const absoluteItemTop = this.getItemTopOffset() + item.top;
1453
1714
  if (absoluteItemTop + item.height <= scrollTop) continue;
1454
1715
  if (absoluteItemTop >= scrollTop + viewportHeight) break;
1455
1716
  if (absoluteItemTop >= scrollTop) return {
@@ -1481,14 +1742,14 @@ var CodeView = class CodeView {
1481
1742
  resolveAnchoredScrollTop(anchor) {
1482
1743
  const item = this.idToItem.get(anchor.id);
1483
1744
  if (item == null) return;
1484
- const { paddingTop } = this.getLayout();
1745
+ const itemTopOffset = this.getItemTopOffset();
1485
1746
  if (anchor.type === "item") {
1486
- const absoluteItemTop = paddingTop + item.top;
1747
+ const absoluteItemTop = itemTopOffset + item.top;
1487
1748
  return this.clampScrollTop(absoluteItemTop - anchor.viewportOffset);
1488
1749
  }
1489
1750
  const linePosition = item.type === "diff" ? item.instance.getLinePosition(anchor.lineNumber, anchor.side) : item.instance.getLinePosition(anchor.lineNumber);
1490
1751
  if (linePosition == null) return;
1491
- const absoluteLineTop = paddingTop + item.top + linePosition.top;
1752
+ const absoluteLineTop = itemTopOffset + item.top + linePosition.top;
1492
1753
  return this.clampScrollTop(absoluteLineTop - anchor.viewportOffset);
1493
1754
  }
1494
1755
  /**
@@ -1545,11 +1806,10 @@ var CodeView = class CodeView {
1545
1806
  }
1546
1807
  flushSlotCoordinator() {
1547
1808
  if (this.slotCoordinator == null) return;
1548
- const { onSnapshotChange } = this.slotCoordinator;
1549
- const slotSnapshot = getSlotSnapshot(this.getRenderedItems(), this.slotCoordinator);
1550
- if (areSlotSnapshotsEqual(this.slotSnapshot, slotSnapshot)) return;
1809
+ const slotSnapshot = this.buildSlotSnapshot(this.slotCoordinator);
1810
+ if (areManagedSnapshotsEqual(this.slotSnapshot, slotSnapshot)) return;
1551
1811
  this.slotSnapshot = slotSnapshot;
1552
- onSnapshotChange(slotSnapshot);
1812
+ this.slotCoordinator.onSnapshotChange(slotSnapshot);
1553
1813
  }
1554
1814
  notifyScroll() {
1555
1815
  if (this.scrollListeners.size === 0) return;
@@ -1618,8 +1878,11 @@ var CodeView = class CodeView {
1618
1878
  const item = this.items[index];
1619
1879
  if (item == null) throw new Error("CodeView.recomputeLayout: invalid item index");
1620
1880
  item.top = runningTop;
1621
- if (item.type === "diff") item.height = item.instance.prepareCodeViewItem(item.item.fileDiff, runningTop, reset, item.item.annotations ?? []);
1622
- else item.height = item.instance.prepareCodeViewItem(item.item.file, runningTop, reset, item.item.annotations ?? []);
1881
+ if (item.type === "diff") {
1882
+ const fileDiff = item.instance.consumeCodeViewLayoutChanges(item.item.fileDiff);
1883
+ if (fileDiff != null) item.item.fileDiff = fileDiff;
1884
+ item.height = item.instance.prepareCodeViewItem(item.item.fileDiff, runningTop, reset, item.item.annotations ?? []);
1885
+ } else item.height = item.instance.prepareCodeViewItem(item.item.file, runningTop, reset, item.item.annotations ?? []);
1623
1886
  runningTop += item.height;
1624
1887
  if (index < this.items.length - 1) runningTop += layout.gap;
1625
1888
  }
@@ -1697,7 +1960,7 @@ function syncRenderedItemOrder(container, element, prevElement) {
1697
1960
  function hasAnnotations(item) {
1698
1961
  return (item.annotations?.length ?? 0) > 0;
1699
1962
  }
1700
- function getSlotSnapshot(renderedItems, { hasHeaderRenderers, hasAnnotationRenderer, hasGutterRenderer }) {
1963
+ function getSlotItems(renderedItems, { hasHeaderRenderers, hasAnnotationRenderer, hasGutterRenderer }) {
1701
1964
  if (renderedItems.length === 0) return;
1702
1965
  if (hasHeaderRenderers || hasGutterRenderer) return renderedItems;
1703
1966
  if (!hasAnnotationRenderer) return;
@@ -1705,17 +1968,7 @@ function getSlotSnapshot(renderedItems, { hasHeaderRenderers, hasAnnotationRende
1705
1968
  for (const renderedItem of renderedItems) if (hasAnnotations(renderedItem.item)) slotSnapshot.push(renderedItem);
1706
1969
  return slotSnapshot.length > 0 ? slotSnapshot : void 0;
1707
1970
  }
1708
- function areSlotSnapshotsEqual(previous, next) {
1709
- if (previous == null || next == null) return previous === next;
1710
- if (previous.length !== next.length) return false;
1711
- for (let index = 0; index < previous.length; index++) {
1712
- const previousItem = previous[index];
1713
- const nextItem = next[index];
1714
- if (previousItem == null || nextItem == null || previousItem.id !== nextItem.id || previousItem.type !== nextItem.type || previousItem.element !== nextItem.element || previousItem.version !== nextItem.version) return false;
1715
- }
1716
- return true;
1717
- }
1718
1971
  //#endregion
1719
- export { CodeView };
1972
+ export { CODE_VIEW_DIFF_OPTION_KEYS, CODE_VIEW_FILE_OPTION_KEYS, CodeView };
1720
1973
 
1721
1974
  //# sourceMappingURL=CodeView.js.map