@incremark/vue 0.0.4 → 0.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.
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  // src/composables/useIncremark.ts
2
- import { ref, shallowRef, computed, markRaw } from "vue";
2
+ import { ref, shallowRef, computed, markRaw, watch, onUnmounted } from "vue";
3
3
  import {
4
- createIncremarkParser
4
+ createIncremarkParser,
5
+ createBlockTransformer,
6
+ defaultPlugins
5
7
  } from "@incremark/core";
6
8
  function useIncremark(options = {}) {
7
9
  const parser = createIncremarkParser(options);
@@ -9,6 +11,28 @@ function useIncremark(options = {}) {
9
11
  const pendingBlocks = shallowRef([]);
10
12
  const isLoading = ref(false);
11
13
  const markdown = ref("");
14
+ const typewriterEnabled = ref(options.typewriter?.enabled ?? !!options.typewriter);
15
+ const displayBlocksRef = shallowRef([]);
16
+ const isTypewriterProcessing = ref(false);
17
+ const isTypewriterPaused = ref(false);
18
+ const typewriterEffect = ref(options.typewriter?.effect ?? "none");
19
+ const typewriterCursor = ref(options.typewriter?.cursor ?? "|");
20
+ let transformer = null;
21
+ if (options.typewriter) {
22
+ const twOptions = options.typewriter;
23
+ transformer = createBlockTransformer({
24
+ charsPerTick: twOptions.charsPerTick ?? [1, 3],
25
+ tickInterval: twOptions.tickInterval ?? 30,
26
+ effect: twOptions.effect ?? "none",
27
+ pauseOnHidden: twOptions.pauseOnHidden ?? true,
28
+ plugins: twOptions.plugins ?? defaultPlugins,
29
+ onChange: (blocks2) => {
30
+ displayBlocksRef.value = blocks2;
31
+ isTypewriterProcessing.value = transformer?.isProcessing() ?? false;
32
+ isTypewriterPaused.value = transformer?.isPausedState() ?? false;
33
+ }
34
+ });
35
+ }
12
36
  const ast = computed(() => ({
13
37
  type: "root",
14
38
  children: [
@@ -16,7 +40,55 @@ function useIncremark(options = {}) {
16
40
  ...pendingBlocks.value.map((b) => b.node)
17
41
  ]
18
42
  }));
19
- const blocks = computed(() => {
43
+ const sourceBlocks = computed(() => {
44
+ return completedBlocks.value.map((block) => ({
45
+ id: block.id,
46
+ node: block.node,
47
+ status: block.status
48
+ }));
49
+ });
50
+ if (transformer) {
51
+ watch(
52
+ sourceBlocks,
53
+ (blocks2) => {
54
+ transformer.push(blocks2);
55
+ const currentDisplaying = displayBlocksRef.value.find((b) => !b.isDisplayComplete);
56
+ if (currentDisplaying) {
57
+ const updated = blocks2.find((b) => b.id === currentDisplaying.id);
58
+ if (updated) {
59
+ transformer.update(updated);
60
+ }
61
+ }
62
+ },
63
+ { immediate: true, deep: true }
64
+ );
65
+ }
66
+ function addCursorToNode(node, cursor) {
67
+ const cloned = JSON.parse(JSON.stringify(node));
68
+ function addToLast(n) {
69
+ if (n.children && n.children.length > 0) {
70
+ for (let i = n.children.length - 1; i >= 0; i--) {
71
+ if (addToLast(n.children[i])) {
72
+ return true;
73
+ }
74
+ }
75
+ n.children.push({ type: "text", value: cursor });
76
+ return true;
77
+ }
78
+ if (n.type === "text" && typeof n.value === "string") {
79
+ n.value += cursor;
80
+ return true;
81
+ }
82
+ if (typeof n.value === "string") {
83
+ n.value += cursor;
84
+ return true;
85
+ }
86
+ return false;
87
+ }
88
+ addToLast(cloned);
89
+ return cloned;
90
+ }
91
+ const rawBlocks = computed(() => {
20
92
  const result = [];
21
93
  for (const block of completedBlocks.value) {
22
94
  result.push({ ...block, stableId: block.id });
@@ -29,6 +101,29 @@ function useIncremark(options = {}) {
29
101
  }
30
102
  return result;
31
103
  });
104
+ const blocks = computed(() => {
105
+ if (!typewriterEnabled.value || !transformer) {
106
+ return rawBlocks.value;
107
+ }
108
+ return displayBlocksRef.value.map((db, index) => {
109
+ const isPending = !db.isDisplayComplete;
110
+ const isLastPending = isPending && index === displayBlocksRef.value.length - 1;
111
+ let node = db.displayNode;
112
+ if (typewriterEffect.value === "typing" && isLastPending) {
113
+ node = addCursorToNode(db.displayNode, typewriterCursor.value);
114
+ }
115
+ return {
116
+ id: db.id,
117
+ stableId: db.id,
118
+ status: db.isDisplayComplete ? "completed" : "pending",
119
+ isLastPending,
120
+ node,
121
+ startOffset: 0,
122
+ endOffset: 0,
123
+ rawText: ""
124
+ };
125
+ });
126
+ });
32
127
  function append(chunk) {
33
128
  isLoading.value = true;
34
129
  const update = parser.append(chunk);
@@ -64,8 +159,9 @@ function useIncremark(options = {}) {
64
159
  pendingBlocks.value = [];
65
160
  markdown.value = "";
66
161
  isLoading.value = false;
162
+ transformer?.reset();
67
163
  }
68
- function render13(content) {
164
+ function render14(content) {
69
165
  const update = parser.render(content);
70
166
  markdown.value = parser.getBuffer();
71
167
  completedBlocks.value = parser.getCompletedBlocks().map((b) => markRaw(b));
@@ -73,6 +169,43 @@ function useIncremark(options = {}) {
73
169
  isLoading.value = false;
74
170
  return update;
75
171
  }
172
+ const typewriter = {
173
+ enabled: typewriterEnabled,
174
+ isProcessing: computed(() => isTypewriterProcessing.value),
175
+ isPaused: computed(() => isTypewriterPaused.value),
176
+ effect: computed(() => typewriterEffect.value),
177
+ skip: () => transformer?.skip(),
178
+ pause: () => {
179
+ transformer?.pause();
180
+ isTypewriterPaused.value = true;
181
+ },
182
+ resume: () => {
183
+ transformer?.resume();
184
+ isTypewriterPaused.value = false;
185
+ },
186
+ setOptions: (opts) => {
187
+ if (opts.enabled !== void 0) {
188
+ typewriterEnabled.value = opts.enabled;
189
+ }
190
+ if (opts.charsPerTick !== void 0 || opts.tickInterval !== void 0 || opts.effect !== void 0 || opts.pauseOnHidden !== void 0) {
191
+ transformer?.setOptions({
192
+ charsPerTick: opts.charsPerTick,
193
+ tickInterval: opts.tickInterval,
194
+ effect: opts.effect,
195
+ pauseOnHidden: opts.pauseOnHidden
196
+ });
197
+ }
198
+ if (opts.effect !== void 0) {
199
+ typewriterEffect.value = opts.effect;
200
+ }
201
+ if (opts.cursor !== void 0) {
202
+ typewriterCursor.value = opts.cursor;
203
+ }
204
+ }
205
+ };
206
+ onUnmounted(() => {
207
+ transformer?.destroy();
208
+ });
76
209
  return {
77
210
  /** 已收集的完整 Markdown 字符串 */
78
211
  markdown,
@@ -82,7 +215,7 @@ function useIncremark(options = {}) {
82
215
  pendingBlocks,
83
216
  /** 当前完整的 AST */
84
217
  ast,
85
- /** 所有块(完成 + 待处理),带稳定 ID */
218
+ /** 用于渲染的 blocks(根据打字机设置自动处理) */
86
219
  blocks,
87
220
  /** 是否正在加载 */
88
221
  isLoading,
@@ -92,12 +225,14 @@ function useIncremark(options = {}) {
92
225
  finalize,
93
226
  /** 强制中断 */
94
227
  abort,
95
- /** 重置解析器 */
228
+ /** 重置解析器和打字机 */
96
229
  reset,
97
230
  /** 一次性渲染(reset + append + finalize) */
98
- render: render13,
231
+ render: render14,
99
232
  /** 解析器实例 */
100
- parser
233
+ parser,
234
+ /** 打字机控制 */
235
+ typewriter
101
236
  };
102
237
  }
103
238
 
@@ -126,7 +261,7 @@ function useStreamRenderer(options) {
126
261
  }
127
262
 
128
263
  // src/composables/useDevTools.ts
129
- import { onMounted, onUnmounted } from "vue";
264
+ import { onMounted, onUnmounted as onUnmounted2 } from "vue";
130
265
  import { createDevTools } from "@incremark/devtools";
131
266
  function useDevTools(incremark, options = {}) {
132
267
  const devtools = createDevTools(options);
@@ -147,27 +282,87 @@ function useDevTools(incremark, options = {}) {
147
282
  onMounted(() => {
148
283
  devtools.mount();
149
284
  });
150
- onUnmounted(() => {
285
+ onUnmounted2(() => {
151
286
  devtools.unmount();
152
287
  incremark.parser.setOnChange(void 0);
153
288
  });
154
289
  return devtools;
155
290
  }
156
291
 
292
+ // src/composables/useBlockTransformer.ts
293
+ import { ref as ref3, watch as watch2, computed as computed3, onUnmounted as onUnmounted3 } from "vue";
294
+ import {
295
+ createBlockTransformer as createBlockTransformer2
296
+ } from "@incremark/core";
297
+ function useBlockTransformer(sourceBlocks, options = {}) {
298
+ const displayBlocksRef = ref3([]);
299
+ const isProcessingRef = ref3(false);
300
+ const isPausedRef = ref3(false);
301
+ const effectRef = ref3(options.effect ?? "none");
302
+ const transformer = createBlockTransformer2({
303
+ ...options,
304
+ onChange: (blocks) => {
305
+ displayBlocksRef.value = blocks;
306
+ isProcessingRef.value = transformer.isProcessing();
307
+ isPausedRef.value = transformer.isPausedState();
308
+ }
309
+ });
310
+ watch2(
311
+ sourceBlocks,
312
+ (blocks) => {
313
+ transformer.push(blocks);
314
+ const currentDisplaying = displayBlocksRef.value.find((b) => !b.isDisplayComplete);
315
+ if (currentDisplaying) {
316
+ const updated = blocks.find((b) => b.id === currentDisplaying.id);
317
+ if (updated) {
318
+ transformer.update(updated);
319
+ }
320
+ }
321
+ },
322
+ { immediate: true, deep: true }
323
+ );
324
+ onUnmounted3(() => {
325
+ transformer.destroy();
326
+ });
327
+ return {
328
+ displayBlocks: computed3(() => displayBlocksRef.value),
329
+ isProcessing: computed3(() => isProcessingRef.value),
330
+ isPaused: computed3(() => isPausedRef.value),
331
+ effect: computed3(() => effectRef.value),
332
+ skip: () => transformer.skip(),
333
+ reset: () => transformer.reset(),
334
+ pause: () => {
335
+ transformer.pause();
336
+ isPausedRef.value = true;
337
+ },
338
+ resume: () => {
339
+ transformer.resume();
340
+ isPausedRef.value = false;
341
+ },
342
+ setOptions: (opts) => {
343
+ transformer.setOptions(opts);
344
+ if (opts.effect !== void 0) {
345
+ effectRef.value = opts.effect;
346
+ }
347
+ },
348
+ transformer
349
+ };
350
+ }
351
+
157
352
  // sfc-script:/Users/yishuai/develop/ai/markdown/packages/vue/src/components/Incremark.vue?type=script
158
353
  import { defineComponent as _defineComponent11 } from "vue";
159
- import { computed as computed7 } from "vue";
354
+ import { computed as computed8 } from "vue";
160
355
 
161
356
  // sfc-script:/Users/yishuai/develop/ai/markdown/packages/vue/src/components/IncremarkHeading.vue?type=script
162
357
  import { defineComponent as _defineComponent3 } from "vue";
163
- import { computed as computed4 } from "vue";
358
+ import { computed as computed5 } from "vue";
164
359
 
165
360
  // sfc-script:/Users/yishuai/develop/ai/markdown/packages/vue/src/components/IncremarkInline.vue?type=script
166
361
  import { defineComponent as _defineComponent2 } from "vue";
167
362
 
168
363
  // sfc-script:/Users/yishuai/develop/ai/markdown/packages/vue/src/components/IncremarkMath.vue?type=script
169
364
  import { defineComponent as _defineComponent } from "vue";
170
- import { computed as computed3, ref as ref3, watch, shallowRef as shallowRef2, onUnmounted as onUnmounted2 } from "vue";
365
+ import { computed as computed4, ref as ref4, watch as watch3, shallowRef as shallowRef2, onUnmounted as onUnmounted4 } from "vue";
171
366
  var IncremarkMath_default = /* @__PURE__ */ _defineComponent({
172
367
  __name: "IncremarkMath",
173
368
  props: {
@@ -177,13 +372,13 @@ var IncremarkMath_default = /* @__PURE__ */ _defineComponent({
177
372
  setup(__props, { expose: __expose }) {
178
373
  __expose();
179
374
  const props = __props;
180
- const renderedHtml = ref3("");
181
- const renderError = ref3("");
182
- const isLoading = ref3(false);
375
+ const renderedHtml = ref4("");
376
+ const renderError = ref4("");
377
+ const isLoading = ref4(false);
183
378
  const katexRef = shallowRef2(null);
184
379
  let renderTimer = null;
185
- const isInline = computed3(() => props.node.type === "inlineMath");
186
- const formula = computed3(() => props.node.value);
380
+ const isInline = computed4(() => props.node.type === "inlineMath");
381
+ const formula = computed4(() => props.node.value);
187
382
  function scheduleRender() {
188
383
  if (!formula.value) {
189
384
  renderedHtml.value = "";
@@ -218,12 +413,12 @@ var IncremarkMath_default = /* @__PURE__ */ _defineComponent({
218
413
  isLoading.value = false;
219
414
  }
220
415
  }
221
- onUnmounted2(() => {
416
+ onUnmounted4(() => {
222
417
  if (renderTimer) {
223
418
  clearTimeout(renderTimer);
224
419
  }
225
420
  });
226
- watch(formula, scheduleRender, { immediate: true });
421
+ watch3(formula, scheduleRender, { immediate: true });
227
422
  const __returned__ = { props, renderedHtml, renderError, isLoading, katexRef, get renderTimer() {
228
423
  return renderTimer;
229
424
  }, set renderTimer(v) {
@@ -326,10 +521,28 @@ var IncremarkInline_default = /* @__PURE__ */ _defineComponent2({
326
521
  },
327
522
  setup(__props, { expose: __expose }) {
328
523
  __expose();
329
- function escapeHtml(str) {
330
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
524
+ function getStableText(node) {
525
+ if (!node.chunks || node.chunks.length === 0) {
526
+ return node.value;
527
+ }
528
+ return node.value.slice(0, node.stableLength ?? 0);
529
+ }
530
+ function hasChunks(node) {
531
+ return node.type === "text" && "chunks" in node && Array.isArray(node.chunks);
331
532
  }
332
- const __returned__ = { escapeHtml, IncremarkMath: IncremarkMath_default2 };
533
+ function getChunks(node) {
534
+ if (hasChunks(node)) {
535
+ return node.chunks;
536
+ }
537
+ return void 0;
538
+ }
539
+ function isHtmlNode(node) {
540
+ return node.type === "html";
541
+ }
542
+ function isInlineMath(node) {
543
+ return node.type === "inlineMath";
544
+ }
545
+ const __returned__ = { getStableText, hasChunks, getChunks, isHtmlNode, isInlineMath, IncremarkMath: IncremarkMath_default2 };
333
546
  Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
334
547
  return __returned__;
335
548
  }
@@ -340,6 +553,7 @@ import { renderList as _renderList, Fragment as _Fragment2, openBlock as _openBl
340
553
  var _hoisted_12 = { class: "incremark-inline-code" };
341
554
  var _hoisted_22 = ["href"];
342
555
  var _hoisted_32 = ["src", "alt"];
556
+ var _hoisted_42 = ["innerHTML"];
343
557
  function render2(_ctx, _cache, $props, $setup, $data, $options) {
344
558
  const _component_IncremarkInline = _resolveComponent("IncremarkInline", true);
345
559
  return _openBlock2(true), _createElementBlock2(
@@ -350,20 +564,40 @@ function render2(_ctx, _cache, $props, $setup, $data, $options) {
350
564
  _Fragment2,
351
565
  { key: idx },
352
566
  [
353
- _createCommentVNode2(" \u6587\u672C "),
567
+ _createCommentVNode2(" \u6587\u672C\uFF08\u652F\u6301 chunks \u6E10\u5165\u52A8\u753B\uFF09 "),
354
568
  node.type === "text" ? (_openBlock2(), _createElementBlock2(
355
569
  _Fragment2,
356
570
  { key: 0 },
357
571
  [
572
+ _createCommentVNode2(" \u7A33\u5B9A\u6587\u672C\uFF08\u5DF2\u7ECF\u663E\u793A\u8FC7\u7684\u90E8\u5206\uFF0C\u65E0\u52A8\u753B\uFF09 "),
358
573
  _createTextVNode(
359
- _toDisplayString2(node.value),
574
+ " " + _toDisplayString2($setup.getStableText(node)) + " ",
360
575
  1
361
576
  /* TEXT */
362
- )
577
+ ),
578
+ _createCommentVNode2(" \u65B0\u589E\u7684 chunk \u90E8\u5206\uFF08\u5E26\u6E10\u5165\u52A8\u753B\uFF09 "),
579
+ (_openBlock2(true), _createElementBlock2(
580
+ _Fragment2,
581
+ null,
582
+ _renderList($setup.getChunks(node), (chunk) => {
583
+ return _openBlock2(), _createElementBlock2(
584
+ "span",
585
+ {
586
+ key: chunk.createdAt,
587
+ class: "incremark-fade-in"
588
+ },
589
+ _toDisplayString2(chunk.text),
590
+ 1
591
+ /* TEXT */
592
+ );
593
+ }),
594
+ 128
595
+ /* KEYED_FRAGMENT */
596
+ ))
363
597
  ],
364
598
  64
365
599
  /* STABLE_FRAGMENT */
366
- )) : node.type === "inlineMath" ? (_openBlock2(), _createElementBlock2(
600
+ )) : $setup.isInlineMath(node) ? (_openBlock2(), _createElementBlock2(
367
601
  _Fragment2,
368
602
  { key: 1 },
369
603
  [
@@ -473,6 +707,17 @@ function render2(_ctx, _cache, $props, $setup, $data, $options) {
473
707
  ],
474
708
  2112
475
709
  /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */
710
+ )) : $setup.isHtmlNode(node) ? (_openBlock2(), _createElementBlock2(
711
+ _Fragment2,
712
+ { key: 9 },
713
+ [
714
+ _createCommentVNode2(" \u539F\u59CB HTML "),
715
+ _createElementVNode2("span", {
716
+ innerHTML: node.value
717
+ }, null, 8, _hoisted_42)
718
+ ],
719
+ 2112
720
+ /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */
476
721
  )) : _createCommentVNode2("v-if", true)
477
722
  ],
478
723
  64
@@ -498,7 +743,7 @@ var IncremarkHeading_default = /* @__PURE__ */ _defineComponent3({
498
743
  setup(__props, { expose: __expose }) {
499
744
  __expose();
500
745
  const props = __props;
501
- const tag = computed4(() => `h${props.node.depth}`);
746
+ const tag = computed5(() => `h${props.node.depth}`);
502
747
  const __returned__ = { props, tag, IncremarkInline: IncremarkInline_default2 };
503
748
  Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
504
749
  return __returned__;
@@ -559,7 +804,7 @@ var IncremarkParagraph_default2 = IncremarkParagraph_default;
559
804
 
560
805
  // sfc-script:/Users/yishuai/develop/ai/markdown/packages/vue/src/components/IncremarkCode.vue?type=script
561
806
  import { defineComponent as _defineComponent5 } from "vue";
562
- import { computed as computed5, ref as ref4, watch as watch2, shallowRef as shallowRef3, onUnmounted as onUnmounted3 } from "vue";
807
+ import { computed as computed6, ref as ref5, watch as watch4, shallowRef as shallowRef3, onUnmounted as onUnmounted5 } from "vue";
563
808
  var IncremarkCode_default = /* @__PURE__ */ _defineComponent5({
564
809
  __name: "IncremarkCode",
565
810
  props: {
@@ -571,22 +816,22 @@ var IncremarkCode_default = /* @__PURE__ */ _defineComponent5({
571
816
  setup(__props, { expose: __expose }) {
572
817
  __expose();
573
818
  const props = __props;
574
- const copied = ref4(false);
575
- const highlightedHtml = ref4("");
576
- const isHighlighting = ref4(false);
577
- const highlightError = ref4(false);
578
- const mermaidSvg = ref4("");
579
- const mermaidError = ref4("");
580
- const mermaidLoading = ref4(false);
819
+ const copied = ref5(false);
820
+ const highlightedHtml = ref5("");
821
+ const isHighlighting = ref5(false);
822
+ const highlightError = ref5(false);
823
+ const mermaidSvg = ref5("");
824
+ const mermaidError = ref5("");
825
+ const mermaidLoading = ref5(false);
581
826
  const mermaidRef = shallowRef3(null);
582
827
  let mermaidTimer = null;
583
- const mermaidViewMode = ref4("preview");
828
+ const mermaidViewMode = ref5("preview");
584
829
  function toggleMermaidView() {
585
830
  mermaidViewMode.value = mermaidViewMode.value === "preview" ? "source" : "preview";
586
831
  }
587
- const language = computed5(() => props.node.lang || "text");
588
- const code = computed5(() => props.node.value);
589
- const isMermaid = computed5(() => language.value === "mermaid");
832
+ const language = computed6(() => props.node.lang || "text");
833
+ const code = computed6(() => props.node.value);
834
+ const isMermaid = computed6(() => language.value === "mermaid");
590
835
  const highlighterRef = shallowRef3(null);
591
836
  const loadedLanguages = /* @__PURE__ */ new Set();
592
837
  const loadedThemes = /* @__PURE__ */ new Set();
@@ -624,7 +869,7 @@ var IncremarkCode_default = /* @__PURE__ */ _defineComponent5({
624
869
  mermaidLoading.value = false;
625
870
  }
626
871
  }
627
- onUnmounted3(() => {
872
+ onUnmounted5(() => {
628
873
  if (mermaidTimer) {
629
874
  clearTimeout(mermaidTimer);
630
875
  }
@@ -677,7 +922,7 @@ var IncremarkCode_default = /* @__PURE__ */ _defineComponent5({
677
922
  isHighlighting.value = false;
678
923
  }
679
924
  }
680
- watch2([code, () => props.theme, isMermaid], highlight, { immediate: true });
925
+ watch4([code, () => props.theme, isMermaid], highlight, { immediate: true });
681
926
  async function copyCode() {
682
927
  try {
683
928
  await navigator.clipboard.writeText(code.value);
@@ -706,7 +951,7 @@ var _hoisted_14 = {
706
951
  };
707
952
  var _hoisted_23 = { class: "mermaid-header" };
708
953
  var _hoisted_33 = { class: "mermaid-actions" };
709
- var _hoisted_42 = ["disabled"];
954
+ var _hoisted_43 = ["disabled"];
710
955
  var _hoisted_52 = { class: "mermaid-content" };
711
956
  var _hoisted_62 = {
712
957
  key: 0,
@@ -747,7 +992,7 @@ function render5(_ctx, _cache, $props, $setup, $data, $options) {
747
992
  onClick: $setup.toggleMermaidView,
748
993
  type: "button",
749
994
  disabled: !$setup.mermaidSvg
750
- }, _toDisplayString3($setup.mermaidViewMode === "preview" ? "\u6E90\u7801" : "\u9884\u89C8"), 9, _hoisted_42),
995
+ }, _toDisplayString3($setup.mermaidViewMode === "preview" ? "\u6E90\u7801" : "\u9884\u89C8"), 9, _hoisted_43),
751
996
  _createElementVNode3(
752
997
  "button",
753
998
  {
@@ -903,7 +1148,7 @@ var IncremarkCode_default2 = IncremarkCode_default;
903
1148
 
904
1149
  // sfc-script:/Users/yishuai/develop/ai/markdown/packages/vue/src/components/IncremarkList.vue?type=script
905
1150
  import { defineComponent as _defineComponent6 } from "vue";
906
- import { computed as computed6 } from "vue";
1151
+ import { computed as computed7 } from "vue";
907
1152
  var IncremarkList_default = /* @__PURE__ */ _defineComponent6({
908
1153
  __name: "IncremarkList",
909
1154
  props: {
@@ -912,7 +1157,7 @@ var IncremarkList_default = /* @__PURE__ */ _defineComponent6({
912
1157
  setup(__props, { expose: __expose }) {
913
1158
  __expose();
914
1159
  const props = __props;
915
- const tag = computed6(() => props.node.ordered ? "ol" : "ul");
1160
+ const tag = computed7(() => props.node.ordered ? "ol" : "ul");
916
1161
  function getItemContent(item) {
917
1162
  const firstChild = item.children[0];
918
1163
  if (firstChild?.type === "paragraph") {
@@ -1234,7 +1479,7 @@ var Incremark_default = /* @__PURE__ */ _defineComponent11({
1234
1479
  math: IncremarkMath_default2,
1235
1480
  inlineMath: IncremarkMath_default2
1236
1481
  };
1237
- const mergedComponents = computed7(() => ({
1482
+ const mergedComponents = computed8(() => ({
1238
1483
  ...defaultComponents,
1239
1484
  ...props.components
1240
1485
  }));
@@ -1265,7 +1510,8 @@ function render11(_ctx, _cache, $props, $setup, $data, $options) {
1265
1510
  class: _normalizeClass2([
1266
1511
  "incremark-block",
1267
1512
  block.status === "completed" ? $props.completedClass : $props.pendingClass,
1268
- { "incremark-show-status": $props.showBlockStatus }
1513
+ { "incremark-show-status": $props.showBlockStatus },
1514
+ { "incremark-last-pending": block.isLastPending }
1269
1515
  ])
1270
1516
  },
1271
1517
  [
@@ -1332,7 +1578,160 @@ function render12(_ctx, _cache, $props, $setup, $data, $options) {
1332
1578
  IncremarkRenderer_default.render = render12;
1333
1579
  IncremarkRenderer_default.__file = "src/components/IncremarkRenderer.vue";
1334
1580
  var IncremarkRenderer_default2 = IncremarkRenderer_default;
1581
+
1582
+ // sfc-script:/Users/yishuai/develop/ai/markdown/packages/vue/src/components/AutoScrollContainer.vue?type=script
1583
+ import { defineComponent as _defineComponent13 } from "vue";
1584
+ import { ref as ref6, onMounted as onMounted2, onUnmounted as onUnmounted6, nextTick } from "vue";
1585
+ var AutoScrollContainer_default = /* @__PURE__ */ _defineComponent13({
1586
+ __name: "AutoScrollContainer",
1587
+ props: {
1588
+ enabled: { type: Boolean, required: false, default: true },
1589
+ threshold: { type: Number, required: false, default: 50 },
1590
+ behavior: { type: null, required: false, default: "instant" }
1591
+ },
1592
+ setup(__props, { expose: __expose }) {
1593
+ const props = __props;
1594
+ const containerRef = ref6(null);
1595
+ const isUserScrolledUp = ref6(false);
1596
+ let lastScrollTop = 0;
1597
+ let lastScrollHeight = 0;
1598
+ function isNearBottom() {
1599
+ const container = containerRef.value;
1600
+ if (!container) return true;
1601
+ const { scrollTop, scrollHeight, clientHeight } = container;
1602
+ return scrollHeight - scrollTop - clientHeight <= props.threshold;
1603
+ }
1604
+ function scrollToBottom(force = false) {
1605
+ const container = containerRef.value;
1606
+ if (!container) return;
1607
+ if (isUserScrolledUp.value && !force) return;
1608
+ container.scrollTo({
1609
+ top: container.scrollHeight,
1610
+ behavior: props.behavior
1611
+ });
1612
+ }
1613
+ function hasScrollbar() {
1614
+ const container = containerRef.value;
1615
+ if (!container) return false;
1616
+ return container.scrollHeight > container.clientHeight;
1617
+ }
1618
+ function handleScroll() {
1619
+ const container = containerRef.value;
1620
+ if (!container) return;
1621
+ const { scrollTop, scrollHeight, clientHeight } = container;
1622
+ if (scrollHeight <= clientHeight) {
1623
+ isUserScrolledUp.value = false;
1624
+ lastScrollTop = 0;
1625
+ lastScrollHeight = scrollHeight;
1626
+ return;
1627
+ }
1628
+ if (isNearBottom()) {
1629
+ isUserScrolledUp.value = false;
1630
+ } else {
1631
+ const isScrollingUp = scrollTop < lastScrollTop;
1632
+ const isContentUnchanged = scrollHeight === lastScrollHeight;
1633
+ if (isScrollingUp && isContentUnchanged) {
1634
+ isUserScrolledUp.value = true;
1635
+ }
1636
+ }
1637
+ lastScrollTop = scrollTop;
1638
+ lastScrollHeight = scrollHeight;
1639
+ }
1640
+ let observer = null;
1641
+ onMounted2(() => {
1642
+ if (!containerRef.value) return;
1643
+ lastScrollTop = containerRef.value.scrollTop;
1644
+ lastScrollHeight = containerRef.value.scrollHeight;
1645
+ observer = new MutationObserver(() => {
1646
+ nextTick(() => {
1647
+ if (!containerRef.value) return;
1648
+ if (!hasScrollbar()) {
1649
+ isUserScrolledUp.value = false;
1650
+ }
1651
+ lastScrollHeight = containerRef.value.scrollHeight;
1652
+ if (props.enabled && !isUserScrolledUp.value) {
1653
+ scrollToBottom();
1654
+ }
1655
+ });
1656
+ });
1657
+ observer.observe(containerRef.value, {
1658
+ childList: true,
1659
+ subtree: true,
1660
+ characterData: true
1661
+ });
1662
+ });
1663
+ onUnmounted6(() => {
1664
+ observer?.disconnect();
1665
+ });
1666
+ __expose({
1667
+ /** 强制滚动到底部 */
1668
+ scrollToBottom: () => scrollToBottom(true),
1669
+ /** 是否用户手动向上滚动了 */
1670
+ isUserScrolledUp: () => isUserScrolledUp.value,
1671
+ /** 容器元素引用 */
1672
+ container: containerRef
1673
+ });
1674
+ const __returned__ = { props, containerRef, isUserScrolledUp, get lastScrollTop() {
1675
+ return lastScrollTop;
1676
+ }, set lastScrollTop(v) {
1677
+ lastScrollTop = v;
1678
+ }, get lastScrollHeight() {
1679
+ return lastScrollHeight;
1680
+ }, set lastScrollHeight(v) {
1681
+ lastScrollHeight = v;
1682
+ }, isNearBottom, scrollToBottom, hasScrollbar, handleScroll, get observer() {
1683
+ return observer;
1684
+ }, set observer(v) {
1685
+ observer = v;
1686
+ } };
1687
+ Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });
1688
+ return __returned__;
1689
+ }
1690
+ });
1691
+
1692
+ // sfc-template:/Users/yishuai/develop/ai/markdown/packages/vue/src/components/AutoScrollContainer.vue?type=template
1693
+ import { renderSlot as _renderSlot, openBlock as _openBlock13, createElementBlock as _createElementBlock11 } from "vue";
1694
+ function render13(_ctx, _cache, $props, $setup, $data, $options) {
1695
+ return _openBlock13(), _createElementBlock11(
1696
+ "div",
1697
+ {
1698
+ ref: "containerRef",
1699
+ class: "auto-scroll-container",
1700
+ onScroll: $setup.handleScroll
1701
+ },
1702
+ [
1703
+ _renderSlot(_ctx.$slots, "default", {}, void 0, true)
1704
+ ],
1705
+ 544
1706
+ /* NEED_HYDRATION, NEED_PATCH */
1707
+ );
1708
+ }
1709
+
1710
+ // src/components/AutoScrollContainer.vue
1711
+ AutoScrollContainer_default.render = render13;
1712
+ AutoScrollContainer_default.__file = "src/components/AutoScrollContainer.vue";
1713
+ AutoScrollContainer_default.__scopeId = "data-v-e0d180b8";
1714
+ var AutoScrollContainer_default2 = AutoScrollContainer_default;
1715
+
1716
+ // src/index.ts
1717
+ import {
1718
+ BlockTransformer as BlockTransformer2,
1719
+ createBlockTransformer as createBlockTransformer3,
1720
+ countChars,
1721
+ sliceAst,
1722
+ cloneNode,
1723
+ codeBlockPlugin,
1724
+ mermaidPlugin,
1725
+ imagePlugin,
1726
+ mathPlugin,
1727
+ thematicBreakPlugin,
1728
+ defaultPlugins as defaultPlugins2,
1729
+ allPlugins,
1730
+ createPlugin
1731
+ } from "@incremark/core";
1335
1732
  export {
1733
+ AutoScrollContainer_default2 as AutoScrollContainer,
1734
+ BlockTransformer2 as BlockTransformer,
1336
1735
  Incremark_default2 as Incremark,
1337
1736
  IncremarkBlockquote_default2 as IncremarkBlockquote,
1338
1737
  IncremarkCode_default2 as IncremarkCode,
@@ -1345,6 +1744,19 @@ export {
1345
1744
  IncremarkRenderer_default2 as IncremarkRenderer,
1346
1745
  IncremarkTable_default2 as IncremarkTable,
1347
1746
  IncremarkThematicBreak_default2 as IncremarkThematicBreak,
1747
+ allPlugins,
1748
+ cloneNode,
1749
+ codeBlockPlugin,
1750
+ countChars,
1751
+ createBlockTransformer3 as createBlockTransformer,
1752
+ createPlugin,
1753
+ defaultPlugins2 as defaultPlugins,
1754
+ imagePlugin,
1755
+ mathPlugin,
1756
+ mermaidPlugin,
1757
+ sliceAst,
1758
+ thematicBreakPlugin,
1759
+ useBlockTransformer,
1348
1760
  useDevTools,
1349
1761
  useIncremark,
1350
1762
  useStreamRenderer