whistle.mockbubu 2.2.7 → 2.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whistle.mockbubu",
3
- "version": "2.2.7",
3
+ "version": "2.2.8",
4
4
  "description": "mock response data",
5
5
  "scripts": {
6
6
  "lint": "eslint . --ext .js",
package/public/js/app.js CHANGED
@@ -1466,8 +1466,10 @@ __webpack_require__.r(__webpack_exports__);
1466
1466
  /* harmony import */ var core_js_modules_esnext_iterator_for_each_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! core-js/modules/esnext.iterator.for-each.js */ "./node_modules/.pnpm/core-js@3.8.3/node_modules/core-js/modules/esnext.iterator.for-each.js");
1467
1467
  /* harmony import */ var core_js_modules_esnext_iterator_for_each_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(core_js_modules_esnext_iterator_for_each_js__WEBPACK_IMPORTED_MODULE_1__);
1468
1468
  /* harmony import */ var monaco_editor__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! monaco-editor */ "include-loader!./node_modules/.pnpm/monaco-editor@0.55.1/node_modules/monaco-editor/esm/vs/editor/editor.main.js");
1469
- /* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! vue */ "./node_modules/.pnpm/vue@2.7.14/node_modules/vue/dist/vue.runtime.esm.js");
1469
+ /* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! vue */ "./node_modules/.pnpm/vue@2.7.14/node_modules/vue/dist/vue.runtime.esm.js");
1470
1470
  /* harmony import */ var _utils_monaco_previews__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../utils/monaco-previews */ "./src/utils/monaco-previews.js");
1471
+ /* harmony import */ var _utils_monaco_fast_paste__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../utils/monaco-fast-paste */ "./src/utils/monaco-fast-paste.js");
1472
+
1471
1473
 
1472
1474
 
1473
1475
 
@@ -1507,16 +1509,18 @@ const __default__ = {
1507
1509
  function getMonacoTheme() {
1508
1510
  return document.documentElement.getAttribute('data-theme') === 'dark' ? 'vs-dark' : 'vs';
1509
1511
  }
1510
- const monacoRef = (0,vue__WEBPACK_IMPORTED_MODULE_4__.ref)(null);
1511
- const monacoInstance = (0,vue__WEBPACK_IMPORTED_MODULE_4__.ref)(null);
1512
- const error = (0,vue__WEBPACK_IMPORTED_MODULE_4__.ref)(false);
1513
- const internalChange = (0,vue__WEBPACK_IMPORTED_MODULE_4__.ref)(false);
1514
- const showScrollTop = (0,vue__WEBPACK_IMPORTED_MODULE_4__.ref)(false);
1515
- const scrollTimer = (0,vue__WEBPACK_IMPORTED_MODULE_4__.ref)(null);
1516
- const isButtonHovered = (0,vue__WEBPACK_IMPORTED_MODULE_4__.ref)(false);
1517
- const lastScrollTime = (0,vue__WEBPACK_IMPORTED_MODULE_4__.ref)(0);
1512
+ const monacoRef = (0,vue__WEBPACK_IMPORTED_MODULE_5__.ref)(null);
1513
+ const monacoInstance = (0,vue__WEBPACK_IMPORTED_MODULE_5__.ref)(null);
1514
+ const error = (0,vue__WEBPACK_IMPORTED_MODULE_5__.ref)(false);
1515
+ const internalChange = (0,vue__WEBPACK_IMPORTED_MODULE_5__.ref)(false);
1516
+ const showScrollTop = (0,vue__WEBPACK_IMPORTED_MODULE_5__.ref)(false);
1517
+ const scrollTimer = (0,vue__WEBPACK_IMPORTED_MODULE_5__.ref)(null);
1518
+ const isButtonHovered = (0,vue__WEBPACK_IMPORTED_MODULE_5__.ref)(false);
1519
+ const lastScrollTime = (0,vue__WEBPACK_IMPORTED_MODULE_5__.ref)(0);
1518
1520
  let previewTimer = null;
1519
1521
  let themeObserver = null;
1522
+ let contentChangeTimer = null;
1523
+ let fastPasteHandle = null;
1520
1524
  function applyPreviews(editorInstance) {
1521
1525
  if (!editorInstance) return;
1522
1526
  if (previewTimer) clearTimeout(previewTimer);
@@ -1564,23 +1568,51 @@ const __default__ = {
1564
1568
  enabled: 'on'
1565
1569
  }
1566
1570
  });
