@mastra/playground-ui 51.2.0 → 51.3.0-alpha.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/components/MarkdownRenderer.cjs.js +3 -65
  3. package/dist/components/MarkdownRenderer.cjs.js.map +1 -1
  4. package/dist/components/MarkdownRenderer.es.js +1 -67
  5. package/dist/components/MarkdownRenderer.es.js.map +1 -1
  6. package/dist/components/ai/message-reveal.cjs.js +50 -0
  7. package/dist/components/ai/message-reveal.cjs.js.map +1 -0
  8. package/dist/components/ai/message-reveal.d.ts +2 -0
  9. package/dist/components/ai/message-reveal.es.js +44 -0
  10. package/dist/components/ai/message-reveal.es.js.map +1 -0
  11. package/dist/src/ds/components/Arrival/arrival.stories.d.ts +6 -0
  12. package/dist/src/ds/components/Card/card.stories.d.ts +8 -0
  13. package/dist/src/ds/components/Columns/columns.stories.d.ts +7 -0
  14. package/dist/src/ds/components/DataDetailsPanel/data-details-panel.stories.d.ts +9 -0
  15. package/dist/src/ds/components/DataFilter/select-data-filter.stories.d.ts +8 -0
  16. package/dist/src/ds/components/DateRangeTimeline/date-range-timeline.stories.d.ts +8 -0
  17. package/dist/src/ds/components/ErrorState/error-state.stories.d.ts +7 -0
  18. package/dist/src/ds/components/ListSearch/list-search.stories.d.ts +7 -0
  19. package/dist/src/ds/components/LogsDataList/logs-data-list.stories.d.ts +8 -0
  20. package/dist/src/ds/components/MessageScroller/message-scroller.stories.d.ts +7 -0
  21. package/dist/src/ds/components/MetricsLineChart/metrics-line-chart.stories.d.ts +8 -0
  22. package/dist/src/ds/components/PageHeader/page-header.stories.d.ts +8 -0
  23. package/dist/src/ds/components/PageLayout/page-layout.stories.d.ts +8 -0
  24. package/dist/src/ds/components/PendingIndicator/pending-indicator.stories.d.ts +7 -0
  25. package/dist/src/ds/components/PermissionDenied/permission-denied.stories.d.ts +8 -0
  26. package/dist/src/ds/components/PrevNextNav/prev-next-nav.stories.d.ts +8 -0
  27. package/dist/src/ds/components/SessionExpired/session-expired.stories.d.ts +7 -0
  28. package/dist/src/ds/components/Shimmer/shimmer.stories.d.ts +7 -0
  29. package/dist/src/ds/components/StatusBadge/status-badge.stories.d.ts +8 -0
  30. package/dist/src/ds/components/ThreadList/thread-list.stories.d.ts +8 -0
  31. package/dist/src/ds/components/ThreadRail/thread-rail.stories.d.ts +7 -0
  32. package/dist/src/ds/components/TokenBudget/token-budget.stories.d.ts +8 -0
  33. package/dist/src/ds/components/ai/message-reveal/index.d.ts +1 -0
  34. package/dist/src/ds/components/ai/message-reveal/message-reveal.d.ts +22 -0
  35. package/dist/style.css +80 -0
  36. package/dist/use-reveal-Sa456jZb.cjs +70 -0
  37. package/dist/use-reveal-Sa456jZb.cjs.map +1 -0
  38. package/dist/use-reveal-d77hxHz9.js +68 -0
  39. package/dist/use-reveal-d77hxHz9.js.map +1 -0
  40. package/package.json +7 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @mastra/playground-ui
2
2
 
