@anvia/react-ui 0.6.2 → 0.7.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.
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  Composer,
3
3
  Thread
4
- } from "../chunk-LM5SU5AP.js";
5
- import "../chunk-7I6XUWM7.js";
6
- import "../chunk-UESNSQGQ.js";
4
+ } from "../chunk-FABSIZZ7.js";
5
+ import "../chunk-JWZDNM5D.js";
6
+ import "../chunk-ULGJLLZE.js";
7
7
  import "../chunk-SBYUKODG.js";
8
8
  import "../chunk-T3ZFZ3NP.js";
9
9
  import {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Message,
3
3
  shiftComposerEntityRanges
4
- } from "./chunk-7I6XUWM7.js";
4
+ } from "./chunk-JWZDNM5D.js";
5
5
  import {
6
6
  Attachment
7
7
  } from "./chunk-SBYUKODG.js";
@@ -1572,4 +1572,4 @@ export {
1572
1572
  Composer,
1573
1573
  Thread
1574
1574
  };
1575
- //# sourceMappingURL=chunk-LM5SU5AP.js.map
1575
+ //# sourceMappingURL=chunk-FABSIZZ7.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StreamMarkdown
3
- } from "./chunk-UESNSQGQ.js";
3
+ } from "./chunk-ULGJLLZE.js";
4
4
  import {
5
5
  Attachment
6
6
  } from "./chunk-SBYUKODG.js";
@@ -1057,4 +1057,4 @@ export {
1057
1057
  shiftComposerEntityRanges,
1058
1058
  Message
1059
1059
  };
1060
- //# sourceMappingURL=chunk-7I6XUWM7.js.map
1060
+ //# sourceMappingURL=chunk-JWZDNM5D.js.map
@@ -7,6 +7,7 @@ import {
7
7
  createElement,
8
8
  forwardRef,
9
9
  memo,
10
+ useCallback,
10
11
  useMemo,
11
12
  useRef
12
13
  } from "react";