1567
- monacoInstance.value.onDidChangeModelContent(() => {
1568
- if (internalChange.value) return;
1569
- const raw = monacoInstance.value.getValue();
1570
- try {
1571
- const jsonData = JSON.parse(raw);
1572
- error.value = false;
1573
- internalChange.value = true;
1574
- emit('json-change', jsonData);
1575
- emit('input', jsonData);
1576
- (0,vue__WEBPACK_IMPORTED_MODULE_4__.nextTick)(() => {
1577
- internalChange.value = false;
1578
- });
1579
- } catch (e) {
1580
- error.value = true;
1581
- emit('has-error', e);
1571
+
1572
+ // ─── 安装大内容快速粘贴优化(独立模块) ─────────────────────────────────────
1573
+ fastPasteHandle = (0,_utils_monaco_fast_paste__WEBPACK_IMPORTED_MODULE_4__.installFastPaste)(monacoInstance.value, {
1574
+ isReadOnly: () => props.readOnly,
1575
+ onContentChange: raw => {
1576
+ try {
1577
+ const jsonData = JSON.parse(raw);
1578
+ error.value = false;
1579
+ internalChange.value = true;
1580
+ emit('json-change', jsonData);
1581
+ emit('input', jsonData);
1582
+ (0,vue__WEBPACK_IMPORTED_MODULE_5__.nextTick)(() => {
1583
+ internalChange.value = false;
1584
+ });
1585
+ } catch (err) {
1586
+ error.value = true;
1587
+ emit('has-error', err);
1588
+ }
1589
+ applyPreviews(monacoInstance.value);
1582
1590
  }
1583
- applyPreviews(monacoInstance.value);
1591
+ });
1592
+
1593
+ // ─── 常规内容变更处理 ────────────────────────────────────────────────────────
1594
+ monacoInstance.value.onDidChangeModelContent(() => {
1595
+ if (internalChange.value || fastPasteHandle?.isPasting) return;
1596
+ // 防抖:避免连续编辑时频繁触发 JSON.parse + Vue 响应式更新
1597
+ if (contentChangeTimer) clearTimeout(contentChangeTimer);
1598
+ contentChangeTimer = setTimeout(() => {
1599
+ if (!monacoInstance.value) return;
1600
+ const raw = monacoInstance.value.getValue();
1601
+ try {
1602
+ const jsonData = JSON.parse(raw);
1603
+ error.value = false;
1604
+ internalChange.value = true;
1605
+ emit('json-change', jsonData);
1606
+ emit('input', jsonData);
1607
+ (0,vue__WEBPACK_IMPORTED_MODULE_5__.nextTick)(() => {
1608
+ internalChange.value = false;
1609
+ });
1610
+ } catch (e) {
1611
+ error.value = true;
1612
+ emit('has-error', e);
1613
+ }
1614
+ applyPreviews(monacoInstance.value);
1615
+ }, 300);
1584
1616
  });
1585
1617
  monacoInstance.value.onDidScrollChange(() => {
1586
1618
  lastScrollTime.value = Date.now();
@@ -1608,6 +1640,11 @@ const __default__ = {
1608
1640
  themeObserver = null;
1609
1641
  }
1610
1642
  if (previewTimer) clearTimeout(previewTimer);
1643
+ if (contentChangeTimer) clearTimeout(contentChangeTimer);
1644
+ if (fastPasteHandle) {
1645
+ fastPasteHandle.dispose();
1646
+ fastPasteHandle = null;
1647
+ }
1611
1648
  if (monacoInstance.value) {
1612
1649
  (monacoInstance.value._imageWidgets || []).forEach(w => monacoInstance.value.removeContentWidget(w));
1613
1650
  if (monacoInstance.value._colorDecIds?.length) {
@@ -1680,34 +1717,32 @@ const __default__ = {
1680
1717
  });
1681
1718
 
1682
1719
  // ─── 生命周期 ─────────────────────────────────────────────
1683
- (0,vue__WEBPACK_IMPORTED_MODULE_4__.onMounted)(() => {
1720
+ (0,vue__WEBPACK_IMPORTED_MODULE_5__.onMounted)(() => {
1684
1721
  initMonaco();
1685
1722
  });
1686
- (0,vue__WEBPACK_IMPORTED_MODULE_4__.onBeforeUnmount)(() => {
1723
+ (0,vue__WEBPACK_IMPORTED_MODULE_5__.onBeforeUnmount)(() => {
1687
1724
  destroyMonaco();
1688
1725
  if (scrollTimer.value) clearTimeout(scrollTimer.value);
1689
1726
  });
1690
1727
 
1691
1728
  // ─── 监听外部 value 变化 ──────────────────────────────────
1692
- (0,vue__WEBPACK_IMPORTED_MODULE_4__.watch)(() => props.value, async val => {
1729
+ (0,vue__WEBPACK_IMPORTED_MODULE_5__.watch)(() => props.value, async val => {
1693
1730
  if (internalChange.value) return;
1694
1731
  if (monacoInstance.value) {
1695
1732
  const str = valueToString(val);
1696
1733
  if (str !== monacoInstance.value.getValue()) {
1697
1734
  internalChange.value = true;
1698
1735
  monacoInstance.value.setValue(str);
1699
- await (0,vue__WEBPACK_IMPORTED_MODULE_4__.nextTick)();
1736
+ await (0,vue__WEBPACK_IMPORTED_MODULE_5__.nextTick)();
1700
1737
  internalChange.value = false;
1701
1738
  applyPreviews(monacoInstance.value);
1702
1739
  }
1703
1740
  error.value = false;
1704
1741
  }
1705
- }, {
1706
- deep: true
1707
1742
  });
1708
1743
 
1709
1744
  // 只读状态变化时动态更新
1710
- (0,vue__WEBPACK_IMPORTED_MODULE_4__.watch)(() => props.readOnly, val => {
1745
+ (0,vue__WEBPACK_IMPORTED_MODULE_5__.watch)(() => props.readOnly, val => {
1711
1746
  monacoInstance.value?.updateOptions({
1712
1747
  readOnly: val
1713
1748
  });
@@ -1727,6 +1762,8 @@ const __default__ = {
1727
1762
  lastScrollTime,
1728
1763
  previewTimer,
1729
1764
  themeObserver,
1765
+ contentChangeTimer,
1766
+ fastPasteHandle,
1730
1767
  applyPreviews,
1731
1768
  localeMap,
1732
1769
  valueToString,
@@ -11843,6 +11880,173 @@ function createSingletonHook(hook) {
11843
11880
 
11844
11881
  /***/ }),
11845
11882
 
11883
+ /***/ "./src/utils/monaco-fast-paste.js":
11884
+ /*!****************************************!*\
11885
+ !*** ./src/utils/monaco-fast-paste.js ***!
11886
+ \****************************************/
11887
+ /***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
11888
+
11889
+ "use strict";
11890
+ __webpack_require__.r(__webpack_exports__);
11891
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
11892
+ /* harmony export */ installFastPaste: function() { return /* binding */ installFastPaste; }
11893
+ /* harmony export */ });
11894
+ /* harmony import */ var monaco_editor__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! monaco-editor */ "include-loader!./node_modules/.pnpm/monaco-editor@0.55.1/node_modules/monaco-editor/esm/vs/editor/editor.main.js");
11895
+ /**
11896
+ * Monaco 大文本粘贴优化模块
11897
+ *
11898
+ * 解决 Monaco standalone 版本粘贴大文本时的性能问题。
11899
+ * 原因:Monaco 的 executeEdits 对大文本做 piece tree 增量操作 + 同步 tokenization,
11900
+ * 导致 3.5MB 文本粘贴耗时 20s+。
11901
+ * 方案:拦截粘贴事件,用 model.setValue() 直接重建 buffer(毫秒级),
11902
+ * 并提供自定义 undo/redo 栈弥补 setValue 丢失原生 undo 的问题。
11903
+ */
11904
+
11905
+
11906
+ // 超过此行数时启用快速粘贴
11907
+ const FAST_PASTE_LINE_THRESHOLD = 10000;
11908
+
11909
+ /**
11910
+ * 为 Monaco 编辑器实例安装快速粘贴优化
11911
+ * @param {object} editor - Monaco editor 实例
11912
+ * @param {object} options
11913
+ * @param {Function} options.onContentChange - 内容变更后的回调,参数为 model.getValue()
11914
+ * @param {Function} [options.isReadOnly] - 返回当前是否只读
11915
+ * @returns {{ dispose: Function }} 调用 dispose() 移除所有监听
11916
+ */
11917
+ function installFastPaste(editor, {
11918
+ onContentChange,
11919
+ isReadOnly
11920
+ }) {
11921
+ const undoStack = [];
11922
+ let redoStack = [];
11923
+ let isPasting = false;
11924
+
11925
+ /**
11926
+ * 执行 setValue 并通知外部
11927
+ */
11928
+ function setValueAndNotify(model, newText) {
11929
+ isPasting = true;
11930
+ model.setValue(newText);
11931
+ isPasting = false;
11932
+ // 切 plaintext 阻止 setValue 后的同步 tokenization,延迟恢复
11933
+ monaco_editor__WEBPACK_IMPORTED_MODULE_0__.editor.setModelLanguage(model, 'plaintext');
11934
+ setTimeout(() => {
11935
+ monaco_editor__WEBPACK_IMPORTED_MODULE_0__.editor.setModelLanguage(model, 'json');
11936
+ }, 50);
11937
+ // 通知外部
11938
+ setTimeout(() => {
11939
+ onContentChange?.(model.getValue());
11940
+ }, 100);
11941
+ }
11942
+
11943
+ // ─── document 级 paste 拦截 ─────────────────────────────────────────
11944
+ const handlePaste = e => {
11945
+ if (!editor || isReadOnly?.()) return;
11946
+ if (!editor.hasTextFocus()) return;
11947
+ const model = editor.getModel();
11948
+ if (!model) return;
11949
+ const clipText = e.clipboardData?.getData('text/plain');
11950
+ const t0 = performance.now();
11951
+ if (!clipText || clipText.length < 500) return;
11952
+ const oldLineCount = model.getLineCount();
11953
+ const newLineCount = clipText.split('\n').length;
11954
+ // 旧内容 和 即将粘贴的新内容 行数均超过设定值时触发
11955
+ const shouldUseFastPaste = oldLineCount > FAST_PASTE_LINE_THRESHOLD && newLineCount > FAST_PASTE_LINE_THRESHOLD;
11956
+ if (!shouldUseFastPaste) return;
11957
+ e.preventDefault();
11958
+ e.stopImmediatePropagation();
11959
+ const selection = editor.getSelection();
11960
+
11961
+ // 保存旧内容到 undo 栈
11962
+ const t1 = performance.now();
11963
+ const oldText = model.getValue();
11964
+ undoStack.push(oldText);
11965
+ redoStack = [];
11966
+
11967
+ // 拼接新文本
11968
+ const startOffset = model.getOffsetAt(selection.getStartPosition());
11969
+ const endOffset = model.getOffsetAt(selection.getEndPosition());
11970
+ const newText = oldText.substring(0, startOffset) + clipText + oldText.substring(endOffset);
11971
+ const t2 = performance.now();
11972
+
11973
+ // setValue
11974
+ isPasting = true;
11975
+ model.setValue(newText);
11976
+ const t3 = performance.now();
11977
+
11978
+ // 设置光标
11979
+ const cursorOffset = startOffset + clipText.length;
11980
+ const cursorPos = model.getPositionAt(cursorOffset);
11981
+ editor.setPosition(cursorPos);
11982
+ editor.revealPosition(cursorPos);
11983
+ isPasting = false;
11984
+
11985
+ // 切 plaintext 阻止同步 tokenization
11986
+ monaco_editor__WEBPACK_IMPORTED_MODULE_0__.editor.setModelLanguage(model, 'plaintext');
11987
+ const t4 = performance.now();
11988
+ console.log(`[FastPaste] ✅ 完成! chars=${clipText.length}, oldLines=${oldLineCount}, newLines=${newLineCount}, ` + `拼接=${(t2 - t1).toFixed(1)}ms, setValue=${(t3 - t2).toFixed(1)}ms, ` + `总耗时=${(t4 - t0).toFixed(1)}ms`);
11989
+
11990
+ // 延迟恢复 JSON(只 tokenize 可视区)
11991
+ setTimeout(() => {
11992
+ monaco_editor__WEBPACK_IMPORTED_MODULE_0__.editor.setModelLanguage(model, 'json');
11993
+ }, 50);
11994
+
11995
+ // 延迟通知外部
11996
+ setTimeout(() => {
11997
+ onContentChange?.(model.getValue());
11998
+ }, 300);
11999
+ };
12000
+ document.addEventListener('paste', handlePaste, true);
12001
+
12002
+ // ─── 自定义 undo/redo(先走 Monaco 内部,空了再走自定义栈)──────────
12003
+ const undoDisposable = editor.addCommand(monaco_editor__WEBPACK_IMPORTED_MODULE_0__.KeyMod.CtrlCmd | monaco_editor__WEBPACK_IMPORTED_MODULE_0__.KeyCode.KeyZ, () => {
12004
+ const model = editor.getModel();
12005
+ if (!model) return;
12006
+
12007
+ // 先尝试 Monaco 内部 undo
12008
+ const before = model.getValue();
12009
+ editor.trigger('keyboard', 'undo', null);
12010
+ const after = model.getValue();
12011
+ if (before !== after) return;
12012
+
12013
+ // Monaco undo stack 已空,从自定义栈恢复
12014
+ if (undoStack.length > 0) {
12015
+ redoStack.push(before);
12016
+ setValueAndNotify(model, undoStack.pop());
12017
+ }
12018
+ });
12019
+ const redoDisposable = editor.addCommand(monaco_editor__WEBPACK_IMPORTED_MODULE_0__.KeyMod.CtrlCmd | monaco_editor__WEBPACK_IMPORTED_MODULE_0__.KeyMod.Shift | monaco_editor__WEBPACK_IMPORTED_MODULE_0__.KeyCode.KeyZ, () => {
12020
+ const model = editor.getModel();
12021
+ if (!model) return;
12022
+
12023
+ // 先尝试 Monaco 内部 redo
12024
+ const before = model.getValue();
12025
+ editor.trigger('keyboard', 'redo', null);
12026
+ const after = model.getValue();
12027
+ if (before !== after) return;
12028
+
12029
+ // Monaco redo stack 已空,从自定义栈恢复
12030
+ if (redoStack.length > 0) {
12031
+ undoStack.push(before);
12032
+ setValueAndNotify(model, redoStack.pop());
12033
+ }
12034
+ });
12035
+
12036
+ // ─── 返回 isPasting 状态的 getter + dispose ─────────────────────────
12037
+ return {
12038
+ /** 当前是否在执行快速粘贴(外部 onDidChangeModelContent 应跳过) */
12039
+ get isPasting() {
12040
+ return isPasting;
12041
+ },
12042
+ dispose() {
12043
+ document.removeEventListener('paste', handlePaste, true);
12044
+ }
12045
+ };
12046
+ }
12047
+
12048
+ /***/ }),
12049
+
11846
12050
  /***/ "./src/utils/monaco-previews.js":
11847
12051
  /*!**************************************!*\
11848
12052
  !*** ./src/utils/monaco-previews.js ***!