3
+ ## 51.3.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added `@mastra/playground-ui/components/ai/message-reveal`, which paces a whole assistant message — prose, reasoning, tool rows, cards — on one clock, so its parts arrive in the order the model wrote them. ([#22408](https://github.com/mastra-ai/mastra/pull/22408))
8
+
9
+ Before, a renderer paced its own text with `useRevealedText`, which could only slow prose down: everything written between two passages landed at once, so a tool row appeared while the sentence before it was still being typed out.
10
+
11
+ ```tsx
12
+ import { useRevealedParts } from '@mastra/playground-ui/components/ai/message-reveal';
13
+
14
+ const parts = useRevealedParts(message.content.parts, streaming);
15
+
16
+ <MessageFactory message={{ ...message, content: { ...message.content, parts } }} {...renderers} />;
17
+ ```
18
+
19
+ The projection happens above the renderers, so none of them has to know a reveal is running.
20
+
21
+ ### Patch Changes
22
+
23
+ - Added Storybook examples for every published Playground UI component that did not already have coverage. ([#22422](https://github.com/mastra-ai/mastra/pull/22422))
24
+
25
+ - Updated dependencies [[`bae1502`](https://github.com/mastra-ai/mastra/commit/bae150254b06a4da6964d7c137af97f336362359)]:
26
+ - @mastra/core@1.63.1-alpha.0
27
+ - @mastra/client-js@1.42.3-alpha.0
28
+ - @mastra/react@1.4.8-alpha.0
29
+
3
30
  ## 51.2.0
4
31
 
5
32
  ### Minor Changes
@@ -3,72 +3,10 @@
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const markdownRenderer = require('../markdown-renderer-BQugZq4m.cjs');
6
- const React = require('react');
6
+ const useReveal = require('../use-reveal-Sa456jZb.cjs');
7
+
7
8
 
8
- const BUFFER_MS = 1e3;
9
- const CORRECT_MS = 1e3;
10
- const FLOW_MS = 700;
11
- const EASE_MS = 400;
12
- const SLOWEST = 10;
13
- const FASTEST = 60;
14
- const MAX_LAG = 300;
15
- function measure(flow, now, gained) {
16
- const elapsed = Math.max(0, now - flow.at);
17
- flow.rate = flow.rate * Math.exp(-elapsed / FLOW_MS) + gained / (FLOW_MS / 1e3);
18
- flow.at = Math.max(flow.at, now);
19
- return flow.rate;
20
- }
21
- function prefersReducedMotion() {
22
- if (typeof window === "undefined" || !window.matchMedia) return false;
23
- return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
24
- }
25
- function wordStops(text) {
26
- const stops = [0];
27
- const word = /\S+/g;
28
- for (let match = word.exec(text); match; match = word.exec(text)) stops.push(match.index + match[0].length);
29
- return stops;
30
- }
31
- function useRevealedText(text, streaming) {
32
- const [calm] = React.useState(prefersReducedMotion);
33
- const stops = React.useMemo(() => wordStops(text), [text]);
34
- const words = stops.length - 1;
35
- const [landed, setLanded] = React.useState(() => streaming && !calm ? 0 : words);
36
- const shown = calm ? words : Math.min(words, Math.max(landed, words - MAX_LAG));
37
- if (shown !== landed) setLanded(shown);
38
- const chasing = shown < words;
39
- const goal = React.useRef(words);
40
- const cursor = React.useRef(shown);
41
- const flow = React.useRef({ rate: 0, at: 0 });
42
- const pace = React.useRef(SLOWEST);
43
- React.useEffect(() => {
44
- measure(flow.current, performance.now(), Math.max(0, words - goal.current));
45
- goal.current = words;
46
- if (cursor.current < shown || cursor.current > words) cursor.current = shown;
47
- });
48
- React.useEffect(() => {
49
- if (!chasing) return;
50
- let drawn = performance.now();
51
- let frame = requestAnimationFrame(function step(now) {
52
- const elapsed = Math.max(0, now - drawn);
53
- drawn = Math.max(drawn, now);
54
- const arriving = measure(flow.current, now, 0);
55
- const from = cursor.current;
56
- const behind = goal.current - from;
57
- if (behind > 0) {
58
- const held = arriving * BUFFER_MS / 1e3;
59
- const aim = Math.min(FASTEST, Math.max(SLOWEST, arriving + (behind - held) * 1e3 / CORRECT_MS));
60
- pace.current += (aim - pace.current) * Math.min(1, elapsed / EASE_MS);
61
- cursor.current = Math.min(goal.current, Math.floor(from) + 1, from + elapsed * pace.current / 1e3);
62
- if (Math.floor(cursor.current) !== Math.floor(from)) setLanded(Math.floor(cursor.current));
63
- }
64
- frame = requestAnimationFrame(step);
65
- });
66
- return () => cancelAnimationFrame(frame);
67
- }, [chasing]);
68
- if (!chasing) return text;
69
- return text.slice(0, stops[shown]);
70
- }
71
9
 
72
10
  exports.MarkdownRenderer = markdownRenderer.MarkdownRenderer;
73
- exports.useRevealedText = useRevealedText;
11
+ exports.useRevealedText = useReveal.useRevealedText;
74
12
  //# sourceMappingURL=MarkdownRenderer.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MarkdownRenderer.cjs.js","sources":["../../src/ds/components/MarkdownRenderer/use-reveal.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\n\n/** How much of the reply the reveal aims to be holding back, as time at the speed it is playing. */\nconst BUFFER_MS = 1000;\n\n/** How long it takes to work off a buffer that is too full or too empty. */\nconst CORRECT_MS = 1000;\n\n/** How long a word keeps counting towards the speed the reply is arriving at. */\nconst FLOW_MS = 700;\n\n/** How long the reveal takes to adopt a new speed, so a change of pace is never a jolt. */\nconst EASE_MS = 400;\n\n/** Words per second it will not go below: a short burst between two tool calls still reads briskly. */\nconst SLOWEST = 10;\n\n/** Words per second it will not go past: one word a frame, all a screen can draw. */\nconst FASTEST = 60;\n\n/** Words it will never fall further behind than — about five seconds at full speed. */\nconst MAX_LAG = 300;\n\ninterface Flow {\n /** Words per second the reply is arriving at, as an exponential average. */\n rate: number;\n at: number;\n}\n\n/** Decays what earlier words still count for, then deposits the ones that just arrived. */\nfunction measure(flow: Flow, now: number, gained: number): number {\n // rAF timestamps and performance.now() are sampled at different points of a\n // frame, so a fresh loop can read time slightly behind the last deposit.\n const elapsed = Math.max(0, now - flow.at);\n flow.rate = flow.rate * Math.exp(-elapsed / FLOW_MS) + gained / (FLOW_MS / 1000);\n flow.at = Math.max(flow.at, now);\n\n return flow.rate;\n}\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || !window.matchMedia) return false;\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\n/** Where the reveal can cut the text: before the first word, then after each one. */\nfunction wordStops(text: string): number[] {\n const stops = [0];\n const word = /\\S+/g;\n\n for (let match = word.exec(text); match; match = word.exec(text)) stops.push(match.index + match[0].length);\n\n return stops;\n}\n\n/**\n * A jitter buffer for a streamed reply, counted in words. Chunks reach the\n * browser unevenly — a proxy flushes, a tool call ends, the model changes pace —\n * and drawing each one on arrival makes a reply lurch: ten words at once, then\n * nothing for a fifth of a second.\n *\n * So the reveal runs on its own clock: a frame loop that chunks never interrupt,\n * laying down at most one word per frame however far behind it is. It plays at\n * the speed the reply is arriving at, measured directly, and leans on that only\n * to work the buffer back to `BUFFER_MS` of held text when it drifts. Measuring\n * the speed rather than inferring it from the buffer is what lets the buffer stay\n * shallow: a reply that lasts two seconds still finds its pace in the first few\n * hundred milliseconds, where a deeper buffer would spend the whole reply filling\n * and then sprint to catch up.\n *\n * The reply it paces is whatever the caller hands it, so a caller with more than\n * prose to lay down — a tool row between two paragraphs — paces the whole of it\n * from one place and draws the rest against the same cursor.\n *\n * Nothing here reads `streaming` past the first render: a reply that stops\n * arriving is one whose measured speed decays, which winds the buffer down on its\n * own. Words, not characters, are what a reader sees land, and they are not the\n * same length: an even flow of characters still drops \"and if we do\" in one frame\n * and spends four on \"implementation\".\n */\nexport function useRevealedText(text: string, streaming: boolean): string {\n const [calm] = useState(prefersReducedMotion);\n const stops = useMemo(() => wordStops(text), [text]);\n const words = stops.length - 1;\n\n const [landed, setLanded] = useState(() => (streaming && !calm ? 0 : words));\n\n // Winds the cursor back when a shorter reply replaces the one on screen, which\n // no key can express: same element, same message, new text.\n const shown = calm ? words : Math.min(words, Math.max(landed, words - MAX_LAG));\n if (shown !== landed) setLanded(shown);\n\n const chasing = shown < words;\n const goal = useRef(words);\n const cursor = useRef(shown);\n const flow = useRef<Flow>({ rate: 0, at: 0 });\n const pace = useRef(SLOWEST);\n\n useEffect(() => {\n measure(flow.current, performance.now(), Math.max(0, words - goal.current));\n goal.current = words;\n\n if (cursor.current < shown || cursor.current > words) cursor.current = shown;\n });\n\n useEffect(() => {\n if (!chasing) return;\n\n let drawn = performance.now();\n let frame = requestAnimationFrame(function step(now) {\n const elapsed = Math.max(0, now - drawn);\n drawn = Math.max(drawn, now);\n\n const arriving = measure(flow.current, now, 0);\n const from = cursor.current;\n const behind = goal.current - from;\n\n if (behind > 0) {\n const held = (arriving * BUFFER_MS) / 1000;\n const aim = Math.min(FASTEST, Math.max(SLOWEST, arriving + ((behind - held) * 1000) / CORRECT_MS));\n\n pace.current += (aim - pace.current) * Math.min(1, elapsed / EASE_MS);\n cursor.current = Math.min(goal.current, Math.floor(from) + 1, from + (elapsed * pace.current) / 1000);\n\n if (Math.floor(cursor.current) !== Math.floor(from)) setLanded(Math.floor(cursor.current));\n }\n\n frame = requestAnimationFrame(step);\n });\n\n return () => cancelAnimationFrame(frame);\n }, [chasing]);\n\n if (!chasing) return text;\n\n return text.slice(0, stops[shown]);\n}\n"],"names":["useState","useMemo","useRef","useEffect"],"mappings":"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"}
1
+ {"version":3,"file":"MarkdownRenderer.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
@@ -1,69 +1,3 @@
1
1
  export { M as MarkdownRenderer } from '../markdown-renderer-LOqipQki.js';
2
- import { useState, useMemo, useRef, useEffect } from 'react';
3
-
4
- const BUFFER_MS = 1e3;
5
- const CORRECT_MS = 1e3;
6
- const FLOW_MS = 700;
7
- const EASE_MS = 400;
8
- const SLOWEST = 10;
9
- const FASTEST = 60;
10
- const MAX_LAG = 300;
11
- function measure(flow, now, gained) {
12
- const elapsed = Math.max(0, now - flow.at);
13
- flow.rate = flow.rate * Math.exp(-elapsed / FLOW_MS) + gained / (FLOW_MS / 1e3);
14
- flow.at = Math.max(flow.at, now);
15
- return flow.rate;
16
- }
17
- function prefersReducedMotion() {
18
- if (typeof window === "undefined" || !window.matchMedia) return false;
19
- return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
20
- }
21
- function wordStops(text) {
22
- const stops = [0];
23
- const word = /\S+/g;
24
- for (let match = word.exec(text); match; match = word.exec(text)) stops.push(match.index + match[0].length);
25
- return stops;
26
- }
27
- function useRevealedText(text, streaming) {
28
- const [calm] = useState(prefersReducedMotion);
29
- const stops = useMemo(() => wordStops(text), [text]);
30
- const words = stops.length - 1;
31
- const [landed, setLanded] = useState(() => streaming && !calm ? 0 : words);
32
- const shown = calm ? words : Math.min(words, Math.max(landed, words - MAX_LAG));
33
- if (shown !== landed) setLanded(shown);
34
- const chasing = shown < words;
35
- const goal = useRef(words);
36
- const cursor = useRef(shown);
37
- const flow = useRef({ rate: 0, at: 0 });
38
- const pace = useRef(SLOWEST);
39
- useEffect(() => {
40
- measure(flow.current, performance.now(), Math.max(0, words - goal.current));
41
- goal.current = words;
42
- if (cursor.current < shown || cursor.current > words) cursor.current = shown;
43
- });
44
- useEffect(() => {
45
- if (!chasing) return;
46
- let drawn = performance.now();
47
- let frame = requestAnimationFrame(function step(now) {
48
- const elapsed = Math.max(0, now - drawn);
49
- drawn = Math.max(drawn, now);
50
- const arriving = measure(flow.current, now, 0);
51
- const from = cursor.current;
52
- const behind = goal.current - from;
53
- if (behind > 0) {
54
- const held = arriving * BUFFER_MS / 1e3;
55
- const aim = Math.min(FASTEST, Math.max(SLOWEST, arriving + (behind - held) * 1e3 / CORRECT_MS));
56
- pace.current += (aim - pace.current) * Math.min(1, elapsed / EASE_MS);
57
- cursor.current = Math.min(goal.current, Math.floor(from) + 1, from + elapsed * pace.current / 1e3);
58
- if (Math.floor(cursor.current) !== Math.floor(from)) setLanded(Math.floor(cursor.current));
59
- }
60
- frame = requestAnimationFrame(step);
61
- });
62
- return () => cancelAnimationFrame(frame);
63
- }, [chasing]);
64
- if (!chasing) return text;
65
- return text.slice(0, stops[shown]);
66
- }
67
-
68
- export { useRevealedText };
2
+ export { u as useRevealedText } from '../use-reveal-d77hxHz9.js';
69
3
  //# sourceMappingURL=MarkdownRenderer.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MarkdownRenderer.es.js","sources":["../../src/ds/components/MarkdownRenderer/use-reveal.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\n\n/** How much of the reply the reveal aims to be holding back, as time at the speed it is playing. */\nconst BUFFER_MS = 1000;\n\n/** How long it takes to work off a buffer that is too full or too empty. */\nconst CORRECT_MS = 1000;\n\n/** How long a word keeps counting towards the speed the reply is arriving at. */\nconst FLOW_MS = 700;\n\n/** How long the reveal takes to adopt a new speed, so a change of pace is never a jolt. */\nconst EASE_MS = 400;\n\n/** Words per second it will not go below: a short burst between two tool calls still reads briskly. */\nconst SLOWEST = 10;\n\n/** Words per second it will not go past: one word a frame, all a screen can draw. */\nconst FASTEST = 60;\n\n/** Words it will never fall further behind than — about five seconds at full speed. */\nconst MAX_LAG = 300;\n\ninterface Flow {\n /** Words per second the reply is arriving at, as an exponential average. */\n rate: number;\n at: number;\n}\n\n/** Decays what earlier words still count for, then deposits the ones that just arrived. */\nfunction measure(flow: Flow, now: number, gained: number): number {\n // rAF timestamps and performance.now() are sampled at different points of a\n // frame, so a fresh loop can read time slightly behind the last deposit.\n const elapsed = Math.max(0, now - flow.at);\n flow.rate = flow.rate * Math.exp(-elapsed / FLOW_MS) + gained / (FLOW_MS / 1000);\n flow.at = Math.max(flow.at, now);\n\n return flow.rate;\n}\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || !window.matchMedia) return false;\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\n/** Where the reveal can cut the text: before the first word, then after each one. */\nfunction wordStops(text: string): number[] {\n const stops = [0];\n const word = /\\S+/g;\n\n for (let match = word.exec(text); match; match = word.exec(text)) stops.push(match.index + match[0].length);\n\n return stops;\n}\n\n/**\n * A jitter buffer for a streamed reply, counted in words. Chunks reach the\n * browser unevenly — a proxy flushes, a tool call ends, the model changes pace —\n * and drawing each one on arrival makes a reply lurch: ten words at once, then\n * nothing for a fifth of a second.\n *\n * So the reveal runs on its own clock: a frame loop that chunks never interrupt,\n * laying down at most one word per frame however far behind it is. It plays at\n * the speed the reply is arriving at, measured directly, and leans on that only\n * to work the buffer back to `BUFFER_MS` of held text when it drifts. Measuring\n * the speed rather than inferring it from the buffer is what lets the buffer stay\n * shallow: a reply that lasts two seconds still finds its pace in the first few\n * hundred milliseconds, where a deeper buffer would spend the whole reply filling\n * and then sprint to catch up.\n *\n * The reply it paces is whatever the caller hands it, so a caller with more than\n * prose to lay down — a tool row between two paragraphs — paces the whole of it\n * from one place and draws the rest against the same cursor.\n *\n * Nothing here reads `streaming` past the first render: a reply that stops\n * arriving is one whose measured speed decays, which winds the buffer down on its\n * own. Words, not characters, are what a reader sees land, and they are not the\n * same length: an even flow of characters still drops \"and if we do\" in one frame\n * and spends four on \"implementation\".\n */\nexport function useRevealedText(text: string, streaming: boolean): string {\n const [calm] = useState(prefersReducedMotion);\n const stops = useMemo(() => wordStops(text), [text]);\n const words = stops.length - 1;\n\n const [landed, setLanded] = useState(() => (streaming && !calm ? 0 : words));\n\n // Winds the cursor back when a shorter reply replaces the one on screen, which\n // no key can express: same element, same message, new text.\n const shown = calm ? words : Math.min(words, Math.max(landed, words - MAX_LAG));\n if (shown !== landed) setLanded(shown);\n\n const chasing = shown < words;\n const goal = useRef(words);\n const cursor = useRef(shown);\n const flow = useRef<Flow>({ rate: 0, at: 0 });\n const pace = useRef(SLOWEST);\n\n useEffect(() => {\n measure(flow.current, performance.now(), Math.max(0, words - goal.current));\n goal.current = words;\n\n if (cursor.current < shown || cursor.current > words) cursor.current = shown;\n });\n\n useEffect(() => {\n if (!chasing) return;\n\n let drawn = performance.now();\n let frame = requestAnimationFrame(function step(now) {\n const elapsed = Math.max(0, now - drawn);\n drawn = Math.max(drawn, now);\n\n const arriving = measure(flow.current, now, 0);\n const from = cursor.current;\n const behind = goal.current - from;\n\n if (behind > 0) {\n const held = (arriving * BUFFER_MS) / 1000;\n const aim = Math.min(FASTEST, Math.max(SLOWEST, arriving + ((behind - held) * 1000) / CORRECT_MS));\n\n pace.current += (aim - pace.current) * Math.min(1, elapsed / EASE_MS);\n cursor.current = Math.min(goal.current, Math.floor(from) + 1, from + (elapsed * pace.current) / 1000);\n\n if (Math.floor(cursor.current) !== Math.floor(from)) setLanded(Math.floor(cursor.current));\n }\n\n frame = requestAnimationFrame(step);\n });\n\n return () => cancelAnimationFrame(frame);\n }, [chasing]);\n\n if (!chasing) return text;\n\n return text.slice(0, stops[shown]);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MarkdownRenderer.es.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+
5
+ const useReveal = require('../../use-reveal-Sa456jZb.cjs');
6
+
7
+ const PASSAGE = "\n\n";
8
+ const ROW_MARK = Array.from({ length: 6 }, () => "").join(" ");
9
+ function partProse(part) {
10
+ if (part.type === "text") return part.text;
11
+ if (part.type === "reasoning") return part.reasoning;
12
+ return void 0;
13
+ }
14
+ function messageScript(parts) {
15
+ return parts.map((part) => partProse(part) ?? ROW_MARK).join(PASSAGE);
16
+ }
17
+ function revealedParts(parts, shown) {
18
+ const script = messageScript(parts);
19
+ if (shown.length >= script.length) return parts;
20
+ const revealed = [];
21
+ let read = 0;
22
+ for (const part of parts) {
23
+ const start = read === 0 ? 0 : read + PASSAGE.length;
24
+ if (part.type !== "text" && part.type !== "reasoning") {
25
+ read = start + ROW_MARK.length;
26
+ if (shown.length < read) break;
27
+ revealed.push(part);
28
+ continue;
29
+ }
30
+ const written = part.type === "text" ? part.text : part.reasoning;
31
+ read = start + written.length;
32
+ const text = shown.slice(start, read);
33
+ if (text === written) {
34
+ revealed.push(part);
35
+ continue;
36
+ }
37
+ if (text) revealed.push(part.type === "text" ? { ...part, text } : { ...part, reasoning: text });
38
+ break;
39
+ }
40
+ return revealed;
41
+ }
42
+ function useRevealedParts(parts, streaming) {
43
+ const shown = useReveal.useRevealedText(messageScript(parts), streaming);
44
+ return revealedParts(parts, shown);
45
+ }
46
+
47
+ exports.messageScript = messageScript;
48
+ exports.revealedParts = revealedParts;
49
+ exports.useRevealedParts = useRevealedParts;
50
+ //# sourceMappingURL=message-reveal.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-reveal.cjs.js","sources":["../../../src/ds/components/ai/message-reveal/message-reveal.ts"],"sourcesContent":["import type { MastraDBMessage } from '@mastra/core/agent/message-list';\n\nimport { useRevealedText } from '../../MarkdownRenderer/use-reveal';\n\ntype MessagePart = MastraDBMessage['content']['parts'][number];\n\n/** What separates two passages of prose that were streamed as separate blocks. */\nconst PASSAGE = '\\n\\n';\n\n/**\n * A part that is not prose, written into the reveal's script as words so it takes a\n * beat on the same clock: a burst of parallel tool calls cascades in row by row, at\n * the pace the reply is moving, instead of landing as one block.\n */\nconst ROW_MARK = Array.from({ length: 6 }, () => '').join(' ');\n\n/**\n * The words a part was written as, for the kinds the model writes word by word.\n * Reasoning streams delta by delta exactly like prose, so its passage takes its\n * words' time on the clock rather than one row's beat — a beat would hold the\n * block back whole, then let it land at once and grow unpaced.\n */\nfunction partProse(part: MessagePart): string | undefined {\n if (part.type === 'text') return part.text;\n if (part.type === 'reasoning') return part.reasoning;\n return undefined;\n}\n\n/** What the reveal paces: the whole message, rows and cards written in as beats between the prose. */\nexport function messageScript(parts: MessagePart[]): string {\n return parts.map(part => partProse(part) ?? ROW_MARK).join(PASSAGE);\n}\n\n/**\n * The parts of a message a reveal has reached, `shown` being a prefix of the script.\n *\n * A reply is not only prose: a tool row, a card, a reasoning block sit between its\n * passages, and they were written in that order. Pacing the prose alone would lay a\n * sentence down word by word while the row that follows it is already on screen — so\n * every part waits its turn on the one clock, and the reader is handed the answer in\n * the order it was written.\n */\nexport function revealedParts(parts: MessagePart[], shown: string): MessagePart[] {\n const script = messageScript(parts);\n if (shown.length >= script.length) return parts;\n\n const revealed: MessagePart[] = [];\n let read = 0;\n\n for (const part of parts) {\n const start = read === 0 ? 0 : read + PASSAGE.length;\n\n if (part.type !== 'text' && part.type !== 'reasoning') {\n read = start + ROW_MARK.length;\n if (shown.length < read) break;\n revealed.push(part);\n continue;\n }\n\n const written = part.type === 'text' ? part.text : part.reasoning;\n read = start + written.length;\n\n const text = shown.slice(start, read);\n if (text === written) {\n revealed.push(part);\n continue;\n }\n\n if (text) revealed.push(part.type === 'text' ? { ...part, text } : { ...part, reasoning: text });\n break;\n }\n\n return revealed;\n}\n\n/**\n * A message's parts as far as the reveal has laid them down. Hand the result to\n * `MessageFactory` in place of the message's own parts and the whole reply — prose,\n * reasoning, tool rows, cards — arrives at the pace it was written, from one clock,\n * without a renderer having to know a reveal is running.\n */\nexport function useRevealedParts(parts: MessagePart[], streaming: boolean): MessagePart[] {\n const shown = useRevealedText(messageScript(parts), streaming);\n\n return revealedParts(parts, shown);\n}\n"],"names":["useRevealedText"],"mappings":";;;;;;AAOA,MAAM,OAAA,GAAU,MAAA;AAOhB,MAAM,QAAA,GAAW,KAAA,CAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,CAAA,EAAE,EAAG,MAAM,GAAG,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAQ9D,SAAS,UAAU,IAAA,EAAuC;AACxD,EAAA,IAAI,IAAA,CAAK,IAAA,KAAS,MAAA,EAAQ,OAAO,IAAA,CAAK,IAAA;AACtC,EAAA,IAAI,IAAA,CAAK,IAAA,KAAS,WAAA,EAAa,OAAO,IAAA,CAAK,SAAA;AAC3C,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,cAAc,KAAA,EAA8B;AAC1D,EAAA,OAAO,KAAA,CAAM,IAAI,CAAA,IAAA,KAAQ,SAAA,CAAU,IAAI,CAAA,IAAK,QAAQ,CAAA,CAAE,IAAA,CAAK,OAAO,CAAA;AACpE;AAWO,SAAS,aAAA,CAAc,OAAsB,KAAA,EAA8B;AAChF,EAAA,MAAM,MAAA,GAAS,cAAc,KAAK,CAAA;AAClC,EAAA,IAAI,KAAA,CAAM,MAAA,IAAU,MAAA,CAAO,MAAA,EAAQ,OAAO,KAAA;AAE1C,EAAA,MAAM,WAA0B,EAAC;AACjC,EAAA,IAAI,IAAA,GAAO,CAAA;AAEX,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,KAAA,GAAQ,IAAA,KAAS,CAAA,GAAI,CAAA,GAAI,OAAO,OAAA,CAAQ,MAAA;AAE9C,IAAA,IAAI,IAAA,CAAK,IAAA,KAAS,MAAA,IAAU,IAAA,CAAK,SAAS,WAAA,EAAa;AACrD,MAAA,IAAA,GAAO,QAAQ,QAAA,CAAS,MAAA;AACxB,MAAA,IAAI,KAAA,CAAM,SAAS,IAAA,EAAM;AACzB,MAAA,QAAA,CAAS,KAAK,IAAI,CAAA;AAClB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAU,IAAA,CAAK,IAAA,KAAS,MAAA,GAAS,IAAA,CAAK,OAAO,IAAA,CAAK,SAAA;AACxD,IAAA,IAAA,GAAO,QAAQ,OAAA,CAAQ,MAAA;AAEvB,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,KAAA,EAAO,IAAI,CAAA;AACpC,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,QAAA,CAAS,KAAK,IAAI,CAAA;AAClB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,MAAM,QAAA,CAAS,IAAA,CAAK,IAAA,CAAK,IAAA,KAAS,SAAS,EAAE,GAAG,IAAA,EAAM,IAAA,KAAS,EAAE,GAAG,IAAA,EAAM,SAAA,EAAW,MAAM,CAAA;AAC/F,IAAA;AAAA,EACF;AAEA,EAAA,OAAO,QAAA;AACT;AAQO,SAAS,gBAAA,CAAiB,OAAsB,SAAA,EAAmC;AACxF,EAAA,MAAM,KAAA,GAAQA,yBAAA,CAAgB,aAAA,CAAc,KAAK,GAAG,SAAS,CAAA;AAE7D,EAAA,OAAO,aAAA,CAAc,OAAO,KAAK,CAAA;AACnC;;;;;;"}
@@ -0,0 +1,2 @@
1
+ export * from '../../src/ds/components/ai/message-reveal/index'
2
+ export {}
@@ -0,0 +1,44 @@
1
+ import { u as useRevealedText } from '../../use-reveal-d77hxHz9.js';
2
+
3
+ const PASSAGE = "\n\n";
4
+ const ROW_MARK = Array.from({ length: 6 }, () => "").join(" ");
5
+ function partProse(part) {
6
+ if (part.type === "text") return part.text;
7
+ if (part.type === "reasoning") return part.reasoning;
8
+ return void 0;
9
+ }
10
+ function messageScript(parts) {
11
+ return parts.map((part) => partProse(part) ?? ROW_MARK).join(PASSAGE);
12
+ }
13
+ function revealedParts(parts, shown) {
14
+ const script = messageScript(parts);
15
+ if (shown.length >= script.length) return parts;
16
+ const revealed = [];
17
+ let read = 0;
18
+ for (const part of parts) {
19
+ const start = read === 0 ? 0 : read + PASSAGE.length;
20
+ if (part.type !== "text" && part.type !== "reasoning") {
21
+ read = start + ROW_MARK.length;
22
+ if (shown.length < read) break;
23
+ revealed.push(part);
24
+ continue;
25
+ }
26
+ const written = part.type === "text" ? part.text : part.reasoning;
27
+ read = start + written.length;
28
+ const text = shown.slice(start, read);
29
+ if (text === written) {
30
+ revealed.push(part);
31
+ continue;
32
+ }
33
+ if (text) revealed.push(part.type === "text" ? { ...part, text } : { ...part, reasoning: text });
34
+ break;
35
+ }
36
+ return revealed;
37
+ }
38
+ function useRevealedParts(parts, streaming) {
39
+ const shown = useRevealedText(messageScript(parts), streaming);
40
+ return revealedParts(parts, shown);
41
+ }
42
+
43
+ export { messageScript, revealedParts, useRevealedParts };
44
+ //# sourceMappingURL=message-reveal.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-reveal.es.js","sources":["../../../src/ds/components/ai/message-reveal/message-reveal.ts"],"sourcesContent":["import type { MastraDBMessage } from '@mastra/core/agent/message-list';\n\nimport { useRevealedText } from '../../MarkdownRenderer/use-reveal';\n\ntype MessagePart = MastraDBMessage['content']['parts'][number];\n\n/** What separates two passages of prose that were streamed as separate blocks. */\nconst PASSAGE = '\\n\\n';\n\n/**\n * A part that is not prose, written into the reveal's script as words so it takes a\n * beat on the same clock: a burst of parallel tool calls cascades in row by row, at\n * the pace the reply is moving, instead of landing as one block.\n */\nconst ROW_MARK = Array.from({ length: 6 }, () => '').join(' ');\n\n/**\n * The words a part was written as, for the kinds the model writes word by word.\n * Reasoning streams delta by delta exactly like prose, so its passage takes its\n * words' time on the clock rather than one row's beat — a beat would hold the\n * block back whole, then let it land at once and grow unpaced.\n */\nfunction partProse(part: MessagePart): string | undefined {\n if (part.type === 'text') return part.text;\n if (part.type === 'reasoning') return part.reasoning;\n return undefined;\n}\n\n/** What the reveal paces: the whole message, rows and cards written in as beats between the prose. */\nexport function messageScript(parts: MessagePart[]): string {\n return parts.map(part => partProse(part) ?? ROW_MARK).join(PASSAGE);\n}\n\n/**\n * The parts of a message a reveal has reached, `shown` being a prefix of the script.\n *\n * A reply is not only prose: a tool row, a card, a reasoning block sit between its\n * passages, and they were written in that order. Pacing the prose alone would lay a\n * sentence down word by word while the row that follows it is already on screen — so\n * every part waits its turn on the one clock, and the reader is handed the answer in\n * the order it was written.\n */\nexport function revealedParts(parts: MessagePart[], shown: string): MessagePart[] {\n const script = messageScript(parts);\n if (shown.length >= script.length) return parts;\n\n const revealed: MessagePart[] = [];\n let read = 0;\n\n for (const part of parts) {\n const start = read === 0 ? 0 : read + PASSAGE.length;\n\n if (part.type !== 'text' && part.type !== 'reasoning') {\n read = start + ROW_MARK.length;\n if (shown.length < read) break;\n revealed.push(part);\n continue;\n }\n\n const written = part.type === 'text' ? part.text : part.reasoning;\n read = start + written.length;\n\n const text = shown.slice(start, read);\n if (text === written) {\n revealed.push(part);\n continue;\n }\n\n if (text) revealed.push(part.type === 'text' ? { ...part, text } : { ...part, reasoning: text });\n break;\n }\n\n return revealed;\n}\n\n/**\n * A message's parts as far as the reveal has laid them down. Hand the result to\n * `MessageFactory` in place of the message's own parts and the whole reply — prose,\n * reasoning, tool rows, cards — arrives at the pace it was written, from one clock,\n * without a renderer having to know a reveal is running.\n */\nexport function useRevealedParts(parts: MessagePart[], streaming: boolean): MessagePart[] {\n const shown = useRevealedText(messageScript(parts), streaming);\n\n return revealedParts(parts, shown);\n}\n"],"names":[],"mappings":";;AAOA,MAAM,OAAA,GAAU,MAAA;AAOhB,MAAM,QAAA,GAAW,KAAA,CAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,CAAA,EAAE,EAAG,MAAM,GAAG,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAQ9D,SAAS,UAAU,IAAA,EAAuC;AACxD,EAAA,IAAI,IAAA,CAAK,IAAA,KAAS,MAAA,EAAQ,OAAO,IAAA,CAAK,IAAA;AACtC,EAAA,IAAI,IAAA,CAAK,IAAA,KAAS,WAAA,EAAa,OAAO,IAAA,CAAK,SAAA;AAC3C,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,cAAc,KAAA,EAA8B;AAC1D,EAAA,OAAO,KAAA,CAAM,IAAI,CAAA,IAAA,KAAQ,SAAA,CAAU,IAAI,CAAA,IAAK,QAAQ,CAAA,CAAE,IAAA,CAAK,OAAO,CAAA;AACpE;AAWO,SAAS,aAAA,CAAc,OAAsB,KAAA,EAA8B;AAChF,EAAA,MAAM,MAAA,GAAS,cAAc,KAAK,CAAA;AAClC,EAAA,IAAI,KAAA,CAAM,MAAA,IAAU,MAAA,CAAO,MAAA,EAAQ,OAAO,KAAA;AAE1C,EAAA,MAAM,WAA0B,EAAC;AACjC,EAAA,IAAI,IAAA,GAAO,CAAA;AAEX,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,KAAA,GAAQ,IAAA,KAAS,CAAA,GAAI,CAAA,GAAI,OAAO,OAAA,CAAQ,MAAA;AAE9C,IAAA,IAAI,IAAA,CAAK,IAAA,KAAS,MAAA,IAAU,IAAA,CAAK,SAAS,WAAA,EAAa;AACrD,MAAA,IAAA,GAAO,QAAQ,QAAA,CAAS,MAAA;AACxB,MAAA,IAAI,KAAA,CAAM,SAAS,IAAA,EAAM;AACzB,MAAA,QAAA,CAAS,KAAK,IAAI,CAAA;AAClB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,UAAU,IAAA,CAAK,IAAA,KAAS,MAAA,GAAS,IAAA,CAAK,OAAO,IAAA,CAAK,SAAA;AACxD,IAAA,IAAA,GAAO,QAAQ,OAAA,CAAQ,MAAA;AAEvB,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,KAAA,EAAO,IAAI,CAAA;AACpC,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,QAAA,CAAS,KAAK,IAAI,CAAA;AAClB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,MAAM,QAAA,CAAS,IAAA,CAAK,IAAA,CAAK,IAAA,KAAS,SAAS,EAAE,GAAG,IAAA,EAAM,IAAA,KAAS,EAAE,GAAG,IAAA,EAAM,SAAA,EAAW,MAAM,CAAA;AAC/F,IAAA;AAAA,EACF;AAEA,EAAA,OAAO,QAAA;AACT;AAQO,SAAS,gBAAA,CAAiB,OAAsB,SAAA,EAAmC;AACxF,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,aAAA,CAAc,KAAK,GAAG,SAAS,CAAA;AAE7D,EAAA,OAAO,aAAA,CAAc,OAAO,KAAK,CAAA;AACnC;;;;"}
@@ -0,0 +1,6 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { ArrivalScope } from './arrival';
3
+ declare const meta: Meta<typeof ArrivalScope>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof ArrivalScope>;
6
+ export declare const ExistingAndNewContent: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { Card } from './Card';
3
+ declare const meta: Meta<typeof Card>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Card>;
6
+ export declare const Default: Story;
7
+ export declare const AppearancesAndElevation: Story;
8
+ export declare const InteractiveLinks: Story;
@@ -0,0 +1,7 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { Columns } from './index';
3
+ declare const meta: Meta<typeof Columns>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Columns>;
6
+ export declare const ResponsiveGrid: Story;
7
+ export declare const HorizontallyScrollable: Story;
@@ -0,0 +1,9 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { DataDetailsPanel } from './index';
3
+ declare const meta: Meta<typeof DataDetailsPanel>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof DataDetailsPanel>;
6
+ export declare const Populated: Story;
7
+ export declare const Loading: Story;
8
+ export declare const Empty: Story;
9
+ export declare const Collapsed: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { SelectDataFilter } from './select-data-filter';
3
+ declare const meta: Meta<typeof SelectDataFilter>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof SelectDataFilter>;
6
+ export declare const Default: Story;
7
+ export declare const WithActiveFilters: Story;
8
+ export declare const Disabled: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { DateRangeTimeline } from './DateRangeTimeline';
3
+ declare const meta: Meta<typeof DateRangeTimeline>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof DateRangeTimeline>;
6
+ export declare const Default: Story;
7
+ export declare const SingleDay: Story;
8
+ export declare const MultiYear: Story;
@@ -0,0 +1,7 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { ErrorState } from './ErrorState';
3
+ declare const meta: Meta<typeof ErrorState>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof ErrorState>;
6
+ export declare const Default: Story;
7
+ export declare const WithRecoveryAction: Story;
@@ -0,0 +1,7 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { ListSearch } from './list-search';
3
+ declare const meta: Meta<typeof ListSearch>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof ListSearch>;
6
+ export declare const Default: Story;
7
+ export declare const PresetValue: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { LogsDataList } from './index';
3
+ declare const meta: Meta<typeof LogsDataList>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof LogsDataList>;
6
+ export declare const Populated: Story;
7
+ export declare const NoMatches: Story;
8
+ export declare const Loading: Story;
@@ -0,0 +1,7 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { MessageScroller } from './message-scroller';
3
+ declare const meta: Meta<typeof MessageScroller>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof MessageScroller>;
6
+ export declare const Conversation: Story;
7
+ export declare const AutoFollow: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { MetricsLineChart } from './metrics-line-chart';
3
+ declare const meta: Meta<typeof MetricsLineChart>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof MetricsLineChart>;
6
+ export declare const MultipleSeries: Story;
7
+ export declare const SinglePoint: Story;
8
+ export declare const FixedDomain: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { PageHeader } from './page-header';
3
+ declare const meta: Meta<typeof PageHeader>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof PageHeader>;
6
+ export declare const Default: Story;
7
+ export declare const Loading: Story;
8
+ export declare const SmallerTitle: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { PageLayout } from './index';
3
+ declare const meta: Meta<typeof PageLayout>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof PageLayout>;
6
+ export declare const FullPage: Story;
7
+ export declare const NarrowSettings: Story;
8
+ export declare const CenteredEmptyState: Story;
@@ -0,0 +1,7 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { PendingIndicator } from './pending-indicator';
3
+ declare const meta: Meta<typeof PendingIndicator>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof PendingIndicator>;
6
+ export declare const Default: Story;
7
+ export declare const InConversation: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { PermissionDenied } from './PermissionDenied';
3
+ declare const meta: Meta<typeof PermissionDenied>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof PermissionDenied>;
6
+ export declare const Default: Story;
7
+ export declare const ResourceSpecific: Story;
8
+ export declare const CustomRecovery: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { PrevNextNav } from './prev-next-nav';
3
+ declare const meta: Meta<typeof PrevNextNav>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof PrevNextNav>;
6
+ export declare const BetweenItems: Story;
7
+ export declare const FirstItem: Story;
8
+ export declare const LastItem: Story;
@@ -0,0 +1,7 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { SessionExpired } from './SessionExpired';
3
+ declare const meta: Meta<typeof SessionExpired>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof SessionExpired>;
6
+ export declare const Default: Story;
7
+ export declare const CustomCopy: Story;
@@ -0,0 +1,7 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { Shimmer } from './shimmer';
3
+ declare const meta: Meta<typeof Shimmer>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Shimmer>;
6
+ export declare const Default: Story;
7
+ export declare const ActiveToSettled: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { StatusBadge } from './StatusBadge';
3
+ declare const meta: Meta<typeof StatusBadge>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof StatusBadge>;
6
+ export declare const Default: Story;
7
+ export declare const Variants: Story;
8
+ export declare const SizesAndPulse: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { ThreadList } from './thread-list';
3
+ declare const meta: Meta<typeof ThreadList>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof ThreadList>;
6
+ export declare const Default: Story;
7
+ export declare const Embedded: Story;
8
+ export declare const Empty: Story;
@@ -0,0 +1,7 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { ThreadRail } from './thread-rail';
3
+ declare const meta: Meta<typeof ThreadRail>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof ThreadRail>;
6
+ export declare const ConversationTimeline: Story;
7
+ export declare const Empty: Story;
@@ -0,0 +1,8 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { TokenBudget } from './token-budget';
3
+ declare const meta: Meta<typeof TokenBudget>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof TokenBudget>;
6
+ export declare const Default: Story;
7
+ export declare const Budgets: Story;
8
+ export declare const Details: Story;
@@ -0,0 +1 @@
1
+ export * from './message-reveal';
@@ -0,0 +1,22 @@
1
+ import { MastraDBMessage } from '@mastra/core/agent/message-list';
2
+ type MessagePart = MastraDBMessage['content']['parts'][number];
3
+ /** What the reveal paces: the whole message, rows and cards written in as beats between the prose. */
4
+ export declare function messageScript(parts: MessagePart[]): string;
5
+ /**
6
+ * The parts of a message a reveal has reached, `shown` being a prefix of the script.
7
+ *
8
+ * A reply is not only prose: a tool row, a card, a reasoning block sit between its
9
+ * passages, and they were written in that order. Pacing the prose alone would lay a
10
+ * sentence down word by word while the row that follows it is already on screen — so
11
+ * every part waits its turn on the one clock, and the reader is handed the answer in
12
+ * the order it was written.
13
+ */
14
+ export declare function revealedParts(parts: MessagePart[], shown: string): MessagePart[];
15
+ /**
16
+ * A message's parts as far as the reveal has laid them down. Hand the result to
17
+ * `MessageFactory` in place of the message's own parts and the whole reply — prose,
18
+ * reasoning, tool rows, cards — arrives at the pace it was written, from one clock,
19
+ * without a renderer having to know a reveal is running.
20
+ */
21
+ export declare function useRevealedParts(parts: MessagePart[], streaming: boolean): MessagePart[];
22
+ export {};
package/dist/style.css CHANGED
@@ -1621,6 +1621,10 @@
1621
1621
  margin-right: var(--spacing-8);
1622
1622
  }
1623
1623
 
1624
+ .mr-12 {
1625
+ margin-right: var(--spacing-12);
1626
+ }
1627
+
1624
1628
  .-mb-12 {
1625
1629
  margin-bottom: calc(var(--spacing-12) * -1);
1626
1630
  }
@@ -1706,6 +1710,14 @@
1706
1710
  margin-left: var(--spacing-8);
1707
1711
  }
1708
1712
 
1713
+ .ml-10 {
1714
+ margin-left: var(--spacing-10);
1715
+ }
1716
+
1717
+ .ml-12 {
1718
+ margin-left: var(--spacing-12);
1719
+ }
1720
+
1709
1721
  .ml-\[14px\] {
1710
1722
  margin-left: 14px;
1711
1723
  }
@@ -1988,6 +2000,10 @@
1988
2000
  height: var(--spacing-40);
1989
2001
  }
1990
2002
 
2003
+ .h-56 {
2004
+ height: var(--spacing-56);
2005
+ }
2006
+
1991
2007
  .h-60 {
1992
2008
  height: var(--spacing-60);
1993
2009
  }
@@ -1996,10 +2012,18 @@
1996
2012
  height: var(--spacing-64);
1997
2013
  }
1998
2014
 
2015
+ .h-72 {
2016
+ height: var(--spacing-72);
2017
+ }
2018
+
1999
2019
  .h-80 {
2000
2020
  height: var(--spacing-80);
2001
2021
  }
2002
2022
 
2023
+ .h-96 {
2024
+ height: var(--spacing-96);
2025
+ }
2026
+
2003
2027
  .h-\[1\.8rem\] {
2004
2028
  height: 1.8rem;
2005
2029
  }
@@ -2012,6 +2036,22 @@
2012
2036
  height: 30vh;
2013
2037
  }
