stream-monaco 0.0.13 → 0.0.15

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.
package/README.md CHANGED
@@ -80,6 +80,13 @@ The `useMonaco()` function returns an object with the following methods:
80
80
  - **`setUpdateThrottleMs(ms)`** - Change update throttle at runtime
81
81
  - **`getUpdateThrottleMs()`** - Get current throttle value
82
82
 
83
+ #### Diff streaming highlight tip
84
+
85
+ Monaco's diff computation is async and cancels/restarts when models change. If you stream updates too frequently (e.g. per token / every frame), the diff may only finish once streaming stops, so the difference highlights appear "at the end".
86
+
87
+ - Set `diffUpdateThrottleMs` (default: 50) to let the diff worker complete intermediate computations during streaming.
88
+ - Set it to `0` to restore pure RAF batching (most responsive, but may delay diff highlights under heavy streaming).
89
+
83
90
  ### Install
84
91
 
85
92
  ```bash
@@ -473,6 +480,7 @@ cleanupEditor()
473
480
  ### Troubleshooting
474
481
 
475
482
  - Editor invisible after build: configure Monaco web workers correctly.
483
+ - Diff editor renders blank during early mount/streaming: ensure Monaco workers are configured before `createEditor`/`createDiffEditor` (e.g. call `preloadMonacoWorkers()` as early as possible).
476
484
  - Theme not applied: ensure theme name is included in `themes`.
477
485
  - Language highlighting missing: ensure the language is included and supported by Shiki.
478
486
 
package/README.zh-CN.md CHANGED
@@ -720,11 +720,15 @@ onUnmounted(() => {
720
720
 
721
721
  确保正确配置了 Monaco Editor 的 Web Workers(参考上面的 Vite/Webpack 配置)。
722
722
 
723
- #### 2. 主题不生效
723
+ #### 2. Diff 编辑器流式更新时内容区空白
724
+
725
+ 确保在调用 `createEditor` / `createDiffEditor` 之前已正确配置 Monaco 的 workers(建议尽早调用 `preloadMonacoWorkers()`)。
726
+
727
+ #### 3. 主题不生效
724
728
 
725
729
  检查主题名称是否正确,确保主题已在 `themes` 数组中注册。
726
730
 
727
- #### 3. 语言高亮不工作
731
+ #### 4. 语言高亮不工作
728
732
 
729
733
  确保语言已在 `languages` 数组中包含,并且 Shiki 支持该语言。
730
734
 
package/dist/index.cjs CHANGED
@@ -516,11 +516,15 @@ var DiffEditorManager = class {
516
516
  revealBatchOnIdleMsOption;
517
517
  scrollWatcherSuppressionMs = 500;
518
518
  diffScrollWatcherSuppressionTimer = null;
519
- appendBufferDiff = [];
519
+ appendBufferOriginalDiff = [];
520
+ appendBufferModifiedDiff = [];
520
521
  appendBufferDiffScheduled = false;
522
+ diffUpdateThrottleMs = 50;
523
+ lastAppendFlushTimeDiff = 0;
524
+ appendFlushThrottleTimerDiff = null;
521
525
  rafScheduler = createRafScheduler();
522
526
  diffHeightManager = null;
523
- constructor(options, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines, diffAutoScroll, revealDebounceMsOption) {
527
+ constructor(options, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines, diffAutoScroll, revealDebounceMsOption, diffUpdateThrottleMsOption) {
524
528
  this.options = options;
525
529
  this.maxHeightValue = maxHeightValue;
526
530
  this.maxHeightCSS = maxHeightCSS;
@@ -531,6 +535,40 @@ var DiffEditorManager = class {
531
535
  this.diffAutoScroll = diffAutoScroll;
532
536
  this.revealDebounceMsOption = revealDebounceMsOption;
533
537
  }
538
+ scheduleFlushAppendBufferDiff() {
539
+ if (this.appendBufferDiffScheduled) return;
540
+ this.appendBufferDiffScheduled = true;
541
+ const schedule = () => {
542
+ this.rafScheduler.schedule("appendDiff", () => this.flushAppendBufferDiff());
543
+ };
544
+ const throttle = this.diffUpdateThrottleMs;
545
+ if (!throttle) {
546
+ schedule();
547
+ return;
548
+ }
549
+ const now = Date.now();
550
+ const since = now - this.lastAppendFlushTimeDiff;
551
+ if (since >= throttle) {
552
+ schedule();
553
+ return;
554
+ }
555
+ if (this.appendFlushThrottleTimerDiff != null) return;
556
+ const wait = throttle - since;
557
+ this.appendFlushThrottleTimerDiff = setTimeout(() => {
558
+ this.appendFlushThrottleTimerDiff = null;
559
+ schedule();
560
+ }, wait);
561
+ }
562
+ flushOriginalAppendBufferSync() {
563
+ if (!this.originalModel) return;
564
+ if (this.appendBufferOriginalDiff.length === 0) return;
565
+ this.rafScheduler.cancel("appendDiff");
566
+ this.appendBufferDiffScheduled = false;
567
+ const text = this.appendBufferOriginalDiff.join("");
568
+ this.appendBufferOriginalDiff.length = 0;
569
+ if (!text) return;
570
+ this.appendToModel(this.originalModel, text);
571
+ }
534
572
  computedHeight() {
535
573
  var _originalEditor$getMo, _modifiedEditor$getMo, _originalEditor$getSc, _modifiedEditor$getSc;
536
574
  if (!this.diffEditorView) return Math.min(1 * 18 + padding, this.maxHeightValue);
@@ -778,6 +816,7 @@ var DiffEditorManager = class {
778
816
  });
779
817
  this.lastKnownOriginalCode = originalCode;
780
818
  this.lastKnownModifiedCode = modifiedCode;
819
+ this.diffUpdateThrottleMs = this.options.diffUpdateThrottleMs ?? 50;
781
820
  this.shouldAutoScrollDiff = !!(this.autoScrollInitial && this.diffAutoScroll);
782
821
  if (this.diffScrollWatcher) {
783
822
  this.diffScrollWatcher.dispose();
@@ -894,16 +933,14 @@ var DiffEditorManager = class {
894
933
  const prevM = this.lastKnownModifiedCode;
895
934
  let didImmediate = false;
896
935
  if (originalCode !== prevO && originalCode.startsWith(prevO)) {
897
- this.appendToModel(this.originalModel, originalCode.slice(prevO.length));
936
+ this.appendOriginal(originalCode.slice(prevO.length));
898
937
  this.lastKnownOriginalCode = originalCode;
899
938
  didImmediate = true;
900
939
  }
901
940
  if (modifiedCode !== prevM && modifiedCode.startsWith(prevM)) {
902
- const prevLine = this.modifiedModel.getLineCount();
903
- this.appendToModel(this.modifiedModel, modifiedCode.slice(prevM.length));
941
+ this.appendModified(modifiedCode.slice(prevM.length));
904
942
  this.lastKnownModifiedCode = modifiedCode;
905
943
  didImmediate = true;
906
- this.maybeScrollDiffToBottom(this.modifiedModel.getLineCount(), prevLine);
907
944
  }
908
945
  if (originalCode !== this.lastKnownOriginalCode || modifiedCode !== this.lastKnownModifiedCode) {
909
946
  this.pendingDiffUpdate = {
@@ -921,8 +958,11 @@ var DiffEditorManager = class {
921
958
  }
922
959
  const prev = this.lastKnownOriginalCode ?? this.originalModel.getValue();
923
960
  if (prev === newCode) return;
924
- if (newCode.startsWith(prev) && prev.length < newCode.length) this.appendToModel(this.originalModel, newCode.slice(prev.length));
925
- else this.applyMinimalEditToModel(this.originalModel, prev, newCode);
961
+ if (newCode.startsWith(prev) && prev.length < newCode.length) this.appendOriginal(newCode.slice(prev.length), codeLanguage);
962
+ else {
963
+ this.flushOriginalAppendBufferSync();
964
+ this.applyMinimalEditToModel(this.originalModel, prev, newCode);
965
+ }
926
966
  this.lastKnownOriginalCode = newCode;
927
967
  }
928
968
  updateModified(newCode, codeLanguage) {
@@ -933,12 +973,12 @@ var DiffEditorManager = class {
933
973
  }
934
974
  const prev = this.lastKnownModifiedCode ?? this.modifiedModel.getValue();
935
975
  if (prev === newCode) return;
936
- const prevLine = this.modifiedModel.getLineCount();
937
- if (newCode.startsWith(prev) && prev.length < newCode.length) {
938
- this.appendToModel(this.modifiedModel, newCode.slice(prev.length));
939
- this.maybeScrollDiffToBottom(this.modifiedModel.getLineCount(), prevLine);
940
- } else {
941
- this.applyMinimalEditToModel(this.modifiedModel, prev, newCode);
976
+ if (newCode.startsWith(prev) && prev.length < newCode.length) this.appendModified(newCode.slice(prev.length), codeLanguage);
977
+ else {
978
+ this.flushModifiedAppendBufferSync();
979
+ const prevAfterFlush = this.modifiedModel.getValue();
980
+ const prevLine = this.modifiedModel.getLineCount();
981
+ this.applyMinimalEditToModel(this.modifiedModel, prevAfterFlush, newCode);
942
982
  const newLine = this.modifiedModel.getLineCount();
943
983
  if (newLine !== prevLine) {
944
984
  const shouldImmediate = this.shouldPerformImmediateRevealDiff();
@@ -972,8 +1012,8 @@ var DiffEditorManager = class {
972
1012
  const lang = processedLanguage(codeLanguage);
973
1013
  if (lang && this.originalModel.getLanguageId() !== lang) monaco_shim_exports.editor.setModelLanguage(this.originalModel, lang);
974
1014
  }
975
- this.appendToModel(this.originalModel, appendText);
976
- this.lastKnownOriginalCode = this.originalModel.getValue();
1015
+ this.appendBufferOriginalDiff.push(appendText);
1016
+ this.scheduleFlushAppendBufferDiff();
977
1017
  }
978
1018
  appendModified(appendText, codeLanguage) {
979
1019
  if (!this.diffEditorView || !this.modifiedModel || !appendText) return;
@@ -981,11 +1021,8 @@ var DiffEditorManager = class {
981
1021
  const lang = processedLanguage(codeLanguage);
982
1022
  if (lang && this.modifiedModel.getLanguageId() !== lang) monaco_shim_exports.editor.setModelLanguage(this.modifiedModel, lang);
983
1023
  }
984
- this.appendBufferDiff.push(appendText);
985
- if (!this.appendBufferDiffScheduled) {
986
- this.appendBufferDiffScheduled = true;
987
- this.rafScheduler.schedule("appendDiff", () => this.flushAppendBufferDiff());
988
- }
1024
+ this.appendBufferModifiedDiff.push(appendText);
1025
+ this.scheduleFlushAppendBufferDiff();
989
1026
  }
990
1027
  setLanguage(language, languages$1) {
991
1028
  if (!languages$1.includes(language)) {
@@ -1009,7 +1046,12 @@ var DiffEditorManager = class {
1009
1046
  this.pendingDiffUpdate = null;
1010
1047
  this.rafScheduler.cancel("appendDiff");
1011
1048
  this.appendBufferDiffScheduled = false;
1012
- this.appendBufferDiff.length = 0;
1049
+ this.appendBufferOriginalDiff.length = 0;
1050
+ this.appendBufferModifiedDiff.length = 0;
1051
+ if (this.appendFlushThrottleTimerDiff != null) {
1052
+ clearTimeout(this.appendFlushThrottleTimerDiff);
1053
+ this.appendFlushThrottleTimerDiff = null;
1054
+ }
1013
1055
  this.rafScheduler.cancel("content-size-change-diff");
1014
1056
  this.rafScheduler.cancel("sync-last-known-modified");
1015
1057
  if (this.diffScrollWatcher) {
@@ -1056,6 +1098,14 @@ var DiffEditorManager = class {
1056
1098
  safeClean() {
1057
1099
  this.rafScheduler.cancel("diff");
1058
1100
  this.pendingDiffUpdate = null;
1101
+ this.rafScheduler.cancel("appendDiff");
1102
+ this.appendBufferDiffScheduled = false;
1103
+ this.appendBufferOriginalDiff.length = 0;
1104
+ this.appendBufferModifiedDiff.length = 0;
1105
+ if (this.appendFlushThrottleTimerDiff != null) {
1106
+ clearTimeout(this.appendFlushThrottleTimerDiff);
1107
+ this.appendFlushThrottleTimerDiff = null;
1108
+ }
1059
1109
  if (this.diffScrollWatcher) {
1060
1110
  this.diffScrollWatcher.dispose();
1061
1111
  this.diffScrollWatcher = null;
@@ -1107,6 +1157,8 @@ var DiffEditorManager = class {
1107
1157
  }
1108
1158
  const { original, modified, lang } = this.pendingDiffUpdate;
1109
1159
  this.pendingDiffUpdate = null;
1160
+ this.flushOriginalAppendBufferSync();
1161
+ this.flushModifiedAppendBufferSync();
1110
1162
  if (lang) {
1111
1163
  const plang = processedLanguage(lang);
1112
1164
  if (plang) {
@@ -1124,14 +1176,7 @@ var DiffEditorManager = class {
1124
1176
  else this.applyMinimalEditToModel(o, prevO, original);
1125
1177
  this.lastKnownOriginalCode = original;
1126
1178
  }
1127
- let prevM = this.lastKnownModifiedCode;
1128
- const buffered = this.appendBufferDiff.length > 0 ? this.appendBufferDiff.join("") : "";
1129
- if (this.appendBufferDiff.length > 0) try {
1130
- prevM = m.getValue() + buffered;
1131
- this.lastKnownModifiedCode = prevM;
1132
- } catch {
1133
- prevM = (this.lastKnownModifiedCode ?? "") + buffered;
1134
- }
1179
+ const prevM = m.getValue();
1135
1180
  const prevMLineCount = m.getLineCount();
1136
1181
  if (prevM !== modified) {
1137
1182
  if (modified.startsWith(prevM) && prevM.length < modified.length) this.appendToModel(m, modified.slice(prevM.length));
@@ -1151,17 +1196,33 @@ var DiffEditorManager = class {
1151
1196
  }
1152
1197
  }
1153
1198
  }
1199
+ flushModifiedAppendBufferSync() {
1200
+ if (!this.modifiedModel) return;
1201
+ if (this.appendBufferModifiedDiff.length === 0) return;
1202
+ this.rafScheduler.cancel("appendDiff");
1203
+ this.appendBufferDiffScheduled = false;
1204
+ const text = this.appendBufferModifiedDiff.join("");
1205
+ this.appendBufferModifiedDiff.length = 0;
1206
+ if (!text) return;
1207
+ this.appendToModel(this.modifiedModel, text);
1208
+ }
1154
1209
  async flushAppendBufferDiff() {
1155
1210
  if (!this.diffEditorView) return;
1156
- if (this.appendBufferDiff.length === 0) return;
1211
+ if (this.appendBufferOriginalDiff.length === 0 && this.appendBufferModifiedDiff.length === 0) return;
1212
+ this.lastAppendFlushTimeDiff = Date.now();
1157
1213
  this.appendBufferDiffScheduled = false;
1214
+ if (this.originalModel && this.appendBufferOriginalDiff.length > 0) {
1215
+ const oText = this.appendBufferOriginalDiff.join("");
1216
+ this.appendBufferOriginalDiff.length = 0;
1217
+ if (oText) this.appendToModel(this.originalModel, oText);
1218
+ }
1158
1219
  const me = this.diffEditorView.getModifiedEditor();
1159
1220
  const model = me.getModel();
1160
1221
  if (!model) {
1161
- this.appendBufferDiff.length = 0;
1222
+ this.appendBufferModifiedDiff.length = 0;
1162
1223
  return;
1163
1224
  }
1164
- let parts = this.appendBufferDiff.splice(0);
1225
+ let parts = this.appendBufferModifiedDiff.splice(0);
1165
1226
  const prevLineInit = model.getLineCount();
1166
1227
  const totalText = parts.join("");
1167
1228
  const totalChars = totalText.length;
@@ -1236,7 +1297,7 @@ var DiffEditorManager = class {
1236
1297
  return;
1237
1298
  }
1238
1299
  const text = totalText;
1239
- this.appendBufferDiff.length = 0;
1300
+ this.appendBufferModifiedDiff.length = 0;
1240
1301
  prevLine = model.getLineCount();
1241
1302
  const lastColumn = model.getLineMaxColumn(prevLine);
1242
1303
  const range = new monaco_shim_exports.Range(prevLine, lastColumn, prevLine, lastColumn);
@@ -1956,15 +2017,17 @@ async function preloadMonacoWorkers() {
1956
2017
  javascript: workerUrlTs
1957
2018
  };
1958
2019
  try {
1959
- await Promise.all(unique.map((u) => fetch(u, {
1960
- method: "GET",
1961
- cache: "force-cache"
1962
- }).catch(() => void 0)));
1963
2020
  self.MonacoEnvironment = { getWorker(_, label) {
1964
2021
  const url = workerUrlByLabel[label] ?? workerUrlEditor;
1965
2022
  return new Worker(url, { type: "module" });
1966
2023
  } };
1967
2024
  } catch {}
2025
+ try {
2026
+ await Promise.all(unique.map((u) => fetch(u, {
2027
+ method: "GET",
2028
+ cache: "force-cache"
2029
+ }).catch(() => void 0)));
2030
+ } catch {}
1968
2031
  }
1969
2032
 
1970
2033
  //#endregion
@@ -2444,7 +2507,7 @@ function useMonaco(monacoOptions = {}) {
2444
2507
  try {
2445
2508
  monaco_shim_exports.editor.setTheme(initialThemeName);
2446
2509
  } catch {}
2447
- diffMgr = new DiffEditorManager(monacoOptions, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines, diffAutoScroll, monacoOptions.revealDebounceMs);
2510
+ diffMgr = new DiffEditorManager(monacoOptions, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines, diffAutoScroll, monacoOptions.revealDebounceMs, monacoOptions.diffUpdateThrottleMs);
2448
2511
  diffEditorView = await diffMgr.createDiffEditor(container, originalCode, modifiedCode, language, initialThemeName);
2449
2512
  if (typeof monacoOptions.onThemeChange === "function") monacoOptions.onThemeChange(initialThemeName);
2450
2513
  const models = diffMgr.getDiffModels();
package/dist/index.d.cts CHANGED
@@ -69,6 +69,18 @@ interface MonacoOptions extends monaco$1.editor.IStandaloneEditorConstructionOpt
69
69
  * - Default (library): 50
70
70
  */
71
71
  updateThrottleMs?: number;
72
+ /**
73
+ * Time window (ms) used to throttle diff streaming updates in addition to RAF batching.
74
+ * This affects `appendOriginal`/`appendModified` and the fast-path append branches of `updateDiff`.
75
+ *
76
+ * Why: Monaco's diff computation is async and cancels/restarts when models change.
77
+ * If you apply edits every frame (or per token), the diff may only finish once
78
+ * streaming stops, so the highlights appear "at the end".
79
+ *
80
+ * - 0 means only RAF-based coalescing (more responsive, but can starve diff computation).
81
+ * - Default (library): 50
82
+ */
83
+ diffUpdateThrottleMs?: number;
72
84
  /**
73
85
  * When attempting the "minimal edit" algorithm, if prev.length + next.length
74
86
  * exceeds this number the library will fall back to full `setValue` to avoid
package/dist/index.d.ts CHANGED
@@ -68,6 +68,18 @@ interface MonacoOptions extends monaco$1.editor.IStandaloneEditorConstructionOpt
68
68
  * - Default (library): 50
69
69
  */
70
70
  updateThrottleMs?: number;
71
+ /**
72
+ * Time window (ms) used to throttle diff streaming updates in addition to RAF batching.
73
+ * This affects `appendOriginal`/`appendModified` and the fast-path append branches of `updateDiff`.
74
+ *
75
+ * Why: Monaco's diff computation is async and cancels/restarts when models change.
76
+ * If you apply edits every frame (or per token), the diff may only finish once
77
+ * streaming stops, so the highlights appear "at the end".
78
+ *
79
+ * - 0 means only RAF-based coalescing (more responsive, but can starve diff computation).
80
+ * - Default (library): 50
81
+ */
82
+ diffUpdateThrottleMs?: number;
71
83
  /**
72
84
  * When attempting the "minimal edit" algorithm, if prev.length + next.length
73
85
  * exceeds this number the library will fall back to full `setValue` to avoid
package/dist/index.js CHANGED
@@ -488,11 +488,15 @@ var DiffEditorManager = class {
488
488
  revealBatchOnIdleMsOption;
489
489
  scrollWatcherSuppressionMs = 500;
490
490
  diffScrollWatcherSuppressionTimer = null;
491
- appendBufferDiff = [];
491
+ appendBufferOriginalDiff = [];
492
+ appendBufferModifiedDiff = [];
492
493
  appendBufferDiffScheduled = false;
494
+ diffUpdateThrottleMs = 50;
495
+ lastAppendFlushTimeDiff = 0;
496
+ appendFlushThrottleTimerDiff = null;
493
497
  rafScheduler = createRafScheduler();
494
498
  diffHeightManager = null;
495
- constructor(options, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines, diffAutoScroll, revealDebounceMsOption) {
499
+ constructor(options, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines, diffAutoScroll, revealDebounceMsOption, diffUpdateThrottleMsOption) {
496
500
  this.options = options;
497
501
  this.maxHeightValue = maxHeightValue;
498
502
  this.maxHeightCSS = maxHeightCSS;
@@ -503,6 +507,40 @@ var DiffEditorManager = class {
503
507
  this.diffAutoScroll = diffAutoScroll;
504
508
  this.revealDebounceMsOption = revealDebounceMsOption;
505
509
  }
510
+ scheduleFlushAppendBufferDiff() {
511
+ if (this.appendBufferDiffScheduled) return;
512
+ this.appendBufferDiffScheduled = true;
513
+ const schedule = () => {
514
+ this.rafScheduler.schedule("appendDiff", () => this.flushAppendBufferDiff());
515
+ };
516
+ const throttle = this.diffUpdateThrottleMs;
517
+ if (!throttle) {
518
+ schedule();
519
+ return;
520
+ }
521
+ const now = Date.now();
522
+ const since = now - this.lastAppendFlushTimeDiff;
523
+ if (since >= throttle) {
524
+ schedule();
525
+ return;
526
+ }
527
+ if (this.appendFlushThrottleTimerDiff != null) return;
528
+ const wait = throttle - since;
529
+ this.appendFlushThrottleTimerDiff = setTimeout(() => {
530
+ this.appendFlushThrottleTimerDiff = null;
531
+ schedule();
532
+ }, wait);
533
+ }
534
+ flushOriginalAppendBufferSync() {
535
+ if (!this.originalModel) return;
536
+ if (this.appendBufferOriginalDiff.length === 0) return;
537
+ this.rafScheduler.cancel("appendDiff");
538
+ this.appendBufferDiffScheduled = false;
539
+ const text = this.appendBufferOriginalDiff.join("");
540
+ this.appendBufferOriginalDiff.length = 0;
541
+ if (!text) return;
542
+ this.appendToModel(this.originalModel, text);
543
+ }
506
544
  computedHeight() {
507
545
  var _originalEditor$getMo, _modifiedEditor$getMo, _originalEditor$getSc, _modifiedEditor$getSc;
508
546
  if (!this.diffEditorView) return Math.min(1 * 18 + padding, this.maxHeightValue);
@@ -750,6 +788,7 @@ var DiffEditorManager = class {
750
788
  });
751
789
  this.lastKnownOriginalCode = originalCode;
752
790
  this.lastKnownModifiedCode = modifiedCode;
791
+ this.diffUpdateThrottleMs = this.options.diffUpdateThrottleMs ?? 50;
753
792
  this.shouldAutoScrollDiff = !!(this.autoScrollInitial && this.diffAutoScroll);
754
793
  if (this.diffScrollWatcher) {
755
794
  this.diffScrollWatcher.dispose();
@@ -866,16 +905,14 @@ var DiffEditorManager = class {
866
905
  const prevM = this.lastKnownModifiedCode;
867
906
  let didImmediate = false;
868
907
  if (originalCode !== prevO && originalCode.startsWith(prevO)) {
869
- this.appendToModel(this.originalModel, originalCode.slice(prevO.length));
908
+ this.appendOriginal(originalCode.slice(prevO.length));
870
909
  this.lastKnownOriginalCode = originalCode;
871
910
  didImmediate = true;
872
911
  }
873
912
  if (modifiedCode !== prevM && modifiedCode.startsWith(prevM)) {
874
- const prevLine = this.modifiedModel.getLineCount();
875
- this.appendToModel(this.modifiedModel, modifiedCode.slice(prevM.length));
913
+ this.appendModified(modifiedCode.slice(prevM.length));
876
914
  this.lastKnownModifiedCode = modifiedCode;
877
915
  didImmediate = true;
878
- this.maybeScrollDiffToBottom(this.modifiedModel.getLineCount(), prevLine);
879
916
  }
880
917
  if (originalCode !== this.lastKnownOriginalCode || modifiedCode !== this.lastKnownModifiedCode) {
881
918
  this.pendingDiffUpdate = {
@@ -893,8 +930,11 @@ var DiffEditorManager = class {
893
930
  }
894
931
  const prev = this.lastKnownOriginalCode ?? this.originalModel.getValue();
895
932
  if (prev === newCode) return;
896
- if (newCode.startsWith(prev) && prev.length < newCode.length) this.appendToModel(this.originalModel, newCode.slice(prev.length));
897
- else this.applyMinimalEditToModel(this.originalModel, prev, newCode);
933
+ if (newCode.startsWith(prev) && prev.length < newCode.length) this.appendOriginal(newCode.slice(prev.length), codeLanguage);
934
+ else {
935
+ this.flushOriginalAppendBufferSync();
936
+ this.applyMinimalEditToModel(this.originalModel, prev, newCode);
937
+ }
898
938
  this.lastKnownOriginalCode = newCode;
899
939
  }
900
940
  updateModified(newCode, codeLanguage) {
@@ -905,12 +945,12 @@ var DiffEditorManager = class {
905
945
  }
906
946
  const prev = this.lastKnownModifiedCode ?? this.modifiedModel.getValue();
907
947
  if (prev === newCode) return;
908
- const prevLine = this.modifiedModel.getLineCount();
909
- if (newCode.startsWith(prev) && prev.length < newCode.length) {
910
- this.appendToModel(this.modifiedModel, newCode.slice(prev.length));
911
- this.maybeScrollDiffToBottom(this.modifiedModel.getLineCount(), prevLine);
912
- } else {
913
- this.applyMinimalEditToModel(this.modifiedModel, prev, newCode);
948
+ if (newCode.startsWith(prev) && prev.length < newCode.length) this.appendModified(newCode.slice(prev.length), codeLanguage);
949
+ else {
950
+ this.flushModifiedAppendBufferSync();
951
+ const prevAfterFlush = this.modifiedModel.getValue();
952
+ const prevLine = this.modifiedModel.getLineCount();
953
+ this.applyMinimalEditToModel(this.modifiedModel, prevAfterFlush, newCode);
914
954
  const newLine = this.modifiedModel.getLineCount();
915
955
  if (newLine !== prevLine) {
916
956
  const shouldImmediate = this.shouldPerformImmediateRevealDiff();
@@ -944,8 +984,8 @@ var DiffEditorManager = class {
944
984
  const lang = processedLanguage(codeLanguage);
945
985
  if (lang && this.originalModel.getLanguageId() !== lang) monaco_shim_exports.editor.setModelLanguage(this.originalModel, lang);
946
986
  }
947
- this.appendToModel(this.originalModel, appendText);
948
- this.lastKnownOriginalCode = this.originalModel.getValue();
987
+ this.appendBufferOriginalDiff.push(appendText);
988
+ this.scheduleFlushAppendBufferDiff();
949
989
  }
950
990
  appendModified(appendText, codeLanguage) {
951
991
  if (!this.diffEditorView || !this.modifiedModel || !appendText) return;
@@ -953,11 +993,8 @@ var DiffEditorManager = class {
953
993
  const lang = processedLanguage(codeLanguage);
954
994
  if (lang && this.modifiedModel.getLanguageId() !== lang) monaco_shim_exports.editor.setModelLanguage(this.modifiedModel, lang);
955
995
  }
956
- this.appendBufferDiff.push(appendText);
957
- if (!this.appendBufferDiffScheduled) {
958
- this.appendBufferDiffScheduled = true;
959
- this.rafScheduler.schedule("appendDiff", () => this.flushAppendBufferDiff());
960
- }
996
+ this.appendBufferModifiedDiff.push(appendText);
997
+ this.scheduleFlushAppendBufferDiff();
961
998
  }
962
999
  setLanguage(language, languages$1) {
963
1000
  if (!languages$1.includes(language)) {
@@ -981,7 +1018,12 @@ var DiffEditorManager = class {
981
1018
  this.pendingDiffUpdate = null;
982
1019
  this.rafScheduler.cancel("appendDiff");
983
1020
  this.appendBufferDiffScheduled = false;
984
- this.appendBufferDiff.length = 0;
1021
+ this.appendBufferOriginalDiff.length = 0;
1022
+ this.appendBufferModifiedDiff.length = 0;
1023
+ if (this.appendFlushThrottleTimerDiff != null) {
1024
+ clearTimeout(this.appendFlushThrottleTimerDiff);
1025
+ this.appendFlushThrottleTimerDiff = null;
1026
+ }
985
1027
  this.rafScheduler.cancel("content-size-change-diff");
986
1028
  this.rafScheduler.cancel("sync-last-known-modified");
987
1029
  if (this.diffScrollWatcher) {
@@ -1028,6 +1070,14 @@ var DiffEditorManager = class {
1028
1070
  safeClean() {
1029
1071
  this.rafScheduler.cancel("diff");
1030
1072
  this.pendingDiffUpdate = null;
1073
+ this.rafScheduler.cancel("appendDiff");
1074
+ this.appendBufferDiffScheduled = false;
1075
+ this.appendBufferOriginalDiff.length = 0;
1076
+ this.appendBufferModifiedDiff.length = 0;
1077
+ if (this.appendFlushThrottleTimerDiff != null) {
1078
+ clearTimeout(this.appendFlushThrottleTimerDiff);
1079
+ this.appendFlushThrottleTimerDiff = null;
1080
+ }
1031
1081
  if (this.diffScrollWatcher) {
1032
1082
  this.diffScrollWatcher.dispose();
1033
1083
  this.diffScrollWatcher = null;
@@ -1079,6 +1129,8 @@ var DiffEditorManager = class {
1079
1129
  }
1080
1130
  const { original, modified, lang } = this.pendingDiffUpdate;
1081
1131
  this.pendingDiffUpdate = null;
1132
+ this.flushOriginalAppendBufferSync();
1133
+ this.flushModifiedAppendBufferSync();
1082
1134
  if (lang) {
1083
1135
  const plang = processedLanguage(lang);
1084
1136
  if (plang) {
@@ -1096,14 +1148,7 @@ var DiffEditorManager = class {
1096
1148
  else this.applyMinimalEditToModel(o, prevO, original);
1097
1149
  this.lastKnownOriginalCode = original;
1098
1150
  }
1099
- let prevM = this.lastKnownModifiedCode;
1100
- const buffered = this.appendBufferDiff.length > 0 ? this.appendBufferDiff.join("") : "";
1101
- if (this.appendBufferDiff.length > 0) try {
1102
- prevM = m.getValue() + buffered;
1103
- this.lastKnownModifiedCode = prevM;
1104
- } catch {
1105
- prevM = (this.lastKnownModifiedCode ?? "") + buffered;
1106
- }
1151
+ const prevM = m.getValue();
1107
1152
  const prevMLineCount = m.getLineCount();
1108
1153
  if (prevM !== modified) {
1109
1154
  if (modified.startsWith(prevM) && prevM.length < modified.length) this.appendToModel(m, modified.slice(prevM.length));
@@ -1123,17 +1168,33 @@ var DiffEditorManager = class {
1123
1168
  }
1124
1169
  }
1125
1170
  }
1171
+ flushModifiedAppendBufferSync() {
1172
+ if (!this.modifiedModel) return;
1173
+ if (this.appendBufferModifiedDiff.length === 0) return;
1174
+ this.rafScheduler.cancel("appendDiff");
1175
+ this.appendBufferDiffScheduled = false;
1176
+ const text = this.appendBufferModifiedDiff.join("");
1177
+ this.appendBufferModifiedDiff.length = 0;
1178
+ if (!text) return;
1179
+ this.appendToModel(this.modifiedModel, text);
1180
+ }
1126
1181
  async flushAppendBufferDiff() {
1127
1182
  if (!this.diffEditorView) return;
1128
- if (this.appendBufferDiff.length === 0) return;
1183
+ if (this.appendBufferOriginalDiff.length === 0 && this.appendBufferModifiedDiff.length === 0) return;
1184
+ this.lastAppendFlushTimeDiff = Date.now();
1129
1185
  this.appendBufferDiffScheduled = false;
1186
+ if (this.originalModel && this.appendBufferOriginalDiff.length > 0) {
1187
+ const oText = this.appendBufferOriginalDiff.join("");
1188
+ this.appendBufferOriginalDiff.length = 0;
1189
+ if (oText) this.appendToModel(this.originalModel, oText);
1190
+ }
1130
1191
  const me = this.diffEditorView.getModifiedEditor();
1131
1192
  const model = me.getModel();
1132
1193
  if (!model) {
1133
- this.appendBufferDiff.length = 0;
1194
+ this.appendBufferModifiedDiff.length = 0;
1134
1195
  return;
1135
1196
  }
1136
- let parts = this.appendBufferDiff.splice(0);
1197
+ let parts = this.appendBufferModifiedDiff.splice(0);
1137
1198
  const prevLineInit = model.getLineCount();
1138
1199
  const totalText = parts.join("");
1139
1200
  const totalChars = totalText.length;
@@ -1208,7 +1269,7 @@ var DiffEditorManager = class {
1208
1269
  return;
1209
1270
  }
1210
1271
  const text = totalText;
1211
- this.appendBufferDiff.length = 0;
1272
+ this.appendBufferModifiedDiff.length = 0;
1212
1273
  prevLine = model.getLineCount();
1213
1274
  const lastColumn = model.getLineMaxColumn(prevLine);
1214
1275
  const range = new monaco_shim_exports.Range(prevLine, lastColumn, prevLine, lastColumn);
@@ -1928,15 +1989,17 @@ async function preloadMonacoWorkers() {
1928
1989
  javascript: workerUrlTs
1929
1990
  };
1930
1991
  try {
1931
- await Promise.all(unique.map((u) => fetch(u, {
1932
- method: "GET",
1933
- cache: "force-cache"
1934
- }).catch(() => void 0)));
1935
1992
  self.MonacoEnvironment = { getWorker(_, label) {
1936
1993
  const url = workerUrlByLabel[label] ?? workerUrlEditor;
1937
1994
  return new Worker(url, { type: "module" });
1938
1995
  } };
1939
1996
  } catch {}
1997
+ try {
1998
+ await Promise.all(unique.map((u) => fetch(u, {
1999
+ method: "GET",
2000
+ cache: "force-cache"
2001
+ }).catch(() => void 0)));
2002
+ } catch {}
1940
2003
  }
1941
2004
 
1942
2005
  //#endregion
@@ -2416,7 +2479,7 @@ function useMonaco(monacoOptions = {}) {
2416
2479
  try {
2417
2480
  monaco_shim_exports.editor.setTheme(initialThemeName);
2418
2481
  } catch {}
2419
- diffMgr = new DiffEditorManager(monacoOptions, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines, diffAutoScroll, monacoOptions.revealDebounceMs);
2482
+ diffMgr = new DiffEditorManager(monacoOptions, maxHeightValue, maxHeightCSS, autoScrollOnUpdate, autoScrollInitial, autoScrollThresholdPx, autoScrollThresholdLines, diffAutoScroll, monacoOptions.revealDebounceMs, monacoOptions.diffUpdateThrottleMs);
2420
2483
  diffEditorView = await diffMgr.createDiffEditor(container, originalCode, modifiedCode, language, initialThemeName);
2421
2484
  if (typeof monacoOptions.onThemeChange === "function") monacoOptions.onThemeChange(initialThemeName);
2422
2485
  const models = diffMgr.getDiffModels();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stream-monaco",
3
3
  "type": "module",
4
- "version": "0.0.13",
4
+ "version": "0.0.15",
5
5
  "description": "A framework-agnostic library for integrating Monaco Editor with Shiki highlighting, optimized for streaming updates.",
6
6
  "author": "Simon He",
7
7
  "license": "MIT",
@@ -92,6 +92,7 @@
92
92
  "start": "esno src/index.ts",
93
93
  "bench": "node scripts/stream-benchmark.mjs",
94
94
  "bench:playwright": "node scripts/playwright-bench.mjs",
95
+ "smoke:diff": "node scripts/playwright-diff-smoke.mjs",
95
96
  "test": "vitest",
96
97
  "typecheck": "tsc --noEmit"
97
98
  }