@incremark/react 0.0.4 → 0.0.5

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
@@ -7,6 +7,7 @@ Incremark 的 React 18+ 集成库。
7
7
  ## 特性
8
8
 
9
9
  - 📦 **开箱即用** - 提供 `useIncremark` hook 和 `<Incremark>` 组件
10
+ - ⌨️ **打字机效果** - 内置 `useBlockTransformer` 实现逐字符显示
10
11
  - 🎨 **可定制** - 支持自定义渲染组件
11
12
  - ⚡ **高性能** - 利用 React 的 reconciliation 机制
12
13
  - 🔧 **DevTools** - 内置开发者工具
@@ -71,11 +72,96 @@ function App() {
71
72
  | `blocks` | `Block[]` | 所有块 |
72
73
  | `completedBlocks` | `Block[]` | 已完成块 |
73
74
  | `pendingBlocks` | `Block[]` | 待处理块 |
75
+ | `isLoading` | `boolean` | 是否正在加载 |
74
76
  | `append` | `Function` | 追加内容 |
75
77
  | `finalize` | `Function` | 完成解析 |
76
78
  | `reset` | `Function` | 重置状态 |
77
79
  | `render` | `Function` | 一次性渲染(reset + append + finalize) |
78
80
 
81
+ ### useBlockTransformer(sourceBlocks, options)
82
+
83
+ 打字机效果 hook。作为解析器和渲染器之间的中间层,控制内容的逐步显示。
84
+
85
+ **参数:**
86
+
87
+ | 参数 | 类型 | 说明 |
88
+ |------|------|------|
89
+ | `sourceBlocks` | `SourceBlock[]` | 源 blocks(通常来自 `completedBlocks`) |
90
+ | `options.charsPerTick` | `number` | 每次显示的字符数(默认:2) |
91
+ | `options.tickInterval` | `number` | 每次显示的间隔时间 ms(默认:50) |
92
+ | `options.plugins` | `TransformerPlugin[]` | 插件列表(用于特殊块的处理) |
93
+
94
+ **返回值:**
95
+
96
+ | 属性 | 类型 | 说明 |
97
+ |------|------|------|
98
+ | `displayBlocks` | `DisplayBlock[]` | 用于渲染的 blocks |
99
+ | `isProcessing` | `boolean` | 是否正在处理中 |
100
+ | `skip` | `Function` | 跳过动画,立即显示全部内容 |
101
+ | `reset` | `Function` | 重置状态 |
102
+ | `setOptions` | `Function` | 动态更新配置 |
103
+
104
+ **使用示例:**
105
+
106
+ ```tsx
107
+ import { useMemo, useState, useEffect } from 'react'
108
+ import { useIncremark, useBlockTransformer, Incremark, defaultPlugins } from '@incremark/react'
109
+
110
+ function App() {
111
+ const { completedBlocks, append, finalize, reset: resetParser } = useIncremark()
112
+
113
+ // 配置打字机速度
114
+ const [typewriterSpeed, setTypewriterSpeed] = useState(2)
115
+ const [typewriterInterval, setTypewriterInterval] = useState(50)
116
+
117
+ // 转换为 SourceBlock 格式
118
+ const sourceBlocks = useMemo(() =>
119
+ completedBlocks.map(block => ({
120
+ id: block.id,
121
+ node: block.node,
122
+ status: block.status
123
+ })),
124
+ [completedBlocks]
125
+ )
126
+
127
+ // 使用 BlockTransformer
128
+ const {
129
+ displayBlocks,
130
+ isProcessing,
131
+ skip,
132
+ reset: resetTransformer,
133
+ setOptions
134
+ } = useBlockTransformer(sourceBlocks, {
135
+ charsPerTick: typewriterSpeed,
136
+ tickInterval: typewriterInterval,
137
+ plugins: defaultPlugins
138
+ })
139
+
140
+ // 监听配置变化
141
+ useEffect(() => {
142
+ setOptions({ charsPerTick: typewriterSpeed, tickInterval: typewriterInterval })
143
+ }, [typewriterSpeed, typewriterInterval, setOptions])
144
+
145
+ // 转换为渲染格式
146
+ const renderBlocks = useMemo(() =>
147
+ displayBlocks.map(db => ({
148
+ ...db,
149
+ stableId: db.id,
150
+ node: db.displayNode,
151
+ status: db.isDisplayComplete ? 'completed' : 'pending'
152
+ })),
153
+ [displayBlocks]
154
+ )
155
+
156
+ return (
157
+ <div>
158
+ <Incremark blocks={renderBlocks} />
159
+ {isProcessing && <button onClick={skip}>跳过</button>}
160
+ </div>
161
+ )
162
+ }
163
+ ```
164
+
79
165
  ### useDevTools(incremark)
80
166
 
81
167
  启用 DevTools。
@@ -93,9 +179,18 @@ useDevTools(incremark)
93
179
  <Incremark
94
180
  blocks={blocks}
95
181
  components={{ heading: MyHeading }}
182
+ showBlockStatus={true}
96
183
  />
97
184
  ```
98
185
 
186
+ **Props:**
187
+
188
+ | Prop | 类型 | 说明 |
189
+ |------|------|------|
190
+ | `blocks` | `Block[]` | 要渲染的 blocks |
191
+ | `components` | `object` | 自定义组件映射 |
192
+ | `showBlockStatus` | `boolean` | 是否显示块状态(pending/completed) |
193
+
99
194
  ## 自定义组件
100
195
 
101
196
  ```tsx
@@ -114,6 +209,44 @@ function App() {
114
209
  }
115
210
  ```
116
211
 
212
+ ## 插件系统
213
+
214
+ BlockTransformer 支持插件来处理特殊类型的块:
215
+
216
+ ```tsx
217
+ import {
218
+ defaultPlugins,
219
+ codeBlockPlugin,
220
+ imagePlugin,
221
+ mermaidPlugin,
222
+ mathPlugin,
223
+ thematicBreakPlugin,
224
+ createPlugin
225
+ } from '@incremark/react'
226
+
227
+ // 使用默认插件集
228
+ const { displayBlocks } = useBlockTransformer(sourceBlocks, {
229
+ plugins: defaultPlugins
230
+ })
231
+
232
+ // 或自定义插件
233
+ const myPlugin = createPlugin({
234
+ name: 'my-plugin',
235
+ match: (node) => node.type === 'myType',
236
+ transform: (node) => ({ displayNode: node, isComplete: true })
237
+ })
238
+ ```
239
+
240
+ **内置插件:**
241
+
242
+ | 插件 | 说明 |
243
+ |------|------|
244
+ | `codeBlockPlugin` | 代码块整体显示 |
245
+ | `imagePlugin` | 图片整体显示 |
246
+ | `mermaidPlugin` | Mermaid 图表整体显示 |
247
+ | `mathPlugin` | 数学公式整体显示 |
248
+ | `thematicBreakPlugin` | 分隔线整体显示 |
249
+
117
250
  ## 与 React Query 集成
118
251
 
119
252
  ```tsx
@@ -143,7 +276,61 @@ function StreamingContent() {
143
276
  }
144
277
  ```
145
278
 
279
+ ## AutoScrollContainer
280
+
281
+ 自动滚动容器组件,适用于流式内容场景:
282
+
283
+ ```tsx
284
+ import { useRef, useState } from 'react'
285
+ import { AutoScrollContainer, Incremark, type AutoScrollContainerRef } from '@incremark/react'
286
+
287
+ function App() {
288
+ const scrollRef = useRef<AutoScrollContainerRef>(null)
289
+ const [autoScrollEnabled, setAutoScrollEnabled] = useState(true)
290
+
291
+ return (
292
+ <div>
293
+ <AutoScrollContainer
294
+ ref={scrollRef}
295
+ enabled={autoScrollEnabled}
296
+ threshold={50}
297
+ behavior="smooth"
298
+ >
299
+ <Incremark blocks={blocks} />
300
+ </AutoScrollContainer>
301
+
302
+ {/* 显示滚动状态 */}
303
+ {scrollRef.current?.isUserScrolledUp() && (
304
+ <span>用户已暂停自动滚动</span>
305
+ )}
306
+
307
+ {/* 强制滚动到底部 */}
308
+ <button onClick={() => scrollRef.current?.scrollToBottom()}>
309
+ 滚动到底部
310
+ </button>
311
+ </div>
312
+ )
313
+ }
314
+ ```
315
+
316
+ **Props:**
317
+
318
+ | Prop | 类型 | 默认值 | 说明 |
319
+ |------|------|--------|------|
320
+ | `enabled` | `boolean` | `true` | 是否启用自动滚动 |
321
+ | `threshold` | `number` | `50` | 触发自动滚动的底部阈值(像素) |
322
+ | `behavior` | `ScrollBehavior` | `'instant'` | 滚动行为 |
323
+ | `className` | `string` | - | 容器类名 |
324
+ | `style` | `CSSProperties` | - | 容器样式 |
325
+
326
+ **Ref 方法(通过 useRef):**
327
+
328
+ | 方法 | 说明 |
329
+ |------|------|
330
+ | `scrollToBottom()` | 强制滚动到底部 |
331
+ | `isUserScrolledUp()` | 返回用户是否手动向上滚动了 |
332
+ | `container` | 容器 DOM 元素引用 |
333
+
146
334
  ## License
147
335
 
148
336
  MIT
149
-
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { ParserOptions, ParsedBlock, Root, IncrementalUpdate, IncremarkParser, RootContent } from '@incremark/core';
2
- export { IncrementalUpdate, ParsedBlock, ParserOptions, Root, RootContent } from '@incremark/core';
1
+ import { ParserOptions, ParsedBlock, Root, IncrementalUpdate, IncremarkParser, SourceBlock, TransformerOptions, DisplayBlock, AnimationEffect, BlockTransformer, RootContent } from '@incremark/core';
2
+ export { AnimationEffect, BlockTransformer, DisplayBlock, IncrementalUpdate, ParsedBlock, ParserOptions, Root, RootContent, SourceBlock, TransformerOptions, TransformerPlugin, TransformerState, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin } from '@incremark/core';
3
3
  import * as _incremark_devtools from '@incremark/devtools';
4
4
  import { DevToolsOptions } from '@incremark/devtools';
5
5
  import React from 'react';
@@ -77,8 +77,78 @@ interface UseDevToolsOptions extends DevToolsOptions {
77
77
  */
78
78
  declare function useDevTools(incremark: UseIncremarkReturn, options?: UseDevToolsOptions): _incremark_devtools.IncremarkDevTools | null;
79
79
 
80
+ interface UseBlockTransformerOptions extends Omit<TransformerOptions, 'onChange'> {
81
+ }
82
+ interface UseBlockTransformerReturn<T = unknown> {
83
+ /** 用于渲染的 display blocks */
84
+ displayBlocks: DisplayBlock<T>[];
85
+ /** 是否正在处理中 */
86
+ isProcessing: boolean;
87
+ /** 是否已暂停 */
88
+ isPaused: boolean;
89
+ /** 当前动画效果 */
90
+ effect: AnimationEffect;
91
+ /** 跳过所有动画 */
92
+ skip: () => void;
93
+ /** 重置状态 */
94
+ reset: () => void;
95
+ /** 暂停动画 */
96
+ pause: () => void;
97
+ /** 恢复动画 */
98
+ resume: () => void;
99
+ /** 动态更新配置 */
100
+ setOptions: (options: Partial<Pick<TransformerOptions, 'charsPerTick' | 'tickInterval' | 'effect' | 'pauseOnHidden'>>) => void;
101
+ /** transformer 实例(用于高级用法) */
102
+ transformer: BlockTransformer<T>;
103
+ }
104
+ /**
105
+ * React Hook: Block Transformer
106
+ *
107
+ * 用于控制 blocks 的逐步显示(打字机效果)
108
+ * 作为解析器和渲染器之间的中间层
109
+ *
110
+ * 特性:
111
+ * - 使用 requestAnimationFrame 实现流畅动画
112
+ * - 支持随机步长 `charsPerTick: [1, 3]`
113
+ * - 支持动画效果 `effect: 'typing'`
114
+ * - 页面不可见时自动暂停
115
+ *
116
+ * @example
117
+ * ```tsx
118
+ * import { useIncremark, useBlockTransformer, defaultPlugins } from '@incremark/react'
119
+ *
120
+ * function App() {
121
+ * const { completedBlocks, append, finalize } = useIncremark()
122
+ *
123
+ * // 转换为 SourceBlock 格式
124
+ * const sourceBlocks = useMemo(() => completedBlocks.map(block => ({
125
+ * id: block.id,
126
+ * node: block.node,
127
+ * status: block.status
128
+ * })), [completedBlocks])
129
+ *
130
+ * // 添加打字机效果
131
+ * const { displayBlocks, isProcessing, skip, effect } = useBlockTransformer(sourceBlocks, {
132
+ * charsPerTick: [1, 3], // 随机步长
133
+ * tickInterval: 30,
134
+ * effect: 'typing', // 光标效果
135
+ * plugins: defaultPlugins
136
+ * })
137
+ *
138
+ * return (
139
+ * <div className={effect === 'typing' ? 'typing' : ''}>
140
+ * <Incremark blocks={displayBlocks} />
141
+ * {isProcessing && <button onClick={skip}>跳过</button>}
142
+ * </div>
143
+ * )
144
+ * }
145
+ * ```
146
+ */
147
+ declare function useBlockTransformer<T = unknown>(sourceBlocks: SourceBlock<T>[], options?: UseBlockTransformerOptions): UseBlockTransformerReturn<T>;
148
+
80
149
  interface BlockWithStableId extends ParsedBlock {
81
150
  stableId: string;
151
+ isLastPending?: boolean;
82
152
  }
83
153
  interface IncremarkProps {
84
154
  /** 要渲染的块列表 */
@@ -118,4 +188,46 @@ interface IncremarkRendererProps {
118
188
  */
119
189
  declare const IncremarkRenderer: React.FC<IncremarkRendererProps>;
120
190
 
121
- export { Incremark, type IncremarkProps, IncremarkRenderer, type IncremarkRendererProps, type UseDevToolsOptions, type UseIncremarkOptions, type UseIncremarkReturn, useDevTools, useIncremark };
191
+ interface AutoScrollContainerProps {
192
+ /** 子元素 */
193
+ children: React.ReactNode;
194
+ /** 是否启用自动滚动 */
195
+ enabled?: boolean;
196
+ /** 触发自动滚动的底部阈值(像素) */
197
+ threshold?: number;
198
+ /** 滚动行为 */
199
+ behavior?: ScrollBehavior;
200
+ /** 容器样式 */
201
+ style?: React.CSSProperties;
202
+ /** 容器类名 */
203
+ className?: string;
204
+ }
205
+ interface AutoScrollContainerRef {
206
+ /** 强制滚动到底部 */
207
+ scrollToBottom: () => void;
208
+ /** 是否用户手动向上滚动了 */
209
+ isUserScrolledUp: () => boolean;
210
+ /** 容器元素引用 */
211
+ container: HTMLDivElement | null;
212
+ }
213
+ /**
214
+ * 自动滚动容器
215
+ *
216
+ * 当内容更新时自动滚动到底部。
217
+ * 如果用户手动向上滚动,则暂停自动滚动,直到用户再次滚动到底部。
218
+ *
219
+ * @example
220
+ * ```tsx
221
+ * const scrollRef = useRef<AutoScrollContainerRef>(null)
222
+ *
223
+ * <AutoScrollContainer ref={scrollRef} enabled={true}>
224
+ * <Incremark blocks={blocks} />
225
+ * </AutoScrollContainer>
226
+ *
227
+ * // 强制滚动到底部
228
+ * scrollRef.current?.scrollToBottom()
229
+ * ```
230
+ */
231
+ declare const AutoScrollContainer: React.ForwardRefExoticComponent<AutoScrollContainerProps & React.RefAttributes<AutoScrollContainerRef>>;
232
+
233
+ export { AutoScrollContainer, type AutoScrollContainerProps, type AutoScrollContainerRef, Incremark, type IncremarkProps, IncremarkRenderer, type IncremarkRendererProps, type UseBlockTransformerOptions, type UseBlockTransformerReturn, type UseDevToolsOptions, type UseIncremarkOptions, type UseIncremarkReturn, useBlockTransformer, useDevTools, useIncremark };
package/dist/index.js CHANGED
@@ -137,6 +137,80 @@ function useDevTools(incremark, options = {}) {
137
137
  return devtoolsRef.current;
138
138
  }
139
139
 
140
+ // src/hooks/useBlockTransformer.ts
141
+ import { useState as useState2, useCallback as useCallback2, useRef as useRef3, useEffect as useEffect2 } from "react";
142
+ import {
143
+ createBlockTransformer
144
+ } from "@incremark/core";
145
+ function useBlockTransformer(sourceBlocks, options = {}) {
146
+ const [displayBlocks, setDisplayBlocks] = useState2([]);
147
+ const [isProcessing, setIsProcessing] = useState2(false);
148
+ const [isPaused, setIsPaused] = useState2(false);
149
+ const [effect, setEffect] = useState2(options.effect ?? "none");
150
+ const transformerRef = useRef3(null);
151
+ if (!transformerRef.current) {
152
+ transformerRef.current = createBlockTransformer({
153
+ ...options,
154
+ onChange: (blocks) => {
155
+ setDisplayBlocks(blocks);
156
+ setIsProcessing(transformerRef.current?.isProcessing() ?? false);
157
+ setIsPaused(transformerRef.current?.isPausedState() ?? false);
158
+ }
159
+ });
160
+ }
161
+ const transformer = transformerRef.current;
162
+ useEffect2(() => {
163
+ transformer.push(sourceBlocks);
164
+ const currentDisplaying = displayBlocks.find((b) => !b.isDisplayComplete);
165
+ if (currentDisplaying) {
166
+ const updated = sourceBlocks.find((b) => b.id === currentDisplaying.id);
167
+ if (updated) {
168
+ transformer.update(updated);
169
+ }
170
+ }
171
+ }, [sourceBlocks, transformer]);
172
+ useEffect2(() => {
173
+ return () => {
174
+ transformer.destroy();
175
+ };
176
+ }, [transformer]);
177
+ const skip = useCallback2(() => {
178
+ transformer.skip();
179
+ }, [transformer]);
180
+ const reset = useCallback2(() => {
181
+ transformer.reset();
182
+ }, [transformer]);
183
+ const pause = useCallback2(() => {
184
+ transformer.pause();
185
+ setIsPaused(true);
186
+ }, [transformer]);
187
+ const resume = useCallback2(() => {
188
+ transformer.resume();
189
+ setIsPaused(false);
190
+ }, [transformer]);
191
+ const setOptionsCallback = useCallback2(
192
+ (opts) => {
193
+ transformer.setOptions(opts);
194
+ if (opts.effect !== void 0) {
195
+ setEffect(opts.effect);
196
+ }
197
+ },
198
+ [transformer]
199
+ );
200
+ return {
201
+ displayBlocks,
202
+ isProcessing,
203
+ isPaused,
204
+ effect,
205
+ skip,
206
+ reset,
207
+ pause,
208
+ resume,
209
+ setOptions: setOptionsCallback,
210
+ transformer
211
+ };
212
+ }
213
+
140
214
  // src/components/IncremarkRenderer.tsx
141
215
  import React from "react";
142
216
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -210,18 +284,176 @@ var Incremark = ({
210
284
  showBlockStatus = true,
211
285
  className = ""
212
286
  }) => {
213
- return /* @__PURE__ */ jsx2("div", { className: `incremark ${className}`, children: blocks.map((block) => /* @__PURE__ */ jsx2(
214
- "div",
215
- {
216
- className: `incremark-block ${showBlockStatus && block.status === "pending" ? "pending" : ""}`,
217
- children: /* @__PURE__ */ jsx2(IncremarkRenderer, { node: block.node, components })
218
- },
219
- block.stableId
220
- )) });
287
+ return /* @__PURE__ */ jsx2("div", { className: `incremark ${className}`, children: blocks.map((block) => {
288
+ const isPending = block.status === "pending";
289
+ const classes = [
290
+ "incremark-block",
291
+ isPending ? "incremark-pending" : "incremark-completed",
292
+ block.isLastPending ? "incremark-last-pending" : ""
293
+ ].filter(Boolean).join(" ");
294
+ return /* @__PURE__ */ jsx2("div", { className: classes, children: /* @__PURE__ */ jsx2(IncremarkRenderer, { node: block.node, components }) }, block.stableId);
295
+ }) });
221
296
  };
297
+
298
+ // src/components/AutoScrollContainer.tsx
299
+ import {
300
+ useRef as useRef4,
301
+ useEffect as useEffect3,
302
+ useCallback as useCallback3,
303
+ useState as useState3,
304
+ forwardRef,
305
+ useImperativeHandle
306
+ } from "react";
307
+ import { jsx as jsx3 } from "react/jsx-runtime";
308
+ var AutoScrollContainer = forwardRef(
309
+ ({
310
+ children,
311
+ enabled = true,
312
+ threshold = 50,
313
+ behavior = "instant",
314
+ style,
315
+ className
316
+ }, ref) => {
317
+ const containerRef = useRef4(null);
318
+ const [isUserScrolledUp, setIsUserScrolledUp] = useState3(false);
319
+ const lastScrollTopRef = useRef4(0);
320
+ const lastScrollHeightRef = useRef4(0);
321
+ const isNearBottom = useCallback3(() => {
322
+ const container = containerRef.current;
323
+ if (!container) return true;
324
+ const { scrollTop, scrollHeight, clientHeight } = container;
325
+ return scrollHeight - scrollTop - clientHeight <= threshold;
326
+ }, [threshold]);
327
+ const scrollToBottom = useCallback3(
328
+ (force = false) => {
329
+ const container = containerRef.current;
330
+ if (!container) return;
331
+ if (isUserScrolledUp && !force) return;
332
+ container.scrollTo({
333
+ top: container.scrollHeight,
334
+ behavior
335
+ });
336
+ },
337
+ [isUserScrolledUp, behavior]
338
+ );
339
+ const hasScrollbar = useCallback3(() => {
340
+ const container = containerRef.current;
341
+ if (!container) return false;
342
+ return container.scrollHeight > container.clientHeight;
343
+ }, []);
344
+ const handleScroll = useCallback3(() => {
345
+ const container = containerRef.current;
346
+ if (!container) return;
347
+ const { scrollTop, scrollHeight, clientHeight } = container;
348
+ if (scrollHeight <= clientHeight) {
349
+ setIsUserScrolledUp(false);
350
+ lastScrollTopRef.current = 0;
351
+ lastScrollHeightRef.current = scrollHeight;
352
+ return;
353
+ }
354
+ if (isNearBottom()) {
355
+ setIsUserScrolledUp(false);
356
+ } else {
357
+ const isScrollingUp = scrollTop < lastScrollTopRef.current;
358
+ const isContentUnchanged = scrollHeight === lastScrollHeightRef.current;
359
+ if (isScrollingUp && isContentUnchanged) {
360
+ setIsUserScrolledUp(true);
361
+ }
362
+ }
363
+ lastScrollTopRef.current = scrollTop;
364
+ lastScrollHeightRef.current = scrollHeight;
365
+ }, [isNearBottom]);
366
+ useEffect3(() => {
367
+ const container = containerRef.current;
368
+ if (container) {
369
+ lastScrollTopRef.current = container.scrollTop;
370
+ lastScrollHeightRef.current = container.scrollHeight;
371
+ }
372
+ }, []);
373
+ useEffect3(() => {
374
+ const container = containerRef.current;
375
+ if (!container || !enabled) return;
376
+ const observer = new MutationObserver(() => {
377
+ requestAnimationFrame(() => {
378
+ if (!containerRef.current) return;
379
+ const { scrollHeight, clientHeight } = containerRef.current;
380
+ if (scrollHeight <= clientHeight) {
381
+ setIsUserScrolledUp(false);
382
+ }
383
+ lastScrollHeightRef.current = scrollHeight;
384
+ if (!isUserScrolledUp) {
385
+ scrollToBottom();
386
+ }
387
+ });
388
+ });
389
+ observer.observe(container, {
390
+ childList: true,
391
+ subtree: true,
392
+ characterData: true
393
+ });
394
+ return () => observer.disconnect();
395
+ }, [enabled, isUserScrolledUp, scrollToBottom]);
396
+ useImperativeHandle(
397
+ ref,
398
+ () => ({
399
+ scrollToBottom: () => scrollToBottom(true),
400
+ isUserScrolledUp: () => isUserScrolledUp,
401
+ container: containerRef.current
402
+ }),
403
+ [scrollToBottom, isUserScrolledUp]
404
+ );
405
+ return /* @__PURE__ */ jsx3(
406
+ "div",
407
+ {
408
+ ref: containerRef,
409
+ className,
410
+ style: {
411
+ overflowY: "auto",
412
+ height: "100%",
413
+ ...style
414
+ },
415
+ onScroll: handleScroll,
416
+ children
417
+ }
418
+ );
419
+ }
420
+ );
421
+ AutoScrollContainer.displayName = "AutoScrollContainer";
422
+
423
+ // src/index.ts
424
+ import {
425
+ BlockTransformer as BlockTransformer2,
426
+ createBlockTransformer as createBlockTransformer2,
427
+ countChars,
428
+ sliceAst,
429
+ cloneNode,
430
+ codeBlockPlugin,
431
+ mermaidPlugin,
432
+ imagePlugin,
433
+ mathPlugin,
434
+ thematicBreakPlugin,
435
+ defaultPlugins,
436
+ allPlugins,
437
+ createPlugin
438
+ } from "@incremark/core";
222
439
  export {
440
+ AutoScrollContainer,
441
+ BlockTransformer2 as BlockTransformer,
223
442
  Incremark,
224
443
  IncremarkRenderer,
444
+ allPlugins,
445
+ cloneNode,
446
+ codeBlockPlugin,
447
+ countChars,
448
+ createBlockTransformer2 as createBlockTransformer,
449
+ createPlugin,
450
+ defaultPlugins,
451
+ imagePlugin,
452
+ mathPlugin,
453
+ mermaidPlugin,
454
+ sliceAst,
455
+ thematicBreakPlugin,
456
+ useBlockTransformer,
225
457
  useDevTools,
226
458
  useIncremark
227
459
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@incremark/react",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "license": "MIT",
5
5
  "description": "Incremark React integration - Incremental Markdown parser for AI streaming",
6
6
  "type": "module",
@@ -19,10 +19,10 @@
19
19
  ],
20
20
  "peerDependencies": {
21
21
  "react": ">=18.0.0",
22
- "@incremark/core": "0.0.4"
22
+ "@incremark/core": "0.0.5"
23
23
  },
24
24
  "dependencies": {
25
- "@incremark/devtools": "0.0.4"
25
+ "@incremark/devtools": "0.0.5"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/react": "^18.2.0",