2014
2038
 
2039
+ .h-\[32rem\] {
2040
+ height: 32rem;
2041
+ }
2042
+
2043
+ .h-\[34rem\] {
2044
+ height: 34rem;
2045
+ }
2046
+
2047
+ .h-\[36rem\] {
2048
+ height: 36rem;
2049
+ }
2050
+
2051
+ .h-\[38rem\] {
2052
+ height: 38rem;
2053
+ }
2054
+
2015
2055
  .h-\[160px\] {
2016
2056
  height: 160px;
2017
2057
  }
@@ -2241,10 +2281,18 @@
2241
2281
  min-height: var(--spacing-60);
2242
2282
  }
2243
2283
 
2284
+ .min-h-64 {
2285
+ min-height: var(--spacing-64);
2286
+ }
2287
+
2244
2288
  .min-h-72 {
2245
2289
  min-height: var(--spacing-72);
2246
2290
  }
2247
2291
 
2292
+ .min-h-\[34rem\] {
2293
+ min-height: 34rem;
2294
+ }
2295
+
2248
2296
  .min-h-\[70cqh\] {
2249
2297
  min-height: 70cqh;
2250
2298
  }
@@ -2501,6 +2549,30 @@
2501
2549
  width: calc(100vw - 2rem);
2502
2550
  }