@@ -53,16 +54,22 @@ function splitStreamMarkdownBlocks(content, options = {}) {
53
54
  // src/stream/markdown-reveal.ts
54
55
  var streamRevealConfig = {
55
56
  bandGraphemes: 2,
57
+ durationMs: 180,
56
58
  minimumOpacity: 0.12,
57
59
  tailGraphemes: 24
58
60
  };
59
- function createStreamGradientRevealPlugin(frameId) {
61
+ function createStreamGradientRevealPlugin(lifecycleScope, lifecycle) {
60
62
  return function streamGradientRevealPlugin() {
61
63
  return (tree) => {
64
+ if (lifecycle.activeScope !== lifecycleScope) {
65
+ lifecycle.activeScope = lifecycleScope;
66
+ lifecycle.settledRevealIds.clear();
67
+ lifecycle.startedAtByRevealId.clear();
68
+ }
62
69
  const targets = collectTextTargets(tree);
63
70
  const renderedText = targets.map((target) => target.node.value ?? "").join("");
64
- const bands = createRevealBands(renderedText);
65
- wrapStreamRevealBands(targets, bands, frameId);
71
+ const graphemes = createRevealGraphemes(renderedText);
72
+ wrapStreamRevealGraphemes(targets, graphemes, lifecycleScope, lifecycle);
66
73
  };
67
74
  };
68
75
  }
@@ -95,56 +102,77 @@ function collectTextTargets(tree) {
95
102
  visit(tree, void 0, 0);
96
103
  return targets;
97
104
  }
98
- function createRevealBands(content) {
105
+ function createRevealGraphemes(content) {
99
106
  const tail = segmentTailGraphemeRanges(content, streamRevealConfig.tailGraphemes);
100
- const bands = [];
101
- for (let index = 0; index < tail.length; index += streamRevealConfig.bandGraphemes) {
102
- const first = tail[index];
103
- const endIndex = Math.min(index + streamRevealConfig.bandGraphemes, tail.length) - 1;
104
- const last = tail[endIndex];
105
- if (first === void 0 || last === void 0) {
107
+ const graphemes = [];
108
+ for (let index = 0; index < tail.length; index += 1) {
109
+ const grapheme = tail[index];
110
+ const bandStartIndex = Math.floor(index / streamRevealConfig.bandGraphemes) * streamRevealConfig.bandGraphemes;
111
+ const bandEndIndex = Math.min(bandStartIndex + streamRevealConfig.bandGraphemes, tail.length) - 1;
112
+ if (grapheme === void 0) {
106
113
  continue;
107
114
  }
108
- const progress = tail.length === 1 ? 1 : endIndex / (tail.length - 1);
109
- bands.push({
110
- startOffset: first.startOffset,
111
- endOffset: last.endOffset,
115
+ const progress = tail.length === 1 ? 1 : bandEndIndex / (tail.length - 1);
116
+ graphemes.push({
117
+ startOffset: grapheme.startOffset,
118
+ endOffset: grapheme.endOffset,
112
119
  opacity: Math.max(
113
120
  streamRevealConfig.minimumOpacity,
114
121
  1 - progress * (1 - streamRevealConfig.minimumOpacity)
115
122
  )
116
123
  });
117
124
  }
118
- return bands;
125
+ return graphemes;
119
126
  }
120
- function wrapStreamRevealBands(targets, bands, frameId) {
127
+ function wrapStreamRevealGraphemes(targets, graphemes, lifecycleScope, lifecycle) {
128
+ const activeRevealIds = /* @__PURE__ */ new Set();
129
+ const nowMs = Date.now();
121
130
  for (const target of [...targets].reverse()) {
122
131
  const value = target.node.value ?? "";
123
- const intersectingBands = bands.filter(
124
- (band) => band.endOffset > target.startOffset && band.startOffset < target.endOffset
132
+ const intersectingGraphemes = graphemes.filter(
133
+ (grapheme) => grapheme.endOffset > target.startOffset && grapheme.startOffset < target.endOffset
125
134
  );
126
- if (intersectingBands.length === 0) {
135
+ if (intersectingGraphemes.length === 0) {
127
136
  continue;
128
137
  }
129
138
  const replacement = [];
130
139
  let cursor = 0;
131
- for (const band of intersectingBands) {
132
- const start = Math.max(0, band.startOffset - target.startOffset);
133
- const end = Math.min(value.length, band.endOffset - target.startOffset);
140
+ for (const grapheme of intersectingGraphemes) {
141
+ const start = Math.max(0, grapheme.startOffset - target.startOffset);
142
+ const end = Math.min(value.length, grapheme.endOffset - target.startOffset);
134
143
  if (start > cursor) {
135
144
  replacement.push({ type: "text", value: value.slice(cursor, start) });
136
145
  }
137
146
  if (end > start) {
138
- replacement.push({
139
- type: "element",
140
- tagName: "span",
141
- properties: {
142
- "data-anvia-stream-frame-id": `${frameId}:${band.startOffset}`,
143
- "data-anvia-stream-opacity": String(band.opacity),
144
- "data-anvia-stream-reveal": ""
145
- },
146
- children: [{ type: "text", value: value.slice(start, end) }]
147
- });
147
+ const text = value.slice(start, end);
148
+ const absoluteStart = target.startOffset + start;
149
+ const absoluteEnd = target.startOffset + end;
150
+ const revealId = `${lifecycleScope}:${absoluteStart}:${absoluteEnd}:${hashText(text)}`;
151
+ activeRevealIds.add(revealId);
152
+ const startedAt = lifecycle.startedAtByRevealId.get(revealId) ?? nowMs;
153
+ lifecycle.startedAtByRevealId.set(revealId, startedAt);
154
+ const elapsedMs = Math.max(0, nowMs - startedAt);
155
+ if (elapsedMs >= streamRevealConfig.durationMs) {
156
+ lifecycle.settledRevealIds.add(revealId);
157
+ }
158
+ if (lifecycle.settledRevealIds.has(revealId)) {
159
+ replacement.push({ type: "text", value: text });
160
+ } else {
161
+ const progress = Math.min(elapsedMs / streamRevealConfig.durationMs, 1);
162
+ const opacity = grapheme.opacity + (1 - grapheme.opacity) * progress;
163
+ const remainingMs = Math.max(1, streamRevealConfig.durationMs - elapsedMs);
164
+ replacement.push({
165
+ type: "element",
166
+ tagName: "span",
167
+ properties: {
168
+ "data-anvia-stream-duration-ms": String(remainingMs),
169
+ "data-anvia-stream-opacity": String(opacity),
170
+ "data-anvia-stream-reveal": "",
171
+ "data-anvia-stream-reveal-id": revealId
172
+ },
173
+ children: [{ type: "text", value: text }]
174
+ });
175
+ }
148
176
  }
149
177
  cursor = Math.max(cursor, end);
150
178
  }
@@ -153,6 +181,23 @@ function wrapStreamRevealBands(targets, bands, frameId) {
153
181
  }
154
182
  target.parent.children?.splice(target.index, 1, ...replacement);
155
183
  }
184
+ pruneRevealLifecycle(lifecycle, activeRevealIds);
185
+ }
186
+ function pruneRevealLifecycle(lifecycle, activeRevealIds) {
187
+ for (const revealId of lifecycle.startedAtByRevealId.keys()) {
188
+ if (!activeRevealIds.has(revealId)) lifecycle.startedAtByRevealId.delete(revealId);
189
+ }
190
+ for (const revealId of lifecycle.settledRevealIds) {
191
+ if (!activeRevealIds.has(revealId)) lifecycle.settledRevealIds.delete(revealId);
192
+ }
193
+ }
194
+ function hashText(text) {
195
+ let hash = 2166136261;
196
+ for (let index = 0; index < text.length; index += 1) {
197
+ hash ^= text.charCodeAt(index);
198
+ hash = Math.imul(hash, 16777619);
199
+ }
200
+ return (hash >>> 0).toString(36);
156
201
  }
157
202
  function segmentTailGraphemeRanges(content, limit) {
158
203
  if (typeof Intl !== "undefined" && typeof Intl.Segmenter === "function") {
@@ -196,19 +241,41 @@ import { createElement as createElement2 } from "react";
196
241
  var defaultRemarkPlugins = [remarkGfm];
197
242
  var StreamMarkdown = forwardRef(
198
243
  function StreamMarkdown2({ components, content, live = false, remarkPlugins, remarkRehypeOptions, ...props }, ref) {
199
- const previousContentRef = useRef(content);
200
- const frameIdRef = useRef(0);
201
- if (previousContentRef.current !== content) {
202
- previousContentRef.current = content;
203
- frameIdRef.current += 1;
244
+ const revealLifecycleRef = useRef({
245
+ activeScope: null,
246
+ content,
247
+ id: 0,
248
+ settledRevealIds: /* @__PURE__ */ new Set(),
249
+ startedAtByRevealId: /* @__PURE__ */ new Map()
250
+ });
251
+ const revealLifecycle = revealLifecycleRef.current;
252
+ if (revealLifecycle.content !== content) {
253
+ if (!content.startsWith(revealLifecycle.content)) {
254
+ revealLifecycle.id += 1;
255
+ revealLifecycle.activeScope = null;
256
+ revealLifecycle.settledRevealIds.clear();
257
+ revealLifecycle.startedAtByRevealId.clear();
258
+ }
259
+ revealLifecycle.content = content;
260
+ }
261
+ if (!live && revealLifecycle.activeScope !== null) {
262
+ for (const revealId of revealLifecycle.startedAtByRevealId.keys()) {
263
+ revealLifecycle.settledRevealIds.add(revealId);
264
+ }
204
265
  }
266
+ const settleReveal = useCallback((revealId) => {
267
+ revealLifecycleRef.current.settledRevealIds.add(revealId);
268
+ }, []);
205
269
  const blocks = useMemo(
206
270
  () => splitStreamMarkdownBlocks(content, {
207
271
  singleDocument: remarkPlugins !== void 0
208
272
  }),
209
273
  [content, remarkPlugins]
210
274
  );
211
- const streamingComponents = useMemo(() => withStreamRevealComponent(components), [components]);
275
+ const streamingComponents = useMemo(
276
+ () => withStreamRevealComponent(components, settleReveal),
277
+ [components, settleReveal]
278
+ );
212
279
  return renderPrimitive(
213
280
  "div",
214
281
  {
@@ -218,8 +285,9 @@ var StreamMarkdown = forwardRef(
218
285
  {
219
286
  components: streamingComponents,
220
287
  content: block.content,
221
- frameId: live && index === blocks.length - 1 ? frameIdRef.current : 0,
222
288
  live: live && index === blocks.length - 1,
289
+ revealLifecycle,
290
+ revealScope: `${revealLifecycle.id}:${block.startOffset}`,
223
291
  remarkPlugins: remarkPlugins ?? defaultRemarkPlugins,
224
292
  remarkRehypeOptions
225
293
  },
@@ -234,8 +302,8 @@ var StreamMarkdown = forwardRef(
234
302
  );
235
303
  var StreamMarkdownBlock = memo(function StreamMarkdownBlock2(props) {
236
304
  const revealPlugin = useMemo(
237
- () => createStreamGradientRevealPlugin(props.frameId),
238
- [props.frameId]
305
+ () => createStreamGradientRevealPlugin(props.revealScope, props.revealLifecycle),
306
+ [props.revealScope, props.revealLifecycle]
239
307
  );
240
308
  return /* @__PURE__ */ jsx(
241
309
  ReactMarkdown,
@@ -248,7 +316,7 @@ var StreamMarkdownBlock = memo(function StreamMarkdownBlock2(props) {
248
316
  }
249
317
  );
250
318
  });
251
- function withStreamRevealComponent(components) {
319
+ function withStreamRevealComponent(components, onRevealSettled) {
252
320
  const consumerSpan = components?.span;
253
321
  return {
254
322
  ...components,
@@ -256,7 +324,7 @@ function withStreamRevealComponent(components) {
256
324
  const safeProps = componentPropsWithoutKey(spanProps);
257
325
  const internalProps = safeProps;
258
326
  if (internalProps["data-anvia-stream-reveal"] !== void 0) {
259
- return streamRevealSpan(internalProps);
327
+ return streamRevealSpan(internalProps, onRevealSettled);
260
328
  }
261
329
  if (consumerSpan !== void 0) {
262
330
  return createElement(consumerSpan, safeProps);
@@ -275,22 +343,32 @@ function componentPropsWithoutKey(props) {
275
343
  }
276
344
  return result;
277
345
  }
278
- function streamRevealSpan(props) {
346
+ function streamRevealSpan(props, onRevealSettled) {
279
347
  const {
280
348
  children,
281
349
  node: _node,
282
- "data-anvia-stream-frame-id": frameId,
350
+ "data-anvia-stream-duration-ms": durationMs,
283
351
  "data-anvia-stream-opacity": opacity,
352
+ "data-anvia-stream-reveal-id": revealId,
353
+ onAnimationEnd,
284
354
  ...elementProps
285
355
  } = props;
286
356
  const parsedOpacity = Number(opacity);
357
+ const parsedDurationMs = Number(durationMs);
287
358
  return /* @__PURE__ */ createElement2(
288
359
  "span",
289
360
  {
290
361
  ...elementProps,
291
- "data-anvia-stream-frame-id": frameId,
292
- key: frameId ?? "stream-frame",
293
- style: { opacity: Number.isFinite(parsedOpacity) ? parsedOpacity : 1 }
362
+ "data-anvia-stream-reveal-id": revealId,
363
+ key: revealId ?? "stream-reveal",
364
+ onAnimationEnd: (event) => {
365
+ onAnimationEnd?.(event);
366
+ if (revealId !== void 0) onRevealSettled(revealId);
367
+ },
368
+ style: {
369
+ "--anvia-stream-reveal-opacity": Number.isFinite(parsedOpacity) ? parsedOpacity : 1,
370
+ "--anvia-stream-reveal-duration": `${Number.isFinite(parsedDurationMs) ? parsedDurationMs : 180}ms`
371
+ }
294
372
  },
295
373
  children
296
374
  );
@@ -299,4 +377,4 @@ function streamRevealSpan(props) {
299
377
  export {
300
378
  StreamMarkdown
301
379
  };
302
- //# sourceMappingURL=chunk-UESNSQGQ.js.map
380
+ //# sourceMappingURL=chunk-ULGJLLZE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stream/markdown.tsx","../src/stream/markdown-blocks.ts","../src/stream/markdown-reveal.ts"],"sourcesContent":["import {\n type ComponentPropsWithoutRef,\n type CSSProperties,\n createElement,\n forwardRef,\n memo,\n type ReactNode,\n useCallback,\n useMemo,\n useRef,\n} from \"react\";\nimport ReactMarkdown, {\n type Components,\n type Options as ReactMarkdownOptions,\n} from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { type PrimitiveProps, renderPrimitive } from \"../primitives\";\nimport { splitStreamMarkdownBlocks } from \"./markdown-blocks\";\nimport { createStreamGradientRevealPlugin, type StreamRevealLifecycle } from \"./markdown-reveal\";\n\nexport type StreamMarkdownProps = Omit<PrimitiveProps<\"div\">, \"children\"> & {\n components?: Components;\n content: string;\n live?: boolean;\n remarkPlugins?: ReactMarkdownOptions[\"remarkPlugins\"];\n remarkRehypeOptions?: ReactMarkdownOptions[\"remarkRehypeOptions\"];\n};\n\nconst defaultRemarkPlugins: NonNullable<ReactMarkdownOptions[\"remarkPlugins\"]> = [remarkGfm];\n\nexport const StreamMarkdown = forwardRef<HTMLDivElement, StreamMarkdownProps>(\n function StreamMarkdown(\n { components, content, live = false, remarkPlugins, remarkRehypeOptions, ...props },\n ref,\n ) {\n const revealLifecycleRef = useRef({\n activeScope: null,\n content,\n id: 0,\n settledRevealIds: new Set<string>(),\n startedAtByRevealId: new Map<string, number>(),\n } satisfies StreamRevealLifecycle & { content: string; id: number });\n const revealLifecycle = revealLifecycleRef.current;\n if (revealLifecycle.content !== content) {\n if (!content.startsWith(revealLifecycle.content)) {\n revealLifecycle.id += 1;\n revealLifecycle.activeScope = null;\n revealLifecycle.settledRevealIds.clear();\n revealLifecycle.startedAtByRevealId.clear();\n }\n revealLifecycle.content = content;\n }\n if (!live && revealLifecycle.activeScope !== null) {\n for (const revealId of revealLifecycle.startedAtByRevealId.keys()) {\n revealLifecycle.settledRevealIds.add(revealId);\n }\n }\n const settleReveal = useCallback((revealId: string) => {\n revealLifecycleRef.current.settledRevealIds.add(revealId);\n }, []);\n\n const blocks = useMemo(\n () =>\n splitStreamMarkdownBlocks(content, {\n singleDocument: remarkPlugins !== undefined,\n }),\n [content, remarkPlugins],\n );\n const streamingComponents = useMemo(\n () => withStreamRevealComponent(components, settleReveal),\n [components, settleReveal],\n );\n\n return renderPrimitive(\n \"div\",\n {\n ...props,\n children: blocks.map((block, index) => (\n <StreamMarkdownBlock\n components={streamingComponents}\n content={block.content}\n live={live && index === blocks.length - 1}\n revealLifecycle={revealLifecycle}\n revealScope={`${revealLifecycle.id}:${block.startOffset}`}\n remarkPlugins={remarkPlugins ?? defaultRemarkPlugins}\n remarkRehypeOptions={remarkRehypeOptions}\n key={block.startOffset}\n />\n )),\n \"data-anvia-stream-markdown\": \"\",\n \"data-live\": live ? \"\" : undefined,\n } as PrimitiveProps<\"div\">,\n ref,\n );\n },\n);\n\nconst StreamMarkdownBlock = memo(function StreamMarkdownBlock(props: {\n components: Components;\n content: string;\n live: boolean;\n revealLifecycle: StreamRevealLifecycle;\n revealScope: string;\n remarkPlugins: ReactMarkdownOptions[\"remarkPlugins\"];\n remarkRehypeOptions: ReactMarkdownOptions[\"remarkRehypeOptions\"];\n}) {\n const revealPlugin = useMemo(\n () => createStreamGradientRevealPlugin(props.revealScope, props.revealLifecycle),\n [props.revealScope, props.revealLifecycle],\n );\n return (\n <ReactMarkdown\n components={props.components}\n rehypePlugins={props.live ? [revealPlugin] : undefined}\n remarkPlugins={props.remarkPlugins}\n remarkRehypeOptions={props.remarkRehypeOptions}\n >\n {props.content}\n </ReactMarkdown>\n );\n});\n\nfunction withStreamRevealComponent(\n components: Components | undefined,\n onRevealSettled: (revealId: string) => void,\n): Components {\n const consumerSpan = components?.span;\n return {\n ...components,\n span(spanProps) {\n const safeProps = componentPropsWithoutKey(spanProps);\n const internalProps = safeProps as typeof safeProps & {\n \"data-anvia-stream-duration-ms\"?: string | undefined;\n \"data-anvia-stream-opacity\"?: string | undefined;\n \"data-anvia-stream-reveal\"?: string | undefined;\n \"data-anvia-stream-reveal-id\"?: string | undefined;\n };\n if (internalProps[\"data-anvia-stream-reveal\"] !== undefined) {\n return streamRevealSpan(internalProps, onRevealSettled);\n }\n if (consumerSpan !== undefined) {\n return createElement(consumerSpan, safeProps);\n }\n const { node: _node, ...elementProps } = safeProps;\n return <span {...elementProps} />;\n },\n };\n}\n\nfunction componentPropsWithoutKey<T extends object>(props: T): T {\n const result: Record<string, unknown> = {};\n for (const name of Object.keys(props)) {\n if (name !== \"key\") {\n result[name] = (props as Record<string, unknown>)[name];\n }\n }\n return result as T;\n}\n\nfunction streamRevealSpan(\n props: ComponentPropsWithoutRef<\"span\"> & {\n node?: unknown;\n \"data-anvia-stream-duration-ms\"?: string | undefined;\n \"data-anvia-stream-opacity\"?: string | undefined;\n \"data-anvia-stream-reveal\"?: string | undefined;\n \"data-anvia-stream-reveal-id\"?: string | undefined;\n },\n onRevealSettled: (revealId: string) => void,\n): ReactNode {\n const {\n children,\n node: _node,\n \"data-anvia-stream-duration-ms\": durationMs,\n \"data-anvia-stream-opacity\": opacity,\n \"data-anvia-stream-reveal-id\": revealId,\n onAnimationEnd,\n ...elementProps\n } = props;\n const parsedOpacity = Number(opacity);\n const parsedDurationMs = Number(durationMs);\n return (\n <span\n {...elementProps}\n data-anvia-stream-reveal-id={revealId}\n key={revealId ?? \"stream-reveal\"}\n onAnimationEnd={(event) => {\n onAnimationEnd?.(event);\n if (revealId !== undefined) onRevealSettled(revealId);\n }}\n style={\n {\n \"--anvia-stream-reveal-opacity\": Number.isFinite(parsedOpacity) ? parsedOpacity : 1,\n \"--anvia-stream-reveal-duration\": `${Number.isFinite(parsedDurationMs) ? parsedDurationMs : 180}ms`,\n } as CSSProperties\n }\n >\n {children}\n </span>\n );\n}\n","import { lexer } from \"marked\";\n\nexport type StreamMarkdownBlock = {\n content: string;\n startOffset: number;\n};\n\nexport function splitStreamMarkdownBlocks(\n content: string,\n options: { singleDocument?: boolean } = {},\n): StreamMarkdownBlock[] {\n if (content.length === 0) {\n return [];\n }\n if (options.singleDocument === true) {\n return [{ content, startOffset: 0 }];\n }\n\n try {\n const tokens = lexer(content, { gfm: false });\n if (tokens.some((token) => token.type === \"def\") || Object.keys(tokens.links).length > 0) {\n return [{ content, startOffset: 0 }];\n }\n\n const rawBlocks: string[] = [];\n for (const token of tokens) {\n if (token.type === \"space\" && rawBlocks.length > 0) {\n const lastIndex = rawBlocks.length - 1;\n rawBlocks[lastIndex] = `${rawBlocks[lastIndex] ?? \"\"}${token.raw}`;\n } else {\n rawBlocks.push(token.raw);\n }\n }\n\n if (rawBlocks.length === 0 || rawBlocks.join(\"\") !== content) {\n return [{ content, startOffset: 0 }];\n }\n\n let startOffset = 0;\n return rawBlocks.map((block) => {\n const result = { content: block, startOffset };\n startOffset += block.length;\n return result;\n });\n } catch {\n return [{ content, startOffset: 0 }];\n }\n}\n","export const streamRevealConfig = {\n bandGraphemes: 2,\n durationMs: 180,\n minimumOpacity: 0.12,\n tailGraphemes: 24,\n} as const;\n\nexport type StreamRevealLifecycle = {\n activeScope: string | null;\n settledRevealIds: Set<string>;\n startedAtByRevealId: Map<string, number>;\n};\n\ntype MarkdownNode = {\n type: string;\n tagName?: string;\n value?: string;\n properties?: Record<string, unknown>;\n children?: MarkdownNode[];\n};\n\ntype TextTarget = {\n endOffset: number;\n index: number;\n node: MarkdownNode;\n parent: MarkdownNode;\n startOffset: number;\n};\n\ntype RevealGrapheme = {\n endOffset: number;\n opacity: number;\n startOffset: number;\n};\n\nexport function createStreamGradientRevealPlugin(\n lifecycleScope: string,\n lifecycle: StreamRevealLifecycle,\n) {\n return function streamGradientRevealPlugin() {\n return (tree: MarkdownNode) => {\n if (lifecycle.activeScope !== lifecycleScope) {\n lifecycle.activeScope = lifecycleScope;\n lifecycle.settledRevealIds.clear();\n lifecycle.startedAtByRevealId.clear();\n }\n const targets = collectTextTargets(tree);\n const renderedText = targets.map((target) => target.node.value ?? \"\").join(\"\");\n const graphemes = createRevealGraphemes(renderedText);\n wrapStreamRevealGraphemes(targets, graphemes, lifecycleScope, lifecycle);\n };\n };\n}\n\nfunction collectTextTargets(tree: MarkdownNode): TextTarget[] {\n const targets: TextTarget[] = [];\n let offset = 0;\n\n function visit(node: MarkdownNode, parent: MarkdownNode | undefined, index: number): void {\n if (node.type === \"element\" && node.tagName === \"pre\") {\n return;\n }\n if (node.type === \"text\" && node.value !== undefined && parent !== undefined) {\n if (!/\\S/u.test(node.value)) {\n return;\n }\n const startOffset = offset;\n offset += node.value.length;\n targets.push({\n endOffset: offset,\n index,\n node,\n parent,\n startOffset,\n });\n return;\n }\n for (const [childIndex, child] of (node.children ?? []).entries()) {\n visit(child, node, childIndex);\n }\n }\n\n visit(tree, undefined, 0);\n return targets;\n}\n\nfunction createRevealGraphemes(content: string): RevealGrapheme[] {\n const tail = segmentTailGraphemeRanges(content, streamRevealConfig.tailGraphemes);\n const graphemes: RevealGrapheme[] = [];\n\n for (let index = 0; index < tail.length; index += 1) {\n const grapheme = tail[index];\n const bandStartIndex =\n Math.floor(index / streamRevealConfig.bandGraphemes) * streamRevealConfig.bandGraphemes;\n const bandEndIndex =\n Math.min(bandStartIndex + streamRevealConfig.bandGraphemes, tail.length) - 1;\n if (grapheme === undefined) {\n continue;\n }\n const progress = tail.length === 1 ? 1 : bandEndIndex / (tail.length - 1);\n graphemes.push({\n startOffset: grapheme.startOffset,\n endOffset: grapheme.endOffset,\n opacity: Math.max(\n streamRevealConfig.minimumOpacity,\n 1 - progress * (1 - streamRevealConfig.minimumOpacity),\n ),\n });\n }\n return graphemes;\n}\n\nfunction wrapStreamRevealGraphemes(\n targets: readonly TextTarget[],\n graphemes: readonly RevealGrapheme[],\n lifecycleScope: string,\n lifecycle: StreamRevealLifecycle,\n): void {\n const activeRevealIds = new Set<string>();\n const nowMs = Date.now();\n for (const target of [...targets].reverse()) {\n const value = target.node.value ?? \"\";\n const intersectingGraphemes = graphemes.filter(\n (grapheme) =>\n grapheme.endOffset > target.startOffset && grapheme.startOffset < target.endOffset,\n );\n if (intersectingGraphemes.length === 0) {\n continue;\n }\n\n const replacement: MarkdownNode[] = [];\n let cursor = 0;\n for (const grapheme of intersectingGraphemes) {\n const start = Math.max(0, grapheme.startOffset - target.startOffset);\n const end = Math.min(value.length, grapheme.endOffset - target.startOffset);\n if (start > cursor) {\n replacement.push({ type: \"text\", value: value.slice(cursor, start) });\n }\n if (end > start) {\n const text = value.slice(start, end);\n const absoluteStart = target.startOffset + start;\n const absoluteEnd = target.startOffset + end;\n const revealId = `${lifecycleScope}:${absoluteStart}:${absoluteEnd}:${hashText(text)}`;\n activeRevealIds.add(revealId);\n const startedAt = lifecycle.startedAtByRevealId.get(revealId) ?? nowMs;\n lifecycle.startedAtByRevealId.set(revealId, startedAt);\n const elapsedMs = Math.max(0, nowMs - startedAt);\n if (elapsedMs >= streamRevealConfig.durationMs) {\n lifecycle.settledRevealIds.add(revealId);\n }\n if (lifecycle.settledRevealIds.has(revealId)) {\n replacement.push({ type: \"text\", value: text });\n } else {\n const progress = Math.min(elapsedMs / streamRevealConfig.durationMs, 1);\n const opacity = grapheme.opacity + (1 - grapheme.opacity) * progress;\n const remainingMs = Math.max(1, streamRevealConfig.durationMs - elapsedMs);\n replacement.push({\n type: \"element\",\n tagName: \"span\",\n properties: {\n \"data-anvia-stream-duration-ms\": String(remainingMs),\n \"data-anvia-stream-opacity\": String(opacity),\n \"data-anvia-stream-reveal\": \"\",\n \"data-anvia-stream-reveal-id\": revealId,\n },\n children: [{ type: \"text\", value: text }],\n });\n }\n }\n cursor = Math.max(cursor, end);\n }\n if (cursor < value.length) {\n replacement.push({ type: \"text\", value: value.slice(cursor) });\n }\n target.parent.children?.splice(target.index, 1, ...replacement);\n }\n\n pruneRevealLifecycle(lifecycle, activeRevealIds);\n}\n\nfunction pruneRevealLifecycle(\n lifecycle: StreamRevealLifecycle,\n activeRevealIds: ReadonlySet<string>,\n): void {\n for (const revealId of lifecycle.startedAtByRevealId.keys()) {\n if (!activeRevealIds.has(revealId)) lifecycle.startedAtByRevealId.delete(revealId);\n }\n for (const revealId of lifecycle.settledRevealIds) {\n if (!activeRevealIds.has(revealId)) lifecycle.settledRevealIds.delete(revealId);\n }\n}\n\nfunction hashText(text: string): string {\n let hash = 2_166_136_261;\n for (let index = 0; index < text.length; index += 1) {\n hash ^= text.charCodeAt(index);\n hash = Math.imul(hash, 16_777_619);\n }\n return (hash >>> 0).toString(36);\n}\n\nfunction segmentTailGraphemeRanges(\n content: string,\n limit: number,\n): Array<{\n endOffset: number;\n startOffset: number;\n}> {\n if (typeof Intl !== \"undefined\" && typeof Intl.Segmenter === \"function\") {\n const segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n const segments = segmenter.segment(content);\n const ranges: Array<{ endOffset: number; startOffset: number }> = [];\n let cursor = content.length;\n while (cursor > 0 && ranges.length < limit) {\n const segment = segments.containing(cursor - 1);\n if (segment === undefined) {\n break;\n }\n ranges.unshift({\n startOffset: segment.index,\n endOffset: segment.index + segment.segment.length,\n });\n cursor = segment.index;\n }\n return ranges;\n }\n\n const ranges: Array<{ endOffset: number; startOffset: number }> = [];\n let cursor = content.length;\n while (cursor > 0 && ranges.length < limit) {\n let startOffset = cursor - 1;\n const code = content.charCodeAt(startOffset);\n if (code >= 0xdc00 && code <= 0xdfff && startOffset > 0) {\n const previousCode = content.charCodeAt(startOffset - 1);\n if (previousCode >= 0xd800 && previousCode <= 0xdbff) {\n startOffset -= 1;\n }\n }\n ranges.unshift({ startOffset, endOffset: cursor });\n cursor = startOffset;\n }\n return ranges;\n}\n"],"mappings":"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","names":["ranges","cursor","createElement","StreamMarkdown","StreamMarkdownBlock"]}
package/dist/index.d.ts CHANGED
@@ -11,13 +11,29 @@ export { T as ThreadContextValue, u as useThread } from './thread-uEm8hH0s.js';
11
11
  export { T as ThreadListController, a as ThreadListItemContextValue, b as ThreadListProvider, c as ThreadListProviderProps, d as ThreadListRecord, u as useThreadList, e as useThreadListItem } from './thread-list-On8db4XL.js';
12
12
  export { Composer, ComposerSubmitMessage, ComposerSubmitMessageArgs, Thread } from './chat/index.js';
13
13
  export { Completion } from './completion/index.js';
14
+ import * as react from 'react';
15
+ import { ReactNode } from 'react';
16
+ import { UseChatResult } from '@anvia/react';
17
+ import { P as PrimitiveProps } from './render-CRKWam2v.js';
18
+ export { a as PrimitiveRef } from './render-CRKWam2v.js';
14
19
  export { HumanInput } from './human-input/index.js';
15
20
  export { Image } from './image/index.js';
16
21
  export { Message, MessageAttachmentPart, MessageEntityProps, MessagePartsFilter, MessageStreamOptions, MessageToolPart, MessageToolRenderWhen } from './message/index.js';
17
22
  export { SelectionToolbar } from './selection-toolbar/index.js';
18
- export { P as PrimitiveProps, a as PrimitiveRef } from './render-CRKWam2v.js';
19
23
  export { StreamMarkdown, StreamMarkdownProps } from './stream/index.js';
20
24
  export { ThreadList, ThreadListItem } from './thread-list/index.js';
21
- import '@anvia/react';
22
- import 'react';
23
25
  import 'react-markdown';
26
+
27
+ type ContextUsage = NonNullable<UseChatResult["contextUsage"]>;
28
+ type ContextMeterProps = Omit<PrimitiveProps<"div">, "children"> & {
29
+ usage?: ContextUsage;
30
+ display?: "remaining" | "used";
31
+ children?: ReactNode | ((usage: ContextUsage) => ReactNode);
32
+ };
33
+ declare const ContextMeter: react.ForwardRefExoticComponent<Omit<PrimitiveProps<"div">, "children"> & {
34
+ usage?: ContextUsage;
35
+ display?: "remaining" | "used";
36
+ children?: ReactNode | ((usage: ContextUsage) => ReactNode);
37
+ } & react.RefAttributes<HTMLDivElement>>;
38
+
39
+ export { ContextMeter, type ContextMeterProps, PrimitiveProps };
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  Composer,
10
10
  Thread
11
- } from "./chunk-LM5SU5AP.js";
11
+ } from "./chunk-FABSIZZ7.js";
12
12
  import {
13
13
  Completion
14
14
  } from "./chunk-VEYJNQVW.js";
@@ -20,14 +20,16 @@ import {
20
20
  } from "./chunk-7HR34SOF.js";
21
21
  import {
22
22
  Message
23
- } from "./chunk-7I6XUWM7.js";
23
+ } from "./chunk-JWZDNM5D.js";
24
24
  import {
25
25
  StreamMarkdown
26
- } from "./chunk-UESNSQGQ.js";
26
+ } from "./chunk-ULGJLLZE.js";
27
27
  import {
28
28
  Attachment
29
29
  } from "./chunk-SBYUKODG.js";
30
- import "./chunk-T3ZFZ3NP.js";
30
+ import {
31
+ renderPrimitive
32
+ } from "./chunk-T3ZFZ3NP.js";
31
33
  import {
32
34
  ChatProvider,
33
35
  CompletionProvider,
@@ -49,12 +51,50 @@ import {
49
51
  useThreadList,
50
52
  useThreadListItem
51
53
  } from "./chunk-43U2XQP2.js";
54
+
55
+ // src/context-meter.tsx
56
+ import { forwardRef } from "react";
57
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
58
+ var ContextMeter = forwardRef(function ContextMeter2({ usage, display = "remaining", children, ...props }, ref) {
59
+ if (usage === void 0) {
60
+ return null;
61
+ }
62
+ const percent = display === "remaining" ? usage.remainingPercent : usage.usedPercent;
63
+ const roundedPercent = Math.round(percent);
64
+ const label = `${roundedPercent}% context ${display === "remaining" ? "left" : "used"}`;
65
+ const content = typeof children === "function" ? children(usage) : children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
66
+ /* @__PURE__ */ jsx("span", { "data-anvia-context-meter-label": "", children: label }),
67
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-anvia-context-meter-track": "", children: /* @__PURE__ */ jsx(
68
+ "span",
69
+ {
70
+ "data-anvia-context-meter-value": "",
71
+ style: { width: `${Math.min(100, Math.max(0, percent))}%` }
72
+ }
73
+ ) })
74
+ ] });
75
+ return renderPrimitive(
76
+ "div",
77
+ {
78
+ ...props,
79
+ children: content,
80
+ "aria-label": props["aria-label"] ?? label,
81
+ "aria-valuemax": 100,
82
+ "aria-valuemin": 0,
83
+ "aria-valuenow": roundedPercent,
84
+ "data-anvia-context-meter": "",
85
+ "data-display": display,
86
+ role: props.role ?? "progressbar"
87
+ },
88
+ ref
89
+ );
90
+ });
52
91
  export {
53
92
  Attachment,
54
93
  ChatProvider,
55
94
  Completion,
56
95
  CompletionProvider,
57
96
  Composer,
97
+ ContextMeter,
58
98
  HumanInput,
59
99
  Image,
60
100
  Message,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/context-meter.tsx"],"sourcesContent":["import type { UseChatResult } from \"@anvia/react\";\nimport { forwardRef, type ReactNode } from \"react\";\n\nimport { type PrimitiveProps, renderPrimitive } from \"./primitives\";\n\ntype ContextUsage = NonNullable<UseChatResult[\"contextUsage\"]>;\n\nexport type ContextMeterProps = Omit<PrimitiveProps<\"div\">, \"children\"> & {\n usage?: ContextUsage;\n display?: \"remaining\" | \"used\";\n children?: ReactNode | ((usage: ContextUsage) => ReactNode);\n};\n\nexport const ContextMeter = forwardRef<HTMLDivElement, ContextMeterProps>(function ContextMeter(\n { usage, display = \"remaining\", children, ...props },\n ref,\n) {\n if (usage === undefined) {\n return null;\n }\n\n const percent = display === \"remaining\" ? usage.remainingPercent : usage.usedPercent;\n const roundedPercent = Math.round(percent);\n const label = `${roundedPercent}% context ${display === \"remaining\" ? \"left\" : \"used\"}`;\n const content =\n typeof children === \"function\"\n ? children(usage)\n : (children ?? (\n <>\n <span data-anvia-context-meter-label=\"\">{label}</span>\n <span aria-hidden=\"true\" data-anvia-context-meter-track=\"\">\n <span\n data-anvia-context-meter-value=\"\"\n style={{ width: `${Math.min(100, Math.max(0, percent))}%` }}\n />\n </span>\n </>\n ));\n\n return renderPrimitive(\n \"div\",\n {\n ...props,\n children: content,\n \"aria-label\": props[\"aria-label\"] ?? label,\n \"aria-valuemax\": 100,\n \"aria-valuemin\": 0,\n \"aria-valuenow\": roundedPercent,\n \"data-anvia-context-meter\": \"\",\n \"data-display\": display,\n role: props.role ?? \"progressbar\",\n } as PrimitiveProps<\"div\">,\n ref,\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,SAAS,kBAAkC;AA2BjC,mBACE,KADF;AAfH,IAAM,eAAe,WAA8C,SAASA,cACjF,EAAE,OAAO,UAAU,aAAa,UAAU,GAAG,MAAM,GACnD,KACA;AACA,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,YAAY,cAAc,MAAM,mBAAmB,MAAM;AACzE,QAAM,iBAAiB,KAAK,MAAM,OAAO;AACzC,QAAM,QAAQ,GAAG,cAAc,aAAa,YAAY,cAAc,SAAS,MAAM;AACrF,QAAM,UACJ,OAAO,aAAa,aAChB,SAAS,KAAK,IACb,YACC,iCACE;AAAA,wBAAC,UAAK,kCAA+B,IAAI,iBAAM;AAAA,IAC/C,oBAAC,UAAK,eAAY,QAAO,kCAA+B,IACtD;AAAA,MAAC;AAAA;AAAA,QACC,kCAA+B;AAAA,QAC/B,OAAO,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,CAAC,IAAI;AAAA;AAAA,IAC5D,GACF;AAAA,KACF;AAGR,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,GAAG;AAAA,MACH,UAAU;AAAA,MACV,cAAc,MAAM,YAAY,KAAK;AAAA,MACrC,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,MACjB,4BAA4B;AAAA,MAC5B,gBAAgB;AAAA,MAChB,MAAM,MAAM,QAAQ;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AACF,CAAC;","names":["ContextMeter"]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Message
3
- } from "../chunk-7I6XUWM7.js";
4
- import "../chunk-UESNSQGQ.js";
3
+ } from "../chunk-JWZDNM5D.js";
4
+ import "../chunk-ULGJLLZE.js";
5
5
  import "../chunk-SBYUKODG.js";
6
6
  import "../chunk-T3ZFZ3NP.js";
7
7
  import {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StreamMarkdown
3
- } from "../chunk-UESNSQGQ.js";
3
+ } from "../chunk-ULGJLLZE.js";
4
4
  import "../chunk-T3ZFZ3NP.js";
5
5
  export {
6
6
  StreamMarkdown
@@ -5,5 +5,13 @@
5
5
  }
6
6
 
7
7
  [data-anvia-stream-reveal] {
8
- animation: anvia-stream-gradient-settle 180ms linear both;
8
+ opacity: var(--anvia-stream-reveal-opacity, 1);
9
+ animation: anvia-stream-gradient-settle var(--anvia-stream-reveal-duration, 180ms) linear both;
10
+ }
11
+
12
+ @media (prefers-reduced-motion: reduce) {
13
+ [data-anvia-stream-reveal] {
14
+ opacity: 1;
15
+ animation: none;
16
+ }
9
17
  }
package/dist/styles.css CHANGED
@@ -85,6 +85,28 @@
85
85
  z-index: 60;
86
86
  }
87
87
 
88
+ [data-anvia-context-meter] {
89
+ display: inline-grid;
90
+ min-width: 8rem;
91
+ gap: 0.375rem;
92
+ font-size: 0.75rem;
93
+ }
94
+
95
+ [data-anvia-context-meter-track] {
96
+ display: block;
97
+ height: 0.25rem;
98
+ overflow: hidden;
99
+ border-radius: 9999px;
100
+ background: color-mix(in srgb, currentcolor 20%, transparent);
101
+ }
102
+
103
+ [data-anvia-context-meter-value] {
104
+ display: block;
105
+ height: 100%;
106
+ border-radius: inherit;
107
+ background: currentcolor;
108
+ }
109
+
88
110
  [data-state="disabled"] {
89
111
  opacity: 0.55;
90
112
  }
@@ -96,11 +118,13 @@
96
118
  }
97
119
 
98
120
  [data-anvia-stream-reveal] {
99
- animation: anvia-stream-gradient-settle 180ms linear both;
121
+ opacity: var(--anvia-stream-reveal-opacity, 1);
122
+ animation: anvia-stream-gradient-settle var(--anvia-stream-reveal-duration, 180ms) linear both;
100
123
  }
101
124
 
102
125
  @media (prefers-reduced-motion: reduce) {
103
126
  [data-anvia-stream-reveal] {
127
+ opacity: 1;
104
128
  animation: none;
105
129
  }
106
130
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anvia/react-ui",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Composable React UI primitives for Anvia chat and completion experiences.",
5
5
  "author": "anvia",
6
6
  "maintainer": "Indra Zulfi",
@@ -100,7 +100,7 @@
100
100
  "tsup": "^8.5.0",
101
101
  "typescript": "^5.9.3",
102
102
  "vitest": "^4.0.8",
103
- "@anvia/react": "0.9.2"
103
+ "@anvia/react": "0.11.0"
104
104
  },
105
105
  "scripts": {
106
106
  "build": "tsup src/index.ts src/attachment/index.ts src/chat/index.ts src/completion/index.ts src/human-input/index.ts src/image/index.ts src/message/index.ts src/selection-toolbar/index.ts src/shared.ts src/stream/index.ts src/thread-list/index.ts --format esm --dts --sourcemap --clean --external react --external react-dom && cp src/styles.css dist/styles.css && cp src/stream/styles.css dist/stream/styles.css",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/stream/markdown.tsx","../src/stream/markdown-blocks.ts","../src/stream/markdown-reveal.ts"],"sourcesContent":["import {\n type ComponentPropsWithoutRef,\n createElement,\n forwardRef,\n memo,\n type ReactNode,\n useMemo,\n useRef,\n} from \"react\";\nimport ReactMarkdown, {\n type Components,\n type Options as ReactMarkdownOptions,\n} from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { type PrimitiveProps, renderPrimitive } from \"../primitives\";\nimport { splitStreamMarkdownBlocks } from \"./markdown-blocks\";\nimport { createStreamGradientRevealPlugin } from \"./markdown-reveal\";\n\nexport type StreamMarkdownProps = Omit<PrimitiveProps<\"div\">, \"children\"> & {\n components?: Components;\n content: string;\n live?: boolean;\n remarkPlugins?: ReactMarkdownOptions[\"remarkPlugins\"];\n remarkRehypeOptions?: ReactMarkdownOptions[\"remarkRehypeOptions\"];\n};\n\nconst defaultRemarkPlugins: NonNullable<ReactMarkdownOptions[\"remarkPlugins\"]> = [remarkGfm];\n\nexport const StreamMarkdown = forwardRef<HTMLDivElement, StreamMarkdownProps>(\n function StreamMarkdown(\n { components, content, live = false, remarkPlugins, remarkRehypeOptions, ...props },\n ref,\n ) {\n const previousContentRef = useRef(content);\n const frameIdRef = useRef(0);\n if (previousContentRef.current !== content) {\n previousContentRef.current = content;\n frameIdRef.current += 1;\n }\n\n const blocks = useMemo(\n () =>\n splitStreamMarkdownBlocks(content, {\n singleDocument: remarkPlugins !== undefined,\n }),\n [content, remarkPlugins],\n );\n const streamingComponents = useMemo(() => withStreamRevealComponent(components), [components]);\n\n return renderPrimitive(\n \"div\",\n {\n ...props,\n children: blocks.map((block, index) => (\n <StreamMarkdownBlock\n components={streamingComponents}\n content={block.content}\n frameId={live && index === blocks.length - 1 ? frameIdRef.current : 0}\n live={live && index === blocks.length - 1}\n remarkPlugins={remarkPlugins ?? defaultRemarkPlugins}\n remarkRehypeOptions={remarkRehypeOptions}\n key={block.startOffset}\n />\n )),\n \"data-anvia-stream-markdown\": \"\",\n \"data-live\": live ? \"\" : undefined,\n } as PrimitiveProps<\"div\">,\n ref,\n );\n },\n);\n\nconst StreamMarkdownBlock = memo(function StreamMarkdownBlock(props: {\n components: Components;\n content: string;\n frameId: number;\n live: boolean;\n remarkPlugins: ReactMarkdownOptions[\"remarkPlugins\"];\n remarkRehypeOptions: ReactMarkdownOptions[\"remarkRehypeOptions\"];\n}) {\n const revealPlugin = useMemo(\n () => createStreamGradientRevealPlugin(props.frameId),\n [props.frameId],\n );\n return (\n <ReactMarkdown\n components={props.components}\n rehypePlugins={props.live ? [revealPlugin] : undefined}\n remarkPlugins={props.remarkPlugins}\n remarkRehypeOptions={props.remarkRehypeOptions}\n >\n {props.content}\n </ReactMarkdown>\n );\n});\n\nfunction withStreamRevealComponent(components: Components | undefined): Components {\n const consumerSpan = components?.span;\n return {\n ...components,\n span(spanProps) {\n const safeProps = componentPropsWithoutKey(spanProps);\n const internalProps = safeProps as typeof safeProps & {\n \"data-anvia-stream-frame-id\"?: string | undefined;\n \"data-anvia-stream-opacity\"?: string | undefined;\n \"data-anvia-stream-reveal\"?: string | undefined;\n };\n if (internalProps[\"data-anvia-stream-reveal\"] !== undefined) {\n return streamRevealSpan(internalProps);\n }\n if (consumerSpan !== undefined) {\n return createElement(consumerSpan, safeProps);\n }\n const { node: _node, ...elementProps } = safeProps;\n return <span {...elementProps} />;\n },\n };\n}\n\nfunction componentPropsWithoutKey<T extends object>(props: T): T {\n const result: Record<string, unknown> = {};\n for (const name of Object.keys(props)) {\n if (name !== \"key\") {\n result[name] = (props as Record<string, unknown>)[name];\n }\n }\n return result as T;\n}\n\nfunction streamRevealSpan(\n props: ComponentPropsWithoutRef<\"span\"> & {\n node?: unknown;\n \"data-anvia-stream-frame-id\"?: string | undefined;\n \"data-anvia-stream-opacity\"?: string | undefined;\n \"data-anvia-stream-reveal\"?: string | undefined;\n },\n): ReactNode {\n const {\n children,\n node: _node,\n \"data-anvia-stream-frame-id\": frameId,\n \"data-anvia-stream-opacity\": opacity,\n ...elementProps\n } = props;\n const parsedOpacity = Number(opacity);\n return (\n <span\n {...elementProps}\n data-anvia-stream-frame-id={frameId}\n key={frameId ?? \"stream-frame\"}\n style={{ opacity: Number.isFinite(parsedOpacity) ? parsedOpacity : 1 }}\n >\n {children}\n </span>\n );\n}\n","import { lexer } from \"marked\";\n\nexport type StreamMarkdownBlock = {\n content: string;\n startOffset: number;\n};\n\nexport function splitStreamMarkdownBlocks(\n content: string,\n options: { singleDocument?: boolean } = {},\n): StreamMarkdownBlock[] {\n if (content.length === 0) {\n return [];\n }\n if (options.singleDocument === true) {\n return [{ content, startOffset: 0 }];\n }\n\n try {\n const tokens = lexer(content, { gfm: false });\n if (tokens.some((token) => token.type === \"def\") || Object.keys(tokens.links).length > 0) {\n return [{ content, startOffset: 0 }];\n }\n\n const rawBlocks: string[] = [];\n for (const token of tokens) {\n if (token.type === \"space\" && rawBlocks.length > 0) {\n const lastIndex = rawBlocks.length - 1;\n rawBlocks[lastIndex] = `${rawBlocks[lastIndex] ?? \"\"}${token.raw}`;\n } else {\n rawBlocks.push(token.raw);\n }\n }\n\n if (rawBlocks.length === 0 || rawBlocks.join(\"\") !== content) {\n return [{ content, startOffset: 0 }];\n }\n\n let startOffset = 0;\n return rawBlocks.map((block) => {\n const result = { content: block, startOffset };\n startOffset += block.length;\n return result;\n });\n } catch {\n return [{ content, startOffset: 0 }];\n }\n}\n","export const streamRevealConfig = {\n bandGraphemes: 2,\n minimumOpacity: 0.12,\n tailGraphemes: 24,\n} as const;\n\ntype MarkdownNode = {\n type: string;\n tagName?: string;\n value?: string;\n properties?: Record<string, unknown>;\n children?: MarkdownNode[];\n};\n\ntype TextTarget = {\n endOffset: number;\n index: number;\n node: MarkdownNode;\n parent: MarkdownNode;\n startOffset: number;\n};\n\ntype RevealBand = {\n endOffset: number;\n opacity: number;\n startOffset: number;\n};\n\nexport function createStreamGradientRevealPlugin(frameId: number) {\n return function streamGradientRevealPlugin() {\n return (tree: MarkdownNode) => {\n const targets = collectTextTargets(tree);\n const renderedText = targets.map((target) => target.node.value ?? \"\").join(\"\");\n const bands = createRevealBands(renderedText);\n wrapStreamRevealBands(targets, bands, frameId);\n };\n };\n}\n\nfunction collectTextTargets(tree: MarkdownNode): TextTarget[] {\n const targets: TextTarget[] = [];\n let offset = 0;\n\n function visit(node: MarkdownNode, parent: MarkdownNode | undefined, index: number): void {\n if (node.type === \"element\" && node.tagName === \"pre\") {\n return;\n }\n if (node.type === \"text\" && node.value !== undefined && parent !== undefined) {\n if (!/\\S/u.test(node.value)) {\n return;\n }\n const startOffset = offset;\n offset += node.value.length;\n targets.push({\n endOffset: offset,\n index,\n node,\n parent,\n startOffset,\n });\n return;\n }\n for (const [childIndex, child] of (node.children ?? []).entries()) {\n visit(child, node, childIndex);\n }\n }\n\n visit(tree, undefined, 0);\n return targets;\n}\n\nfunction createRevealBands(content: string): RevealBand[] {\n const tail = segmentTailGraphemeRanges(content, streamRevealConfig.tailGraphemes);\n const bands: RevealBand[] = [];\n\n for (let index = 0; index < tail.length; index += streamRevealConfig.bandGraphemes) {\n const first = tail[index];\n const endIndex = Math.min(index + streamRevealConfig.bandGraphemes, tail.length) - 1;\n const last = tail[endIndex];\n if (first === undefined || last === undefined) {\n continue;\n }\n const progress = tail.length === 1 ? 1 : endIndex / (tail.length - 1);\n bands.push({\n startOffset: first.startOffset,\n endOffset: last.endOffset,\n opacity: Math.max(\n streamRevealConfig.minimumOpacity,\n 1 - progress * (1 - streamRevealConfig.minimumOpacity),\n ),\n });\n }\n return bands;\n}\n\nfunction wrapStreamRevealBands(\n targets: readonly TextTarget[],\n bands: readonly RevealBand[],\n frameId: number,\n): void {\n for (const target of [...targets].reverse()) {\n const value = target.node.value ?? \"\";\n const intersectingBands = bands.filter(\n (band) => band.endOffset > target.startOffset && band.startOffset < target.endOffset,\n );\n if (intersectingBands.length === 0) {\n continue;\n }\n\n const replacement: MarkdownNode[] = [];\n let cursor = 0;\n for (const band of intersectingBands) {\n const start = Math.max(0, band.startOffset - target.startOffset);\n const end = Math.min(value.length, band.endOffset - target.startOffset);\n if (start > cursor) {\n replacement.push({ type: \"text\", value: value.slice(cursor, start) });\n }\n if (end > start) {\n replacement.push({\n type: \"element\",\n tagName: \"span\",\n properties: {\n \"data-anvia-stream-frame-id\": `${frameId}:${band.startOffset}`,\n \"data-anvia-stream-opacity\": String(band.opacity),\n \"data-anvia-stream-reveal\": \"\",\n },\n children: [{ type: \"text\", value: value.slice(start, end) }],\n });\n }\n cursor = Math.max(cursor, end);\n }\n if (cursor < value.length) {\n replacement.push({ type: \"text\", value: value.slice(cursor) });\n }\n target.parent.children?.splice(target.index, 1, ...replacement);\n }\n}\n\nfunction segmentTailGraphemeRanges(\n content: string,\n limit: number,\n): Array<{\n endOffset: number;\n startOffset: number;\n}> {\n if (typeof Intl !== \"undefined\" && typeof Intl.Segmenter === \"function\") {\n const segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n const segments = segmenter.segment(content);\n const ranges: Array<{ endOffset: number; startOffset: number }> = [];\n let cursor = content.length;\n while (cursor > 0 && ranges.length < limit) {\n const segment = segments.containing(cursor - 1);\n if (segment === undefined) {\n break;\n }\n ranges.unshift({\n startOffset: segment.index,\n endOffset: segment.index + segment.segment.length,\n });\n cursor = segment.index;\n }\n return ranges;\n }\n\n const ranges: Array<{ endOffset: number; startOffset: number }> = [];\n let cursor = content.length;\n while (cursor > 0 && ranges.length < limit) {\n let startOffset = cursor - 1;\n const code = content.charCodeAt(startOffset);\n if (code >= 0xdc00 && code <= 0xdfff && startOffset > 0) {\n const previousCode = content.charCodeAt(startOffset - 1);\n if (previousCode >= 0xd800 && previousCode <= 0xdbff) {\n startOffset -= 1;\n }\n }\n ranges.unshift({ startOffset, endOffset: cursor });\n cursor = startOffset;\n }\n return ranges;\n}\n"],"mappings":";;;;;AAAA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OACK;AACP,OAAO,mBAGA;AACP,OAAO,eAAe;;;ACbtB,SAAS,aAAa;AAOf,SAAS,0BACd,SACA,UAAwC,CAAC,GAClB;AACvB,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,CAAC;AAAA,EACV;AACA,MAAI,QAAQ,mBAAmB,MAAM;AACnC,WAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;AAAA,EACrC;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,SAAS,EAAE,KAAK,MAAM,CAAC;AAC5C,QAAI,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,KAAK,KAAK,OAAO,KAAK,OAAO,KAAK,EAAE,SAAS,GAAG;AACxF,aAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;AAAA,IACrC;AAEA,UAAM,YAAsB,CAAC;AAC7B,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,SAAS,WAAW,UAAU,SAAS,GAAG;AAClD,cAAM,YAAY,UAAU,SAAS;AACrC,kBAAU,SAAS,IAAI,GAAG,UAAU,SAAS,KAAK,EAAE,GAAG,MAAM,GAAG;AAAA,MAClE,OAAO;AACL,kBAAU,KAAK,MAAM,GAAG;AAAA,MAC1B;AAAA,IACF;AAEA,QAAI,UAAU,WAAW,KAAK,UAAU,KAAK,EAAE,MAAM,SAAS;AAC5D,aAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;AAAA,IACrC;AAEA,QAAI,cAAc;AAClB,WAAO,UAAU,IAAI,CAAC,UAAU;AAC9B,YAAM,SAAS,EAAE,SAAS,OAAO,YAAY;AAC7C,qBAAe,MAAM;AACrB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,QAAQ;AACN,WAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;AAAA,EACrC;AACF;;;AC/CO,IAAM,qBAAqB;AAAA,EAChC,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,eAAe;AACjB;AAwBO,SAAS,iCAAiC,SAAiB;AAChE,SAAO,SAAS,6BAA6B;AAC3C,WAAO,CAAC,SAAuB;AAC7B,YAAM,UAAU,mBAAmB,IAAI;AACvC,YAAM,eAAe,QAAQ,IAAI,CAAC,WAAW,OAAO,KAAK,SAAS,EAAE,EAAE,KAAK,EAAE;AAC7E,YAAM,QAAQ,kBAAkB,YAAY;AAC5C,4BAAsB,SAAS,OAAO,OAAO;AAAA,IAC/C;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,MAAkC;AAC5D,QAAM,UAAwB,CAAC;AAC/B,MAAI,SAAS;AAEb,WAAS,MAAM,MAAoB,QAAkC,OAAqB;AACxF,QAAI,KAAK,SAAS,aAAa,KAAK,YAAY,OAAO;AACrD;AAAA,IACF;AACA,QAAI,KAAK,SAAS,UAAU,KAAK,UAAU,UAAa,WAAW,QAAW;AAC5E,UAAI,CAAC,MAAM,KAAK,KAAK,KAAK,GAAG;AAC3B;AAAA,MACF;AACA,YAAM,cAAc;AACpB,gBAAU,KAAK,MAAM;AACrB,cAAQ,KAAK;AAAA,QACX,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD;AAAA,IACF;AACA,eAAW,CAAC,YAAY,KAAK,MAAM,KAAK,YAAY,CAAC,GAAG,QAAQ,GAAG;AACjE,YAAM,OAAO,MAAM,UAAU;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,MAAM,QAAW,CAAC;AACxB,SAAO;AACT;AAEA,SAAS,kBAAkB,SAA+B;AACxD,QAAM,OAAO,0BAA0B,SAAS,mBAAmB,aAAa;AAChF,QAAM,QAAsB,CAAC;AAE7B,WAAS,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,mBAAmB,eAAe;AAClF,UAAM,QAAQ,KAAK,KAAK;AACxB,UAAM,WAAW,KAAK,IAAI,QAAQ,mBAAmB,eAAe,KAAK,MAAM,IAAI;AACnF,UAAM,OAAO,KAAK,QAAQ;AAC1B,QAAI,UAAU,UAAa,SAAS,QAAW;AAC7C;AAAA,IACF;AACA,UAAM,WAAW,KAAK,WAAW,IAAI,IAAI,YAAY,KAAK,SAAS;AACnE,UAAM,KAAK;AAAA,MACT,aAAa,MAAM;AAAA,MACnB,WAAW,KAAK;AAAA,MAChB,SAAS,KAAK;AAAA,QACZ,mBAAmB;AAAA,QACnB,IAAI,YAAY,IAAI,mBAAmB;AAAA,MACzC;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,sBACP,SACA,OACA,SACM;AACN,aAAW,UAAU,CAAC,GAAG,OAAO,EAAE,QAAQ,GAAG;AAC3C,UAAM,QAAQ,OAAO,KAAK,SAAS;AACnC,UAAM,oBAAoB,MAAM;AAAA,MAC9B,CAAC,SAAS,KAAK,YAAY,OAAO,eAAe,KAAK,cAAc,OAAO;AAAA,IAC7E;AACA,QAAI,kBAAkB,WAAW,GAAG;AAClC;AAAA,IACF;AAEA,UAAM,cAA8B,CAAC;AACrC,QAAI,SAAS;AACb,eAAW,QAAQ,mBAAmB;AACpC,YAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,cAAc,OAAO,WAAW;AAC/D,YAAM,MAAM,KAAK,IAAI,MAAM,QAAQ,KAAK,YAAY,OAAO,WAAW;AACtE,UAAI,QAAQ,QAAQ;AAClB,oBAAY,KAAK,EAAE,MAAM,QAAQ,OAAO,MAAM,MAAM,QAAQ,KAAK,EAAE,CAAC;AAAA,MACtE;AACA,UAAI,MAAM,OAAO;AACf,oBAAY,KAAK;AAAA,UACf,MAAM;AAAA,UACN,SAAS;AAAA,UACT,YAAY;AAAA,YACV,8BAA8B,GAAG,OAAO,IAAI,KAAK,WAAW;AAAA,YAC5D,6BAA6B,OAAO,KAAK,OAAO;AAAA,YAChD,4BAA4B;AAAA,UAC9B;AAAA,UACA,UAAU,CAAC,EAAE,MAAM,QAAQ,OAAO,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AAAA,QAC7D,CAAC;AAAA,MACH;AACA,eAAS,KAAK,IAAI,QAAQ,GAAG;AAAA,IAC/B;AACA,QAAI,SAAS,MAAM,QAAQ;AACzB,kBAAY,KAAK,EAAE,MAAM,QAAQ,OAAO,MAAM,MAAM,MAAM,EAAE,CAAC;AAAA,IAC/D;AACA,WAAO,OAAO,UAAU,OAAO,OAAO,OAAO,GAAG,GAAG,WAAW;AAAA,EAChE;AACF;AAEA,SAAS,0BACP,SACA,OAIC;AACD,MAAI,OAAO,SAAS,eAAe,OAAO,KAAK,cAAc,YAAY;AACvE,UAAM,YAAY,IAAI,KAAK,UAAU,QAAW,EAAE,aAAa,WAAW,CAAC;AAC3E,UAAM,WAAW,UAAU,QAAQ,OAAO;AAC1C,UAAMA,UAA4D,CAAC;AACnE,QAAIC,UAAS,QAAQ;AACrB,WAAOA,UAAS,KAAKD,QAAO,SAAS,OAAO;AAC1C,YAAM,UAAU,SAAS,WAAWC,UAAS,CAAC;AAC9C,UAAI,YAAY,QAAW;AACzB;AAAA,MACF;AACA,MAAAD,QAAO,QAAQ;AAAA,QACb,aAAa,QAAQ;AAAA,QACrB,WAAW,QAAQ,QAAQ,QAAQ,QAAQ;AAAA,MAC7C,CAAC;AACD,MAAAC,UAAS,QAAQ;AAAA,IACnB;AACA,WAAOD;AAAA,EACT;AAEA,QAAM,SAA4D,CAAC;AACnE,MAAI,SAAS,QAAQ;AACrB,SAAO,SAAS,KAAK,OAAO,SAAS,OAAO;AAC1C,QAAI,cAAc,SAAS;AAC3B,UAAM,OAAO,QAAQ,WAAW,WAAW;AAC3C,QAAI,QAAQ,SAAU,QAAQ,SAAU,cAAc,GAAG;AACvD,YAAM,eAAe,QAAQ,WAAW,cAAc,CAAC;AACvD,UAAI,gBAAgB,SAAU,gBAAgB,OAAQ;AACpD,uBAAe;AAAA,MACjB;AAAA,IACF;AACA,WAAO,QAAQ,EAAE,aAAa,WAAW,OAAO,CAAC;AACjD,aAAS;AAAA,EACX;AACA,SAAO;AACT;;;AF5HU;AA4FN,0BAAAE,sBAAA;AAxHJ,IAAM,uBAA2E,CAAC,SAAS;AAEpF,IAAM,iBAAiB;AAAA,EAC5B,SAASC,gBACP,EAAE,YAAY,SAAS,OAAO,OAAO,eAAe,qBAAqB,GAAG,MAAM,GAClF,KACA;AACA,UAAM,qBAAqB,OAAO,OAAO;AACzC,UAAM,aAAa,OAAO,CAAC;AAC3B,QAAI,mBAAmB,YAAY,SAAS;AAC1C,yBAAmB,UAAU;AAC7B,iBAAW,WAAW;AAAA,IACxB;AAEA,UAAM,SAAS;AAAA,MACb,MACE,0BAA0B,SAAS;AAAA,QACjC,gBAAgB,kBAAkB;AAAA,MACpC,CAAC;AAAA,MACH,CAAC,SAAS,aAAa;AAAA,IACzB;AACA,UAAM,sBAAsB,QAAQ,MAAM,0BAA0B,UAAU,GAAG,CAAC,UAAU,CAAC;AAE7F,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,GAAG;AAAA,QACH,UAAU,OAAO,IAAI,CAAC,OAAO,UAC3B;AAAA,UAAC;AAAA;AAAA,YACC,YAAY;AAAA,YACZ,SAAS,MAAM;AAAA,YACf,SAAS,QAAQ,UAAU,OAAO,SAAS,IAAI,WAAW,UAAU;AAAA,YACpE,MAAM,QAAQ,UAAU,OAAO,SAAS;AAAA,YACxC,eAAe,iBAAiB;AAAA,YAChC;AAAA;AAAA,UACK,MAAM;AAAA,QACb,CACD;AAAA,QACD,8BAA8B;AAAA,QAC9B,aAAa,OAAO,KAAK;AAAA,MAC3B;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,sBAAsB,KAAK,SAASC,qBAAoB,OAO3D;AACD,QAAM,eAAe;AAAA,IACnB,MAAM,iCAAiC,MAAM,OAAO;AAAA,IACpD,CAAC,MAAM,OAAO;AAAA,EAChB;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,YAAY,MAAM;AAAA,MAClB,eAAe,MAAM,OAAO,CAAC,YAAY,IAAI;AAAA,MAC7C,eAAe,MAAM;AAAA,MACrB,qBAAqB,MAAM;AAAA,MAE1B,gBAAM;AAAA;AAAA,EACT;AAEJ,CAAC;AAED,SAAS,0BAA0B,YAAgD;AACjF,QAAM,eAAe,YAAY;AACjC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,KAAK,WAAW;AACd,YAAM,YAAY,yBAAyB,SAAS;AACpD,YAAM,gBAAgB;AAKtB,UAAI,cAAc,0BAA0B,MAAM,QAAW;AAC3D,eAAO,iBAAiB,aAAa;AAAA,MACvC;AACA,UAAI,iBAAiB,QAAW;AAC9B,eAAO,cAAc,cAAc,SAAS;AAAA,MAC9C;AACA,YAAM,EAAE,MAAM,OAAO,GAAG,aAAa,IAAI;AACzC,aAAO,oBAAC,UAAM,GAAG,cAAc;AAAA,IACjC;AAAA,EACF;AACF;AAEA,SAAS,yBAA2C,OAAa;AAC/D,QAAM,SAAkC,CAAC;AACzC,aAAW,QAAQ,OAAO,KAAK,KAAK,GAAG;AACrC,QAAI,SAAS,OAAO;AAClB,aAAO,IAAI,IAAK,MAAkC,IAAI;AAAA,IACxD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,iBACP,OAMW;AACX,QAAM;AAAA,IACJ;AAAA,IACA,MAAM;AAAA,IACN,8BAA8B;AAAA,IAC9B,6BAA6B;AAAA,IAC7B,GAAG;AAAA,EACL,IAAI;AACJ,QAAM,gBAAgB,OAAO,OAAO;AACpC,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,8BAA4B;AAAA,MAC5B,KAAK,WAAW;AAAA,MAChB,OAAO,EAAE,SAAS,OAAO,SAAS,aAAa,IAAI,gBAAgB,EAAE;AAAA;AAAA,IAEpE;AAAA,EACH;AAEJ;","names":["ranges","cursor","createElement","StreamMarkdown","StreamMarkdownBlock"]}