@pierre/diffs 1.1.0-beta.9 → 1.1.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 (218) hide show
  1. package/README.md +7 -18
  2. package/dist/components/AdvancedVirtualizedFileDiff.d.ts.map +1 -1
  3. package/dist/components/AdvancedVirtualizedFileDiff.js +2 -7
  4. package/dist/components/AdvancedVirtualizedFileDiff.js.map +1 -1
  5. package/dist/components/AdvancedVirtualizer.js +1 -1
  6. package/dist/components/AdvancedVirtualizer.js.map +1 -1
  7. package/dist/components/File.d.ts +17 -7
  8. package/dist/components/File.d.ts.map +1 -1
  9. package/dist/components/File.js +111 -54
  10. package/dist/components/File.js.map +1 -1
  11. package/dist/components/FileDiff.d.ts +32 -14
  12. package/dist/components/FileDiff.d.ts.map +1 -1
  13. package/dist/components/FileDiff.js +156 -81
  14. package/dist/components/FileDiff.js.map +1 -1
  15. package/dist/components/UnresolvedFile.d.ts +60 -0
  16. package/dist/components/UnresolvedFile.d.ts.map +1 -0
  17. package/dist/components/UnresolvedFile.js +280 -0
  18. package/dist/components/UnresolvedFile.js.map +1 -0
  19. package/dist/components/VirtualizedFile.js +8 -5
  20. package/dist/components/VirtualizedFile.js.map +1 -1
  21. package/dist/components/VirtualizedFileDiff.d.ts +1 -1
  22. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  23. package/dist/components/VirtualizedFileDiff.js +15 -11
  24. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  25. package/dist/components/Virtualizer.d.ts +3 -1
  26. package/dist/components/Virtualizer.d.ts.map +1 -1
  27. package/dist/components/Virtualizer.js +50 -24
  28. package/dist/components/Virtualizer.js.map +1 -1
  29. package/dist/constants.d.ts +3 -1
  30. package/dist/constants.d.ts.map +1 -1
  31. package/dist/constants.js +8 -1
  32. package/dist/constants.js.map +1 -1
  33. package/dist/highlighter/shared_highlighter.d.ts +4 -2
  34. package/dist/highlighter/shared_highlighter.d.ts.map +1 -1
  35. package/dist/highlighter/shared_highlighter.js +15 -7
  36. package/dist/highlighter/shared_highlighter.js.map +1 -1
  37. package/dist/index.d.ts +9 -7
  38. package/dist/index.js +8 -6
  39. package/dist/managers/InteractionManager.d.ts +146 -0
  40. package/dist/managers/InteractionManager.d.ts.map +1 -0
  41. package/dist/managers/InteractionManager.js +813 -0
  42. package/dist/managers/InteractionManager.js.map +1 -0
  43. package/dist/managers/ResizeManager.d.ts +0 -2
  44. package/dist/managers/ResizeManager.d.ts.map +1 -1
  45. package/dist/managers/ResizeManager.js +43 -32
  46. package/dist/managers/ResizeManager.js.map +1 -1
  47. package/dist/react/File.d.ts +2 -0
  48. package/dist/react/File.d.ts.map +1 -1
  49. package/dist/react/File.js +3 -1
  50. package/dist/react/File.js.map +1 -1
  51. package/dist/react/FileDiff.d.ts +2 -0
  52. package/dist/react/FileDiff.d.ts.map +1 -1
  53. package/dist/react/FileDiff.js +3 -1
  54. package/dist/react/FileDiff.js.map +1 -1
  55. package/dist/react/MultiFileDiff.d.ts +2 -0
  56. package/dist/react/MultiFileDiff.d.ts.map +1 -1
  57. package/dist/react/MultiFileDiff.js +3 -1
  58. package/dist/react/MultiFileDiff.js.map +1 -1
  59. package/dist/react/PatchDiff.d.ts +2 -0
  60. package/dist/react/PatchDiff.d.ts.map +1 -1
  61. package/dist/react/PatchDiff.js +3 -1
  62. package/dist/react/PatchDiff.js.map +1 -1
  63. package/dist/react/UnresolvedFile.d.ts +36 -0
  64. package/dist/react/UnresolvedFile.d.ts.map +1 -0
  65. package/dist/react/UnresolvedFile.js +42 -0
  66. package/dist/react/UnresolvedFile.js.map +1 -0
  67. package/dist/react/constants.d.ts +3 -2
  68. package/dist/react/constants.d.ts.map +1 -1
  69. package/dist/react/constants.js +3 -2
  70. package/dist/react/constants.js.map +1 -1
  71. package/dist/react/index.d.ts +4 -3
  72. package/dist/react/index.js +3 -2
  73. package/dist/react/types.d.ts +11 -2
  74. package/dist/react/types.d.ts.map +1 -1
  75. package/dist/react/utils/renderDiffChildren.d.ts +16 -5
  76. package/dist/react/utils/renderDiffChildren.d.ts.map +1 -1
  77. package/dist/react/utils/renderDiffChildren.js +34 -7
  78. package/dist/react/utils/renderDiffChildren.js.map +1 -1
  79. package/dist/react/utils/renderFileChildren.d.ts +5 -1
  80. package/dist/react/utils/renderFileChildren.d.ts.map +1 -1
  81. package/dist/react/utils/renderFileChildren.js +13 -7
  82. package/dist/react/utils/renderFileChildren.js.map +1 -1
  83. package/dist/react/utils/useFileDiffInstance.d.ts +1 -2
  84. package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
  85. package/dist/react/utils/useFileDiffInstance.js +2 -2
  86. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  87. package/dist/react/utils/useFileInstance.d.ts +1 -2
  88. package/dist/react/utils/useFileInstance.d.ts.map +1 -1
  89. package/dist/react/utils/useFileInstance.js.map +1 -1
  90. package/dist/react/utils/useUnresolvedFileInstance.d.ts +33 -0
  91. package/dist/react/utils/useUnresolvedFileInstance.d.ts.map +1 -0
  92. package/dist/react/utils/useUnresolvedFileInstance.js +87 -0
  93. package/dist/react/utils/useUnresolvedFileInstance.js.map +1 -0
  94. package/dist/renderers/DiffHunksRenderer.d.ts +50 -6
  95. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  96. package/dist/renderers/DiffHunksRenderer.js +145 -45
  97. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  98. package/dist/renderers/FileRenderer.js +1 -1
  99. package/dist/renderers/UnresolvedFileHunksRenderer.d.ts +46 -0
  100. package/dist/renderers/UnresolvedFileHunksRenderer.d.ts.map +1 -0
  101. package/dist/renderers/UnresolvedFileHunksRenderer.js +207 -0
  102. package/dist/renderers/UnresolvedFileHunksRenderer.js.map +1 -0
  103. package/dist/shiki-stream/stream.d.ts +1 -1
  104. package/dist/shiki-stream/stream.d.ts.map +1 -1
  105. package/dist/shiki-stream/stream.js.map +1 -1
  106. package/dist/shiki-stream/tokenizer.d.ts +1 -1
  107. package/dist/shiki-stream/tokenizer.d.ts.map +1 -1
  108. package/dist/shiki-stream/tokenizer.js.map +1 -1
  109. package/dist/shiki-stream/types.d.ts +1 -1
  110. package/dist/shiki-stream/types.d.ts.map +1 -1
  111. package/dist/sprite.d.ts +2 -2
  112. package/dist/sprite.d.ts.map +1 -1
  113. package/dist/sprite.js +3 -0
  114. package/dist/sprite.js.map +1 -1
  115. package/dist/ssr/index.d.ts +3 -3
  116. package/dist/ssr/index.js +2 -2
  117. package/dist/ssr/preloadDiffs.d.ts +23 -14
  118. package/dist/ssr/preloadDiffs.d.ts.map +1 -1
  119. package/dist/ssr/preloadDiffs.js +40 -14
  120. package/dist/ssr/preloadDiffs.js.map +1 -1
  121. package/dist/style.js +1 -1
  122. package/dist/style.js.map +1 -1
  123. package/dist/types.d.ts +29 -2
  124. package/dist/types.d.ts.map +1 -1
  125. package/dist/utils/areMergeConflictActionsEqual.d.ts +7 -0
  126. package/dist/utils/areMergeConflictActionsEqual.d.ts.map +1 -0
  127. package/dist/utils/areMergeConflictActionsEqual.js +11 -0
  128. package/dist/utils/areMergeConflictActionsEqual.js.map +1 -0
  129. package/dist/utils/arePrePropertiesEqual.js +10 -1
  130. package/dist/utils/arePrePropertiesEqual.js.map +1 -1
  131. package/dist/utils/areSelectionPointsEqual.d.ts +7 -0
  132. package/dist/utils/areSelectionPointsEqual.d.ts.map +1 -0
  133. package/dist/utils/areSelectionPointsEqual.js +8 -0
  134. package/dist/utils/areSelectionPointsEqual.js.map +1 -0
  135. package/dist/utils/areSelectionsEqual.d.ts +1 -1
  136. package/dist/utils/areSelectionsEqual.d.ts.map +1 -1
  137. package/dist/utils/areSelectionsEqual.js.map +1 -1
  138. package/dist/utils/createFileHeaderElement.js +5 -2
  139. package/dist/utils/createFileHeaderElement.js.map +1 -1
  140. package/dist/utils/createGutterUtilityContentNode.d.ts +5 -0
  141. package/dist/utils/createGutterUtilityContentNode.d.ts.map +1 -0
  142. package/dist/utils/createGutterUtilityContentNode.js +15 -0
  143. package/dist/utils/createGutterUtilityContentNode.js.map +1 -0
  144. package/dist/utils/createGutterUtilityElement.d.ts +7 -0
  145. package/dist/utils/createGutterUtilityElement.d.ts.map +1 -0
  146. package/dist/utils/createGutterUtilityElement.js +20 -0
  147. package/dist/utils/createGutterUtilityElement.js.map +1 -0
  148. package/dist/utils/createPreElement.d.ts +2 -1
  149. package/dist/utils/createPreElement.d.ts.map +1 -1
  150. package/dist/utils/createPreElement.js +2 -1
  151. package/dist/utils/createPreElement.js.map +1 -1
  152. package/dist/utils/createSeparator.js +1 -1
  153. package/dist/utils/createSeparator.js.map +1 -1
  154. package/dist/utils/createWindowFromScrollPosition.js +12 -11
  155. package/dist/utils/createWindowFromScrollPosition.js.map +1 -1
  156. package/dist/utils/getHighlighterOptions.d.ts +7 -2
  157. package/dist/utils/getHighlighterOptions.d.ts.map +1 -1
  158. package/dist/utils/getHighlighterOptions.js +3 -2
  159. package/dist/utils/getHighlighterOptions.js.map +1 -1
  160. package/dist/utils/getMergeConflictActionSlotName.d.ts +16 -0
  161. package/dist/utils/getMergeConflictActionSlotName.d.ts.map +1 -0
  162. package/dist/utils/getMergeConflictActionSlotName.js +8 -0
  163. package/dist/utils/getMergeConflictActionSlotName.js.map +1 -0
  164. package/dist/utils/getMergeConflictLineTypes.d.ts +15 -0
  165. package/dist/utils/getMergeConflictLineTypes.d.ts.map +1 -0
  166. package/dist/utils/getMergeConflictLineTypes.js +81 -0
  167. package/dist/utils/getMergeConflictLineTypes.js.map +1 -0
  168. package/dist/utils/getOrCreateCodeNode.d.ts +3 -1
  169. package/dist/utils/getOrCreateCodeNode.d.ts.map +1 -1
  170. package/dist/utils/getOrCreateCodeNode.js +5 -3
  171. package/dist/utils/getOrCreateCodeNode.js.map +1 -1
  172. package/dist/utils/hast_utils.d.ts +2 -2
  173. package/dist/utils/hast_utils.d.ts.map +1 -1
  174. package/dist/utils/hast_utils.js +3 -2
  175. package/dist/utils/hast_utils.js.map +1 -1
  176. package/dist/utils/parseMergeConflictDiffFromFile.d.ts +26 -0
  177. package/dist/utils/parseMergeConflictDiffFromFile.d.ts.map +1 -0
  178. package/dist/utils/parseMergeConflictDiffFromFile.js +143 -0
  179. package/dist/utils/parseMergeConflictDiffFromFile.js.map +1 -0
  180. package/dist/utils/resolveMergeConflict.d.ts +7 -0
  181. package/dist/utils/resolveMergeConflict.d.ts.map +1 -0
  182. package/dist/utils/resolveMergeConflict.js +30 -0
  183. package/dist/utils/resolveMergeConflict.js.map +1 -0
  184. package/dist/utils/resolveVirtualFileMetrics.js +1 -0
  185. package/dist/utils/resolveVirtualFileMetrics.js.map +1 -1
  186. package/dist/utils/setWrapperNodeProps.d.ts +2 -1
  187. package/dist/utils/setWrapperNodeProps.d.ts.map +1 -1
  188. package/dist/utils/setWrapperNodeProps.js +5 -1
  189. package/dist/utils/setWrapperNodeProps.js.map +1 -1
  190. package/dist/worker/WorkerPoolManager.d.ts +4 -2
  191. package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
  192. package/dist/worker/WorkerPoolManager.js +16 -9
  193. package/dist/worker/WorkerPoolManager.js.map +1 -1
  194. package/dist/worker/types.d.ts +3 -1
  195. package/dist/worker/types.d.ts.map +1 -1
  196. package/dist/worker/wasm-BlUZCxHM.js +10 -0
  197. package/dist/worker/wasm-BlUZCxHM.js.map +1 -0
  198. package/dist/worker/worker-portable.js +10546 -10106
  199. package/dist/worker/worker-portable.js.map +1 -1
  200. package/dist/worker/worker.js +27 -19
  201. package/dist/worker/worker.js.map +1 -1
  202. package/package.json +3 -7
  203. package/dist/managers/LineSelectionManager.d.ts +0 -64
  204. package/dist/managers/LineSelectionManager.d.ts.map +0 -1
  205. package/dist/managers/LineSelectionManager.js +0 -270
  206. package/dist/managers/LineSelectionManager.js.map +0 -1
  207. package/dist/managers/MouseEventManager.d.ts +0 -71
  208. package/dist/managers/MouseEventManager.d.ts.map +0 -1
  209. package/dist/managers/MouseEventManager.js +0 -358
  210. package/dist/managers/MouseEventManager.js.map +0 -1
  211. package/dist/themes/pierre-dark.js +0 -1328
  212. package/dist/themes/pierre-dark.js.map +0 -1
  213. package/dist/themes/pierre-light.js +0 -1328
  214. package/dist/themes/pierre-light.js.map +0 -1
  215. package/dist/utils/createHoverContentNode.d.ts +0 -5
  216. package/dist/utils/createHoverContentNode.d.ts.map +0 -1
  217. package/dist/utils/createHoverContentNode.js +0 -15
  218. package/dist/utils/createHoverContentNode.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"VirtualizedFileDiff.js","names":["hunkOffsets: number[]","firstVisibleHunk: number | undefined","centerHunk: number | undefined","overflowCounter: number | undefined","lineHeight"],"sources":["../../src/components/VirtualizedFileDiff.ts"],"sourcesContent":["import { DEFAULT_COLLAPSED_CONTEXT_THRESHOLD } from '../constants';\nimport type {\n ExpansionDirections,\n FileDiffMetadata,\n RenderRange,\n RenderWindow,\n VirtualFileMetrics,\n} from '../types';\nimport { iterateOverDiff } from '../utils/iterateOverDiff';\nimport { parseDiffFromFile } from '../utils/parseDiffFromFile';\nimport { resolveVirtualFileMetrics } from '../utils/resolveVirtualFileMetrics';\nimport type { WorkerPoolManager } from '../worker';\nimport {\n FileDiff,\n type FileDiffOptions,\n type FileDiffRenderProps,\n} from './FileDiff';\nimport type { Virtualizer } from './Virtualizer';\n\ninterface ExpandedRegionSpecs {\n fromStart: number;\n fromEnd: number;\n collapsedLines: number;\n renderAll: boolean;\n}\n\nlet instanceId = -1;\n\nexport class VirtualizedFileDiff<\n LAnnotation = undefined,\n> extends FileDiff<LAnnotation> {\n override readonly __id: string = `little-virtualized-file-diff:${++instanceId}`;\n\n public top: number | undefined;\n public height: number = 0;\n private metrics: VirtualFileMetrics;\n // Sparse map: view-specific line index -> measured height\n // Only stores lines that differ what is returned from `getLineHeight`\n private heightCache: Map<number, number> = new Map();\n private isVisible: boolean = false;\n private virtualizer: Virtualizer;\n\n constructor(\n options: FileDiffOptions<LAnnotation> | undefined,\n virtualizer: Virtualizer,\n metrics?: Partial<VirtualFileMetrics>,\n workerManager?: WorkerPoolManager,\n isContainerManaged = false\n ) {\n super(options, workerManager, isContainerManaged);\n const { hunkSeparators = 'line-info' } = this.options;\n this.virtualizer = virtualizer;\n this.metrics = resolveVirtualFileMetrics(\n typeof hunkSeparators === 'function' ? 'custom' : hunkSeparators,\n metrics\n );\n }\n\n // Get the height for a line, using cached value if available.\n // If not cached and hasMetadataLine is true, adds lineHeight for the metadata.\n private getLineHeight(lineIndex: number, hasMetadataLine = false): number {\n const cached = this.heightCache.get(lineIndex);\n if (cached != null) {\n return cached;\n }\n const multiplier = hasMetadataLine ? 2 : 1;\n return this.metrics.lineHeight * multiplier;\n }\n\n // Override setOptions to clear height cache when diffStyle changes\n override setOptions(options: FileDiffOptions<LAnnotation> | undefined): void {\n if (options == null) return;\n const previousDiffStyle = this.options.diffStyle;\n const previousOverflow = this.options.overflow;\n\n super.setOptions(options);\n\n if (\n previousDiffStyle !== this.options.diffStyle ||\n previousOverflow !== this.options.overflow\n ) {\n this.heightCache.clear();\n this.computeApproximateSize();\n this.renderRange = undefined;\n }\n this.virtualizer.instanceChanged(this);\n }\n\n // Measure rendered lines and update height cache.\n // Called after render to reconcile estimated vs actual heights.\n // Definitely need to optimize this in cases where there aren't any custom\n // line heights or in cases of extremely large files...\n public reconcileHeights(): void {\n const { overflow = 'scroll' } = this.options;\n if (this.fileContainer != null) {\n this.top = this.virtualizer.getOffsetInScrollContainer(\n this.fileContainer\n );\n }\n if (this.fileContainer == null || this.fileDiff == null) {\n this.height = 0;\n return;\n }\n // NOTE(amadeus): We can probably be a lot smarter about this, and we\n // should be thinking about ways to improve this\n // If the file has no annotations and we are using the scroll variant, then\n // we can probably skip everything\n if (\n overflow === 'scroll' &&\n this.lineAnnotations.length === 0 &&\n !this.virtualizer.config.resizeDebugging\n ) {\n return;\n }\n const diffStyle = this.getDiffStyle();\n let hasLineHeightChange = false;\n const codeGroups =\n diffStyle === 'split'\n ? [this.codeDeletions, this.codeAdditions]\n : [this.codeUnified];\n\n for (const codeGroup of codeGroups) {\n if (codeGroup == null) continue;\n const content = codeGroup.children[1];\n if (!(content instanceof HTMLElement)) continue;\n for (const line of content.children) {\n if (!(line instanceof HTMLElement)) continue;\n\n const lineIndexAttr = line.dataset.lineIndex;\n if (lineIndexAttr == null) continue;\n\n const lineIndex = parseLineIndex(lineIndexAttr, diffStyle);\n let measuredHeight = line.getBoundingClientRect().height;\n let hasMetadata = false;\n // Annotations or noNewline metadata increase the size of the their\n // attached line\n if (\n line.nextElementSibling instanceof HTMLElement &&\n ('lineAnnotation' in line.nextElementSibling.dataset ||\n 'noNewline' in line.nextElementSibling.dataset)\n ) {\n if ('noNewline' in line.nextElementSibling.dataset) {\n hasMetadata = true;\n }\n measuredHeight +=\n line.nextElementSibling.getBoundingClientRect().height;\n }\n const expectedHeight = this.getLineHeight(lineIndex, hasMetadata);\n\n if (measuredHeight === expectedHeight) {\n continue;\n }\n\n hasLineHeightChange = true;\n // Line is back to standard height (e.g., after window resize)\n // Remove from cache\n if (\n measuredHeight ===\n this.metrics.lineHeight * (hasMetadata ? 2 : 1)\n ) {\n this.heightCache.delete(lineIndex);\n }\n // Non-standard height, cache it\n else {\n this.heightCache.set(lineIndex, measuredHeight);\n }\n }\n }\n\n if (hasLineHeightChange || this.virtualizer.config.resizeDebugging) {\n this.computeApproximateSize();\n }\n }\n\n public onRender = (dirty: boolean): boolean => {\n if (this.fileContainer == null) {\n return false;\n }\n if (dirty) {\n this.top = this.virtualizer.getOffsetInScrollContainer(\n this.fileContainer\n );\n }\n return this.render();\n };\n\n override cleanUp(): void {\n if (this.fileContainer != null) {\n this.virtualizer.disconnect(this.fileContainer);\n }\n super.cleanUp();\n }\n\n override expandHunk(hunkIndex: number, direction: ExpansionDirections): void {\n this.hunksRenderer.expandHunk(hunkIndex, direction);\n this.computeApproximateSize();\n this.renderRange = undefined;\n this.virtualizer.instanceChanged(this);\n // NOTE(amadeus): We should probably defer to the virtualizer to re-render\n // this.rerender();\n }\n\n public setVisibility(visible: boolean): void {\n if (this.fileContainer == null) {\n return;\n }\n if (visible && !this.isVisible) {\n this.top = this.virtualizer.getOffsetInScrollContainer(\n this.fileContainer\n );\n this.isVisible = true;\n } else if (!visible && this.isVisible) {\n this.isVisible = false;\n this.rerender();\n }\n }\n\n // Compute the approximate size of the file using cached line heights.\n // Uses lineHeight for lines without cached measurements.\n // We should probably optimize this if there are no custom line heights...\n // The reason we refer to this as `approximate size` is because heights my\n // dynamically change for a number of reasons so we can never be fully sure\n // if the height is 100% accurate\n private computeApproximateSize(): void {\n this.height = 0;\n if (this.fileDiff == null) {\n return;\n }\n\n const {\n disableFileHeader = false,\n expandUnchanged = false,\n collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD,\n hunkSeparators = 'line-info',\n } = this.options;\n const { diffHeaderHeight, fileGap, hunkSeparatorHeight } = this.metrics;\n const diffStyle = this.getDiffStyle();\n const separatorGap =\n hunkSeparators === 'simple' || hunkSeparators === 'metadata'\n ? 0\n : fileGap;\n\n // Header or initial padding\n if (!disableFileHeader) {\n this.height += diffHeaderHeight;\n } else if (hunkSeparators !== 'simple' && hunkSeparators !== 'metadata') {\n this.height += fileGap;\n }\n\n iterateOverDiff({\n diff: this.fileDiff,\n diffStyle,\n expandedHunks: expandUnchanged\n ? true\n : this.hunksRenderer.getExpandedHunksMap(),\n collapsedContextThreshold,\n callback: ({\n hunkIndex,\n collapsedBefore,\n collapsedAfter,\n deletionLine,\n additionLine,\n }) => {\n const splitLineIndex =\n additionLine != null\n ? additionLine.splitLineIndex\n : deletionLine.splitLineIndex;\n const unifiedLineIndex =\n additionLine != null\n ? additionLine.unifiedLineIndex\n : deletionLine.unifiedLineIndex;\n const hasMetadata =\n (additionLine?.noEOFCR ?? false) || (deletionLine?.noEOFCR ?? false);\n if (collapsedBefore > 0) {\n if (hunkIndex > 0) {\n this.height += separatorGap;\n }\n this.height += hunkSeparatorHeight + separatorGap;\n }\n\n this.height += this.getLineHeight(\n diffStyle === 'split' ? splitLineIndex : unifiedLineIndex,\n hasMetadata\n );\n\n if (collapsedAfter > 0 && hunkSeparators !== 'simple') {\n this.height += separatorGap + hunkSeparatorHeight;\n }\n },\n });\n\n // Bottom padding\n if (this.fileDiff.hunks.length > 0) {\n this.height += fileGap;\n }\n\n if (this.fileContainer != null && this.virtualizer.config.resizeDebugging) {\n const rect = this.fileContainer.getBoundingClientRect();\n if (rect.height !== this.height) {\n console.log(\n 'VirtualizedFileDiff.computeApproximateSize: computed height doesnt match',\n {\n name: this.fileDiff.name,\n elementHeight: rect.height,\n computedHeight: this.height,\n }\n );\n } else {\n console.log(\n 'VirtualizedFileDiff.computeApproximateSize: computed height IS CORRECT'\n );\n }\n }\n }\n\n override render({\n fileContainer,\n oldFile,\n newFile,\n fileDiff,\n ...props\n }: FileDiffRenderProps<LAnnotation> = {}): boolean {\n // NOTE(amadeus): Probably not the safest way to determine first render...\n // but for now...\n const isFirstRender = this.fileContainer == null;\n\n this.fileDiff ??=\n fileDiff ??\n (oldFile != null && newFile != null\n ? // NOTE(amadeus): We might be forcing ourselves to double up the\n // computation of fileDiff (in the super.render() call), so we might want\n // to figure out a way to avoid that. That also could be just as simple as\n // passing through fileDiff though... so maybe we good?\n parseDiffFromFile(oldFile, newFile)\n : undefined);\n\n fileContainer = this.getOrCreateFileContainer(fileContainer);\n\n if (this.fileDiff == null) {\n console.error(\n 'VirtualizedFileDiff.render: attempting to virtually render when we dont have the correct data'\n );\n return false;\n }\n\n this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);\n if (isFirstRender) {\n this.computeApproximateSize();\n // Figure out how to properly manage this...\n this.virtualizer.connect(fileContainer, this);\n this.isVisible = this.virtualizer.isInstanceVisible(\n this.top,\n this.height\n );\n }\n\n if (!this.isVisible) {\n return this.renderPlaceholder(this.height);\n }\n\n const windowSpecs = this.virtualizer.getWindowSpecs();\n const renderRange = this.computeRenderRangeFromWindow(\n this.fileDiff,\n this.top,\n windowSpecs\n );\n return super.render({\n fileDiff: this.fileDiff,\n fileContainer,\n renderRange,\n oldFile,\n newFile,\n ...props,\n });\n }\n\n private getDiffStyle(): 'split' | 'unified' {\n return this.options.diffStyle ?? 'split';\n }\n\n private getExpandedRegion(\n isPartial: boolean,\n hunkIndex: number,\n rangeSize: number\n ): ExpandedRegionSpecs {\n if (rangeSize <= 0 || isPartial) {\n return {\n fromStart: 0,\n fromEnd: 0,\n collapsedLines: Math.max(rangeSize, 0),\n renderAll: false,\n };\n }\n const {\n expandUnchanged = false,\n collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD,\n } = this.options;\n if (expandUnchanged || rangeSize <= collapsedContextThreshold) {\n return {\n fromStart: rangeSize,\n fromEnd: 0,\n collapsedLines: 0,\n renderAll: true,\n };\n }\n const region = this.hunksRenderer.getExpandedHunk(hunkIndex);\n const fromStart = Math.min(Math.max(region.fromStart, 0), rangeSize);\n const fromEnd = Math.min(Math.max(region.fromEnd, 0), rangeSize);\n const expandedCount = fromStart + fromEnd;\n const renderAll = expandedCount >= rangeSize;\n return {\n fromStart,\n fromEnd,\n collapsedLines: Math.max(rangeSize - expandedCount, 0),\n renderAll,\n };\n }\n\n private getExpandedLineCount(\n fileDiff: FileDiffMetadata,\n diffStyle: 'split' | 'unified'\n ): number {\n let count = 0;\n if (fileDiff.isPartial) {\n for (const hunk of fileDiff.hunks) {\n count +=\n diffStyle === 'split' ? hunk.splitLineCount : hunk.unifiedLineCount;\n }\n return count;\n }\n\n for (const [hunkIndex, hunk] of fileDiff.hunks.entries()) {\n const hunkCount =\n diffStyle === 'split' ? hunk.splitLineCount : hunk.unifiedLineCount;\n count += hunkCount;\n const collapsedBefore = Math.max(hunk.collapsedBefore, 0);\n const { fromStart, fromEnd, renderAll } = this.getExpandedRegion(\n fileDiff.isPartial,\n hunkIndex,\n collapsedBefore\n );\n if (collapsedBefore > 0) {\n count += renderAll ? collapsedBefore : fromStart + fromEnd;\n }\n }\n\n const lastHunk = fileDiff.hunks.at(-1);\n if (lastHunk != null && hasFinalHunk(fileDiff)) {\n const additionRemaining =\n fileDiff.additionLines.length -\n (lastHunk.additionLineIndex + lastHunk.additionCount);\n const deletionRemaining =\n fileDiff.deletionLines.length -\n (lastHunk.deletionLineIndex + lastHunk.deletionCount);\n if (lastHunk != null && additionRemaining !== deletionRemaining) {\n throw new Error(\n `VirtualizedFileDiff: trailing context mismatch (additions=${additionRemaining}, deletions=${deletionRemaining}) for ${fileDiff.name}`\n );\n }\n const trailingRangeSize = Math.min(additionRemaining, deletionRemaining);\n if (lastHunk != null && trailingRangeSize > 0) {\n const { fromStart, renderAll } = this.getExpandedRegion(\n fileDiff.isPartial,\n fileDiff.hunks.length,\n trailingRangeSize\n );\n count += renderAll ? trailingRangeSize : fromStart;\n }\n }\n\n return count;\n }\n\n private computeRenderRangeFromWindow(\n fileDiff: FileDiffMetadata,\n fileTop: number,\n { top, bottom }: RenderWindow\n ): RenderRange {\n const {\n disableFileHeader = false,\n expandUnchanged = false,\n collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD,\n hunkSeparators = 'line-info',\n } = this.options;\n const {\n diffHeaderHeight,\n fileGap,\n hunkLineCount,\n hunkSeparatorHeight,\n lineHeight,\n } = this.metrics;\n const diffStyle = this.getDiffStyle();\n const fileHeight = this.height;\n const lineCount = this.getExpandedLineCount(fileDiff, diffStyle);\n\n // Calculate headerRegion before early returns\n const headerRegion = disableFileHeader ? fileGap : diffHeaderHeight;\n\n // File is outside render window\n if (fileTop < top - fileHeight || fileTop > bottom) {\n return {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter:\n fileHeight -\n headerRegion -\n // This last file gap represents the bottom padding that buffers\n // should not account for\n fileGap,\n };\n }\n\n // Whole file is under hunkLineCount, just render it all\n if (lineCount <= hunkLineCount || fileDiff.hunks.length === 0) {\n return {\n startingLine: 0,\n totalLines: hunkLineCount,\n bufferBefore: 0,\n bufferAfter: 0,\n };\n }\n const estimatedTargetLines = Math.ceil(\n Math.max(bottom - top, 0) / lineHeight\n );\n const totalLines =\n Math.ceil(estimatedTargetLines / hunkLineCount) * hunkLineCount +\n hunkLineCount;\n const totalHunks = totalLines / hunkLineCount;\n const overflowHunks = totalHunks;\n const hunkOffsets: number[] = [];\n // Halfway between top & bottom, represented as an absolute position\n const viewportCenter = (top + bottom) / 2;\n const separatorGap =\n hunkSeparators === 'simple' || hunkSeparators === 'metadata'\n ? 0\n : fileGap;\n\n let absoluteLineTop = fileTop + headerRegion;\n let currentLine = 0;\n let firstVisibleHunk: number | undefined;\n let centerHunk: number | undefined;\n let overflowCounter: number | undefined;\n\n iterateOverDiff({\n diff: fileDiff,\n diffStyle,\n expandedHunks: expandUnchanged\n ? true\n : this.hunksRenderer.getExpandedHunksMap(),\n collapsedContextThreshold,\n callback: ({\n hunkIndex,\n collapsedBefore,\n collapsedAfter,\n deletionLine,\n additionLine,\n }) => {\n const splitLineIndex =\n additionLine != null\n ? additionLine.splitLineIndex\n : deletionLine.splitLineIndex;\n const unifiedLineIndex =\n additionLine != null\n ? additionLine.unifiedLineIndex\n : deletionLine.unifiedLineIndex;\n const hasMetadata =\n (additionLine?.noEOFCR ?? false) || (deletionLine?.noEOFCR ?? false);\n let gapAdjustment =\n collapsedBefore > 0\n ? hunkSeparatorHeight +\n separatorGap +\n (hunkIndex > 0 ? separatorGap : 0)\n : 0;\n if (hunkIndex === 0 && hunkSeparators === 'simple') {\n gapAdjustment = 0;\n }\n\n absoluteLineTop += gapAdjustment;\n\n const isAtHunkBoundary = currentLine % hunkLineCount === 0;\n\n // Track the boundary positional offset at a hunk\n if (isAtHunkBoundary) {\n hunkOffsets.push(\n absoluteLineTop - (fileTop + headerRegion + gapAdjustment)\n );\n\n // Check if we should bail (overflow complete)\n if (overflowCounter != null) {\n if (overflowCounter <= 0) {\n return true;\n }\n overflowCounter--;\n }\n }\n\n const lineHeight = this.getLineHeight(\n diffStyle === 'split' ? splitLineIndex : unifiedLineIndex,\n hasMetadata\n );\n\n const currentHunk = Math.floor(currentLine / hunkLineCount);\n\n // Track visible region\n if (absoluteLineTop > top - lineHeight && absoluteLineTop < bottom) {\n firstVisibleHunk ??= currentHunk;\n }\n\n // Track which hunk contains the viewport center\n // If viewport center is above this line and we haven't set centerHunk yet,\n // this is the first line at or past the center\n if (\n centerHunk == null &&\n absoluteLineTop + lineHeight > viewportCenter\n ) {\n centerHunk = currentHunk;\n }\n\n // Start overflow when we are out of the viewport at a hunk boundary\n if (\n overflowCounter == null &&\n absoluteLineTop >= bottom &&\n isAtHunkBoundary\n ) {\n overflowCounter = overflowHunks;\n }\n\n currentLine++;\n absoluteLineTop += lineHeight;\n\n if (collapsedAfter > 0 && hunkSeparators !== 'simple') {\n absoluteLineTop += hunkSeparatorHeight + fileGap;\n }\n\n return false;\n },\n });\n\n // No visible lines found\n if (firstVisibleHunk == null) {\n return {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter:\n fileHeight -\n headerRegion -\n // We gotta subtract the bottom padding off of the buffer\n fileGap,\n };\n }\n\n // Calculate balanced startingLine centered around the viewport center\n // Fall back to firstVisibleHunk if center wasn't found (e.g., center in a gap)\n const collectedHunks = hunkOffsets.length;\n centerHunk ??= firstVisibleHunk;\n const idealStartHunk = Math.round(centerHunk - totalHunks / 2);\n\n // Clamp startHunk: at the beginning, reduce totalLines; at the end, shift startHunk back\n const maxStartHunk = Math.max(0, collectedHunks - totalHunks);\n const startHunk = Math.max(0, Math.min(idealStartHunk, maxStartHunk));\n const startingLine = startHunk * hunkLineCount;\n\n // If we wanted to start before 0, reduce totalLines by the clamped amount\n const clampedTotalLines =\n idealStartHunk < 0\n ? totalLines + idealStartHunk * hunkLineCount\n : totalLines;\n\n // Use hunkOffsets array for efficient buffer calculations\n const bufferBefore = hunkOffsets[startHunk] ?? 0;\n\n // Calculate bufferAfter using hunkOffset if available, otherwise use cumulative height\n const finalHunkIndex = startHunk + clampedTotalLines / hunkLineCount;\n const bufferAfter =\n finalHunkIndex < hunkOffsets.length\n ? fileHeight -\n headerRegion -\n hunkOffsets[finalHunkIndex] -\n // We gotta subtract the bottom padding off of the buffer\n fileGap\n : // We stopped early, calculate from current position\n fileHeight -\n (absoluteLineTop - fileTop) -\n // We gotta subtract the bottom padding off of the buffer\n fileGap;\n\n return {\n startingLine,\n totalLines: clampedTotalLines,\n bufferBefore,\n bufferAfter,\n };\n }\n}\n\nfunction hasFinalHunk(fileDiff: FileDiffMetadata): boolean {\n const lastHunk = fileDiff.hunks.at(-1);\n if (\n lastHunk == null ||\n fileDiff.isPartial ||\n fileDiff.additionLines.length === 0 ||\n fileDiff.deletionLines.length === 0\n ) {\n return false;\n }\n\n return (\n lastHunk.additionLineIndex + lastHunk.additionCount <\n fileDiff.additionLines.length ||\n lastHunk.deletionLineIndex + lastHunk.deletionCount <\n fileDiff.deletionLines.length\n );\n}\n\n// Extracts the view-specific line index from the data-line-index attribute.\n// Format is \"unifiedIndex,splitIndex\"\nfunction parseLineIndex(\n lineIndexAttr: string,\n diffStyle: 'split' | 'unified'\n): number {\n const [unifiedIndex, splitIndex] = lineIndexAttr.split(',').map(Number);\n return diffStyle === 'split' ? splitIndex : unifiedIndex;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"VirtualizedFileDiff.js","names":["hunkOffsets: number[]","firstVisibleHunk: number | undefined","centerHunk: number | undefined","overflowCounter: number | undefined","lineHeight"],"sources":["../../src/components/VirtualizedFileDiff.ts"],"sourcesContent":["import { DEFAULT_COLLAPSED_CONTEXT_THRESHOLD } from '../constants';\nimport type {\n ExpansionDirections,\n FileDiffMetadata,\n RenderRange,\n RenderWindow,\n VirtualFileMetrics,\n} from '../types';\nimport { iterateOverDiff } from '../utils/iterateOverDiff';\nimport { parseDiffFromFile } from '../utils/parseDiffFromFile';\nimport { resolveVirtualFileMetrics } from '../utils/resolveVirtualFileMetrics';\nimport type { WorkerPoolManager } from '../worker';\nimport {\n FileDiff,\n type FileDiffOptions,\n type FileDiffRenderProps,\n} from './FileDiff';\nimport type { Virtualizer } from './Virtualizer';\n\ninterface ExpandedRegionSpecs {\n fromStart: number;\n fromEnd: number;\n collapsedLines: number;\n renderAll: boolean;\n}\n\nlet instanceId = -1;\n\nexport class VirtualizedFileDiff<\n LAnnotation = undefined,\n> extends FileDiff<LAnnotation> {\n override readonly __id: string = `little-virtualized-file-diff:${++instanceId}`;\n\n public top: number | undefined;\n public height: number = 0;\n private metrics: VirtualFileMetrics;\n // Sparse map: view-specific line index -> measured height\n // Only stores lines that differ what is returned from `getLineHeight`\n private heightCache: Map<number, number> = new Map();\n private isVisible: boolean = false;\n private virtualizer: Virtualizer;\n\n constructor(\n options: FileDiffOptions<LAnnotation> | undefined,\n virtualizer: Virtualizer,\n metrics?: Partial<VirtualFileMetrics>,\n workerManager?: WorkerPoolManager,\n isContainerManaged = false\n ) {\n super(options, workerManager, isContainerManaged);\n const { hunkSeparators = 'line-info' } = this.options;\n this.virtualizer = virtualizer;\n this.metrics = resolveVirtualFileMetrics(\n typeof hunkSeparators === 'function' ? 'custom' : hunkSeparators,\n metrics\n );\n }\n\n // Get the height for a line, using cached value if available.\n // If not cached and hasMetadataLine is true, adds lineHeight for the metadata.\n private getLineHeight(lineIndex: number, hasMetadataLine = false): number {\n const cached = this.heightCache.get(lineIndex);\n if (cached != null) {\n return cached;\n }\n const multiplier = hasMetadataLine ? 2 : 1;\n return this.metrics.lineHeight * multiplier;\n }\n\n // Override setOptions to clear height cache when diffStyle changes\n override setOptions(options: FileDiffOptions<LAnnotation> | undefined): void {\n if (options == null) return;\n const previousDiffStyle = this.options.diffStyle;\n const previousOverflow = this.options.overflow;\n const previousCollapsed = this.options.collapsed;\n\n super.setOptions(options);\n\n if (\n previousDiffStyle !== this.options.diffStyle ||\n previousOverflow !== this.options.overflow ||\n previousCollapsed !== this.options.collapsed\n ) {\n this.heightCache.clear();\n this.computeApproximateSize();\n this.renderRange = undefined;\n }\n this.virtualizer.instanceChanged(this);\n }\n\n // Measure rendered lines and update height cache.\n // Called after render to reconcile estimated vs actual heights.\n // Definitely need to optimize this in cases where there aren't any custom\n // line heights or in cases of extremely large files...\n public reconcileHeights(): void {\n const { overflow = 'scroll' } = this.options;\n if (this.fileContainer != null) {\n this.top = this.virtualizer.getOffsetInScrollContainer(\n this.fileContainer\n );\n }\n if (this.fileContainer == null || this.fileDiff == null) {\n this.height = 0;\n return;\n }\n // NOTE(amadeus): We can probably be a lot smarter about this, and we\n // should be thinking about ways to improve this\n // If the file has no annotations and we are using the scroll variant, then\n // we can probably skip everything\n if (\n overflow === 'scroll' &&\n this.lineAnnotations.length === 0 &&\n !this.virtualizer.config.resizeDebugging\n ) {\n return;\n }\n const diffStyle = this.getDiffStyle();\n let hasLineHeightChange = false;\n const codeGroups =\n diffStyle === 'split'\n ? [this.codeDeletions, this.codeAdditions]\n : [this.codeUnified];\n\n for (const codeGroup of codeGroups) {\n if (codeGroup == null) continue;\n const content = codeGroup.children[1];\n if (!(content instanceof HTMLElement)) continue;\n for (const line of content.children) {\n if (!(line instanceof HTMLElement)) continue;\n\n const lineIndexAttr = line.dataset.lineIndex;\n if (lineIndexAttr == null) continue;\n\n const lineIndex = parseLineIndex(lineIndexAttr, diffStyle);\n let measuredHeight = line.getBoundingClientRect().height;\n let hasMetadata = false;\n // Annotations or noNewline metadata increase the size of the their\n // attached line\n if (\n line.nextElementSibling instanceof HTMLElement &&\n ('lineAnnotation' in line.nextElementSibling.dataset ||\n 'noNewline' in line.nextElementSibling.dataset)\n ) {\n if ('noNewline' in line.nextElementSibling.dataset) {\n hasMetadata = true;\n }\n measuredHeight +=\n line.nextElementSibling.getBoundingClientRect().height;\n }\n const expectedHeight = this.getLineHeight(lineIndex, hasMetadata);\n\n if (measuredHeight === expectedHeight) {\n continue;\n }\n\n hasLineHeightChange = true;\n // Line is back to standard height (e.g., after window resize)\n // Remove from cache\n if (\n measuredHeight ===\n this.metrics.lineHeight * (hasMetadata ? 2 : 1)\n ) {\n this.heightCache.delete(lineIndex);\n }\n // Non-standard height, cache it\n else {\n this.heightCache.set(lineIndex, measuredHeight);\n }\n }\n }\n\n if (hasLineHeightChange || this.virtualizer.config.resizeDebugging) {\n this.computeApproximateSize();\n }\n }\n\n public onRender = (dirty: boolean): boolean => {\n if (this.fileContainer == null) {\n return false;\n }\n if (dirty) {\n this.top = this.virtualizer.getOffsetInScrollContainer(\n this.fileContainer\n );\n }\n return this.render();\n };\n\n override cleanUp(): void {\n if (this.fileContainer != null) {\n this.virtualizer.disconnect(this.fileContainer);\n }\n super.cleanUp();\n }\n\n override expandHunk = (\n hunkIndex: number,\n direction: ExpansionDirections,\n expansionLineCountOverride?: number\n ): void => {\n this.hunksRenderer.expandHunk(\n hunkIndex,\n direction,\n expansionLineCountOverride\n );\n this.computeApproximateSize();\n this.renderRange = undefined;\n this.virtualizer.instanceChanged(this);\n };\n\n public setVisibility(visible: boolean): void {\n if (this.fileContainer == null) {\n return;\n }\n this.renderRange = undefined;\n if (visible && !this.isVisible) {\n this.top = this.virtualizer.getOffsetInScrollContainer(\n this.fileContainer\n );\n this.isVisible = true;\n } else if (!visible && this.isVisible) {\n this.isVisible = false;\n this.rerender();\n }\n }\n\n // Compute the approximate size of the file using cached line heights.\n // Uses lineHeight for lines without cached measurements.\n // We should probably optimize this if there are no custom line heights...\n // The reason we refer to this as `approximate size` is because heights my\n // dynamically change for a number of reasons so we can never be fully sure\n // if the height is 100% accurate\n private computeApproximateSize(): void {\n const isFirstCompute = this.height === 0;\n this.height = 0;\n if (this.fileDiff == null) {\n return;\n }\n\n const {\n disableFileHeader = false,\n expandUnchanged = false,\n collapsed = false,\n collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD,\n hunkSeparators = 'line-info',\n } = this.options;\n const { diffHeaderHeight, fileGap, hunkSeparatorHeight } = this.metrics;\n const diffStyle = this.getDiffStyle();\n const separatorGap =\n hunkSeparators !== 'simple' &&\n hunkSeparators !== 'metadata' &&\n hunkSeparators !== 'line-info-basic'\n ? fileGap\n : 0;\n\n // Header or initial padding\n if (!disableFileHeader) {\n this.height += diffHeaderHeight;\n } else if (hunkSeparators !== 'simple' && hunkSeparators !== 'metadata') {\n this.height += fileGap;\n }\n if (collapsed) {\n return;\n }\n\n iterateOverDiff({\n diff: this.fileDiff,\n diffStyle,\n expandedHunks: expandUnchanged\n ? true\n : this.hunksRenderer.getExpandedHunksMap(),\n collapsedContextThreshold,\n callback: ({\n hunkIndex,\n collapsedBefore,\n collapsedAfter,\n deletionLine,\n additionLine,\n }) => {\n const splitLineIndex =\n additionLine != null\n ? additionLine.splitLineIndex\n : deletionLine.splitLineIndex;\n const unifiedLineIndex =\n additionLine != null\n ? additionLine.unifiedLineIndex\n : deletionLine.unifiedLineIndex;\n const hasMetadata =\n (additionLine?.noEOFCR ?? false) || (deletionLine?.noEOFCR ?? false);\n if (collapsedBefore > 0) {\n if (hunkIndex > 0) {\n this.height += separatorGap;\n }\n this.height += hunkSeparatorHeight + separatorGap;\n }\n\n this.height += this.getLineHeight(\n diffStyle === 'split' ? splitLineIndex : unifiedLineIndex,\n hasMetadata\n );\n\n if (collapsedAfter > 0 && hunkSeparators !== 'simple') {\n this.height += separatorGap + hunkSeparatorHeight;\n }\n },\n });\n\n // Bottom padding\n if (this.fileDiff.hunks.length > 0) {\n this.height += fileGap;\n }\n\n if (\n this.fileContainer != null &&\n this.virtualizer.config.resizeDebugging &&\n !isFirstCompute\n ) {\n const rect = this.fileContainer.getBoundingClientRect();\n if (rect.height !== this.height) {\n console.log(\n 'VirtualizedFileDiff.computeApproximateSize: computed height doesnt match',\n {\n name: this.fileDiff.name,\n elementHeight: rect.height,\n computedHeight: this.height,\n }\n );\n } else {\n console.log(\n 'VirtualizedFileDiff.computeApproximateSize: computed height IS CORRECT'\n );\n }\n }\n }\n\n override render({\n fileContainer,\n oldFile,\n newFile,\n fileDiff,\n ...props\n }: FileDiffRenderProps<LAnnotation> = {}): boolean {\n // NOTE(amadeus): Probably not the safest way to determine first render...\n // but for now...\n const isFirstRender = this.fileContainer == null;\n\n this.fileDiff ??=\n fileDiff ??\n (oldFile != null && newFile != null\n ? // NOTE(amadeus): We might be forcing ourselves to double up the\n // computation of fileDiff (in the super.render() call), so we might want\n // to figure out a way to avoid that. That also could be just as simple as\n // passing through fileDiff though... so maybe we good?\n parseDiffFromFile(oldFile, newFile)\n : undefined);\n\n fileContainer = this.getOrCreateFileContainer(fileContainer);\n\n if (this.fileDiff == null) {\n console.error(\n 'VirtualizedFileDiff.render: attempting to virtually render when we dont have the correct data'\n );\n return false;\n }\n\n if (isFirstRender) {\n this.computeApproximateSize();\n this.virtualizer.connect(fileContainer, this);\n this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);\n this.isVisible = this.virtualizer.isInstanceVisible(\n this.top,\n this.height\n );\n } else {\n this.top ??= this.virtualizer.getOffsetInScrollContainer(fileContainer);\n }\n\n if (!this.isVisible) {\n return this.renderPlaceholder(this.height);\n }\n\n const windowSpecs = this.virtualizer.getWindowSpecs();\n const renderRange = this.computeRenderRangeFromWindow(\n this.fileDiff,\n this.top,\n windowSpecs\n );\n return super.render({\n fileDiff: this.fileDiff,\n fileContainer,\n renderRange,\n oldFile,\n newFile,\n ...props,\n });\n }\n\n private getDiffStyle(): 'split' | 'unified' {\n return this.options.diffStyle ?? 'split';\n }\n\n private getExpandedRegion(\n isPartial: boolean,\n hunkIndex: number,\n rangeSize: number\n ): ExpandedRegionSpecs {\n if (rangeSize <= 0 || isPartial) {\n return {\n fromStart: 0,\n fromEnd: 0,\n collapsedLines: Math.max(rangeSize, 0),\n renderAll: false,\n };\n }\n const {\n expandUnchanged = false,\n collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD,\n } = this.options;\n if (expandUnchanged || rangeSize <= collapsedContextThreshold) {\n return {\n fromStart: rangeSize,\n fromEnd: 0,\n collapsedLines: 0,\n renderAll: true,\n };\n }\n const region = this.hunksRenderer.getExpandedHunk(hunkIndex);\n const fromStart = Math.min(Math.max(region.fromStart, 0), rangeSize);\n const fromEnd = Math.min(Math.max(region.fromEnd, 0), rangeSize);\n const expandedCount = fromStart + fromEnd;\n const renderAll = expandedCount >= rangeSize;\n return {\n fromStart,\n fromEnd,\n collapsedLines: Math.max(rangeSize - expandedCount, 0),\n renderAll,\n };\n }\n\n private getExpandedLineCount(\n fileDiff: FileDiffMetadata,\n diffStyle: 'split' | 'unified'\n ): number {\n let count = 0;\n if (fileDiff.isPartial) {\n for (const hunk of fileDiff.hunks) {\n count +=\n diffStyle === 'split' ? hunk.splitLineCount : hunk.unifiedLineCount;\n }\n return count;\n }\n\n for (const [hunkIndex, hunk] of fileDiff.hunks.entries()) {\n const hunkCount =\n diffStyle === 'split' ? hunk.splitLineCount : hunk.unifiedLineCount;\n count += hunkCount;\n const collapsedBefore = Math.max(hunk.collapsedBefore, 0);\n const { fromStart, fromEnd, renderAll } = this.getExpandedRegion(\n fileDiff.isPartial,\n hunkIndex,\n collapsedBefore\n );\n if (collapsedBefore > 0) {\n count += renderAll ? collapsedBefore : fromStart + fromEnd;\n }\n }\n\n const lastHunk = fileDiff.hunks.at(-1);\n if (lastHunk != null && hasFinalHunk(fileDiff)) {\n const additionRemaining =\n fileDiff.additionLines.length -\n (lastHunk.additionLineIndex + lastHunk.additionCount);\n const deletionRemaining =\n fileDiff.deletionLines.length -\n (lastHunk.deletionLineIndex + lastHunk.deletionCount);\n if (lastHunk != null && additionRemaining !== deletionRemaining) {\n throw new Error(\n `VirtualizedFileDiff: trailing context mismatch (additions=${additionRemaining}, deletions=${deletionRemaining}) for ${fileDiff.name}`\n );\n }\n const trailingRangeSize = Math.min(additionRemaining, deletionRemaining);\n if (lastHunk != null && trailingRangeSize > 0) {\n const { fromStart, renderAll } = this.getExpandedRegion(\n fileDiff.isPartial,\n fileDiff.hunks.length,\n trailingRangeSize\n );\n count += renderAll ? trailingRangeSize : fromStart;\n }\n }\n\n return count;\n }\n\n private computeRenderRangeFromWindow(\n fileDiff: FileDiffMetadata,\n fileTop: number,\n { top, bottom }: RenderWindow\n ): RenderRange {\n const {\n disableFileHeader = false,\n expandUnchanged = false,\n collapsedContextThreshold = DEFAULT_COLLAPSED_CONTEXT_THRESHOLD,\n hunkSeparators = 'line-info',\n } = this.options;\n const {\n diffHeaderHeight,\n fileGap,\n hunkLineCount,\n hunkSeparatorHeight,\n lineHeight,\n } = this.metrics;\n const diffStyle = this.getDiffStyle();\n const fileHeight = this.height;\n const lineCount = this.getExpandedLineCount(fileDiff, diffStyle);\n\n // Calculate headerRegion before early returns\n const headerRegion = disableFileHeader ? fileGap : diffHeaderHeight;\n\n // File is outside render window\n if (fileTop < top - fileHeight || fileTop > bottom) {\n return {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter:\n fileHeight -\n headerRegion -\n // This last file gap represents the bottom padding that buffers\n // should not account for\n fileGap,\n };\n }\n\n // Whole file is under hunkLineCount, just render it all\n if (lineCount <= hunkLineCount || fileDiff.hunks.length === 0) {\n return {\n startingLine: 0,\n totalLines: hunkLineCount,\n bufferBefore: 0,\n bufferAfter: 0,\n };\n }\n const estimatedTargetLines = Math.ceil(\n Math.max(bottom - top, 0) / lineHeight\n );\n const totalLines =\n Math.ceil(estimatedTargetLines / hunkLineCount) * hunkLineCount +\n hunkLineCount;\n const totalHunks = totalLines / hunkLineCount;\n const overflowHunks = totalHunks;\n const hunkOffsets: number[] = [];\n // Halfway between top & bottom, represented as an absolute position\n const viewportCenter = (top + bottom) / 2;\n const separatorGap =\n hunkSeparators === 'simple' ||\n hunkSeparators === 'metadata' ||\n hunkSeparators === 'line-info-basic'\n ? 0\n : fileGap;\n\n let absoluteLineTop = fileTop + headerRegion;\n let currentLine = 0;\n let firstVisibleHunk: number | undefined;\n let centerHunk: number | undefined;\n let overflowCounter: number | undefined;\n\n iterateOverDiff({\n diff: fileDiff,\n diffStyle,\n expandedHunks: expandUnchanged\n ? true\n : this.hunksRenderer.getExpandedHunksMap(),\n collapsedContextThreshold,\n callback: ({\n hunkIndex,\n collapsedBefore,\n collapsedAfter,\n deletionLine,\n additionLine,\n }) => {\n const splitLineIndex =\n additionLine != null\n ? additionLine.splitLineIndex\n : deletionLine.splitLineIndex;\n const unifiedLineIndex =\n additionLine != null\n ? additionLine.unifiedLineIndex\n : deletionLine.unifiedLineIndex;\n const hasMetadata =\n (additionLine?.noEOFCR ?? false) || (deletionLine?.noEOFCR ?? false);\n let gapAdjustment =\n collapsedBefore > 0\n ? hunkSeparatorHeight +\n separatorGap +\n (hunkIndex > 0 ? separatorGap : 0)\n : 0;\n if (hunkIndex === 0 && hunkSeparators === 'simple') {\n gapAdjustment = 0;\n }\n\n absoluteLineTop += gapAdjustment;\n\n const isAtHunkBoundary = currentLine % hunkLineCount === 0;\n\n // Track the boundary positional offset at a hunk\n if (isAtHunkBoundary) {\n hunkOffsets.push(\n absoluteLineTop - (fileTop + headerRegion + gapAdjustment)\n );\n\n // Check if we should bail (overflow complete)\n if (overflowCounter != null) {\n if (overflowCounter <= 0) {\n return true;\n }\n overflowCounter--;\n }\n }\n\n const lineHeight = this.getLineHeight(\n diffStyle === 'split' ? splitLineIndex : unifiedLineIndex,\n hasMetadata\n );\n\n const currentHunk = Math.floor(currentLine / hunkLineCount);\n\n // Track visible region\n if (absoluteLineTop > top - lineHeight && absoluteLineTop < bottom) {\n firstVisibleHunk ??= currentHunk;\n }\n\n // Track which hunk contains the viewport center\n // If viewport center is above this line and we haven't set centerHunk yet,\n // this is the first line at or past the center\n if (\n centerHunk == null &&\n absoluteLineTop + lineHeight > viewportCenter\n ) {\n centerHunk = currentHunk;\n }\n\n // Start overflow when we are out of the viewport at a hunk boundary\n if (\n overflowCounter == null &&\n absoluteLineTop >= bottom &&\n isAtHunkBoundary\n ) {\n overflowCounter = overflowHunks;\n }\n\n currentLine++;\n absoluteLineTop += lineHeight;\n\n if (collapsedAfter > 0 && hunkSeparators !== 'simple') {\n absoluteLineTop += hunkSeparatorHeight + separatorGap;\n }\n\n return false;\n },\n });\n\n // No visible lines found\n if (firstVisibleHunk == null) {\n return {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter:\n fileHeight -\n headerRegion -\n // We gotta subtract the bottom padding off of the buffer\n fileGap,\n };\n }\n\n // Calculate balanced startingLine centered around the viewport center\n // Fall back to firstVisibleHunk if center wasn't found (e.g., center in a gap)\n const collectedHunks = hunkOffsets.length;\n centerHunk ??= firstVisibleHunk;\n const idealStartHunk = Math.round(centerHunk - totalHunks / 2);\n\n // Clamp startHunk: at the beginning, reduce totalLines; at the end, shift startHunk back\n const maxStartHunk = Math.max(0, collectedHunks - totalHunks);\n const startHunk = Math.max(0, Math.min(idealStartHunk, maxStartHunk));\n const startingLine = startHunk * hunkLineCount;\n\n // If we wanted to start before 0, reduce totalLines by the clamped amount\n const clampedTotalLines =\n idealStartHunk < 0\n ? totalLines + idealStartHunk * hunkLineCount\n : totalLines;\n\n // Use hunkOffsets array for efficient buffer calculations\n const bufferBefore = hunkOffsets[startHunk] ?? 0;\n\n // Calculate bufferAfter using hunkOffset if available, otherwise use cumulative height\n const finalHunkIndex = startHunk + clampedTotalLines / hunkLineCount;\n const bufferAfter =\n finalHunkIndex < hunkOffsets.length\n ? fileHeight -\n headerRegion -\n hunkOffsets[finalHunkIndex] -\n // We gotta subtract the bottom padding off of the buffer\n fileGap\n : // We stopped early, calculate from current position\n fileHeight -\n (absoluteLineTop - fileTop) -\n // We gotta subtract the bottom padding off of the buffer\n fileGap;\n\n return {\n startingLine,\n totalLines: clampedTotalLines,\n bufferBefore,\n bufferAfter,\n };\n }\n}\n\nfunction hasFinalHunk(fileDiff: FileDiffMetadata): boolean {\n const lastHunk = fileDiff.hunks.at(-1);\n if (\n lastHunk == null ||\n fileDiff.isPartial ||\n fileDiff.additionLines.length === 0 ||\n fileDiff.deletionLines.length === 0\n ) {\n return false;\n }\n\n return (\n lastHunk.additionLineIndex + lastHunk.additionCount <\n fileDiff.additionLines.length ||\n lastHunk.deletionLineIndex + lastHunk.deletionCount <\n fileDiff.deletionLines.length\n );\n}\n\n// Extracts the view-specific line index from the data-line-index attribute.\n// Format is \"unifiedIndex,splitIndex\"\nfunction parseLineIndex(\n lineIndexAttr: string,\n diffStyle: 'split' | 'unified'\n): number {\n const [unifiedIndex, splitIndex] = lineIndexAttr.split(',').map(Number);\n return diffStyle === 'split' ? splitIndex : unifiedIndex;\n}\n"],"mappings":"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"}
@@ -14,14 +14,16 @@ interface VirtualizerConfig {
14
14
  declare class Virtualizer {
15
15
  static __STOP: boolean;
16
16
  static __lastScrollPosition: number;
17
- type: string;
17
+ readonly __id: string;
18
18
  readonly config: VirtualizerConfig;
19
+ type: string;
19
20
  private intersectionObserver;
20
21
  private scrollTop;
21
22
  private height;
22
23
  private scrollHeight;
23
24
  private windowSpecs;
24
25
  private root;
26
+ private contentContainer;
25
27
  private resizeObserver;
26
28
  private observers;
27
29
  private visibleInstances;
@@ -1 +1 @@
1
- {"version":3,"file":"Virtualizer.d.ts","names":["VirtualWindowSpecs","SubscribedInstance","VirtualizerConfig","Virtualizer","Partial","HTMLElement","Document","Element"],"sources":["../../src/components/Virtualizer.d.ts"],"sourcesContent":["import type { VirtualWindowSpecs } from '../types';\ninterface SubscribedInstance {\n onRender(dirty: boolean): boolean;\n reconcileHeights(): void;\n setVisibility(visible: boolean): void;\n}\nexport interface VirtualizerConfig {\n overscrollSize: number;\n intersectionObserverMargin: number;\n resizeDebugging: boolean;\n}\nexport declare class Virtualizer {\n static __STOP: boolean;\n static __lastScrollPosition: number;\n type: string;\n readonly config: VirtualizerConfig;\n private intersectionObserver;\n private scrollTop;\n private height;\n private scrollHeight;\n private windowSpecs;\n private root;\n private resizeObserver;\n private observers;\n private visibleInstances;\n private visibleInstancesDirty;\n private instancesChanged;\n private scrollDirty;\n private heightDirty;\n private scrollHeightDirty;\n private renderedObservers;\n private connectQueue;\n constructor(config?: Partial<VirtualizerConfig>);\n setup(root: HTMLElement | Document, contentContainer?: Element): void;\n instanceChanged(instance: SubscribedInstance): void;\n getWindowSpecs(): VirtualWindowSpecs;\n isInstanceVisible(elementTop: number, elementHeight: number): boolean;\n private handleContainerResize;\n private setupWindow;\n private setupElement;\n cleanUp(): void;\n getOffsetInScrollContainer(element: HTMLElement): number;\n connect(container: HTMLElement, instance: SubscribedInstance): () => void;\n disconnect(container: HTMLElement): void;\n private handleWindowResize;\n private handleWindowScroll;\n private handleElementScroll;\n private computeRenderRangeAndEmit;\n private scrollFix;\n private applyScrollFix;\n private getScrollAnchor;\n private handleIntersectionChange;\n private getScrollTop;\n private getScrollHeight;\n private getHeight;\n private markDOMDirty;\n private getScrollContainerElement;\n}\nexport {};\n//# sourceMappingURL=Virtualizer.d.ts.map"],"mappings":";;;UACUC,kBAAAA;;EAAAA,gBAAAA,EAAAA,EAAAA,IAAkB;EAKXC,aAAAA,CAAAA,OAAiB,EAAA,OAAA,CAAA,EAAA,IAAA;AAKlC;AAIqBA,UATJA,iBAAAA,CASIA;EAiBYA,cAAAA,EAAAA,MAAAA;EAARE,0BAAAA,EAAAA,MAAAA;EACTC,eAAAA,EAAAA,OAAAA;;AAA2CE,cAtBtCJ,WAAAA,CAsBsCI;EAC7BN,OAAAA,MAAAA,EAAAA,OAAAA;EACRD,OAAAA,oBAAAA,EAAAA,MAAAA;EAMkBK,IAAAA,EAAAA,MAAAA;EACjBA,SAAAA,MAAAA,EA3BFH,iBA2BEG;EAAuBJ,QAAAA,oBAAAA;EACpBI,QAAAA,SAAAA;EAAW,QAAA,MAAA;;;;;;;;;;;;;;uBAXZD,QAAQF;cACjBG,cAAcC,6BAA6BC;4BAC7BN;oBACRD;;;;;;sCAMkBK;qBACjBA,uBAAuBJ;wBACpBI"}
1
+ {"version":3,"file":"Virtualizer.d.ts","names":["VirtualWindowSpecs","SubscribedInstance","VirtualizerConfig","Virtualizer","Partial","HTMLElement","Document","Element"],"sources":["../../src/components/Virtualizer.d.ts"],"sourcesContent":["import type { VirtualWindowSpecs } from '../types';\ninterface SubscribedInstance {\n onRender(dirty: boolean): boolean;\n reconcileHeights(): void;\n setVisibility(visible: boolean): void;\n}\nexport interface VirtualizerConfig {\n overscrollSize: number;\n intersectionObserverMargin: number;\n resizeDebugging: boolean;\n}\nexport declare class Virtualizer {\n static __STOP: boolean;\n static __lastScrollPosition: number;\n readonly __id: string;\n readonly config: VirtualizerConfig;\n type: string;\n private intersectionObserver;\n private scrollTop;\n private height;\n private scrollHeight;\n private windowSpecs;\n private root;\n private contentContainer;\n private resizeObserver;\n private observers;\n private visibleInstances;\n private visibleInstancesDirty;\n private instancesChanged;\n private scrollDirty;\n private heightDirty;\n private scrollHeightDirty;\n private renderedObservers;\n private connectQueue;\n constructor(config?: Partial<VirtualizerConfig>);\n setup(root: HTMLElement | Document, contentContainer?: Element): void;\n instanceChanged(instance: SubscribedInstance): void;\n getWindowSpecs(): VirtualWindowSpecs;\n isInstanceVisible(elementTop: number, elementHeight: number): boolean;\n private handleContainerResize;\n private setupWindow;\n private setupElement;\n cleanUp(): void;\n getOffsetInScrollContainer(element: HTMLElement): number;\n connect(container: HTMLElement, instance: SubscribedInstance): () => void;\n disconnect(container: HTMLElement): void;\n private handleWindowResize;\n private handleWindowScroll;\n private handleElementScroll;\n private computeRenderRangeAndEmit;\n private scrollFix;\n private applyScrollFix;\n private getScrollAnchor;\n private handleIntersectionChange;\n private getScrollTop;\n private getScrollHeight;\n private getHeight;\n private markDOMDirty;\n private getScrollContainerElement;\n}\nexport {};\n//# sourceMappingURL=Virtualizer.d.ts.map"],"mappings":";;;UACUC,kBAAAA;;EAAAA,gBAAAA,EAAAA,EAAAA,IAAkB;EAKXC,aAAAA,CAAAA,OAAiB,EAAA,OAAA,CAAA,EAAA,IAAA;AAKlC;AAIqBA,UATJA,iBAAAA,CASIA;EAmBYA,cAAAA,EAAAA,MAAAA;EAARE,0BAAAA,EAAAA,MAAAA;EACTC,eAAAA,EAAAA,OAAAA;;AAA2CE,cAxBtCJ,WAAAA,CAwBsCI;EAC7BN,OAAAA,MAAAA,EAAAA,OAAAA;EACRD,OAAAA,oBAAAA,EAAAA,MAAAA;EAMkBK,SAAAA,IAAAA,EAAAA,MAAAA;EACjBA,SAAAA,MAAAA,EA7BFH,iBA6BEG;EAAuBJ,IAAAA,EAAAA,MAAAA;EACpBI,QAAAA,oBAAAA;EAAW,QAAA,SAAA;;;;;;;;;;;;;;;;uBAXZD,QAAQF;cACjBG,cAAcC,6BAA6BC;4BAC7BN;oBACRD;;;;;;sCAMkBK;qBACjBA,uBAAuBJ;wBACpBI"}
@@ -17,11 +17,13 @@ const DEFAULT_VIRTUALIZER_CONFIG = {
17
17
  resizeDebugging: false
18
18
  };
19
19
  let lastSize = 0;
20
+ let instance = -1;
20
21
  var Virtualizer = class Virtualizer {
21
22
  static __STOP = false;
22
23
  static __lastScrollPosition = 0;
23
- type = "basic";
24
+ __id = `virtualizer-${++instance}`;
24
25
  config;
26
+ type = "basic";
25
27
  intersectionObserver;
26
28
  scrollTop = 0;
27
29
  height = 0;
@@ -31,6 +33,7 @@ var Virtualizer = class Virtualizer {
31
33
  bottom: 0
32
34
  };
33
35
  root;
36
+ contentContainer;
34
37
  resizeObserver;
35
38
  observers = /* @__PURE__ */ new Map();
36
39
  visibleInstances = /* @__PURE__ */ new Map();
@@ -69,13 +72,14 @@ var Virtualizer = class Virtualizer {
69
72
  Virtualizer.__STOP = true;
70
73
  }
71
74
  };
72
- for (const [container, instance] of this.connectQueue.entries()) this.connect(container, instance);
75
+ for (const [container, instance$1] of this.connectQueue.entries()) this.connect(container, instance$1);
73
76
  this.connectQueue.clear();
74
77
  this.markDOMDirty();
75
78
  queueRender(this.computeRenderRangeAndEmit);
76
79
  }
77
- instanceChanged(instance) {
78
- this.instancesChanged.add(instance);
80
+ instanceChanged(instance$1) {
81
+ this.instancesChanged.add(instance$1);
82
+ this.markDOMDirty();
79
83
  queueRender(this.computeRenderRangeAndEmit);
80
84
  }
81
85
  getWindowSpecs() {
@@ -106,7 +110,7 @@ var Virtualizer = class Virtualizer {
106
110
  this.scrollHeightDirty = true;
107
111
  shouldQueueUpdate = true;
108
112
  if (this.config.resizeDebugging) {
109
- console.log("Virtualizer: content size change", {
113
+ console.log("Virtualizer: content size change", this.__id, {
110
114
  sizeChange: blockSize - lastSize,
111
115
  newSize: blockSize
112
116
  });
@@ -118,11 +122,11 @@ var Virtualizer = class Virtualizer {
118
122
  this.heightDirty = true;
119
123
  shouldQueueUpdate = true;
120
124
  }
121
- } else if (entry.target === this.root.firstElementChild) {
125
+ } else if (entry.target === this.contentContainer) {
122
126
  this.scrollHeightDirty = true;
123
127
  shouldQueueUpdate = true;
124
128
  if (this.config.resizeDebugging) {
125
- console.log("Virtualizer: scroller size change", {
129
+ console.log("Virtualizer: scroller size change", this.__id, {
126
130
  sizeChange: blockSize - lastSize,
127
131
  newSize: blockSize
128
132
  });
@@ -143,34 +147,56 @@ var Virtualizer = class Virtualizer {
143
147
  this.root.addEventListener("scroll", this.handleElementScroll, { passive: true });
144
148
  this.resizeObserver?.observe(this.root);
145
149
  contentContainer ??= this.root.firstElementChild ?? void 0;
146
- if (contentContainer != null) this.resizeObserver?.observe(contentContainer);
150
+ if (contentContainer instanceof HTMLElement) {
151
+ this.contentContainer = contentContainer;
152
+ this.resizeObserver?.observe(contentContainer);
153
+ }
147
154
  }
148
155
  cleanUp() {
156
+ this.resizeObserver?.disconnect();
157
+ this.resizeObserver = void 0;
149
158
  this.intersectionObserver?.disconnect();
150
159
  this.intersectionObserver = void 0;
160
+ this.root?.removeEventListener("scroll", this.handleElementScroll);
161
+ window.removeEventListener("scroll", this.handleWindowScroll);
162
+ window.removeEventListener("resize", this.handleWindowResize);
151
163
  this.root = void 0;
164
+ this.contentContainer = void 0;
165
+ this.observers.clear();
166
+ this.visibleInstances.clear();
167
+ this.instancesChanged.clear();
168
+ this.connectQueue.clear();
169
+ this.visibleInstancesDirty = false;
170
+ this.windowSpecs = {
171
+ top: 0,
172
+ bottom: 0
173
+ };
174
+ this.scrollTop = 0;
175
+ this.height = 0;
176
+ this.scrollHeight = 0;
152
177
  }
153
178
  getOffsetInScrollContainer(element) {
154
179
  return this.getScrollTop() + getRelativeBoundingTop(element, this.getScrollContainerElement());
155
180
  }
156
- connect(container, instance) {
181
+ connect(container, instance$1) {
157
182
  if (this.observers.has(container)) throw new Error("Virtualizer.connect: instance is already connected...");
158
- if (this.intersectionObserver == null) this.connectQueue.set(container, instance);
183
+ if (this.intersectionObserver == null) this.connectQueue.set(container, instance$1);
159
184
  else {
160
185
  this.intersectionObserver.observe(container);
161
- this.observers.set(container, instance);
162
- this.instancesChanged.add(instance);
186
+ this.observers.set(container, instance$1);
187
+ this.instancesChanged.add(instance$1);
163
188
  this.markDOMDirty();
164
189
  queueRender(this.computeRenderRangeAndEmit);
165
190
  }
166
191
  return () => this.disconnect(container);
167
192
  }
168
193
  disconnect(container) {
169
- const instance = this.observers.get(container);
194
+ const instance$1 = this.observers.get(container);
170
195
  this.connectQueue.delete(container);
171
- if (instance == null) return;
196
+ if (instance$1 == null) return;
172
197
  this.intersectionObserver?.unobserve(container);
173
198
  this.observers.delete(container);
199
+ if (this.visibleInstances.delete(container)) this.visibleInstancesDirty = true;
174
200
  this.markDOMDirty();
175
201
  queueRender(this.computeRenderRangeAndEmit);
176
202
  }
@@ -208,14 +234,14 @@ var Virtualizer = class Virtualizer {
208
234
  this.renderedObservers = this.observers.size;
209
235
  const anchor = this.getScrollAnchor(this.height);
210
236
  const updatedInstances = /* @__PURE__ */ new Set();
211
- for (const instance of this.visibleInstances.values()) if (instance.onRender(wrapperDirty)) updatedInstances.add(instance);
212
- for (const instance of this.instancesChanged) {
213
- if (updatedInstances.has(instance)) continue;
214
- if (instance.onRender(wrapperDirty)) updatedInstances.add(instance);
237
+ for (const instance$1 of wrapperDirty ? this.observers.values() : this.visibleInstances.values()) if (instance$1.onRender(wrapperDirty)) updatedInstances.add(instance$1);
238
+ for (const instance$1 of this.instancesChanged) {
239
+ if (updatedInstances.has(instance$1)) continue;
240
+ if (instance$1.onRender(wrapperDirty)) updatedInstances.add(instance$1);
215
241
  }
216
242
  this.scrollFix(anchor);
217
243
  if (this.instancesChanged.size > 0) this.markDOMDirty();
218
- for (const instance of updatedInstances) instance.reconcileHeights();
244
+ for (const instance$1 of updatedInstances) instance$1.reconcileHeights();
219
245
  if (this.instancesChanged.size > 0 || wrapperDirty) queueRender(this.computeRenderRangeAndEmit);
220
246
  updatedInstances.clear();
221
247
  this.instancesChanged.clear();
@@ -303,14 +329,14 @@ var Virtualizer = class Virtualizer {
303
329
  this.scrollDirty = true;
304
330
  for (const { target, isIntersecting } of entries) {
305
331
  if (!(target instanceof HTMLElement)) throw new Error("Virtualizer.handleIntersectionChange: target not an HTMLElement");
306
- const instance = this.observers.get(target);
307
- if (instance == null) throw new Error("Virtualizer.handleIntersectionChange: no instance for target");
332
+ const instance$1 = this.observers.get(target);
333
+ if (instance$1 == null) throw new Error("Virtualizer.handleIntersectionChange: no instance for target");
308
334
  if (isIntersecting && !this.visibleInstances.has(target)) {
309
- instance.setVisibility(true);
310
- this.visibleInstances.set(target, instance);
335
+ instance$1.setVisibility(true);
336
+ this.visibleInstances.set(target, instance$1);
311
337
  this.visibleInstancesDirty = true;
312
338
  } else if (!isIntersecting && this.visibleInstances.has(target)) {
313
- instance.setVisibility(false);
339
+ instance$1.setVisibility(false);
314
340
  this.visibleInstances.delete(target);
315
341
  this.visibleInstancesDirty = true;
316
342
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Virtualizer.js","names":["DEFAULT_VIRTUALIZER_CONFIG: VirtualizerConfig","top","bestAnchor: ScrollAnchor | undefined","fileOffset: number","fileTypeOffset: 'top' | 'bottom'","bestLineIndex: string | undefined","bestLineOffset: number | undefined"],"sources":["../../src/components/Virtualizer.ts"],"sourcesContent":["import { queueRender } from '../managers/UniversalRenderingManager';\nimport type { VirtualWindowSpecs } from '../types';\nimport { areVirtualWindowSpecsEqual } from '../utils/areVirtualWindowSpecsEqual';\nimport { createWindowFromScrollPosition } from '../utils/createWindowFromScrollPosition';\n\ninterface SubscribedInstance {\n onRender(dirty: boolean): boolean;\n reconcileHeights(): void;\n setVisibility(visible: boolean): void;\n}\n\ninterface ScrollAnchor {\n fileElement: HTMLElement;\n fileTypeOffset: 'top' | 'bottom';\n fileOffset: number;\n lineIndex: string | undefined;\n lineOffset: number | undefined;\n}\n\n// 800 seems like the healthy overscan required to\n// keep safari from blanking... if we catch it tho, maybe 900\nconst DEFAULT_OVERSCROLL_SIZE = 1000;\nconst INTERSECTION_OBSERVER_MARGIN = DEFAULT_OVERSCROLL_SIZE * 4;\nconst INTERSECTION_OBSERVER_THRESHOLD = [0, 0.000001, 0.99999, 1];\n\nexport interface VirtualizerConfig {\n overscrollSize: number;\n intersectionObserverMargin: number;\n resizeDebugging: boolean;\n}\n\nconst DEFAULT_VIRTUALIZER_CONFIG: VirtualizerConfig = {\n overscrollSize: DEFAULT_OVERSCROLL_SIZE,\n intersectionObserverMargin: INTERSECTION_OBSERVER_MARGIN,\n resizeDebugging: false,\n};\n\nlet lastSize = 0;\n\nexport class Virtualizer {\n static __STOP: boolean = false;\n static __lastScrollPosition = 0;\n\n public type = 'basic';\n public readonly config: VirtualizerConfig;\n private intersectionObserver: IntersectionObserver | undefined;\n private scrollTop: number = 0;\n private height: number = 0;\n private scrollHeight: number = 0;\n private windowSpecs: VirtualWindowSpecs = { top: 0, bottom: 0 };\n private root: HTMLElement | Document | undefined;\n\n private resizeObserver: ResizeObserver | undefined;\n private observers: Map<HTMLElement, SubscribedInstance> = new Map();\n private visibleInstances: Map<HTMLElement, SubscribedInstance> = new Map();\n private visibleInstancesDirty: boolean = false;\n private instancesChanged: Set<SubscribedInstance> = new Set();\n\n private scrollDirty = true;\n private heightDirty = true;\n private scrollHeightDirty = true;\n private renderedObservers = 0;\n private connectQueue: Map<HTMLElement, SubscribedInstance> = new Map();\n\n constructor(config?: Partial<VirtualizerConfig>) {\n this.config = { ...DEFAULT_VIRTUALIZER_CONFIG, ...config };\n }\n\n setup(root: HTMLElement | Document, contentContainer?: Element): void {\n if (this.root != null) {\n return;\n }\n this.root = root;\n this.resizeObserver = new ResizeObserver(this.handleContainerResize);\n this.intersectionObserver = new IntersectionObserver(\n this.handleIntersectionChange,\n {\n root: this.root,\n threshold: INTERSECTION_OBSERVER_THRESHOLD,\n rootMargin: `${this.config.intersectionObserverMargin}px 0px ${this.config.intersectionObserverMargin}px 0px`,\n // FIXME(amadeus): Figure out the other settings we'll want in here, or\n // if we should make them configurable...\n }\n );\n if (root instanceof Document) {\n this.setupWindow();\n } else {\n this.setupElement(contentContainer);\n }\n\n // FIXME(amadeus): Remove me before release\n window.__INSTANCE = this;\n window.__TOGGLE = () => {\n if (Virtualizer.__STOP) {\n Virtualizer.__STOP = false;\n const scroller = this.getScrollContainerElement() ?? window;\n scroller.scrollTo({ top: Virtualizer.__lastScrollPosition });\n queueRender(this.computeRenderRangeAndEmit);\n } else {\n Virtualizer.__lastScrollPosition = this.getScrollTop();\n Virtualizer.__STOP = true;\n }\n };\n for (const [container, instance] of this.connectQueue.entries()) {\n this.connect(container, instance);\n }\n this.connectQueue.clear();\n this.markDOMDirty();\n queueRender(this.computeRenderRangeAndEmit);\n }\n\n instanceChanged(instance: SubscribedInstance): void {\n this.instancesChanged.add(instance);\n queueRender(this.computeRenderRangeAndEmit);\n }\n\n getWindowSpecs(): VirtualWindowSpecs {\n if (this.windowSpecs.top === 0 && this.windowSpecs.bottom === 0) {\n this.windowSpecs = createWindowFromScrollPosition({\n scrollTop: this.getScrollTop(),\n height: this.getHeight(),\n scrollHeight: this.getScrollHeight(),\n fitPerfectly: false,\n overscrollSize: this.config.overscrollSize,\n });\n }\n return this.windowSpecs;\n }\n\n isInstanceVisible(elementTop: number, elementHeight: number): boolean {\n const scrollTop = this.getScrollTop();\n const height = this.getHeight();\n const margin = this.config.intersectionObserverMargin;\n const top = scrollTop - margin;\n const bottom = scrollTop + height + margin;\n return !(elementTop < top - elementHeight || elementTop > bottom);\n }\n\n private handleContainerResize = (entries: ResizeObserverEntry[]) => {\n if (this.root == null) return;\n let shouldQueueUpdate = false;\n for (const entry of entries) {\n const blockSize = entry.borderBoxSize[0].blockSize;\n if (this.root instanceof Document) {\n if (blockSize !== this.scrollHeight) {\n this.scrollHeightDirty = true;\n shouldQueueUpdate = true;\n if (this.config.resizeDebugging) {\n console.log('Virtualizer: content size change', {\n sizeChange: blockSize - lastSize,\n newSize: blockSize,\n });\n lastSize = blockSize;\n }\n }\n } else {\n if (entry.target === this.root) {\n if (blockSize !== this.height) {\n this.heightDirty = true;\n shouldQueueUpdate = true;\n }\n } else if (entry.target === this.root.firstElementChild) {\n this.scrollHeightDirty = true;\n shouldQueueUpdate = true;\n if (this.config.resizeDebugging) {\n console.log('Virtualizer: scroller size change', {\n sizeChange: blockSize - lastSize,\n newSize: blockSize,\n });\n lastSize = blockSize;\n }\n }\n }\n }\n\n if (shouldQueueUpdate) {\n queueRender(this.computeRenderRangeAndEmit);\n }\n };\n\n private setupWindow() {\n if (this.root == null || !(this.root instanceof Document)) {\n throw new Error('Virtualizer.setupWindow: Invalid setup method');\n }\n window.addEventListener('scroll', this.handleWindowScroll, {\n passive: true,\n });\n window.addEventListener('resize', this.handleWindowResize, {\n passive: true,\n });\n this.resizeObserver?.observe(this.root.documentElement);\n }\n\n private setupElement(contentContainer: Element | undefined) {\n if (this.root == null || this.root instanceof Document) {\n throw new Error('Virtualizer.setupElement: Invalid setup method');\n }\n this.root.addEventListener('scroll', this.handleElementScroll, {\n passive: true,\n });\n this.resizeObserver?.observe(this.root);\n contentContainer ??= this.root.firstElementChild ?? undefined;\n if (contentContainer != null) {\n this.resizeObserver?.observe(contentContainer);\n }\n }\n\n cleanUp(): void {\n this.intersectionObserver?.disconnect();\n this.intersectionObserver = undefined;\n this.root = undefined;\n }\n\n getOffsetInScrollContainer(element: HTMLElement): number {\n return (\n this.getScrollTop() +\n getRelativeBoundingTop(element, this.getScrollContainerElement())\n );\n }\n\n connect(container: HTMLElement, instance: SubscribedInstance): () => void {\n if (this.observers.has(container)) {\n throw new Error('Virtualizer.connect: instance is already connected...');\n }\n // If we are racing against the intersectionObserver, then we should just\n // queue up the connection for when the observer does get set up\n if (this.intersectionObserver == null) {\n this.connectQueue.set(container, instance);\n } else {\n // FIXME(amadeus): Go through the connection phase a bit more closely...\n this.intersectionObserver.observe(container);\n this.observers.set(container, instance);\n this.instancesChanged.add(instance);\n this.markDOMDirty();\n queueRender(this.computeRenderRangeAndEmit);\n }\n return () => this.disconnect(container);\n }\n\n disconnect(container: HTMLElement): void {\n const instance = this.observers.get(container);\n this.connectQueue.delete(container);\n if (instance == null) {\n return;\n }\n this.intersectionObserver?.unobserve(container);\n this.observers.delete(container);\n this.markDOMDirty();\n queueRender(this.computeRenderRangeAndEmit);\n }\n\n private handleWindowResize = () => {\n if (Virtualizer.__STOP || window.innerHeight === this.height) {\n return;\n }\n this.heightDirty = true;\n queueRender(this.computeRenderRangeAndEmit);\n };\n\n private handleWindowScroll = () => {\n if (\n Virtualizer.__STOP ||\n this.root == null ||\n !(this.root instanceof Document)\n ) {\n return;\n }\n this.scrollDirty = true;\n queueRender(this.computeRenderRangeAndEmit);\n };\n\n private handleElementScroll = () => {\n if (\n Virtualizer.__STOP ||\n this.root == null ||\n this.root instanceof Document\n ) {\n return;\n }\n this.scrollDirty = true;\n queueRender(this.computeRenderRangeAndEmit);\n };\n\n private computeRenderRangeAndEmit = () => {\n if (Virtualizer.__STOP) {\n return;\n }\n const wrapperDirty = this.heightDirty || this.scrollHeightDirty;\n if (\n !this.scrollDirty &&\n !this.scrollHeightDirty &&\n !this.heightDirty &&\n this.renderedObservers === this.observers.size &&\n !this.visibleInstancesDirty &&\n this.instancesChanged.size === 0\n ) {\n // NOTE(amadeus): Is this a safe assumption/optimization?\n return;\n }\n\n // If we got an emitted update from a bunch of instances, we should skip\n // the window check first and attempt to render with existing logic first\n // and then queue up a corrected render after\n if (this.instancesChanged.size === 0) {\n const windowSpecs = createWindowFromScrollPosition({\n scrollTop: this.getScrollTop(),\n height: this.getHeight(),\n scrollHeight: this.getScrollHeight(),\n fitPerfectly: false,\n overscrollSize: this.config.overscrollSize,\n });\n if (\n areVirtualWindowSpecsEqual(this.windowSpecs, windowSpecs) &&\n this.renderedObservers === this.observers.size &&\n !this.visibleInstancesDirty &&\n this.instancesChanged.size === 0\n ) {\n return;\n }\n this.windowSpecs = windowSpecs;\n }\n this.visibleInstancesDirty = false;\n this.renderedObservers = this.observers.size;\n const anchor = this.getScrollAnchor(this.height);\n const updatedInstances = new Set<SubscribedInstance>();\n for (const instance of this.visibleInstances.values()) {\n if (instance.onRender(wrapperDirty)) {\n updatedInstances.add(instance);\n }\n }\n for (const instance of this.instancesChanged) {\n if (updatedInstances.has(instance)) continue;\n if (instance.onRender(wrapperDirty)) {\n updatedInstances.add(instance);\n }\n }\n\n this.scrollFix(anchor);\n // Scroll fix may have marked the dom as dirty, but if there instance\n // changes, we should definitely mark as dirty\n if (this.instancesChanged.size > 0) {\n this.markDOMDirty();\n }\n\n for (const instance of updatedInstances) {\n instance.reconcileHeights();\n }\n\n if (this.instancesChanged.size > 0 || wrapperDirty) {\n queueRender(this.computeRenderRangeAndEmit);\n }\n updatedInstances.clear();\n this.instancesChanged.clear();\n };\n\n private scrollFix(anchor: ScrollAnchor | undefined) {\n if (anchor == null) {\n return;\n }\n const scrollContainer = this.getScrollContainerElement();\n const { lineIndex, lineOffset, fileElement, fileOffset, fileTypeOffset } =\n anchor;\n if (lineIndex != null && lineOffset != null) {\n const element = fileElement.shadowRoot?.querySelector(\n `[data-line][data-line-index=\"${lineIndex}\"]`\n );\n if (element instanceof HTMLElement) {\n const top = getRelativeBoundingTop(element, scrollContainer);\n if (top !== lineOffset) {\n const scrollOffset = top - lineOffset;\n this.applyScrollFix(scrollOffset);\n }\n return;\n }\n }\n const top = getRelativeBoundingTop(fileElement, scrollContainer);\n if (fileTypeOffset === 'top') {\n if (top !== fileOffset) {\n this.applyScrollFix(top - fileOffset);\n }\n } else {\n const bottom = top + fileElement.getBoundingClientRect().height;\n if (bottom !== fileOffset) {\n this.applyScrollFix(bottom - fileOffset);\n }\n }\n }\n\n private applyScrollFix(scrollOffset: number) {\n if (this.root == null || this.root instanceof Document) {\n window.scrollTo({\n top: window.scrollY + scrollOffset,\n behavior: 'instant',\n });\n } else {\n this.root.scrollTo({\n top: this.root.scrollTop + scrollOffset,\n behavior: 'instant',\n });\n }\n // Because we fixed our scroll positions, it means something resized or\n // moved around, so we should mark everything as dirty so the\n // reconciliation call will get the latest data when figuring calling\n // .getOffsetInScrollContainer\n this.markDOMDirty();\n }\n\n // This function tries to figure out the closest file or line to the viewport\n // top that's visible to use as a relative marker for how to fix scroll\n // position after issuing dom updates\n private getScrollAnchor(viewportHeight: number): ScrollAnchor | undefined {\n const scrollContainer = this.getScrollContainerElement();\n let bestAnchor: ScrollAnchor | undefined;\n\n for (const [fileElement] of this.visibleInstances.entries()) {\n const fileTop = getRelativeBoundingTop(fileElement, scrollContainer);\n const fileBottom = fileTop + fileElement.offsetHeight;\n\n // Determine file offset and type based on position\n // Only use bottom anchor when entire file is above viewport\n let fileOffset: number;\n let fileTypeOffset: 'top' | 'bottom';\n if (fileBottom <= 0) {\n // Entire file is above viewport - use bottom as anchor\n fileOffset = fileBottom;\n fileTypeOffset = 'bottom';\n } else {\n // File is at least partially visible or below - use top\n fileOffset = fileTop;\n fileTypeOffset = 'top';\n }\n\n // Find the best line (first fully visible) within this file\n let bestLineIndex: string | undefined;\n let bestLineOffset: number | undefined;\n\n // Only search for lines if file potentially intersects viewport\n if (fileBottom > 0 && fileTop < viewportHeight) {\n for (const line of fileElement.shadowRoot?.querySelectorAll(\n '[data-line][data-line-index]'\n ) ?? []) {\n if (!(line instanceof HTMLElement)) continue;\n const lineIndex = line.dataset.lineIndex;\n if (lineIndex == null) continue;\n\n const lineOffset = getRelativeBoundingTop(line, scrollContainer);\n\n // Ignore lines with negative offsets (above viewport top)\n if (lineOffset < 0) continue;\n\n // First visible line in DOM order is the best one because\n // querySelectorAll will grab lines in order as they appear in the\n // DOM\n bestLineIndex = lineIndex;\n bestLineOffset = lineOffset;\n break;\n }\n }\n\n // If we already have an anchor with a visible line, skip files without one\n if (bestAnchor?.lineOffset != null && bestLineOffset == null) {\n continue;\n }\n\n // Decide if this file should become the new best anchor\n let shouldReplace = false;\n // If we don't already have an anchor we should set one\n if (bestAnchor == null) {\n shouldReplace = true;\n }\n // If we found a better line anchor, we should replace the old one\n else if (\n bestLineOffset != null &&\n (bestAnchor.lineOffset == null ||\n bestLineOffset < bestAnchor.lineOffset)\n ) {\n shouldReplace = true;\n }\n // Otherwise we need to compare file only anchors\n else if (bestLineOffset == null && bestAnchor.lineOffset == null) {\n // Favor files with their tops in view\n if (\n fileOffset >= 0 &&\n (bestAnchor.fileOffset < 0 || fileOffset < bestAnchor.fileOffset)\n ) {\n shouldReplace = true;\n }\n // Or the closest file\n else if (\n fileOffset < 0 &&\n bestAnchor.fileOffset < 0 &&\n fileOffset > bestAnchor.fileOffset\n ) {\n shouldReplace = true;\n }\n }\n\n if (shouldReplace) {\n bestAnchor = {\n fileElement,\n fileTypeOffset,\n fileOffset,\n lineIndex: bestLineIndex,\n lineOffset: bestLineOffset,\n };\n }\n }\n\n return bestAnchor;\n }\n\n private handleIntersectionChange = (\n entries: IntersectionObserverEntry[]\n ): void => {\n this.scrollDirty = true;\n for (const { target, isIntersecting } of entries) {\n if (!(target instanceof HTMLElement)) {\n throw new Error(\n 'Virtualizer.handleIntersectionChange: target not an HTMLElement'\n );\n }\n const instance = this.observers.get(target);\n if (instance == null) {\n throw new Error(\n 'Virtualizer.handleIntersectionChange: no instance for target'\n );\n }\n if (isIntersecting && !this.visibleInstances.has(target)) {\n instance.setVisibility(true);\n this.visibleInstances.set(target, instance);\n this.visibleInstancesDirty = true;\n } else if (!isIntersecting && this.visibleInstances.has(target)) {\n instance.setVisibility(false);\n this.visibleInstances.delete(target);\n this.visibleInstancesDirty = true;\n }\n }\n\n if (this.visibleInstancesDirty) {\n // Since this call is already debounced, should we just call\n // computeRenderRangeAndEmit directly?\n queueRender(this.computeRenderRangeAndEmit);\n }\n // Debug logging for visible instances\n // console.log(\n // 'handleIntersectionChange',\n // ...Array.from(this.visibleInstances.keys())\n // );\n };\n\n private getScrollTop() {\n if (!this.scrollDirty) {\n return this.scrollTop;\n }\n this.scrollDirty = false;\n let scrollTop = (() => {\n if (this.root == null) {\n return 0;\n }\n if (this.root instanceof Document) {\n return window.scrollY;\n }\n return this.root.scrollTop;\n })();\n\n // Lets always make sure to clamp scroll position cases of\n // over/bounce scroll\n scrollTop = Math.max(\n 0,\n Math.min(scrollTop, this.getScrollHeight() - this.getHeight())\n );\n this.scrollTop = scrollTop;\n return scrollTop;\n }\n\n private getScrollHeight() {\n if (!this.scrollHeightDirty) {\n return this.scrollHeight;\n }\n this.scrollHeightDirty = false;\n this.scrollHeight = (() => {\n if (this.root == null) {\n return 0;\n }\n if (this.root instanceof Document) {\n return this.root.documentElement.scrollHeight;\n }\n return this.root.scrollHeight;\n })();\n return this.scrollHeight;\n }\n\n private getHeight() {\n if (!this.heightDirty) {\n return this.height;\n }\n this.heightDirty = false;\n this.height = (() => {\n if (this.root == null) {\n return 0;\n }\n if (this.root instanceof Document) {\n return globalThis.innerHeight;\n }\n return this.root.getBoundingClientRect().height;\n })();\n return this.height;\n }\n\n private markDOMDirty() {\n this.scrollDirty = true;\n this.scrollHeightDirty = true;\n this.heightDirty = true;\n }\n\n private getScrollContainerElement(): HTMLElement | undefined {\n return this.root == null || this.root instanceof Document\n ? undefined\n : this.root;\n }\n}\n\n// This function is like a generalized getBoundingClientRect for it's relative\n// scroll container\nfunction getRelativeBoundingTop(\n element: HTMLElement,\n scrollContainer: HTMLElement | undefined\n) {\n const rect = element.getBoundingClientRect();\n const scrollContainerTop = scrollContainer?.getBoundingClientRect().top ?? 0;\n return rect.top - scrollContainerTop;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Virtualizer.js","names":["DEFAULT_VIRTUALIZER_CONFIG: VirtualizerConfig","instance","top","bestAnchor: ScrollAnchor | undefined","fileOffset: number","fileTypeOffset: 'top' | 'bottom'","bestLineIndex: string | undefined","bestLineOffset: number | undefined"],"sources":["../../src/components/Virtualizer.ts"],"sourcesContent":["import { queueRender } from '../managers/UniversalRenderingManager';\nimport type { VirtualWindowSpecs } from '../types';\nimport { areVirtualWindowSpecsEqual } from '../utils/areVirtualWindowSpecsEqual';\nimport { createWindowFromScrollPosition } from '../utils/createWindowFromScrollPosition';\n\ninterface SubscribedInstance {\n onRender(dirty: boolean): boolean;\n reconcileHeights(): void;\n setVisibility(visible: boolean): void;\n}\n\ninterface ScrollAnchor {\n fileElement: HTMLElement;\n fileTypeOffset: 'top' | 'bottom';\n fileOffset: number;\n lineIndex: string | undefined;\n lineOffset: number | undefined;\n}\n\n// 800 seems like the healthy overscan required to\n// keep safari from blanking... if we catch it tho, maybe 900\nconst DEFAULT_OVERSCROLL_SIZE = 1000;\nconst INTERSECTION_OBSERVER_MARGIN = DEFAULT_OVERSCROLL_SIZE * 4;\nconst INTERSECTION_OBSERVER_THRESHOLD = [0, 0.000001, 0.99999, 1];\n\nexport interface VirtualizerConfig {\n overscrollSize: number;\n intersectionObserverMargin: number;\n resizeDebugging: boolean;\n}\n\nconst DEFAULT_VIRTUALIZER_CONFIG: VirtualizerConfig = {\n overscrollSize: DEFAULT_OVERSCROLL_SIZE,\n intersectionObserverMargin: INTERSECTION_OBSERVER_MARGIN,\n resizeDebugging: false,\n};\n\nlet lastSize = 0;\n\nlet instance = -1;\n\nexport class Virtualizer {\n static __STOP: boolean = false;\n static __lastScrollPosition = 0;\n\n public readonly __id: string = `virtualizer-${++instance}`;\n public readonly config: VirtualizerConfig;\n public type = 'basic';\n private intersectionObserver: IntersectionObserver | undefined;\n private scrollTop: number = 0;\n private height: number = 0;\n private scrollHeight: number = 0;\n private windowSpecs: VirtualWindowSpecs = { top: 0, bottom: 0 };\n private root: HTMLElement | Document | undefined;\n private contentContainer: HTMLElement | undefined;\n\n private resizeObserver: ResizeObserver | undefined;\n private observers: Map<HTMLElement, SubscribedInstance> = new Map();\n private visibleInstances: Map<HTMLElement, SubscribedInstance> = new Map();\n private visibleInstancesDirty: boolean = false;\n private instancesChanged: Set<SubscribedInstance> = new Set();\n\n private scrollDirty = true;\n private heightDirty = true;\n private scrollHeightDirty = true;\n private renderedObservers = 0;\n private connectQueue: Map<HTMLElement, SubscribedInstance> = new Map();\n\n constructor(config?: Partial<VirtualizerConfig>) {\n this.config = { ...DEFAULT_VIRTUALIZER_CONFIG, ...config };\n }\n\n setup(root: HTMLElement | Document, contentContainer?: Element): void {\n if (this.root != null) {\n return;\n }\n this.root = root;\n this.resizeObserver = new ResizeObserver(this.handleContainerResize);\n this.intersectionObserver = new IntersectionObserver(\n this.handleIntersectionChange,\n {\n root: this.root,\n threshold: INTERSECTION_OBSERVER_THRESHOLD,\n rootMargin: `${this.config.intersectionObserverMargin}px 0px ${this.config.intersectionObserverMargin}px 0px`,\n // FIXME(amadeus): Figure out the other settings we'll want in here, or\n // if we should make them configurable...\n }\n );\n if (root instanceof Document) {\n this.setupWindow();\n } else {\n this.setupElement(contentContainer);\n }\n\n // FIXME(amadeus): Remove me before release\n window.__INSTANCE = this;\n window.__TOGGLE = () => {\n if (Virtualizer.__STOP) {\n Virtualizer.__STOP = false;\n const scroller = this.getScrollContainerElement() ?? window;\n scroller.scrollTo({ top: Virtualizer.__lastScrollPosition });\n queueRender(this.computeRenderRangeAndEmit);\n } else {\n Virtualizer.__lastScrollPosition = this.getScrollTop();\n Virtualizer.__STOP = true;\n }\n };\n for (const [container, instance] of this.connectQueue.entries()) {\n this.connect(container, instance);\n }\n this.connectQueue.clear();\n this.markDOMDirty();\n queueRender(this.computeRenderRangeAndEmit);\n }\n\n instanceChanged(instance: SubscribedInstance): void {\n this.instancesChanged.add(instance);\n this.markDOMDirty();\n queueRender(this.computeRenderRangeAndEmit);\n }\n\n getWindowSpecs(): VirtualWindowSpecs {\n if (this.windowSpecs.top === 0 && this.windowSpecs.bottom === 0) {\n this.windowSpecs = createWindowFromScrollPosition({\n scrollTop: this.getScrollTop(),\n height: this.getHeight(),\n scrollHeight: this.getScrollHeight(),\n fitPerfectly: false,\n overscrollSize: this.config.overscrollSize,\n });\n }\n return this.windowSpecs;\n }\n\n isInstanceVisible(elementTop: number, elementHeight: number): boolean {\n const scrollTop = this.getScrollTop();\n const height = this.getHeight();\n const margin = this.config.intersectionObserverMargin;\n const top = scrollTop - margin;\n const bottom = scrollTop + height + margin;\n return !(elementTop < top - elementHeight || elementTop > bottom);\n }\n\n private handleContainerResize = (entries: ResizeObserverEntry[]) => {\n if (this.root == null) return;\n let shouldQueueUpdate = false;\n for (const entry of entries) {\n const blockSize = entry.borderBoxSize[0].blockSize;\n if (this.root instanceof Document) {\n if (blockSize !== this.scrollHeight) {\n this.scrollHeightDirty = true;\n shouldQueueUpdate = true;\n if (this.config.resizeDebugging) {\n console.log('Virtualizer: content size change', this.__id, {\n sizeChange: blockSize - lastSize,\n newSize: blockSize,\n });\n lastSize = blockSize;\n }\n }\n } else {\n if (entry.target === this.root) {\n if (blockSize !== this.height) {\n this.heightDirty = true;\n shouldQueueUpdate = true;\n }\n } else if (entry.target === this.contentContainer) {\n this.scrollHeightDirty = true;\n shouldQueueUpdate = true;\n if (this.config.resizeDebugging) {\n console.log('Virtualizer: scroller size change', this.__id, {\n sizeChange: blockSize - lastSize,\n newSize: blockSize,\n });\n lastSize = blockSize;\n }\n }\n }\n }\n\n if (shouldQueueUpdate) {\n queueRender(this.computeRenderRangeAndEmit);\n }\n };\n\n private setupWindow() {\n if (this.root == null || !(this.root instanceof Document)) {\n throw new Error('Virtualizer.setupWindow: Invalid setup method');\n }\n window.addEventListener('scroll', this.handleWindowScroll, {\n passive: true,\n });\n window.addEventListener('resize', this.handleWindowResize, {\n passive: true,\n });\n this.resizeObserver?.observe(this.root.documentElement);\n }\n\n private setupElement(contentContainer: Element | undefined) {\n if (this.root == null || this.root instanceof Document) {\n throw new Error('Virtualizer.setupElement: Invalid setup method');\n }\n this.root.addEventListener('scroll', this.handleElementScroll, {\n passive: true,\n });\n this.resizeObserver?.observe(this.root);\n contentContainer ??= this.root.firstElementChild ?? undefined;\n if (contentContainer instanceof HTMLElement) {\n this.contentContainer = contentContainer;\n this.resizeObserver?.observe(contentContainer);\n }\n }\n\n cleanUp(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n this.intersectionObserver?.disconnect();\n this.intersectionObserver = undefined;\n this.root?.removeEventListener('scroll', this.handleElementScroll);\n window.removeEventListener('scroll', this.handleWindowScroll);\n window.removeEventListener('resize', this.handleWindowResize);\n this.root = undefined;\n this.contentContainer = undefined;\n this.observers.clear();\n this.visibleInstances.clear();\n this.instancesChanged.clear();\n this.connectQueue.clear();\n this.visibleInstancesDirty = false;\n this.windowSpecs = { top: 0, bottom: 0 };\n this.scrollTop = 0;\n this.height = 0;\n this.scrollHeight = 0;\n }\n\n getOffsetInScrollContainer(element: HTMLElement): number {\n return (\n this.getScrollTop() +\n getRelativeBoundingTop(element, this.getScrollContainerElement())\n );\n }\n\n connect(container: HTMLElement, instance: SubscribedInstance): () => void {\n if (this.observers.has(container)) {\n throw new Error('Virtualizer.connect: instance is already connected...');\n }\n // If we are racing against the intersectionObserver, then we should just\n // queue up the connection for when the observer does get set up\n if (this.intersectionObserver == null) {\n this.connectQueue.set(container, instance);\n } else {\n // FIXME(amadeus): Go through the connection phase a bit more closely...\n this.intersectionObserver.observe(container);\n this.observers.set(container, instance);\n this.instancesChanged.add(instance);\n this.markDOMDirty();\n queueRender(this.computeRenderRangeAndEmit);\n }\n return () => this.disconnect(container);\n }\n\n disconnect(container: HTMLElement): void {\n const instance = this.observers.get(container);\n this.connectQueue.delete(container);\n if (instance == null) {\n return;\n }\n this.intersectionObserver?.unobserve(container);\n this.observers.delete(container);\n if (this.visibleInstances.delete(container)) {\n this.visibleInstancesDirty = true;\n }\n this.markDOMDirty();\n queueRender(this.computeRenderRangeAndEmit);\n }\n\n private handleWindowResize = () => {\n if (Virtualizer.__STOP || window.innerHeight === this.height) {\n return;\n }\n this.heightDirty = true;\n queueRender(this.computeRenderRangeAndEmit);\n };\n\n private handleWindowScroll = () => {\n if (\n Virtualizer.__STOP ||\n this.root == null ||\n !(this.root instanceof Document)\n ) {\n return;\n }\n this.scrollDirty = true;\n queueRender(this.computeRenderRangeAndEmit);\n };\n\n private handleElementScroll = () => {\n if (\n Virtualizer.__STOP ||\n this.root == null ||\n this.root instanceof Document\n ) {\n return;\n }\n this.scrollDirty = true;\n queueRender(this.computeRenderRangeAndEmit);\n };\n\n private computeRenderRangeAndEmit = () => {\n if (Virtualizer.__STOP) {\n return;\n }\n const wrapperDirty = this.heightDirty || this.scrollHeightDirty;\n if (\n !this.scrollDirty &&\n !this.scrollHeightDirty &&\n !this.heightDirty &&\n this.renderedObservers === this.observers.size &&\n !this.visibleInstancesDirty &&\n this.instancesChanged.size === 0\n ) {\n // NOTE(amadeus): Is this a safe assumption/optimization?\n return;\n }\n\n // If we got an emitted update from a bunch of instances, we should skip\n // the window check first and attempt to render with existing logic first\n // and then queue up a corrected render after\n if (this.instancesChanged.size === 0) {\n const windowSpecs = createWindowFromScrollPosition({\n scrollTop: this.getScrollTop(),\n height: this.getHeight(),\n scrollHeight: this.getScrollHeight(),\n fitPerfectly: false,\n overscrollSize: this.config.overscrollSize,\n });\n if (\n areVirtualWindowSpecsEqual(this.windowSpecs, windowSpecs) &&\n this.renderedObservers === this.observers.size &&\n !this.visibleInstancesDirty &&\n this.instancesChanged.size === 0\n ) {\n return;\n }\n this.windowSpecs = windowSpecs;\n }\n this.visibleInstancesDirty = false;\n this.renderedObservers = this.observers.size;\n const anchor = this.getScrollAnchor(this.height);\n const updatedInstances = new Set<SubscribedInstance>();\n // NOTE(amadeus): If the wrapper is dirty, we need to force every component\n // to re-render\n for (const instance of wrapperDirty\n ? this.observers.values()\n : this.visibleInstances.values()) {\n if (instance.onRender(wrapperDirty)) {\n updatedInstances.add(instance);\n }\n }\n for (const instance of this.instancesChanged) {\n if (updatedInstances.has(instance)) continue;\n if (instance.onRender(wrapperDirty)) {\n updatedInstances.add(instance);\n }\n }\n\n this.scrollFix(anchor);\n // Scroll fix may have marked the dom as dirty, but if there instance\n // changes, we should definitely mark as dirty\n if (this.instancesChanged.size > 0) {\n this.markDOMDirty();\n }\n\n for (const instance of updatedInstances) {\n instance.reconcileHeights();\n }\n\n if (this.instancesChanged.size > 0 || wrapperDirty) {\n queueRender(this.computeRenderRangeAndEmit);\n }\n updatedInstances.clear();\n this.instancesChanged.clear();\n };\n\n private scrollFix(anchor: ScrollAnchor | undefined) {\n if (anchor == null) {\n return;\n }\n const scrollContainer = this.getScrollContainerElement();\n const { lineIndex, lineOffset, fileElement, fileOffset, fileTypeOffset } =\n anchor;\n if (lineIndex != null && lineOffset != null) {\n const element = fileElement.shadowRoot?.querySelector(\n `[data-line][data-line-index=\"${lineIndex}\"]`\n );\n if (element instanceof HTMLElement) {\n const top = getRelativeBoundingTop(element, scrollContainer);\n if (top !== lineOffset) {\n const scrollOffset = top - lineOffset;\n this.applyScrollFix(scrollOffset);\n }\n return;\n }\n }\n const top = getRelativeBoundingTop(fileElement, scrollContainer);\n if (fileTypeOffset === 'top') {\n if (top !== fileOffset) {\n this.applyScrollFix(top - fileOffset);\n }\n } else {\n const bottom = top + fileElement.getBoundingClientRect().height;\n if (bottom !== fileOffset) {\n this.applyScrollFix(bottom - fileOffset);\n }\n }\n }\n\n private applyScrollFix(scrollOffset: number) {\n if (this.root == null || this.root instanceof Document) {\n window.scrollTo({\n top: window.scrollY + scrollOffset,\n behavior: 'instant',\n });\n } else {\n this.root.scrollTo({\n top: this.root.scrollTop + scrollOffset,\n behavior: 'instant',\n });\n }\n // Because we fixed our scroll positions, it means something resized or\n // moved around, so we should mark everything as dirty so the\n // reconciliation call will get the latest data when figuring calling\n // .getOffsetInScrollContainer\n this.markDOMDirty();\n }\n\n // This function tries to figure out the closest file or line to the viewport\n // top that's visible to use as a relative marker for how to fix scroll\n // position after issuing dom updates\n private getScrollAnchor(viewportHeight: number): ScrollAnchor | undefined {\n const scrollContainer = this.getScrollContainerElement();\n let bestAnchor: ScrollAnchor | undefined;\n\n for (const [fileElement] of this.visibleInstances.entries()) {\n const fileTop = getRelativeBoundingTop(fileElement, scrollContainer);\n const fileBottom = fileTop + fileElement.offsetHeight;\n\n // Determine file offset and type based on position\n // Only use bottom anchor when entire file is above viewport\n let fileOffset: number;\n let fileTypeOffset: 'top' | 'bottom';\n if (fileBottom <= 0) {\n // Entire file is above viewport - use bottom as anchor\n fileOffset = fileBottom;\n fileTypeOffset = 'bottom';\n } else {\n // File is at least partially visible or below - use top\n fileOffset = fileTop;\n fileTypeOffset = 'top';\n }\n\n // Find the best line (first fully visible) within this file\n let bestLineIndex: string | undefined;\n let bestLineOffset: number | undefined;\n\n // Only search for lines if file potentially intersects viewport\n if (fileBottom > 0 && fileTop < viewportHeight) {\n for (const line of fileElement.shadowRoot?.querySelectorAll(\n '[data-line][data-line-index]'\n ) ?? []) {\n if (!(line instanceof HTMLElement)) continue;\n const lineIndex = line.dataset.lineIndex;\n if (lineIndex == null) continue;\n\n const lineOffset = getRelativeBoundingTop(line, scrollContainer);\n\n // Ignore lines with negative offsets (above viewport top)\n if (lineOffset < 0) continue;\n\n // First visible line in DOM order is the best one because\n // querySelectorAll will grab lines in order as they appear in the\n // DOM\n bestLineIndex = lineIndex;\n bestLineOffset = lineOffset;\n break;\n }\n }\n\n // If we already have an anchor with a visible line, skip files without one\n if (bestAnchor?.lineOffset != null && bestLineOffset == null) {\n continue;\n }\n\n // Decide if this file should become the new best anchor\n let shouldReplace = false;\n // If we don't already have an anchor we should set one\n if (bestAnchor == null) {\n shouldReplace = true;\n }\n // If we found a better line anchor, we should replace the old one\n else if (\n bestLineOffset != null &&\n (bestAnchor.lineOffset == null ||\n bestLineOffset < bestAnchor.lineOffset)\n ) {\n shouldReplace = true;\n }\n // Otherwise we need to compare file only anchors\n else if (bestLineOffset == null && bestAnchor.lineOffset == null) {\n // Favor files with their tops in view\n if (\n fileOffset >= 0 &&\n (bestAnchor.fileOffset < 0 || fileOffset < bestAnchor.fileOffset)\n ) {\n shouldReplace = true;\n }\n // Or the closest file\n else if (\n fileOffset < 0 &&\n bestAnchor.fileOffset < 0 &&\n fileOffset > bestAnchor.fileOffset\n ) {\n shouldReplace = true;\n }\n }\n\n if (shouldReplace) {\n bestAnchor = {\n fileElement,\n fileTypeOffset,\n fileOffset,\n lineIndex: bestLineIndex,\n lineOffset: bestLineOffset,\n };\n }\n }\n\n return bestAnchor;\n }\n\n private handleIntersectionChange = (\n entries: IntersectionObserverEntry[]\n ): void => {\n this.scrollDirty = true;\n for (const { target, isIntersecting } of entries) {\n if (!(target instanceof HTMLElement)) {\n throw new Error(\n 'Virtualizer.handleIntersectionChange: target not an HTMLElement'\n );\n }\n const instance = this.observers.get(target);\n if (instance == null) {\n throw new Error(\n 'Virtualizer.handleIntersectionChange: no instance for target'\n );\n }\n if (isIntersecting && !this.visibleInstances.has(target)) {\n instance.setVisibility(true);\n this.visibleInstances.set(target, instance);\n this.visibleInstancesDirty = true;\n } else if (!isIntersecting && this.visibleInstances.has(target)) {\n instance.setVisibility(false);\n this.visibleInstances.delete(target);\n this.visibleInstancesDirty = true;\n }\n }\n\n if (this.visibleInstancesDirty) {\n // Since this call is already debounced, should we just call\n // computeRenderRangeAndEmit directly?\n queueRender(this.computeRenderRangeAndEmit);\n }\n // Debug logging for visible instances\n // console.log(\n // 'handleIntersectionChange',\n // ...Array.from(this.visibleInstances.keys())\n // );\n };\n\n private getScrollTop() {\n if (!this.scrollDirty) {\n return this.scrollTop;\n }\n this.scrollDirty = false;\n let scrollTop = (() => {\n if (this.root == null) {\n return 0;\n }\n if (this.root instanceof Document) {\n return window.scrollY;\n }\n return this.root.scrollTop;\n })();\n\n // Lets always make sure to clamp scroll position cases of\n // over/bounce scroll\n scrollTop = Math.max(\n 0,\n Math.min(scrollTop, this.getScrollHeight() - this.getHeight())\n );\n this.scrollTop = scrollTop;\n return scrollTop;\n }\n\n private getScrollHeight() {\n if (!this.scrollHeightDirty) {\n return this.scrollHeight;\n }\n this.scrollHeightDirty = false;\n this.scrollHeight = (() => {\n if (this.root == null) {\n return 0;\n }\n if (this.root instanceof Document) {\n return this.root.documentElement.scrollHeight;\n }\n return this.root.scrollHeight;\n })();\n return this.scrollHeight;\n }\n\n private getHeight() {\n if (!this.heightDirty) {\n return this.height;\n }\n this.heightDirty = false;\n this.height = (() => {\n if (this.root == null) {\n return 0;\n }\n if (this.root instanceof Document) {\n return globalThis.innerHeight;\n }\n return this.root.getBoundingClientRect().height;\n })();\n return this.height;\n }\n\n private markDOMDirty() {\n this.scrollDirty = true;\n this.scrollHeightDirty = true;\n this.heightDirty = true;\n }\n\n private getScrollContainerElement(): HTMLElement | undefined {\n return this.root == null || this.root instanceof Document\n ? undefined\n : this.root;\n }\n}\n\n// This function is like a generalized getBoundingClientRect for it's relative\n// scroll container\nfunction getRelativeBoundingTop(\n element: HTMLElement,\n scrollContainer: HTMLElement | undefined\n) {\n const rect = element.getBoundingClientRect();\n const scrollContainerTop = scrollContainer?.getBoundingClientRect().top ?? 0;\n return rect.top - scrollContainerTop;\n}\n"],"mappings":"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"}
@@ -12,6 +12,7 @@ declare const FILENAME_HEADER_REGEX: RegExp;
12
12
  declare const FILENAME_HEADER_REGEX_GIT: RegExp;
13
13
  declare const ALTERNATE_FILE_NAMES_GIT: RegExp;
14
14
  declare const INDEX_LINE_METADATA: RegExp;
15
+ declare const HEADER_PREFIX_SLOT_ID = "header-prefix";
15
16
  declare const HEADER_METADATA_SLOT_ID = "header-metadata";
16
17
  declare const DEFAULT_THEMES: ThemesType;
17
18
  declare const UNSAFE_CSS_ATTRIBUTE = "data-unsafe-css";
@@ -20,6 +21,7 @@ declare const DEFAULT_COLLAPSED_CONTEXT_THRESHOLD = 1;
20
21
  declare const DEFAULT_VIRTUAL_FILE_METRICS: VirtualFileMetrics;
21
22
  declare const DEFAULT_EXPANDED_REGION: HunkExpansionRegion;
22
23
  declare const DEFAULT_RENDER_RANGE: RenderRange;
24
+ declare const EMPTY_RENDER_RANGE: RenderRange;
23
25
  //#endregion
24
- export { ALTERNATE_FILE_NAMES_GIT, COMMIT_METADATA_SPLIT, CORE_CSS_ATTRIBUTE, DEFAULT_COLLAPSED_CONTEXT_THRESHOLD, DEFAULT_EXPANDED_REGION, DEFAULT_RENDER_RANGE, DEFAULT_THEMES, DEFAULT_VIRTUAL_FILE_METRICS, DIFFS_TAG_NAME, FILENAME_HEADER_REGEX, FILENAME_HEADER_REGEX_GIT, FILE_CONTEXT_BLOB, GIT_DIFF_FILE_BREAK_REGEX, HEADER_METADATA_SLOT_ID, HUNK_HEADER, INDEX_LINE_METADATA, SPLIT_WITH_NEWLINES, UNIFIED_DIFF_FILE_BREAK_REGEX, UNSAFE_CSS_ATTRIBUTE };
26
+ export { ALTERNATE_FILE_NAMES_GIT, COMMIT_METADATA_SPLIT, CORE_CSS_ATTRIBUTE, DEFAULT_COLLAPSED_CONTEXT_THRESHOLD, DEFAULT_EXPANDED_REGION, DEFAULT_RENDER_RANGE, DEFAULT_THEMES, DEFAULT_VIRTUAL_FILE_METRICS, DIFFS_TAG_NAME, EMPTY_RENDER_RANGE, FILENAME_HEADER_REGEX, FILENAME_HEADER_REGEX_GIT, FILE_CONTEXT_BLOB, GIT_DIFF_FILE_BREAK_REGEX, HEADER_METADATA_SLOT_ID, HEADER_PREFIX_SLOT_ID, HUNK_HEADER, INDEX_LINE_METADATA, SPLIT_WITH_NEWLINES, UNIFIED_DIFF_FILE_BREAK_REGEX, UNSAFE_CSS_ATTRIBUTE };
25
27
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","names":["HunkExpansionRegion","RenderRange","ThemesType","VirtualFileMetrics","DIFFS_TAG_NAME","COMMIT_METADATA_SPLIT","RegExp","GIT_DIFF_FILE_BREAK_REGEX","UNIFIED_DIFF_FILE_BREAK_REGEX","FILE_CONTEXT_BLOB","HUNK_HEADER","SPLIT_WITH_NEWLINES","FILENAME_HEADER_REGEX","FILENAME_HEADER_REGEX_GIT","ALTERNATE_FILE_NAMES_GIT","INDEX_LINE_METADATA","HEADER_METADATA_SLOT_ID","DEFAULT_THEMES","UNSAFE_CSS_ATTRIBUTE","CORE_CSS_ATTRIBUTE","DEFAULT_COLLAPSED_CONTEXT_THRESHOLD","DEFAULT_VIRTUAL_FILE_METRICS","DEFAULT_EXPANDED_REGION","DEFAULT_RENDER_RANGE"],"sources":["../src/constants.d.ts"],"sourcesContent":["import type { HunkExpansionRegion, RenderRange, ThemesType, VirtualFileMetrics } from './types';\nexport declare const DIFFS_TAG_NAME: \"diffs-container\";\nexport declare const COMMIT_METADATA_SPLIT: RegExp;\nexport declare const GIT_DIFF_FILE_BREAK_REGEX: RegExp;\nexport declare const UNIFIED_DIFF_FILE_BREAK_REGEX: RegExp;\nexport declare const FILE_CONTEXT_BLOB: RegExp;\nexport declare const HUNK_HEADER: RegExp;\nexport declare const SPLIT_WITH_NEWLINES: RegExp;\nexport declare const FILENAME_HEADER_REGEX: RegExp;\nexport declare const FILENAME_HEADER_REGEX_GIT: RegExp;\nexport declare const ALTERNATE_FILE_NAMES_GIT: RegExp;\nexport declare const INDEX_LINE_METADATA: RegExp;\nexport declare const HEADER_METADATA_SLOT_ID = \"header-metadata\";\nexport declare const DEFAULT_THEMES: ThemesType;\nexport declare const UNSAFE_CSS_ATTRIBUTE = \"data-unsafe-css\";\nexport declare const CORE_CSS_ATTRIBUTE = \"data-core-css\";\nexport declare const DEFAULT_COLLAPSED_CONTEXT_THRESHOLD = 1;\nexport declare const DEFAULT_VIRTUAL_FILE_METRICS: VirtualFileMetrics;\nexport declare const DEFAULT_EXPANDED_REGION: HunkExpansionRegion;\nexport declare const DEFAULT_RENDER_RANGE: RenderRange;\n//# sourceMappingURL=constants.d.ts.map"],"mappings":";;;cACqBI;cACAC,uBAAuBC;AADvBF,cAEAG,yBAFiC,EAEND,MAFM;AACjCD,cAEAG,6BAF6B,EAEEF,MAFF;AAC7BC,cAEAE,iBAFiC,EAEdH,MAFQA;AAC3BE,cAEAE,WAFAF,EAEaF,MAFwB;AACrCG,cAEAE,mBAFmBL,EAEEA,MAFI;AACzBI,cAEAE,qBAFmB,EAEIN,MAFJ;AACnBK,cAEAE,yBAF2B,EAEAP,MAFA;AAC3BM,cAEAE,wBAFuBR,EAEGA,MAFG;AAC7BO,cAEAE,mBAFiC,EAEZT,MAFMA;AAC3BQ,cAEAE,uBAAAA,GAF0BV,iBAAM;AAChCS,cAEAE,cAF2B,EAEXf,UAFW;AAC3Bc,cAEAE,oBAAAA,GAFuB,iBAAA;AACvBD,cAEAE,kBAAAA,GAFgBjB,eAAU;AAC1BgB,cAEAE,mCAAAA,GAFoB,CAAA;AACpBD,cAEAE,4BAFkB,EAEYlB,kBAFZ;AAClBiB,cAEAE,uBAFmC,EAEVtB,mBAFU;AACnCqB,cAEAE,oBAFgD,EAE1BtB,WAFQE"}
1
+ {"version":3,"file":"constants.d.ts","names":["HunkExpansionRegion","RenderRange","ThemesType","VirtualFileMetrics","DIFFS_TAG_NAME","COMMIT_METADATA_SPLIT","RegExp","GIT_DIFF_FILE_BREAK_REGEX","UNIFIED_DIFF_FILE_BREAK_REGEX","FILE_CONTEXT_BLOB","HUNK_HEADER","SPLIT_WITH_NEWLINES","FILENAME_HEADER_REGEX","FILENAME_HEADER_REGEX_GIT","ALTERNATE_FILE_NAMES_GIT","INDEX_LINE_METADATA","HEADER_PREFIX_SLOT_ID","HEADER_METADATA_SLOT_ID","DEFAULT_THEMES","UNSAFE_CSS_ATTRIBUTE","CORE_CSS_ATTRIBUTE","DEFAULT_COLLAPSED_CONTEXT_THRESHOLD","DEFAULT_VIRTUAL_FILE_METRICS","DEFAULT_EXPANDED_REGION","DEFAULT_RENDER_RANGE","EMPTY_RENDER_RANGE"],"sources":["../src/constants.d.ts"],"sourcesContent":["import type { HunkExpansionRegion, RenderRange, ThemesType, VirtualFileMetrics } from './types';\nexport declare const DIFFS_TAG_NAME: \"diffs-container\";\nexport declare const COMMIT_METADATA_SPLIT: RegExp;\nexport declare const GIT_DIFF_FILE_BREAK_REGEX: RegExp;\nexport declare const UNIFIED_DIFF_FILE_BREAK_REGEX: RegExp;\nexport declare const FILE_CONTEXT_BLOB: RegExp;\nexport declare const HUNK_HEADER: RegExp;\nexport declare const SPLIT_WITH_NEWLINES: RegExp;\nexport declare const FILENAME_HEADER_REGEX: RegExp;\nexport declare const FILENAME_HEADER_REGEX_GIT: RegExp;\nexport declare const ALTERNATE_FILE_NAMES_GIT: RegExp;\nexport declare const INDEX_LINE_METADATA: RegExp;\nexport declare const HEADER_PREFIX_SLOT_ID = \"header-prefix\";\nexport declare const HEADER_METADATA_SLOT_ID = \"header-metadata\";\nexport declare const DEFAULT_THEMES: ThemesType;\nexport declare const UNSAFE_CSS_ATTRIBUTE = \"data-unsafe-css\";\nexport declare const CORE_CSS_ATTRIBUTE = \"data-core-css\";\nexport declare const DEFAULT_COLLAPSED_CONTEXT_THRESHOLD = 1;\nexport declare const DEFAULT_VIRTUAL_FILE_METRICS: VirtualFileMetrics;\nexport declare const DEFAULT_EXPANDED_REGION: HunkExpansionRegion;\nexport declare const DEFAULT_RENDER_RANGE: RenderRange;\nexport declare const EMPTY_RENDER_RANGE: RenderRange;\n//# sourceMappingURL=constants.d.ts.map"],"mappings":";;;cACqBI;cACAC,uBAAuBC;AADvBF,cAEAG,yBAFiC,EAEND,MAFM;AACjCD,cAEAG,6BAF6B,EAEEF,MAFF;AAC7BC,cAEAE,iBAFiC,EAEdH,MAFQA;AAC3BE,cAEAE,WAFAF,EAEaF,MAFwB;AACrCG,cAEAE,mBAFmBL,EAEEA,MAFI;AACzBI,cAEAE,qBAFmB,EAEIN,MAFJ;AACnBK,cAEAE,yBAF2B,EAEAP,MAFA;AAC3BM,cAEAE,wBAFuBR,EAEGA,MAFG;AAC7BO,cAEAE,mBAFiC,EAEZT,MAFMA;AAC3BQ,cAEAE,qBAAAA,GAF0BV,eAAM;AAChCS,cAEAE,uBAAAA,GAF2B,iBAAA;AAC3BD,cAEAE,cAFqB,EAELhB,UAFK;AACrBe,cAEAE,oBAAAA,GAFuB,iBAAA;AACvBD,cAEAE,kBAAAA,GAFgBlB,eAAU;AAC1BiB,cAEAE,mCAAAA,GAFoB,CAAA;AACpBD,cAEAE,4BAFkB,EAEYnB,kBAFZ;AAClBkB,cAEAE,uBAFmC,EAEVvB,mBAFU;AACnCsB,cAEAE,oBAFgD,EAE1BvB,WAFQE;AAC9BoB,cAEAE,kBAF4C,EAExBxB,WAFKD"}
package/dist/constants.js CHANGED
@@ -10,6 +10,7 @@ const FILENAME_HEADER_REGEX = /^(---|\+\+\+)\s+([^\t\r\n]+)/;
10
10
  const FILENAME_HEADER_REGEX_GIT = /^(---|\+\+\+)\s+[ab]\/([^\t\r\n]+)/;
11
11
  const ALTERNATE_FILE_NAMES_GIT = /^diff --git (?:"a\/(.+?)"|a\/(.+?)) (?:"b\/(.+?)"|b\/(.+?))$/;
12
12
  const INDEX_LINE_METADATA = /^index ([0-9a-f]+)\.\.([0-9a-f]+)(?: (\d+))?$/i;
13
+ const HEADER_PREFIX_SLOT_ID = "header-prefix";
13
14
  const HEADER_METADATA_SLOT_ID = "header-metadata";
14
15
  const DEFAULT_THEMES = {
15
16
  dark: "pierre-dark",
@@ -35,7 +36,13 @@ const DEFAULT_RENDER_RANGE = {
35
36
  bufferBefore: 0,
36
37
  bufferAfter: 0
37
38
  };
39
+ const EMPTY_RENDER_RANGE = {
40
+ startingLine: 0,
41
+ totalLines: 0,
42
+ bufferBefore: 0,
43
+ bufferAfter: 0
44
+ };
38
45
 
39
46
  //#endregion
40
- export { ALTERNATE_FILE_NAMES_GIT, COMMIT_METADATA_SPLIT, CORE_CSS_ATTRIBUTE, DEFAULT_COLLAPSED_CONTEXT_THRESHOLD, DEFAULT_EXPANDED_REGION, DEFAULT_RENDER_RANGE, DEFAULT_THEMES, DEFAULT_VIRTUAL_FILE_METRICS, DIFFS_TAG_NAME, FILENAME_HEADER_REGEX, FILENAME_HEADER_REGEX_GIT, FILE_CONTEXT_BLOB, GIT_DIFF_FILE_BREAK_REGEX, HEADER_METADATA_SLOT_ID, HUNK_HEADER, INDEX_LINE_METADATA, SPLIT_WITH_NEWLINES, UNIFIED_DIFF_FILE_BREAK_REGEX, UNSAFE_CSS_ATTRIBUTE };
47
+ export { ALTERNATE_FILE_NAMES_GIT, COMMIT_METADATA_SPLIT, CORE_CSS_ATTRIBUTE, DEFAULT_COLLAPSED_CONTEXT_THRESHOLD, DEFAULT_EXPANDED_REGION, DEFAULT_RENDER_RANGE, DEFAULT_THEMES, DEFAULT_VIRTUAL_FILE_METRICS, DIFFS_TAG_NAME, EMPTY_RENDER_RANGE, FILENAME_HEADER_REGEX, FILENAME_HEADER_REGEX_GIT, FILE_CONTEXT_BLOB, GIT_DIFF_FILE_BREAK_REGEX, HEADER_METADATA_SLOT_ID, HEADER_PREFIX_SLOT_ID, HUNK_HEADER, INDEX_LINE_METADATA, SPLIT_WITH_NEWLINES, UNIFIED_DIFF_FILE_BREAK_REGEX, UNSAFE_CSS_ATTRIBUTE };
41
48
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","names":["COMMIT_METADATA_SPLIT: RegExp","GIT_DIFF_FILE_BREAK_REGEX: RegExp","UNIFIED_DIFF_FILE_BREAK_REGEX: RegExp","FILE_CONTEXT_BLOB: RegExp","HUNK_HEADER: RegExp","SPLIT_WITH_NEWLINES: RegExp","FILENAME_HEADER_REGEX: RegExp","FILENAME_HEADER_REGEX_GIT: RegExp","ALTERNATE_FILE_NAMES_GIT: RegExp","INDEX_LINE_METADATA: RegExp","DEFAULT_THEMES: ThemesType","DEFAULT_VIRTUAL_FILE_METRICS: VirtualFileMetrics","DEFAULT_EXPANDED_REGION: HunkExpansionRegion","DEFAULT_RENDER_RANGE: RenderRange"],"sources":["../src/constants.ts"],"sourcesContent":["import type {\n HunkExpansionRegion,\n RenderRange,\n ThemesType,\n VirtualFileMetrics,\n} from './types';\n\nexport const DIFFS_TAG_NAME = 'diffs-container' as const;\n\n// Misc patch/content parsing regexes\nexport const COMMIT_METADATA_SPLIT: RegExp = /(?=^From [a-f0-9]+ .+$)/m;\nexport const GIT_DIFF_FILE_BREAK_REGEX: RegExp = /(?=^diff --git)/gm;\nexport const UNIFIED_DIFF_FILE_BREAK_REGEX: RegExp = /(?=^---\\s+\\S)/gm;\nexport const FILE_CONTEXT_BLOB: RegExp = /(?=^@@ )/gm;\nexport const HUNK_HEADER: RegExp =\n /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@(?: (.*))?/m;\nexport const SPLIT_WITH_NEWLINES: RegExp = /(?<=\\n)/;\nexport const FILENAME_HEADER_REGEX: RegExp = /^(---|\\+\\+\\+)\\s+([^\\t\\r\\n]+)/;\nexport const FILENAME_HEADER_REGEX_GIT: RegExp =\n /^(---|\\+\\+\\+)\\s+[ab]\\/([^\\t\\r\\n]+)/;\nexport const ALTERNATE_FILE_NAMES_GIT: RegExp =\n /^diff --git (?:\"a\\/(.+?)\"|a\\/(.+?)) (?:\"b\\/(.+?)\"|b\\/(.+?))$/;\nexport const INDEX_LINE_METADATA: RegExp =\n /^index ([0-9a-f]+)\\.\\.([0-9a-f]+)(?: (\\d+))?$/i;\n\nexport const HEADER_METADATA_SLOT_ID = 'header-metadata';\n\nexport const DEFAULT_THEMES: ThemesType = {\n dark: 'pierre-dark',\n light: 'pierre-light',\n};\n\nexport const UNSAFE_CSS_ATTRIBUTE = 'data-unsafe-css';\nexport const CORE_CSS_ATTRIBUTE = 'data-core-css';\n\nexport const DEFAULT_COLLAPSED_CONTEXT_THRESHOLD = 1;\nexport const DEFAULT_VIRTUAL_FILE_METRICS: VirtualFileMetrics = {\n hunkLineCount: 50,\n lineHeight: 20,\n diffHeaderHeight: 44,\n hunkSeparatorHeight: 32,\n fileGap: 8,\n};\n\nexport const DEFAULT_EXPANDED_REGION: HunkExpansionRegion = Object.freeze({\n fromStart: 0,\n fromEnd: 0,\n});\n\nexport const DEFAULT_RENDER_RANGE: RenderRange = {\n startingLine: 0,\n totalLines: Infinity,\n bufferBefore: 0,\n bufferAfter: 0,\n};\n"],"mappings":";AAOA,MAAa,iBAAiB;AAG9B,MAAaA,wBAAgC;AAC7C,MAAaC,4BAAoC;AACjD,MAAaC,gCAAwC;AACrD,MAAaC,oBAA4B;AACzC,MAAaC,cACX;AACF,MAAaC,sBAA8B;AAC3C,MAAaC,wBAAgC;AAC7C,MAAaC,4BACX;AACF,MAAaC,2BACX;AACF,MAAaC,sBACX;AAEF,MAAa,0BAA0B;AAEvC,MAAaC,iBAA6B;CACxC,MAAM;CACN,OAAO;CACR;AAED,MAAa,uBAAuB;AACpC,MAAa,qBAAqB;AAElC,MAAa,sCAAsC;AACnD,MAAaC,+BAAmD;CAC9D,eAAe;CACf,YAAY;CACZ,kBAAkB;CAClB,qBAAqB;CACrB,SAAS;CACV;AAED,MAAaC,0BAA+C,OAAO,OAAO;CACxE,WAAW;CACX,SAAS;CACV,CAAC;AAEF,MAAaC,uBAAoC;CAC/C,cAAc;CACd,YAAY;CACZ,cAAc;CACd,aAAa;CACd"}
1
+ {"version":3,"file":"constants.js","names":["COMMIT_METADATA_SPLIT: RegExp","GIT_DIFF_FILE_BREAK_REGEX: RegExp","UNIFIED_DIFF_FILE_BREAK_REGEX: RegExp","FILE_CONTEXT_BLOB: RegExp","HUNK_HEADER: RegExp","SPLIT_WITH_NEWLINES: RegExp","FILENAME_HEADER_REGEX: RegExp","FILENAME_HEADER_REGEX_GIT: RegExp","ALTERNATE_FILE_NAMES_GIT: RegExp","INDEX_LINE_METADATA: RegExp","DEFAULT_THEMES: ThemesType","DEFAULT_VIRTUAL_FILE_METRICS: VirtualFileMetrics","DEFAULT_EXPANDED_REGION: HunkExpansionRegion","DEFAULT_RENDER_RANGE: RenderRange","EMPTY_RENDER_RANGE: RenderRange"],"sources":["../src/constants.ts"],"sourcesContent":["import type {\n HunkExpansionRegion,\n RenderRange,\n ThemesType,\n VirtualFileMetrics,\n} from './types';\n\nexport const DIFFS_TAG_NAME = 'diffs-container' as const;\n\n// Misc patch/content parsing regexes\nexport const COMMIT_METADATA_SPLIT: RegExp = /(?=^From [a-f0-9]+ .+$)/m;\nexport const GIT_DIFF_FILE_BREAK_REGEX: RegExp = /(?=^diff --git)/gm;\nexport const UNIFIED_DIFF_FILE_BREAK_REGEX: RegExp = /(?=^---\\s+\\S)/gm;\nexport const FILE_CONTEXT_BLOB: RegExp = /(?=^@@ )/gm;\nexport const HUNK_HEADER: RegExp =\n /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@(?: (.*))?/m;\nexport const SPLIT_WITH_NEWLINES: RegExp = /(?<=\\n)/;\nexport const FILENAME_HEADER_REGEX: RegExp = /^(---|\\+\\+\\+)\\s+([^\\t\\r\\n]+)/;\nexport const FILENAME_HEADER_REGEX_GIT: RegExp =\n /^(---|\\+\\+\\+)\\s+[ab]\\/([^\\t\\r\\n]+)/;\nexport const ALTERNATE_FILE_NAMES_GIT: RegExp =\n /^diff --git (?:\"a\\/(.+?)\"|a\\/(.+?)) (?:\"b\\/(.+?)\"|b\\/(.+?))$/;\nexport const INDEX_LINE_METADATA: RegExp =\n /^index ([0-9a-f]+)\\.\\.([0-9a-f]+)(?: (\\d+))?$/i;\n\nexport const HEADER_PREFIX_SLOT_ID = 'header-prefix';\nexport const HEADER_METADATA_SLOT_ID = 'header-metadata';\n\nexport const DEFAULT_THEMES: ThemesType = {\n dark: 'pierre-dark',\n light: 'pierre-light',\n};\n\nexport const UNSAFE_CSS_ATTRIBUTE = 'data-unsafe-css';\nexport const CORE_CSS_ATTRIBUTE = 'data-core-css';\n\nexport const DEFAULT_COLLAPSED_CONTEXT_THRESHOLD = 1;\nexport const DEFAULT_VIRTUAL_FILE_METRICS: VirtualFileMetrics = {\n hunkLineCount: 50,\n lineHeight: 20,\n diffHeaderHeight: 44,\n hunkSeparatorHeight: 32,\n fileGap: 8,\n};\n\nexport const DEFAULT_EXPANDED_REGION: HunkExpansionRegion = Object.freeze({\n fromStart: 0,\n fromEnd: 0,\n});\n\nexport const DEFAULT_RENDER_RANGE: RenderRange = {\n startingLine: 0,\n totalLines: Infinity,\n bufferBefore: 0,\n bufferAfter: 0,\n};\n\nexport const EMPTY_RENDER_RANGE: RenderRange = {\n startingLine: 0,\n totalLines: 0,\n bufferBefore: 0,\n bufferAfter: 0,\n};\n"],"mappings":";AAOA,MAAa,iBAAiB;AAG9B,MAAaA,wBAAgC;AAC7C,MAAaC,4BAAoC;AACjD,MAAaC,gCAAwC;AACrD,MAAaC,oBAA4B;AACzC,MAAaC,cACX;AACF,MAAaC,sBAA8B;AAC3C,MAAaC,wBAAgC;AAC7C,MAAaC,4BACX;AACF,MAAaC,2BACX;AACF,MAAaC,sBACX;AAEF,MAAa,wBAAwB;AACrC,MAAa,0BAA0B;AAEvC,MAAaC,iBAA6B;CACxC,MAAM;CACN,OAAO;CACR;AAED,MAAa,uBAAuB;AACpC,MAAa,qBAAqB;AAElC,MAAa,sCAAsC;AACnD,MAAaC,+BAAmD;CAC9D,eAAe;CACf,YAAY;CACZ,kBAAkB;CAClB,qBAAqB;CACrB,SAAS;CACV;AAED,MAAaC,0BAA+C,OAAO,OAAO;CACxE,WAAW;CACX,SAAS;CACV,CAAC;AAEF,MAAaC,uBAAoC;CAC/C,cAAc;CACd,YAAY;CACZ,cAAc;CACd,aAAa;CACd;AAED,MAAaC,qBAAkC;CAC7C,cAAc;CACd,YAAY;CACZ,cAAc;CACd,aAAa;CACd"}
@@ -1,14 +1,16 @@
1
- import { DiffsHighlighter, DiffsThemeNames, SupportedLanguages } from "../types.js";
1
+ import { DiffsHighlighter, DiffsThemeNames, HighlighterTypes, SupportedLanguages } from "../types.js";
2
2
 
3
3
  //#region src/highlighter/shared_highlighter.d.ts
4
4
  type CachedOrLoadingHighlighterType = Promise<DiffsHighlighter> | DiffsHighlighter | undefined;
5
5
  interface HighlighterOptions {
6
6
  themes: DiffsThemeNames[];
7
7
  langs: SupportedLanguages[];
8
+ preferredHighlighter?: HighlighterTypes;
8
9
  }
9
10
  declare function getSharedHighlighter({
10
11
  themes,
11
- langs
12
+ langs,
13
+ preferredHighlighter
12
14
  }: HighlighterOptions): Promise<DiffsHighlighter>;
13
15
  declare function isHighlighterLoaded(h?: CachedOrLoadingHighlighterType): h is DiffsHighlighter;
14
16
  declare function getHighlighterIfLoaded(): DiffsHighlighter | undefined;