2503
2551
 
2552
+ .w-\[min\(24rem\,calc\(100vw-2rem\)\)\] {
2553
+ width: min(24rem, 100vw - 2rem);
2554
+ }
2555
+
2556
+ .w-\[min\(34rem\,calc\(100vw-3rem\)\)\] {
2557
+ width: min(34rem, 100vw - 3rem);
2558
+ }
2559
+
2560
+ .w-\[min\(42rem\,calc\(100vw-3rem\)\)\] {
2561
+ width: min(42rem, 100vw - 3rem);
2562
+ }
2563
+
2564
+ .w-\[min\(44rem\,calc\(100vw-2rem\)\)\] {
2565
+ width: min(44rem, 100vw - 2rem);
2566
+ }
2567
+
2568
+ .w-\[min\(48rem\,calc\(100vw-3rem\)\)\] {
2569
+ width: min(48rem, 100vw - 3rem);
2570
+ }
2571
+
2572
+ .w-\[min\(52rem\,calc\(100vw-3rem\)\)\] {
2573
+ width: min(52rem, 100vw - 3rem);
2574
+ }
2575
+
2504
2576
  .w-\[var\(--active-tab-width\)\] {
2505
2577
  width: var(--active-tab-width);
2506
2578
  }
@@ -9268,6 +9340,14 @@
9268
9340
  max-height: none;
9269
9341
  }
9270
9342
 
9343
+ .md\:grid-cols-2 {
9344
+ grid-template-columns: repeat(2, minmax(0, 1fr));
9345
+ }
9346
+
9347
+ .md\:grid-cols-3 {
9348
+ grid-template-columns: repeat(3, minmax(0, 1fr));
9349
+ }
9350
+
9271
9351
  .md\:grid-cols-\[13rem_minmax\(0\,1fr\)\] {
9272
9352
  grid-template-columns: 13rem minmax(0, 1fr);
9273
9353
  }
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ const React = require('react');
4
+
5
+ const BUFFER_MS = 1e3;
6
+ const CORRECT_MS = 1e3;
7
+ const FLOW_MS = 700;
8
+ const EASE_MS = 400;
9
+ const SLOWEST = 10;
10
+ const FASTEST = 60;
11
+ const MAX_LAG = 300;
12
+ function measure(flow, now, gained) {
13
+ const elapsed = Math.max(0, now - flow.at);
14
+ flow.rate = flow.rate * Math.exp(-elapsed / FLOW_MS) + gained / (FLOW_MS / 1e3);
15
+ flow.at = Math.max(flow.at, now);
16
+ return flow.rate;
17
+ }
18
+ function prefersReducedMotion() {
19
+ if (typeof window === "undefined" || !window.matchMedia) return false;
20
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
21
+ }
22
+ function wordStops(text) {
23
+ const stops = [0];
24
+ const word = /\S+/g;
25
+ for (let match = word.exec(text); match; match = word.exec(text)) stops.push(match.index + match[0].length);
26
+ return stops;
27
+ }
28
+ function useRevealedText(text, streaming) {
29
+ const [calm] = React.useState(prefersReducedMotion);
30
+ const stops = React.useMemo(() => wordStops(text), [text]);
31
+ const words = stops.length - 1;
32
+ const [landed, setLanded] = React.useState(() => streaming && !calm ? 0 : words);
33
+ const shown = calm ? words : Math.min(words, Math.max(landed, words - MAX_LAG));
34
+ if (shown !== landed) setLanded(shown);
35
+ const chasing = shown < words;
36
+ const goal = React.useRef(words);
37
+ const cursor = React.useRef(shown);
38
+ const flow = React.useRef({ rate: 0, at: 0 });
39
+ const pace = React.useRef(SLOWEST);
40
+ React.useEffect(() => {
41
+ measure(flow.current, performance.now(), Math.max(0, words - goal.current));
42
+ goal.current = words;
43
+ if (cursor.current < shown || cursor.current > words) cursor.current = shown;
44
+ });
45
+ React.useEffect(() => {
46
+ if (!chasing) return;
47
+ let drawn = performance.now();
48
+ let frame = requestAnimationFrame(function step(now) {
49
+ const elapsed = Math.max(0, now - drawn);
50
+ drawn = Math.max(drawn, now);
51
+ const arriving = measure(flow.current, now, 0);
52
+ const from = cursor.current;
53
+ const behind = goal.current - from;
54
+ if (behind > 0) {
55
+ const held = arriving * BUFFER_MS / 1e3;
56
+ const aim = Math.min(FASTEST, Math.max(SLOWEST, arriving + (behind - held) * 1e3 / CORRECT_MS));
57
+ pace.current += (aim - pace.current) * Math.min(1, elapsed / EASE_MS);
58
+ cursor.current = Math.min(goal.current, Math.floor(from) + 1, from + elapsed * pace.current / 1e3);
59
+ if (Math.floor(cursor.current) !== Math.floor(from)) setLanded(Math.floor(cursor.current));
60
+ }
61
+ frame = requestAnimationFrame(step);
62
+ });
63
+ return () => cancelAnimationFrame(frame);
64
+ }, [chasing]);
65
+ if (!chasing) return text;
66
+ return text.slice(0, stops[shown]);
67
+ }
68
+
69
+ exports.useRevealedText = useRevealedText;
70
+ //# sourceMappingURL=use-reveal-Sa456jZb.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-reveal-Sa456jZb.cjs","sources":["../src/ds/components/MarkdownRenderer/use-reveal.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\n\n/** How much of the reply the reveal aims to be holding back, as time at the speed it is playing. */\nconst BUFFER_MS = 1000;\n\n/** How long it takes to work off a buffer that is too full or too empty. */\nconst CORRECT_MS = 1000;\n\n/** How long a word keeps counting towards the speed the reply is arriving at. */\nconst FLOW_MS = 700;\n\n/** How long the reveal takes to adopt a new speed, so a change of pace is never a jolt. */\nconst EASE_MS = 400;\n\n/** Words per second it will not go below: a short burst between two tool calls still reads briskly. */\nconst SLOWEST = 10;\n\n/** Words per second it will not go past: one word a frame, all a screen can draw. */\nconst FASTEST = 60;\n\n/** Words it will never fall further behind than — about five seconds at full speed. */\nconst MAX_LAG = 300;\n\ninterface Flow {\n /** Words per second the reply is arriving at, as an exponential average. */\n rate: number;\n at: number;\n}\n\n/** Decays what earlier words still count for, then deposits the ones that just arrived. */\nfunction measure(flow: Flow, now: number, gained: number): number {\n // rAF timestamps and performance.now() are sampled at different points of a\n // frame, so a fresh loop can read time slightly behind the last deposit.\n const elapsed = Math.max(0, now - flow.at);\n flow.rate = flow.rate * Math.exp(-elapsed / FLOW_MS) + gained / (FLOW_MS / 1000);\n flow.at = Math.max(flow.at, now);\n\n return flow.rate;\n}\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || !window.matchMedia) return false;\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\n/** Where the reveal can cut the text: before the first word, then after each one. */\nfunction wordStops(text: string): number[] {\n const stops = [0];\n const word = /\\S+/g;\n\n for (let match = word.exec(text); match; match = word.exec(text)) stops.push(match.index + match[0].length);\n\n return stops;\n}\n\n/**\n * A jitter buffer for a streamed reply, counted in words. Chunks reach the\n * browser unevenly — a proxy flushes, a tool call ends, the model changes pace —\n * and drawing each one on arrival makes a reply lurch: ten words at once, then\n * nothing for a fifth of a second.\n *\n * So the reveal runs on its own clock: a frame loop that chunks never interrupt,\n * laying down at most one word per frame however far behind it is. It plays at\n * the speed the reply is arriving at, measured directly, and leans on that only\n * to work the buffer back to `BUFFER_MS` of held text when it drifts. Measuring\n * the speed rather than inferring it from the buffer is what lets the buffer stay\n * shallow: a reply that lasts two seconds still finds its pace in the first few\n * hundred milliseconds, where a deeper buffer would spend the whole reply filling\n * and then sprint to catch up.\n *\n * The reply it paces is whatever the caller hands it, so a caller with more than\n * prose to lay down — a tool row between two paragraphs — paces the whole of it\n * from one place and draws the rest against the same cursor.\n *\n * Nothing here reads `streaming` past the first render: a reply that stops\n * arriving is one whose measured speed decays, which winds the buffer down on its\n * own. Words, not characters, are what a reader sees land, and they are not the\n * same length: an even flow of characters still drops \"and if we do\" in one frame\n * and spends four on \"implementation\".\n */\nexport function useRevealedText(text: string, streaming: boolean): string {\n const [calm] = useState(prefersReducedMotion);\n const stops = useMemo(() => wordStops(text), [text]);\n const words = stops.length - 1;\n\n const [landed, setLanded] = useState(() => (streaming && !calm ? 0 : words));\n\n // Winds the cursor back when a shorter reply replaces the one on screen, which\n // no key can express: same element, same message, new text.\n const shown = calm ? words : Math.min(words, Math.max(landed, words - MAX_LAG));\n if (shown !== landed) setLanded(shown);\n\n const chasing = shown < words;\n const goal = useRef(words);\n const cursor = useRef(shown);\n const flow = useRef<Flow>({ rate: 0, at: 0 });\n const pace = useRef(SLOWEST);\n\n useEffect(() => {\n measure(flow.current, performance.now(), Math.max(0, words - goal.current));\n goal.current = words;\n\n if (cursor.current < shown || cursor.current > words) cursor.current = shown;\n });\n\n useEffect(() => {\n if (!chasing) return;\n\n let drawn = performance.now();\n let frame = requestAnimationFrame(function step(now) {\n const elapsed = Math.max(0, now - drawn);\n drawn = Math.max(drawn, now);\n\n const arriving = measure(flow.current, now, 0);\n const from = cursor.current;\n const behind = goal.current - from;\n\n if (behind > 0) {\n const held = (arriving * BUFFER_MS) / 1000;\n const aim = Math.min(FASTEST, Math.max(SLOWEST, arriving + ((behind - held) * 1000) / CORRECT_MS));\n\n pace.current += (aim - pace.current) * Math.min(1, elapsed / EASE_MS);\n cursor.current = Math.min(goal.current, Math.floor(from) + 1, from + (elapsed * pace.current) / 1000);\n\n if (Math.floor(cursor.current) !== Math.floor(from)) setLanded(Math.floor(cursor.current));\n }\n\n frame = requestAnimationFrame(step);\n });\n\n return () => cancelAnimationFrame(frame);\n }, [chasing]);\n\n if (!chasing) return text;\n\n return text.slice(0, stops[shown]);\n}\n"],"names":["useState","useMemo","useRef","useEffect"],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { useState, useMemo, useRef, useEffect } from 'react';
2
+
3
+ const BUFFER_MS = 1e3;
4
+ const CORRECT_MS = 1e3;
5
+ const FLOW_MS = 700;
6
+ const EASE_MS = 400;
7
+ const SLOWEST = 10;
8
+ const FASTEST = 60;
9
+ const MAX_LAG = 300;
10
+ function measure(flow, now, gained) {
11
+ const elapsed = Math.max(0, now - flow.at);
12
+ flow.rate = flow.rate * Math.exp(-elapsed / FLOW_MS) + gained / (FLOW_MS / 1e3);
13
+ flow.at = Math.max(flow.at, now);
14
+ return flow.rate;
15
+ }
16
+ function prefersReducedMotion() {
17
+ if (typeof window === "undefined" || !window.matchMedia) return false;
18
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
19
+ }
20
+ function wordStops(text) {
21
+ const stops = [0];
22
+ const word = /\S+/g;
23
+ for (let match = word.exec(text); match; match = word.exec(text)) stops.push(match.index + match[0].length);
24
+ return stops;
25
+ }
26
+ function useRevealedText(text, streaming) {
27
+ const [calm] = useState(prefersReducedMotion);
28
+ const stops = useMemo(() => wordStops(text), [text]);
29
+ const words = stops.length - 1;
30
+ const [landed, setLanded] = useState(() => streaming && !calm ? 0 : words);
31
+ const shown = calm ? words : Math.min(words, Math.max(landed, words - MAX_LAG));
32
+ if (shown !== landed) setLanded(shown);
33
+ const chasing = shown < words;
34
+ const goal = useRef(words);
35
+ const cursor = useRef(shown);
36
+ const flow = useRef({ rate: 0, at: 0 });
37
+ const pace = useRef(SLOWEST);
38
+ useEffect(() => {
39
+ measure(flow.current, performance.now(), Math.max(0, words - goal.current));
40
+ goal.current = words;
41
+ if (cursor.current < shown || cursor.current > words) cursor.current = shown;
42
+ });
43
+ useEffect(() => {
44
+ if (!chasing) return;
45
+ let drawn = performance.now();
46
+ let frame = requestAnimationFrame(function step(now) {
47
+ const elapsed = Math.max(0, now - drawn);
48
+ drawn = Math.max(drawn, now);
49
+ const arriving = measure(flow.current, now, 0);
50
+ const from = cursor.current;
51
+ const behind = goal.current - from;
52
+ if (behind > 0) {
53
+ const held = arriving * BUFFER_MS / 1e3;
54
+ const aim = Math.min(FASTEST, Math.max(SLOWEST, arriving + (behind - held) * 1e3 / CORRECT_MS));
55
+ pace.current += (aim - pace.current) * Math.min(1, elapsed / EASE_MS);
56
+ cursor.current = Math.min(goal.current, Math.floor(from) + 1, from + elapsed * pace.current / 1e3);
57
+ if (Math.floor(cursor.current) !== Math.floor(from)) setLanded(Math.floor(cursor.current));
58
+ }
59
+ frame = requestAnimationFrame(step);
60
+ });
61
+ return () => cancelAnimationFrame(frame);
62
+ }, [chasing]);
63
+ if (!chasing) return text;
64
+ return text.slice(0, stops[shown]);
65
+ }
66
+
67
+ export { useRevealedText as u };
68
+ //# sourceMappingURL=use-reveal-d77hxHz9.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-reveal-d77hxHz9.js","sources":["../src/ds/components/MarkdownRenderer/use-reveal.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from 'react';\n\n/** How much of the reply the reveal aims to be holding back, as time at the speed it is playing. */\nconst BUFFER_MS = 1000;\n\n/** How long it takes to work off a buffer that is too full or too empty. */\nconst CORRECT_MS = 1000;\n\n/** How long a word keeps counting towards the speed the reply is arriving at. */\nconst FLOW_MS = 700;\n\n/** How long the reveal takes to adopt a new speed, so a change of pace is never a jolt. */\nconst EASE_MS = 400;\n\n/** Words per second it will not go below: a short burst between two tool calls still reads briskly. */\nconst SLOWEST = 10;\n\n/** Words per second it will not go past: one word a frame, all a screen can draw. */\nconst FASTEST = 60;\n\n/** Words it will never fall further behind than — about five seconds at full speed. */\nconst MAX_LAG = 300;\n\ninterface Flow {\n /** Words per second the reply is arriving at, as an exponential average. */\n rate: number;\n at: number;\n}\n\n/** Decays what earlier words still count for, then deposits the ones that just arrived. */\nfunction measure(flow: Flow, now: number, gained: number): number {\n // rAF timestamps and performance.now() are sampled at different points of a\n // frame, so a fresh loop can read time slightly behind the last deposit.\n const elapsed = Math.max(0, now - flow.at);\n flow.rate = flow.rate * Math.exp(-elapsed / FLOW_MS) + gained / (FLOW_MS / 1000);\n flow.at = Math.max(flow.at, now);\n\n return flow.rate;\n}\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || !window.matchMedia) return false;\n return window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\n/** Where the reveal can cut the text: before the first word, then after each one. */\nfunction wordStops(text: string): number[] {\n const stops = [0];\n const word = /\\S+/g;\n\n for (let match = word.exec(text); match; match = word.exec(text)) stops.push(match.index + match[0].length);\n\n return stops;\n}\n\n/**\n * A jitter buffer for a streamed reply, counted in words. Chunks reach the\n * browser unevenly — a proxy flushes, a tool call ends, the model changes pace —\n * and drawing each one on arrival makes a reply lurch: ten words at once, then\n * nothing for a fifth of a second.\n *\n * So the reveal runs on its own clock: a frame loop that chunks never interrupt,\n * laying down at most one word per frame however far behind it is. It plays at\n * the speed the reply is arriving at, measured directly, and leans on that only\n * to work the buffer back to `BUFFER_MS` of held text when it drifts. Measuring\n * the speed rather than inferring it from the buffer is what lets the buffer stay\n * shallow: a reply that lasts two seconds still finds its pace in the first few\n * hundred milliseconds, where a deeper buffer would spend the whole reply filling\n * and then sprint to catch up.\n *\n * The reply it paces is whatever the caller hands it, so a caller with more than\n * prose to lay down — a tool row between two paragraphs — paces the whole of it\n * from one place and draws the rest against the same cursor.\n *\n * Nothing here reads `streaming` past the first render: a reply that stops\n * arriving is one whose measured speed decays, which winds the buffer down on its\n * own. Words, not characters, are what a reader sees land, and they are not the\n * same length: an even flow of characters still drops \"and if we do\" in one frame\n * and spends four on \"implementation\".\n */\nexport function useRevealedText(text: string, streaming: boolean): string {\n const [calm] = useState(prefersReducedMotion);\n const stops = useMemo(() => wordStops(text), [text]);\n const words = stops.length - 1;\n\n const [landed, setLanded] = useState(() => (streaming && !calm ? 0 : words));\n\n // Winds the cursor back when a shorter reply replaces the one on screen, which\n // no key can express: same element, same message, new text.\n const shown = calm ? words : Math.min(words, Math.max(landed, words - MAX_LAG));\n if (shown !== landed) setLanded(shown);\n\n const chasing = shown < words;\n const goal = useRef(words);\n const cursor = useRef(shown);\n const flow = useRef<Flow>({ rate: 0, at: 0 });\n const pace = useRef(SLOWEST);\n\n useEffect(() => {\n measure(flow.current, performance.now(), Math.max(0, words - goal.current));\n goal.current = words;\n\n if (cursor.current < shown || cursor.current > words) cursor.current = shown;\n });\n\n useEffect(() => {\n if (!chasing) return;\n\n let drawn = performance.now();\n let frame = requestAnimationFrame(function step(now) {\n const elapsed = Math.max(0, now - drawn);\n drawn = Math.max(drawn, now);\n\n const arriving = measure(flow.current, now, 0);\n const from = cursor.current;\n const behind = goal.current - from;\n\n if (behind > 0) {\n const held = (arriving * BUFFER_MS) / 1000;\n const aim = Math.min(FASTEST, Math.max(SLOWEST, arriving + ((behind - held) * 1000) / CORRECT_MS));\n\n pace.current += (aim - pace.current) * Math.min(1, elapsed / EASE_MS);\n cursor.current = Math.min(goal.current, Math.floor(from) + 1, from + (elapsed * pace.current) / 1000);\n\n if (Math.floor(cursor.current) !== Math.floor(from)) setLanded(Math.floor(cursor.current));\n }\n\n frame = requestAnimationFrame(step);\n });\n\n return () => cancelAnimationFrame(frame);\n }, [chasing]);\n\n if (!chasing) return text;\n\n return text.slice(0, stops[shown]);\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mastra/playground-ui",
3
3
  "type": "module",
4
- "version": "51.2.0",
4
+ "version": "51.3.0-alpha.0",
5
5
  "description": "Mastra Playground components",
6
6
  "sideEffects": [
7
7
  "**/*.css"
@@ -161,8 +161,8 @@
161
161
  "react": ">=19.0.0",
162
162
  "react-dom": ">=19.0.0",
163
163
  "tailwindcss": "^4.0.0",
164
- "@mastra/client-js": "^1.42.2",
165
- "@mastra/react": "1.4.7"
164
+ "@mastra/client-js": "^1.42.3-alpha.0",
165
+ "@mastra/react": "1.4.8-alpha.0"
166
166
  },
167
167
  "devDependencies": {
168
168
  "@storybook/addon-a11y": "^10.4.1",
@@ -202,11 +202,11 @@
202
202
  "vite-plugin-dts": "^4.5.4",
203
203
  "vite-plugin-lib-inject-css": "^2.2.2",
204
204
  "vitest": "4.1.10",
205
- "@mastra/client-js": "^1.42.2",
206
205
  "@internal/lint": "0.0.127",
207
- "@mastra/memory": "1.28.0",
208
- "@mastra/react": "1.4.7",
209
- "@mastra/core": "1.63.0"
206
+ "@mastra/client-js": "^1.42.3-alpha.0",
207
+ "@mastra/core": "1.63.1-alpha.0",
208
+ "@mastra/react": "1.4.8-alpha.0",
209
+ "@mastra/memory": "1.28.0"
210
210
  },
211
211
  "homepage": "https://mastra.ai",
212
212
  "repository": {