@lobehub/ui 5.8.0 → 5.9.1

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.
@@ -103,41 +103,43 @@ const StreamdownRender = memo(({ children, ...rest }) => {
103
103
  }, [processedContent, profiler]);
104
104
  const blocks = blocksResult.value;
105
105
  const { getBlockState, charDelay } = useStreamQueue(blocks);
106
- const prevBlockCharCountRef = useRef(/* @__PURE__ */ new Map());
107
106
  const blockCharDelayRef = useRef(/* @__PURE__ */ new Map());
108
- const blockTimelineRef = useRef(/* @__PURE__ */ new Map());
109
- const lastRenderTsRef = useRef(null);
110
- const renderTs = getNow();
111
- const frameDt = lastRenderTsRef.current === null ? 0 : Math.max(0, Math.min(renderTs - lastRenderTsRef.current, 120));
112
- const timelineResult = useMemo(() => {
107
+ const blockBirthsRef = useRef(/* @__PURE__ */ new Map());
108
+ const renderNow = getNow();
109
+ const birthsResult = useMemo(() => {
113
110
  const start = profiler ? getNow() : 0;
114
- const next = /* @__PURE__ */ new Map();
115
- const prevTimeline = blockTimelineRef.current;
116
- const prevCharCounts = prevBlockCharCountRef.current;
117
- for (const block of blocks) {
111
+ const nextBirths = /* @__PURE__ */ new Map();
112
+ const prevBirths = blockBirthsRef.current;
113
+ for (const [index, block] of blocks.entries()) {
114
+ if (getBlockState(index) === "queued") continue;
118
115
  const blockCharCount = countChars(block.content);
119
- const prevCharCount = prevCharCounts.get(block.startOffset) ?? 0;
120
- const prevElapsed = prevTimeline.get(block.startOffset);
121
- const latestCharStart = Math.max(0, (blockCharCount - 1) * charDelay);
122
- if (prevElapsed === void 0 || blockCharCount < prevCharCount) {
123
- next.set(block.startOffset, latestCharStart);
124
- continue;
116
+ const prev = prevBirths.get(block.startOffset);
117
+ let arr;
118
+ if (prev && prev.length === blockCharCount) arr = prev;
119
+ else if (prev && prev.length > blockCharCount) arr = prev.slice(0, blockCharCount);
120
+ else {
121
+ arr = prev ? prev.slice() : [];
122
+ const startIdx = arr.length;
123
+ const cap = renderNow + STREAM_FADE_DURATION;
124
+ for (let i = startIdx; i < blockCharCount; i++) {
125
+ const chained = (i > 0 ? arr[i - 1] : renderNow - charDelay) + charDelay;
126
+ arr.push(Math.min(cap, Math.max(chained, renderNow)));
127
+ }
125
128
  }
126
- const elapsedByTime = prevElapsed + frameDt;
127
- const minElapsed = Math.max(0, latestCharStart - charDelay * 2);
128
- next.set(block.startOffset, Math.max(elapsedByTime, minElapsed));
129
+ nextBirths.set(block.startOffset, arr);
129
130
  }
130
131
  return {
131
132
  durationMs: profiler ? getNow() - start : 0,
132
- value: next
133
+ value: nextBirths
133
134
  };
134
135
  }, [
135
136
  blocks,
136
137
  charDelay,
137
- frameDt,
138
- profiler
138
+ getBlockState,
139
+ profiler,
140
+ renderNow
139
141
  ]);
140
- const timelineForRender = timelineResult.value;
142
+ const birthsForRender = birthsResult.value;
141
143
  useEffect(() => {
142
144
  if (!profiler) return;
143
145
  profiler.recordCalculation({
@@ -167,30 +169,29 @@ const StreamdownRender = memo(({ children, ...rest }) => {
167
169
  useEffect(() => {
168
170
  if (!profiler) return;
169
171
  profiler.recordCalculation({
170
- durationMs: timelineResult.durationMs,
172
+ durationMs: birthsResult.durationMs,
171
173
  itemCount: blocks.length,
172
- name: "block-timeline",
174
+ name: "block-births",
173
175
  textLength: processedContent.length
174
176
  });
175
177
  }, [
178
+ birthsResult.durationMs,
176
179
  blocks.length,
177
180
  processedContent.length,
178
- profiler,
179
- timelineResult.durationMs
181
+ profiler
180
182
  ]);
181
183
  const blockAnimationMetaResult = useMemo(() => {
182
184
  const nextBlockCharDelay = /* @__PURE__ */ new Map();
183
185
  const blockAnimationMeta = /* @__PURE__ */ new Map();
184
186
  for (const [index, block] of blocks.entries()) {
185
187
  const state = getBlockState(index);
186
- const timelineElapsedMs = timelineForRender.get(block.startOffset) ?? 0;
188
+ const births = birthsForRender.get(block.startOffset);
187
189
  const animationMeta = resolveBlockAnimationMeta({
188
- blockCharCount: countChars(block.content),
189
190
  currentCharDelay: charDelay,
190
191
  fadeDuration: STREAM_FADE_DURATION,
192
+ lastElapsedMs: renderNow - (births && births.length > 0 ? births.at(-1) ?? renderNow : renderNow),
191
193
  previousCharDelay: blockCharDelayRef.current.get(block.startOffset),
192
- state,
193
- timelineElapsedMs
194
+ state
194
195
  });
195
196
  nextBlockCharDelay.set(block.startOffset, animationMeta.charDelay);
196
197
  blockAnimationMeta.set(block.startOffset, animationMeta);
@@ -200,23 +201,16 @@ const StreamdownRender = memo(({ children, ...rest }) => {
200
201
  blockCharDelay: nextBlockCharDelay
201
202
  };
202
203
  }, [
204
+ birthsForRender,
203
205
  blocks,
204
206
  charDelay,
205
207
  getBlockState,
206
- timelineForRender
208
+ renderNow
207
209
  ]);
208
210
  useEffect(() => {
209
- const nextCharCount = /* @__PURE__ */ new Map();
210
- for (const block of blocks) nextCharCount.set(block.startOffset, countChars(block.content));
211
211
  blockCharDelayRef.current = blockAnimationMetaResult.blockCharDelay;
212
- prevBlockCharCountRef.current = nextCharCount;
213
- blockTimelineRef.current = timelineForRender;
214
- lastRenderTsRef.current = getNow();
215
- }, [
216
- blockAnimationMetaResult.blockCharDelay,
217
- blocks,
218
- timelineForRender
219
- ]);
212
+ blockBirthsRef.current = birthsForRender;
213
+ }, [birthsForRender, blockAnimationMetaResult.blockCharDelay]);
220
214
  const handleRootRender = useCallback((_, phase, actualDuration, baseDuration) => {
221
215
  profiler?.recordRootCommit({
222
216
  actualDuration,
@@ -257,10 +251,11 @@ const StreamdownRender = memo(({ children, ...rest }) => {
257
251
  if (getBlockState(index) === "queued") return null;
258
252
  const animationMeta = blockAnimationMetaResult.blockAnimationMeta.get(block.startOffset);
259
253
  if (!animationMeta) return null;
254
+ const births = birthsForRender.get(block.startOffset);
260
255
  const plugins = animationMeta.settled ? [...baseRehypePlugins, REVEALED_STREAM_PLUGIN] : [...baseRehypePlugins, [rehypeStreamAnimated, {
261
- charDelay: animationMeta.charDelay,
256
+ births,
262
257
  fadeDuration: STREAM_FADE_DURATION,
263
- timelineElapsedMs: animationMeta.timelineElapsedMs
258
+ nowMs: renderNow
264
259
  }]];
265
260
  const key = `${generatedId}-${block.startOffset}`;
266
261
  const blockNode = /* @__PURE__ */ jsx(StreamdownBlock, {
@@ -1 +1 @@
1
- {"version":3,"file":"StreamdownRender.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/StreamdownRender.tsx"],"sourcesContent":["'use client';\n\nimport { marked } from 'marked';\nimport {\n memo,\n Profiler,\n type ProfilerOnRenderCallback,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n} from 'react';\nimport Markdown, { type Options } from 'react-markdown';\nimport remend from 'remend';\nimport type { Pluggable, PluggableList } from 'unified';\n\nimport {\n useMarkdownComponents,\n useMarkdownContent,\n useMarkdownRehypePlugins,\n useMarkdownRemarkPlugins,\n} from '@/hooks/useMarkdown';\nimport { useMarkdownContext } from '@/Markdown/components/MarkdownProvider';\nimport { rehypeStreamAnimated } from '@/Markdown/plugins/rehypeStreamAnimated';\nimport { useStreamdownProfiler } from '@/Markdown/streamProfiler';\n\nimport { resolveBlockAnimationMeta } from './streamAnimationMeta';\nimport { styles } from './style';\nimport { useSmoothStreamContent } from './useSmoothStreamContent';\nimport { type BlockInfo, useStreamQueue } from './useStreamQueue';\n\nconst STREAM_FADE_DURATION = 280;\nconst REVEALED_STREAM_PLUGIN: Pluggable = [rehypeStreamAnimated, { revealed: true }];\n\nfunction countChars(text: string): number {\n return [...text].length;\n}\n\nfunction getNow(): number {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isDeepEqualValue = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b)) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqualValue(a[i], b[i])) return false;\n }\n return true;\n }\n\n if (!isRecord(a) || !isRecord(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!isDeepEqualValue(a[key], b[key])) return false;\n }\n\n return true;\n};\n\nconst isSamePlugin = (prevPlugin: Pluggable, nextPlugin: Pluggable): boolean => {\n const prevTuple = Array.isArray(prevPlugin) ? prevPlugin : [prevPlugin];\n const nextTuple = Array.isArray(nextPlugin) ? nextPlugin : [nextPlugin];\n\n if (prevTuple.length !== nextTuple.length) return false;\n if (prevTuple[0] !== nextTuple[0]) return false;\n\n return isDeepEqualValue(prevTuple.slice(1), nextTuple.slice(1));\n};\n\nconst isSamePlugins = (\n prevPlugins?: PluggableList | null,\n nextPlugins?: PluggableList | null,\n): boolean => {\n if (prevPlugins === nextPlugins) return true;\n if (!prevPlugins || !nextPlugins) return !prevPlugins && !nextPlugins;\n if (prevPlugins.length !== nextPlugins.length) return false;\n\n for (let i = 0; i < prevPlugins.length; i++) {\n if (!isSamePlugin(prevPlugins[i], nextPlugins[i])) return false;\n }\n\n return true;\n};\n\nconst useStablePlugins = (plugins: PluggableList): PluggableList => {\n const stableRef = useRef<PluggableList>(plugins);\n\n if (!isSamePlugins(stableRef.current, plugins)) {\n stableRef.current = plugins;\n }\n\n return stableRef.current;\n};\n\nconst StreamdownBlock = memo<Options>(\n ({ children, ...rest }) => {\n return <Markdown {...rest}>{children}</Markdown>;\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.components === nextProps.components &&\n isSamePlugins(prevProps.rehypePlugins, nextProps.rehypePlugins) &&\n isSamePlugins(prevProps.remarkPlugins, nextProps.remarkPlugins),\n);\n\nStreamdownBlock.displayName = 'StreamdownBlock';\n\nexport const StreamdownRender = memo<Options>(({ children, ...rest }) => {\n const { streamSmoothingPreset = 'balanced' } = useMarkdownContext();\n const profiler = useStreamdownProfiler();\n const escapedContent = useMarkdownContent(children || '');\n const components = useMarkdownComponents();\n const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());\n const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());\n const generatedId = useId();\n const smoothedContent = useSmoothStreamContent(\n typeof escapedContent === 'string' ? escapedContent : '',\n { preset: streamSmoothingPreset },\n );\n\n const processedContentResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const value = remend(smoothedContent);\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [profiler, smoothedContent]);\n const processedContent = processedContentResult.value;\n\n const blocksResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const tokens = marked.lexer(processedContent);\n let offset = 0;\n\n const value = tokens.map((token) => {\n const block = { content: token.raw, startOffset: offset };\n offset += token.raw.length;\n return block;\n });\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [processedContent, profiler]);\n const blocks: BlockInfo[] = blocksResult.value;\n\n const { getBlockState, charDelay } = useStreamQueue(blocks);\n const prevBlockCharCountRef = useRef<Map<number, number>>(new Map());\n const blockCharDelayRef = useRef<Map<number, number>>(new Map());\n const blockTimelineRef = useRef<Map<number, number>>(new Map());\n const lastRenderTsRef = useRef<number | null>(null);\n\n const renderTs = getNow();\n const frameDt =\n lastRenderTsRef.current === null\n ? 0\n : Math.max(0, Math.min(renderTs - lastRenderTsRef.current, 120));\n\n const timelineResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const next = new Map<number, number>();\n const prevTimeline = blockTimelineRef.current;\n const prevCharCounts = prevBlockCharCountRef.current;\n\n for (const block of blocks) {\n const blockCharCount = countChars(block.content);\n const prevCharCount = prevCharCounts.get(block.startOffset) ?? 0;\n const prevElapsed = prevTimeline.get(block.startOffset);\n const latestCharStart = Math.max(0, (blockCharCount - 1) * charDelay);\n\n if (prevElapsed === undefined || blockCharCount < prevCharCount) {\n next.set(block.startOffset, latestCharStart);\n continue;\n }\n\n const elapsedByTime = prevElapsed + frameDt;\n // Avoid huge hidden backlog when stream updates in bursts.\n const minElapsed = Math.max(0, latestCharStart - charDelay * 2);\n next.set(block.startOffset, Math.max(elapsedByTime, minElapsed));\n }\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value: next,\n };\n }, [blocks, charDelay, frameDt, profiler]);\n const timelineForRender = timelineResult.value;\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: processedContentResult.durationMs,\n name: 'content-normalize',\n textLength: processedContent.length,\n });\n }, [processedContent.length, processedContentResult.durationMs, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: blocksResult.durationMs,\n itemCount: blocks.length,\n name: 'block-lex',\n textLength: processedContent.length,\n });\n }, [blocks.length, blocksResult.durationMs, processedContent.length, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: timelineResult.durationMs,\n itemCount: blocks.length,\n name: 'block-timeline',\n textLength: processedContent.length,\n });\n }, [blocks.length, processedContent.length, profiler, timelineResult.durationMs]);\n\n const blockAnimationMetaResult = useMemo(() => {\n const nextBlockCharDelay = new Map<number, number>();\n const blockAnimationMeta = new Map<number, ReturnType<typeof resolveBlockAnimationMeta>>();\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n const timelineElapsedMs = timelineForRender.get(block.startOffset) ?? 0;\n const animationMeta = resolveBlockAnimationMeta({\n blockCharCount: countChars(block.content),\n currentCharDelay: charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n previousCharDelay: blockCharDelayRef.current.get(block.startOffset),\n state,\n timelineElapsedMs,\n });\n\n nextBlockCharDelay.set(block.startOffset, animationMeta.charDelay);\n blockAnimationMeta.set(block.startOffset, animationMeta);\n }\n\n return {\n blockAnimationMeta,\n blockCharDelay: nextBlockCharDelay,\n };\n }, [blocks, charDelay, getBlockState, timelineForRender]);\n\n useEffect(() => {\n const nextCharCount = new Map<number, number>();\n for (const block of blocks) {\n nextCharCount.set(block.startOffset, countChars(block.content));\n }\n blockCharDelayRef.current = blockAnimationMetaResult.blockCharDelay;\n prevBlockCharCountRef.current = nextCharCount;\n blockTimelineRef.current = timelineForRender;\n lastRenderTsRef.current = getNow();\n }, [blockAnimationMetaResult.blockCharDelay, blocks, timelineForRender]);\n\n const handleRootRender = useCallback<ProfilerOnRenderCallback>(\n (_, phase, actualDuration, baseDuration) => {\n profiler?.recordRootCommit({\n actualDuration,\n baseDuration,\n blockCount: blocks.length,\n phase,\n textLength: processedContent.length,\n });\n },\n [blocks.length, processedContent.length, profiler],\n );\n\n const handleBlockRender = useCallback<ProfilerOnRenderCallback>(\n (id, phase, actualDuration, baseDuration) => {\n if (!profiler) return;\n\n const [, indexText, offsetText] = id.split(':');\n const blockIndex = Number(indexText);\n\n if (!Number.isFinite(blockIndex)) return;\n\n const block = blocks[blockIndex];\n if (!block) return;\n\n profiler.recordBlockCommit({\n actualDuration,\n baseDuration,\n blockChars: countChars(block.content),\n blockIndex,\n blockKey: offsetText ?? String(block.startOffset),\n phase,\n state: getBlockState(blockIndex),\n });\n },\n [blocks, getBlockState, profiler],\n );\n\n const content = (\n <div className={styles.animated}>\n {blocks.map((block, index) => {\n const state = getBlockState(index);\n if (state === 'queued') return null;\n const animationMeta = blockAnimationMetaResult.blockAnimationMeta.get(block.startOffset);\n if (!animationMeta) return null;\n\n const plugins: Pluggable[] = animationMeta.settled\n ? [...baseRehypePlugins, REVEALED_STREAM_PLUGIN]\n : [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n {\n charDelay: animationMeta.charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n timelineElapsedMs: animationMeta.timelineElapsedMs,\n },\n ],\n ];\n\n const key = `${generatedId}-${block.startOffset}`;\n const blockNode = (\n <StreamdownBlock\n {...rest}\n components={components}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n\n if (!profiler) {\n return (\n <StreamdownBlock\n {...rest}\n components={components}\n key={key}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n }\n\n return (\n <Profiler\n id={`streamdown-block:${index}:${block.startOffset}`}\n key={key}\n onRender={handleBlockRender}\n >\n {blockNode}\n </Profiler>\n );\n })}\n </div>\n );\n\n if (!profiler) return content;\n\n return (\n <Profiler id={'streamdown-root'} onRender={handleRootRender}>\n {content}\n </Profiler>\n );\n});\n\nStreamdownRender.displayName = 'StreamdownRender';\n\nexport default StreamdownRender;\n"],"mappings":"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"}
1
+ {"version":3,"file":"StreamdownRender.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/StreamdownRender.tsx"],"sourcesContent":["'use client';\n\nimport { marked } from 'marked';\nimport {\n memo,\n Profiler,\n type ProfilerOnRenderCallback,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n} from 'react';\nimport Markdown, { type Options } from 'react-markdown';\nimport remend from 'remend';\nimport type { Pluggable, PluggableList } from 'unified';\n\nimport {\n useMarkdownComponents,\n useMarkdownContent,\n useMarkdownRehypePlugins,\n useMarkdownRemarkPlugins,\n} from '@/hooks/useMarkdown';\nimport { useMarkdownContext } from '@/Markdown/components/MarkdownProvider';\nimport { rehypeStreamAnimated } from '@/Markdown/plugins/rehypeStreamAnimated';\nimport { useStreamdownProfiler } from '@/Markdown/streamProfiler';\n\nimport { resolveBlockAnimationMeta } from './streamAnimationMeta';\nimport { styles } from './style';\nimport { useSmoothStreamContent } from './useSmoothStreamContent';\nimport { type BlockInfo, useStreamQueue } from './useStreamQueue';\n\nconst STREAM_FADE_DURATION = 280;\nconst REVEALED_STREAM_PLUGIN: Pluggable = [rehypeStreamAnimated, { revealed: true }];\n\nfunction countChars(text: string): number {\n return [...text].length;\n}\n\nfunction getNow(): number {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isDeepEqualValue = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b)) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isDeepEqualValue(a[i], b[i])) return false;\n }\n return true;\n }\n\n if (!isRecord(a) || !isRecord(b)) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if (!isDeepEqualValue(a[key], b[key])) return false;\n }\n\n return true;\n};\n\nconst isSamePlugin = (prevPlugin: Pluggable, nextPlugin: Pluggable): boolean => {\n const prevTuple = Array.isArray(prevPlugin) ? prevPlugin : [prevPlugin];\n const nextTuple = Array.isArray(nextPlugin) ? nextPlugin : [nextPlugin];\n\n if (prevTuple.length !== nextTuple.length) return false;\n if (prevTuple[0] !== nextTuple[0]) return false;\n\n return isDeepEqualValue(prevTuple.slice(1), nextTuple.slice(1));\n};\n\nconst isSamePlugins = (\n prevPlugins?: PluggableList | null,\n nextPlugins?: PluggableList | null,\n): boolean => {\n if (prevPlugins === nextPlugins) return true;\n if (!prevPlugins || !nextPlugins) return !prevPlugins && !nextPlugins;\n if (prevPlugins.length !== nextPlugins.length) return false;\n\n for (let i = 0; i < prevPlugins.length; i++) {\n if (!isSamePlugin(prevPlugins[i], nextPlugins[i])) return false;\n }\n\n return true;\n};\n\nconst useStablePlugins = (plugins: PluggableList): PluggableList => {\n const stableRef = useRef<PluggableList>(plugins);\n\n if (!isSamePlugins(stableRef.current, plugins)) {\n stableRef.current = plugins;\n }\n\n return stableRef.current;\n};\n\nconst StreamdownBlock = memo<Options>(\n ({ children, ...rest }) => {\n return <Markdown {...rest}>{children}</Markdown>;\n },\n (prevProps, nextProps) =>\n prevProps.children === nextProps.children &&\n prevProps.components === nextProps.components &&\n isSamePlugins(prevProps.rehypePlugins, nextProps.rehypePlugins) &&\n isSamePlugins(prevProps.remarkPlugins, nextProps.remarkPlugins),\n);\n\nStreamdownBlock.displayName = 'StreamdownBlock';\n\nexport const StreamdownRender = memo<Options>(({ children, ...rest }) => {\n const { streamSmoothingPreset = 'balanced' } = useMarkdownContext();\n const profiler = useStreamdownProfiler();\n const escapedContent = useMarkdownContent(children || '');\n const components = useMarkdownComponents();\n const baseRehypePlugins = useStablePlugins(useMarkdownRehypePlugins());\n const remarkPlugins = useStablePlugins(useMarkdownRemarkPlugins());\n const generatedId = useId();\n const smoothedContent = useSmoothStreamContent(\n typeof escapedContent === 'string' ? escapedContent : '',\n { preset: streamSmoothingPreset },\n );\n\n const processedContentResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const value = remend(smoothedContent);\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [profiler, smoothedContent]);\n const processedContent = processedContentResult.value;\n\n const blocksResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const tokens = marked.lexer(processedContent);\n let offset = 0;\n\n const value = tokens.map((token) => {\n const block = { content: token.raw, startOffset: offset };\n offset += token.raw.length;\n return block;\n });\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value,\n };\n }, [processedContent, profiler]);\n const blocks: BlockInfo[] = blocksResult.value;\n\n const { getBlockState, charDelay } = useStreamQueue(blocks);\n const blockCharDelayRef = useRef<Map<number, number>>(new Map());\n const blockBirthsRef = useRef<Map<number, number[]>>(new Map());\n\n const renderNow = getNow();\n\n const birthsResult = useMemo(() => {\n const start = profiler ? getNow() : 0;\n const nextBirths = new Map<number, number[]>();\n const prevBirths = blockBirthsRef.current;\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n // Queued blocks are not rendered. Defer birth assignment so that\n // when the block later transitions to animating/streaming, its\n // chars start fading from that moment instead of having already\n // \"aged out\" of the fade window.\n if (state === 'queued') continue;\n\n const blockCharCount = countChars(block.content);\n const prev = prevBirths.get(block.startOffset);\n let arr: number[];\n\n if (prev && prev.length === blockCharCount) {\n arr = prev;\n } else if (prev && prev.length > blockCharCount) {\n // Block content shrunk (stream restart or upstream rewrite).\n arr = prev.slice(0, blockCharCount);\n } else {\n arr = prev ? prev.slice() : [];\n const startIdx = arr.length;\n // Chain each new char monotonically after the previous one so fades\n // never race out of order. Cap how far the fade queue can run ahead\n // of renderNow to prevent stream-faster-than-fade producing seconds\n // of invisible backlog at the tail.\n const cap = renderNow + STREAM_FADE_DURATION;\n for (let i = startIdx; i < blockCharCount; i++) {\n const prevBirth = i > 0 ? (arr[i - 1] as number) : renderNow - charDelay;\n const chained = prevBirth + charDelay;\n arr.push(Math.min(cap, Math.max(chained, renderNow)));\n }\n }\n\n nextBirths.set(block.startOffset, arr);\n }\n\n return {\n durationMs: profiler ? getNow() - start : 0,\n value: nextBirths,\n };\n }, [blocks, charDelay, getBlockState, profiler, renderNow]);\n const birthsForRender = birthsResult.value;\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: processedContentResult.durationMs,\n name: 'content-normalize',\n textLength: processedContent.length,\n });\n }, [processedContent.length, processedContentResult.durationMs, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: blocksResult.durationMs,\n itemCount: blocks.length,\n name: 'block-lex',\n textLength: processedContent.length,\n });\n }, [blocks.length, blocksResult.durationMs, processedContent.length, profiler]);\n\n useEffect(() => {\n if (!profiler) return;\n\n profiler.recordCalculation({\n durationMs: birthsResult.durationMs,\n itemCount: blocks.length,\n name: 'block-births',\n textLength: processedContent.length,\n });\n }, [birthsResult.durationMs, blocks.length, processedContent.length, profiler]);\n\n const blockAnimationMetaResult = useMemo(() => {\n const nextBlockCharDelay = new Map<number, number>();\n const blockAnimationMeta = new Map<number, ReturnType<typeof resolveBlockAnimationMeta>>();\n\n for (const [index, block] of blocks.entries()) {\n const state = getBlockState(index);\n const births = birthsForRender.get(block.startOffset);\n const lastBirthTs = births && births.length > 0 ? (births.at(-1) ?? renderNow) : renderNow;\n const lastElapsedMs = renderNow - lastBirthTs;\n const animationMeta = resolveBlockAnimationMeta({\n currentCharDelay: charDelay,\n fadeDuration: STREAM_FADE_DURATION,\n lastElapsedMs,\n previousCharDelay: blockCharDelayRef.current.get(block.startOffset),\n state,\n });\n\n nextBlockCharDelay.set(block.startOffset, animationMeta.charDelay);\n blockAnimationMeta.set(block.startOffset, animationMeta);\n }\n\n return {\n blockAnimationMeta,\n blockCharDelay: nextBlockCharDelay,\n };\n }, [birthsForRender, blocks, charDelay, getBlockState, renderNow]);\n\n useEffect(() => {\n blockCharDelayRef.current = blockAnimationMetaResult.blockCharDelay;\n blockBirthsRef.current = birthsForRender;\n }, [birthsForRender, blockAnimationMetaResult.blockCharDelay]);\n\n const handleRootRender = useCallback<ProfilerOnRenderCallback>(\n (_, phase, actualDuration, baseDuration) => {\n profiler?.recordRootCommit({\n actualDuration,\n baseDuration,\n blockCount: blocks.length,\n phase,\n textLength: processedContent.length,\n });\n },\n [blocks.length, processedContent.length, profiler],\n );\n\n const handleBlockRender = useCallback<ProfilerOnRenderCallback>(\n (id, phase, actualDuration, baseDuration) => {\n if (!profiler) return;\n\n const [, indexText, offsetText] = id.split(':');\n const blockIndex = Number(indexText);\n\n if (!Number.isFinite(blockIndex)) return;\n\n const block = blocks[blockIndex];\n if (!block) return;\n\n profiler.recordBlockCommit({\n actualDuration,\n baseDuration,\n blockChars: countChars(block.content),\n blockIndex,\n blockKey: offsetText ?? String(block.startOffset),\n phase,\n state: getBlockState(blockIndex),\n });\n },\n [blocks, getBlockState, profiler],\n );\n\n const content = (\n <div className={styles.animated}>\n {blocks.map((block, index) => {\n const state = getBlockState(index);\n if (state === 'queued') return null;\n const animationMeta = blockAnimationMetaResult.blockAnimationMeta.get(block.startOffset);\n if (!animationMeta) return null;\n\n const births = birthsForRender.get(block.startOffset);\n const plugins: Pluggable[] = animationMeta.settled\n ? [...baseRehypePlugins, REVEALED_STREAM_PLUGIN]\n : [\n ...baseRehypePlugins,\n [\n rehypeStreamAnimated,\n {\n births,\n fadeDuration: STREAM_FADE_DURATION,\n nowMs: renderNow,\n },\n ],\n ];\n\n const key = `${generatedId}-${block.startOffset}`;\n const blockNode = (\n <StreamdownBlock\n {...rest}\n components={components}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n\n if (!profiler) {\n return (\n <StreamdownBlock\n {...rest}\n components={components}\n key={key}\n rehypePlugins={plugins}\n remarkPlugins={remarkPlugins}\n >\n {block.content}\n </StreamdownBlock>\n );\n }\n\n return (\n <Profiler\n id={`streamdown-block:${index}:${block.startOffset}`}\n key={key}\n onRender={handleBlockRender}\n >\n {blockNode}\n </Profiler>\n );\n })}\n </div>\n );\n\n if (!profiler) return content;\n\n return (\n <Profiler id={'streamdown-root'} onRender={handleRootRender}>\n {content}\n </Profiler>\n );\n});\n\nStreamdownRender.displayName = 'StreamdownRender';\n\nexport default StreamdownRender;\n"],"mappings":"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"}
@@ -2,14 +2,10 @@
2
2
  const isActiveBlock = (state) => {
3
3
  return state === "animating" || state === "streaming";
4
4
  };
5
- const resolveBlockAnimationMeta = ({ blockCharCount, currentCharDelay, fadeDuration, previousCharDelay, state, timelineElapsedMs }) => {
6
- const charDelay = isActiveBlock(state) ? currentCharDelay : previousCharDelay ?? currentCharDelay;
7
- const latestCharStart = Math.max(0, (blockCharCount - 1) * charDelay);
8
- const settled = state === "revealed" && timelineElapsedMs >= latestCharStart + fadeDuration;
5
+ const resolveBlockAnimationMeta = ({ currentCharDelay, fadeDuration, lastElapsedMs, previousCharDelay, state }) => {
9
6
  return {
10
- charDelay,
11
- settled,
12
- timelineElapsedMs: settled ? latestCharStart + fadeDuration : timelineElapsedMs
7
+ charDelay: isActiveBlock(state) ? currentCharDelay : previousCharDelay ?? currentCharDelay,
8
+ settled: state === "revealed" && lastElapsedMs >= fadeDuration
13
9
  };
14
10
  };
15
11
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"streamAnimationMeta.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/streamAnimationMeta.ts"],"sourcesContent":["import { type BlockState } from './useStreamQueue';\n\nexport interface ResolveBlockAnimationMetaOptions {\n blockCharCount: number;\n currentCharDelay: number;\n fadeDuration: number;\n previousCharDelay?: number;\n state: BlockState;\n timelineElapsedMs: number;\n}\n\nexport interface BlockAnimationMeta {\n charDelay: number;\n settled: boolean;\n timelineElapsedMs: number;\n}\n\nconst isActiveBlock = (state: BlockState) => {\n return state === 'animating' || state === 'streaming';\n};\n\nexport const resolveBlockAnimationMeta = ({\n blockCharCount,\n currentCharDelay,\n fadeDuration,\n previousCharDelay,\n state,\n timelineElapsedMs,\n}: ResolveBlockAnimationMetaOptions): BlockAnimationMeta => {\n const charDelay = isActiveBlock(state)\n ? currentCharDelay\n : (previousCharDelay ?? currentCharDelay);\n const latestCharStart = Math.max(0, (blockCharCount - 1) * charDelay);\n const settled = state === 'revealed' && timelineElapsedMs >= latestCharStart + fadeDuration;\n\n return {\n charDelay,\n settled,\n timelineElapsedMs: settled ? latestCharStart + fadeDuration : timelineElapsedMs,\n };\n};\n"],"mappings":";AAiBA,MAAM,iBAAiB,UAAsB;AAC3C,QAAO,UAAU,eAAe,UAAU;;AAG5C,MAAa,6BAA6B,EACxC,gBACA,kBACA,cACA,mBACA,OACA,wBAC0D;CAC1D,MAAM,YAAY,cAAc,MAAM,GAClC,mBACC,qBAAqB;CAC1B,MAAM,kBAAkB,KAAK,IAAI,IAAI,iBAAiB,KAAK,UAAU;CACrE,MAAM,UAAU,UAAU,cAAc,qBAAqB,kBAAkB;AAE/E,QAAO;EACL;EACA;EACA,mBAAmB,UAAU,kBAAkB,eAAe;EAC/D"}
1
+ {"version":3,"file":"streamAnimationMeta.mjs","names":[],"sources":["../../../src/Markdown/SyntaxMarkdown/streamAnimationMeta.ts"],"sourcesContent":["import { type BlockState } from './useStreamQueue';\n\nexport interface ResolveBlockAnimationMetaOptions {\n currentCharDelay: number;\n fadeDuration: number;\n lastElapsedMs: number;\n previousCharDelay?: number;\n state: BlockState;\n}\n\nexport interface BlockAnimationMeta {\n charDelay: number;\n settled: boolean;\n}\n\nconst isActiveBlock = (state: BlockState) => {\n return state === 'animating' || state === 'streaming';\n};\n\nexport const resolveBlockAnimationMeta = ({\n currentCharDelay,\n fadeDuration,\n lastElapsedMs,\n previousCharDelay,\n state,\n}: ResolveBlockAnimationMetaOptions): BlockAnimationMeta => {\n const charDelay = isActiveBlock(state)\n ? currentCharDelay\n : (previousCharDelay ?? currentCharDelay);\n const settled = state === 'revealed' && lastElapsedMs >= fadeDuration;\n\n return {\n charDelay,\n settled,\n };\n};\n"],"mappings":";AAeA,MAAM,iBAAiB,UAAsB;AAC3C,QAAO,UAAU,eAAe,UAAU;;AAG5C,MAAa,6BAA6B,EACxC,kBACA,cACA,eACA,mBACA,YAC0D;AAM1D,QAAO;EACL,WANgB,cAAc,MAAM,GAClC,mBACC,qBAAqB;EAKxB,SAJc,UAAU,cAAc,iBAAiB;EAKxD"}
@@ -2,11 +2,10 @@ import { Root } from "../../node_modules/@types/hast/index.mjs";
2
2
 
3
3
  //#region src/Markdown/plugins/rehypeStreamAnimated.d.ts
4
4
  interface StreamAnimatedOptions {
5
- baseCharCount?: number;
6
- charDelay?: number;
5
+ births?: number[];
7
6
  fadeDuration?: number;
7
+ nowMs?: number;
8
8
  revealed?: boolean;
9
- timelineElapsedMs?: number;
10
9
  }
11
10
  declare const rehypeStreamAnimated: (options?: StreamAnimatedOptions) => (tree: Root) => void;
12
11
  //#endregion
@@ -23,8 +23,8 @@ function hasClass(node, cls) {
23
23
  return false;
24
24
  }
25
25
  const rehypeStreamAnimated = (options = {}) => {
26
- const { charDelay = 20, fadeDuration = 150, baseCharCount = 0, revealed = false, timelineElapsedMs } = options;
27
- const hasTimeline = typeof timelineElapsedMs === "number" && Number.isFinite(timelineElapsedMs);
26
+ const { births, fadeDuration = 150, nowMs, revealed = false } = options;
27
+ const hasBirths = !revealed && Array.isArray(births) && typeof nowMs === "number";
28
28
  return (tree) => {
29
29
  let globalCharIndex = 0;
30
30
  const shouldSkip = (node) => {
@@ -33,19 +33,17 @@ const rehypeStreamAnimated = (options = {}) => {
33
33
  const wrapText = (node) => {
34
34
  const newChildren = [];
35
35
  for (const child of node.children) if (child.type === "text") for (const char of child.value) {
36
- const relativeIndex = globalCharIndex - baseCharCount;
37
36
  let className = "stream-char";
38
37
  let delay;
39
38
  if (revealed) className = "stream-char stream-char-revealed";
40
- else if (hasTimeline) {
41
- const progress = timelineElapsedMs - globalCharIndex * charDelay;
42
- if (progress >= fadeDuration) className = "stream-char stream-char-revealed";
43
- else delay = -progress;
44
- } else if (relativeIndex >= 0) delay = relativeIndex * charDelay;
45
- else {
46
- const elapsed = -relativeIndex * charDelay;
47
- if (elapsed >= fadeDuration) className = "stream-char stream-char-revealed";
48
- else delay = -elapsed;
39
+ else if (hasBirths) {
40
+ const birthTs = births[globalCharIndex];
41
+ if (birthTs === void 0) className = "stream-char stream-char-revealed";
42
+ else {
43
+ const elapsed = nowMs - birthTs;
44
+ if (elapsed >= fadeDuration) className = "stream-char stream-char-revealed";
45
+ else delay = -elapsed;
46
+ }
49
47
  }
50
48
  const properties = { className };
51
49
  if (delay !== void 0 && delay !== 0) properties.style = `animation-delay:${delay}ms`;
@@ -1 +1 @@
1
- {"version":3,"file":"rehypeStreamAnimated.mjs","names":[],"sources":["../../../src/Markdown/plugins/rehypeStreamAnimated.ts"],"sourcesContent":["import { type Element, type ElementContent, type Root } from 'hast';\nimport { type BuildVisitor } from 'unist-util-visit';\nimport { visit } from 'unist-util-visit';\n\nexport interface StreamAnimatedOptions {\n baseCharCount?: number;\n charDelay?: number;\n fadeDuration?: number;\n revealed?: boolean;\n timelineElapsedMs?: number;\n}\n\nconst BLOCK_TAGS = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li']);\nconst SKIP_TAGS = new Set(['pre', 'code', 'table', 'svg']);\n\nfunction hasClass(node: Element, cls: string): boolean {\n const cn = node.properties?.className;\n if (Array.isArray(cn)) return cn.some((c) => String(c).includes(cls));\n if (typeof cn === 'string') return cn.includes(cls);\n return false;\n}\n\nexport const rehypeStreamAnimated = (options: StreamAnimatedOptions = {}) => {\n const {\n charDelay = 20,\n fadeDuration = 150,\n baseCharCount = 0,\n revealed = false,\n timelineElapsedMs,\n } = options;\n const hasTimeline = typeof timelineElapsedMs === 'number' && Number.isFinite(timelineElapsedMs);\n\n return (tree: Root) => {\n let globalCharIndex = 0;\n\n const shouldSkip = (node: Element): boolean => {\n return SKIP_TAGS.has(node.tagName) || hasClass(node, 'katex');\n };\n\n const wrapText = (node: Element) => {\n const newChildren: ElementContent[] = [];\n for (const child of node.children) {\n if (child.type === 'text') {\n for (const char of child.value) {\n const relativeIndex = globalCharIndex - baseCharCount;\n let className = 'stream-char';\n let delay: number | undefined;\n\n if (revealed) {\n className = 'stream-char stream-char-revealed';\n } else if (hasTimeline) {\n const progress = (timelineElapsedMs as number) - globalCharIndex * charDelay;\n if (progress >= fadeDuration) {\n className = 'stream-char stream-char-revealed';\n } else {\n // Positive delay means \"not started yet\", negative keeps\n // the current in-flight progress on rerender.\n delay = -progress;\n }\n } else if (relativeIndex >= 0) {\n // Newly appended chars start with staggered positive delay.\n delay = relativeIndex * charDelay;\n } else {\n // Previously started chars continue fading with negative delay\n // instead of being immediately switched to revealed.\n const elapsed = -relativeIndex * charDelay;\n if (elapsed >= fadeDuration) {\n className = 'stream-char stream-char-revealed';\n } else {\n delay = -elapsed;\n }\n }\n\n const properties: Record<string, any> = { className };\n if (delay !== undefined && delay !== 0) {\n properties.style = `animation-delay:${delay}ms`;\n }\n newChildren.push({\n children: [{ type: 'text', value: char }],\n properties,\n tagName: 'span',\n type: 'element',\n });\n globalCharIndex++;\n }\n } else if (child.type === 'element') {\n if (!shouldSkip(child)) {\n wrapText(child);\n }\n newChildren.push(child);\n } else {\n newChildren.push(child);\n }\n }\n node.children = newChildren;\n };\n\n visit(tree, 'element', ((node: Element) => {\n if (shouldSkip(node)) return 'skip';\n if (BLOCK_TAGS.has(node.tagName)) {\n wrapText(node);\n return 'skip';\n }\n }) as BuildVisitor<Root, 'element'>);\n };\n};\n"],"mappings":";;AAYA,MAAM,aAAa,IAAI,IAAI;CAAC;CAAK;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAK,CAAC;AAC3E,MAAM,YAAY,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAS;CAAM,CAAC;AAE1D,SAAS,SAAS,MAAe,KAAsB;CACrD,MAAM,KAAK,KAAK,YAAY;AAC5B,KAAI,MAAM,QAAQ,GAAG,CAAE,QAAO,GAAG,MAAM,MAAM,OAAO,EAAE,CAAC,SAAS,IAAI,CAAC;AACrE,KAAI,OAAO,OAAO,SAAU,QAAO,GAAG,SAAS,IAAI;AACnD,QAAO;;AAGT,MAAa,wBAAwB,UAAiC,EAAE,KAAK;CAC3E,MAAM,EACJ,YAAY,IACZ,eAAe,KACf,gBAAgB,GAChB,WAAW,OACX,sBACE;CACJ,MAAM,cAAc,OAAO,sBAAsB,YAAY,OAAO,SAAS,kBAAkB;AAE/F,SAAQ,SAAe;EACrB,IAAI,kBAAkB;EAEtB,MAAM,cAAc,SAA2B;AAC7C,UAAO,UAAU,IAAI,KAAK,QAAQ,IAAI,SAAS,MAAM,QAAQ;;EAG/D,MAAM,YAAY,SAAkB;GAClC,MAAM,cAAgC,EAAE;AACxC,QAAK,MAAM,SAAS,KAAK,SACvB,KAAI,MAAM,SAAS,OACjB,MAAK,MAAM,QAAQ,MAAM,OAAO;IAC9B,MAAM,gBAAgB,kBAAkB;IACxC,IAAI,YAAY;IAChB,IAAI;AAEJ,QAAI,SACF,aAAY;aACH,aAAa;KACtB,MAAM,WAAY,oBAA+B,kBAAkB;AACnE,SAAI,YAAY,aACd,aAAY;SAIZ,SAAQ,CAAC;eAEF,iBAAiB,EAE1B,SAAQ,gBAAgB;SACnB;KAGL,MAAM,UAAU,CAAC,gBAAgB;AACjC,SAAI,WAAW,aACb,aAAY;SAEZ,SAAQ,CAAC;;IAIb,MAAM,aAAkC,EAAE,WAAW;AACrD,QAAI,UAAU,KAAA,KAAa,UAAU,EACnC,YAAW,QAAQ,mBAAmB,MAAM;AAE9C,gBAAY,KAAK;KACf,UAAU,CAAC;MAAE,MAAM;MAAQ,OAAO;MAAM,CAAC;KACzC;KACA,SAAS;KACT,MAAM;KACP,CAAC;AACF;;YAEO,MAAM,SAAS,WAAW;AACnC,QAAI,CAAC,WAAW,MAAM,CACpB,UAAS,MAAM;AAEjB,gBAAY,KAAK,MAAM;SAEvB,aAAY,KAAK,MAAM;AAG3B,QAAK,WAAW;;AAGlB,QAAM,MAAM,aAAa,SAAkB;AACzC,OAAI,WAAW,KAAK,CAAE,QAAO;AAC7B,OAAI,WAAW,IAAI,KAAK,QAAQ,EAAE;AAChC,aAAS,KAAK;AACd,WAAO;;KAEyB"}
1
+ {"version":3,"file":"rehypeStreamAnimated.mjs","names":[],"sources":["../../../src/Markdown/plugins/rehypeStreamAnimated.ts"],"sourcesContent":["import { type Element, type ElementContent, type Root } from 'hast';\nimport { type BuildVisitor } from 'unist-util-visit';\nimport { visit } from 'unist-util-visit';\n\nexport interface StreamAnimatedOptions {\n births?: number[];\n fadeDuration?: number;\n nowMs?: number;\n revealed?: boolean;\n}\n\nconst BLOCK_TAGS = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li']);\nconst SKIP_TAGS = new Set(['pre', 'code', 'table', 'svg']);\n\nfunction hasClass(node: Element, cls: string): boolean {\n const cn = node.properties?.className;\n if (Array.isArray(cn)) return cn.some((c) => String(c).includes(cls));\n if (typeof cn === 'string') return cn.includes(cls);\n return false;\n}\n\nexport const rehypeStreamAnimated = (options: StreamAnimatedOptions = {}) => {\n const { births, fadeDuration = 150, nowMs, revealed = false } = options;\n const hasBirths = !revealed && Array.isArray(births) && typeof nowMs === 'number';\n\n return (tree: Root) => {\n let globalCharIndex = 0;\n\n const shouldSkip = (node: Element): boolean => {\n return SKIP_TAGS.has(node.tagName) || hasClass(node, 'katex');\n };\n\n const wrapText = (node: Element) => {\n const newChildren: ElementContent[] = [];\n for (const child of node.children) {\n if (child.type === 'text') {\n for (const char of child.value) {\n let className = 'stream-char';\n let delay: number | undefined;\n\n if (revealed) {\n className = 'stream-char stream-char-revealed';\n } else if (hasBirths) {\n const birthTs = births![globalCharIndex];\n if (birthTs === undefined) {\n className = 'stream-char stream-char-revealed';\n } else {\n const elapsed = (nowMs as number) - birthTs;\n if (elapsed >= fadeDuration) {\n className = 'stream-char stream-char-revealed';\n } else {\n // Negative delay = already elapsed ms into the fade.\n // Positive delay = not started yet (char born in the future,\n // i.e. staggered within the same commit).\n delay = -elapsed;\n }\n }\n }\n\n const properties: Record<string, any> = { className };\n if (delay !== undefined && delay !== 0) {\n properties.style = `animation-delay:${delay}ms`;\n }\n newChildren.push({\n children: [{ type: 'text', value: char }],\n properties,\n tagName: 'span',\n type: 'element',\n });\n globalCharIndex++;\n }\n } else if (child.type === 'element') {\n if (!shouldSkip(child)) {\n wrapText(child);\n }\n newChildren.push(child);\n } else {\n newChildren.push(child);\n }\n }\n node.children = newChildren;\n };\n\n visit(tree, 'element', ((node: Element) => {\n if (shouldSkip(node)) return 'skip';\n if (BLOCK_TAGS.has(node.tagName)) {\n wrapText(node);\n return 'skip';\n }\n }) as BuildVisitor<Root, 'element'>);\n };\n};\n"],"mappings":";;AAWA,MAAM,aAAa,IAAI,IAAI;CAAC;CAAK;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAK,CAAC;AAC3E,MAAM,YAAY,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAS;CAAM,CAAC;AAE1D,SAAS,SAAS,MAAe,KAAsB;CACrD,MAAM,KAAK,KAAK,YAAY;AAC5B,KAAI,MAAM,QAAQ,GAAG,CAAE,QAAO,GAAG,MAAM,MAAM,OAAO,EAAE,CAAC,SAAS,IAAI,CAAC;AACrE,KAAI,OAAO,OAAO,SAAU,QAAO,GAAG,SAAS,IAAI;AACnD,QAAO;;AAGT,MAAa,wBAAwB,UAAiC,EAAE,KAAK;CAC3E,MAAM,EAAE,QAAQ,eAAe,KAAK,OAAO,WAAW,UAAU;CAChE,MAAM,YAAY,CAAC,YAAY,MAAM,QAAQ,OAAO,IAAI,OAAO,UAAU;AAEzE,SAAQ,SAAe;EACrB,IAAI,kBAAkB;EAEtB,MAAM,cAAc,SAA2B;AAC7C,UAAO,UAAU,IAAI,KAAK,QAAQ,IAAI,SAAS,MAAM,QAAQ;;EAG/D,MAAM,YAAY,SAAkB;GAClC,MAAM,cAAgC,EAAE;AACxC,QAAK,MAAM,SAAS,KAAK,SACvB,KAAI,MAAM,SAAS,OACjB,MAAK,MAAM,QAAQ,MAAM,OAAO;IAC9B,IAAI,YAAY;IAChB,IAAI;AAEJ,QAAI,SACF,aAAY;aACH,WAAW;KACpB,MAAM,UAAU,OAAQ;AACxB,SAAI,YAAY,KAAA,EACd,aAAY;UACP;MACL,MAAM,UAAW,QAAmB;AACpC,UAAI,WAAW,aACb,aAAY;UAKZ,SAAQ,CAAC;;;IAKf,MAAM,aAAkC,EAAE,WAAW;AACrD,QAAI,UAAU,KAAA,KAAa,UAAU,EACnC,YAAW,QAAQ,mBAAmB,MAAM;AAE9C,gBAAY,KAAK;KACf,UAAU,CAAC;MAAE,MAAM;MAAQ,OAAO;MAAM,CAAC;KACzC;KACA,SAAS;KACT,MAAM;KACP,CAAC;AACF;;YAEO,MAAM,SAAS,WAAW;AACnC,QAAI,CAAC,WAAW,MAAM,CACpB,UAAS,MAAM;AAEjB,gBAAY,KAAK,MAAM;SAEvB,aAAY,KAAK,MAAM;AAG3B,QAAK,WAAW;;AAGlB,QAAM,MAAM,aAAa,SAAkB;AACzC,OAAI,WAAW,KAAK,CAAE,QAAO;AAC7B,OAAI,WAAW,IAAI,KAAK,QAAQ,EAAE;AAChC,aAAS,KAAK;AACd,WAAO;;KAEyB"}
@@ -0,0 +1,27 @@
1
+ import { FloatingSheetProps } from "./type.mjs";
2
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/base-ui/FloatingSheet/FloatingSheet.d.ts
5
+ declare function FloatingSheet({
6
+ open: openProp,
7
+ onOpenChange,
8
+ defaultOpen,
9
+ snapPoints: snapPointsProp,
10
+ activeSnapPoint: activeSnapPointProp,
11
+ onSnapPointChange,
12
+ minHeight: minHeightProp,
13
+ maxHeight: maxHeightProp,
14
+ restingHeight: restingHeightProp,
15
+ mode,
16
+ variant,
17
+ width,
18
+ title,
19
+ headerActions,
20
+ dismissible,
21
+ closeThreshold,
22
+ children,
23
+ className
24
+ }: FloatingSheetProps): _$react_jsx_runtime0.JSX.Element;
25
+ //#endregion
26
+ export { FloatingSheet };
27
+ //# sourceMappingURL=FloatingSheet.d.mts.map
@@ -0,0 +1,197 @@
1
+ import { styles } from "./style.mjs";
2
+ import { FloatingSheetHeader } from "./FloatingSheetHeader.mjs";
3
+ import { clamp, dampenValue, resolveSize } from "./helpers.mjs";
4
+ import { useSheetDrag } from "./useSheetDrag.mjs";
5
+ import { useSnapPoints } from "./useSnapPoints.mjs";
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import { cx } from "antd-style";
9
+ //#region src/base-ui/FloatingSheet/FloatingSheet.tsx
10
+ const ANIMATION_MS = 300;
11
+ function FloatingSheet({ open: openProp, onOpenChange, defaultOpen = false, snapPoints: snapPointsProp, activeSnapPoint: activeSnapPointProp, onSnapPointChange, minHeight: minHeightProp = 200, maxHeight: maxHeightProp = .8, restingHeight: restingHeightProp, mode = "overlay", variant = "elevated", width = "100%", title, headerActions, dismissible = true, closeThreshold = .25, children, className }) {
12
+ const s = styles;
13
+ const isControlled = openProp !== void 0;
14
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
15
+ const isOpen = isControlled ? openProp : internalOpen;
16
+ const setOpen = useCallback((value) => {
17
+ if (!isControlled) setInternalOpen(value);
18
+ onOpenChange?.(value);
19
+ }, [isControlled, onOpenChange]);
20
+ const containerRef = useRef(null);
21
+ const sheetRef = useRef(null);
22
+ const [containerHeight, setContainerHeight] = useState(0);
23
+ useEffect(() => {
24
+ const parent = sheetRef.current?.parentElement;
25
+ if (!parent) return;
26
+ containerRef.current = parent;
27
+ const observer = new ResizeObserver((entries) => {
28
+ for (const entry of entries) setContainerHeight(entry.contentRect.height);
29
+ });
30
+ observer.observe(parent);
31
+ setContainerHeight(parent.getBoundingClientRect().height);
32
+ return () => observer.disconnect();
33
+ }, []);
34
+ const minHeightPx = useMemo(() => resolveSize(minHeightProp, containerHeight), [minHeightProp, containerHeight]);
35
+ const maxHeightPx = useMemo(() => resolveSize(maxHeightProp, containerHeight), [maxHeightProp, containerHeight]);
36
+ const restingHeightPx = useMemo(() => restingHeightProp !== void 0 ? clamp(resolveSize(restingHeightProp, containerHeight), minHeightPx, maxHeightPx) : minHeightPx, [
37
+ restingHeightProp,
38
+ containerHeight,
39
+ minHeightPx,
40
+ maxHeightPx
41
+ ]);
42
+ const hasSnapPoints = !!snapPointsProp && snapPointsProp.length > 0;
43
+ const { snapPointHeights, findActiveIndex, getSnapRelease } = useSnapPoints({
44
+ closeThreshold,
45
+ containerHeight,
46
+ containerRef,
47
+ maxHeightPx,
48
+ minHeightPx,
49
+ snapPoints: snapPointsProp ?? []
50
+ });
51
+ const restingHeight = useMemo(() => {
52
+ if (!containerHeight) return 0;
53
+ if (hasSnapPoints && activeSnapPointProp !== void 0) return clamp(resolveSize(activeSnapPointProp, containerHeight), minHeightPx, maxHeightPx);
54
+ if (hasSnapPoints && snapPointHeights.length > 0) return snapPointHeights[0];
55
+ return restingHeightPx;
56
+ }, [
57
+ containerHeight,
58
+ hasSnapPoints,
59
+ activeSnapPointProp,
60
+ snapPointHeights,
61
+ minHeightPx,
62
+ maxHeightPx,
63
+ restingHeightPx
64
+ ]);
65
+ const [height, setHeight] = useState(isOpen ? restingHeight : 0);
66
+ const [isAnimating, setIsAnimating] = useState(false);
67
+ const [isClosing, setIsClosing] = useState(false);
68
+ const heightBeforeDrag = useRef(0);
69
+ const prevOpenRef = useRef(isOpen);
70
+ useEffect(() => {
71
+ const wasOpen = prevOpenRef.current;
72
+ prevOpenRef.current = isOpen;
73
+ if (isOpen && !wasOpen) {
74
+ setIsClosing(false);
75
+ setIsAnimating(true);
76
+ setHeight(restingHeight);
77
+ const timer = setTimeout(() => setIsAnimating(false), ANIMATION_MS);
78
+ return () => clearTimeout(timer);
79
+ }
80
+ if (!isOpen && wasOpen) {
81
+ setIsClosing(true);
82
+ setIsAnimating(true);
83
+ setHeight(0);
84
+ const timer = setTimeout(() => {
85
+ setIsAnimating(false);
86
+ setIsClosing(false);
87
+ }, ANIMATION_MS);
88
+ return () => clearTimeout(timer);
89
+ }
90
+ }, [isOpen]);
91
+ useEffect(() => {
92
+ if (isOpen && !isDragging) setHeight(restingHeight);
93
+ }, [restingHeight]);
94
+ const onDragChange = useCallback((draggedDistance) => {
95
+ const newHeight = heightBeforeDrag.current + draggedDistance;
96
+ if (hasSnapPoints) {
97
+ const highest = snapPointHeights.at(-1) ?? maxHeightPx;
98
+ const lowest = snapPointHeights[0] ?? minHeightPx;
99
+ if (newHeight > highest) setHeight(highest + dampenValue(newHeight - highest));
100
+ else if (newHeight < lowest) {
101
+ const undershoot = lowest - newHeight;
102
+ setHeight(Math.max(0, lowest - dampenValue(undershoot)));
103
+ } else setHeight(newHeight);
104
+ } else setHeight(clamp(newHeight, 0, maxHeightPx));
105
+ }, [
106
+ hasSnapPoints,
107
+ snapPointHeights,
108
+ maxHeightPx,
109
+ minHeightPx
110
+ ]);
111
+ const onDragEnd = useCallback((draggedDistance, velocity) => {
112
+ setIsAnimating(true);
113
+ const currentHeight = heightBeforeDrag.current + draggedDistance;
114
+ if (hasSnapPoints) {
115
+ const result = getSnapRelease({
116
+ activeIndex: findActiveIndex(heightBeforeDrag.current),
117
+ currentHeight,
118
+ dismissible,
119
+ draggedDistance,
120
+ velocity
121
+ });
122
+ if (result.type === "dismiss") {
123
+ setIsClosing(true);
124
+ setHeight(0);
125
+ const timer = setTimeout(() => {
126
+ setOpen(false);
127
+ setIsAnimating(false);
128
+ setIsClosing(false);
129
+ }, ANIMATION_MS);
130
+ return () => clearTimeout(timer);
131
+ }
132
+ setHeight(result.height);
133
+ const originalSnapValue = snapPointsProp?.find((sp) => resolveSize(sp, containerHeight) === result.height || clamp(resolveSize(sp, containerHeight), minHeightPx, maxHeightPx) === result.height);
134
+ if (originalSnapValue !== void 0) onSnapPointChange?.(originalSnapValue);
135
+ } else {
136
+ if (dismissible && currentHeight < minHeightPx * closeThreshold) {
137
+ setIsClosing(true);
138
+ setHeight(0);
139
+ const timer = setTimeout(() => {
140
+ setOpen(false);
141
+ setIsAnimating(false);
142
+ setIsClosing(false);
143
+ }, ANIMATION_MS);
144
+ return () => clearTimeout(timer);
145
+ }
146
+ setHeight(clamp(currentHeight, minHeightPx, maxHeightPx));
147
+ }
148
+ setTimeout(() => setIsAnimating(false), ANIMATION_MS);
149
+ }, [
150
+ hasSnapPoints,
151
+ findActiveIndex,
152
+ getSnapRelease,
153
+ dismissible,
154
+ snapPointsProp,
155
+ containerHeight,
156
+ minHeightPx,
157
+ maxHeightPx,
158
+ closeThreshold,
159
+ setOpen,
160
+ onSnapPointChange
161
+ ]);
162
+ const { isDragging, handleProps } = useSheetDrag({
163
+ enabled: isOpen ?? false,
164
+ onDragChange,
165
+ onDragEnd
166
+ });
167
+ useEffect(() => {
168
+ if (isDragging) heightBeforeDrag.current = height;
169
+ }, [isDragging]);
170
+ const isVisible = isOpen || isClosing || height > 0;
171
+ const shouldAnimate = !isDragging && isAnimating;
172
+ const inlineOverflowUp = mode === "inline" && isVisible ? Math.max(0, height - restingHeightPx) : 0;
173
+ return /* @__PURE__ */ jsxs("div", {
174
+ "data-floating-sheet": "",
175
+ "data-state": isOpen ? "open" : "closed",
176
+ ref: sheetRef,
177
+ className: cx(s.root, variant === "embedded" ? s.embedded : s.elevated, mode === "overlay" ? s.overlay : s.inline, mode === "overlay" ? s.overlayRadius : s.inlineRadius, shouldAnimate && s.transition, !isVisible && s.hidden, className),
178
+ style: {
179
+ height: isVisible ? height : 0,
180
+ marginTop: inlineOverflowUp ? -inlineOverflowUp : void 0,
181
+ width
182
+ },
183
+ children: [/* @__PURE__ */ jsx(FloatingSheetHeader, {
184
+ handleProps,
185
+ headerActions,
186
+ isDragging,
187
+ title
188
+ }), /* @__PURE__ */ jsx("div", {
189
+ className: s.content,
190
+ children
191
+ })]
192
+ });
193
+ }
194
+ //#endregion
195
+ export { FloatingSheet };
196
+
197
+ //# sourceMappingURL=FloatingSheet.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FloatingSheet.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/FloatingSheet.tsx"],"sourcesContent":["import { cx } from 'antd-style';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { FloatingSheetHeader } from './FloatingSheetHeader';\nimport { clamp, dampenValue, resolveSize } from './helpers';\nimport { styles } from './style';\nimport type { FloatingSheetProps } from './type';\nimport { useSheetDrag } from './useSheetDrag';\nimport { useSnapPoints } from './useSnapPoints';\n\nconst ANIMATION_MS = 300;\n\nexport function FloatingSheet({\n open: openProp,\n onOpenChange,\n defaultOpen = false,\n snapPoints: snapPointsProp,\n activeSnapPoint: activeSnapPointProp,\n onSnapPointChange,\n minHeight: minHeightProp = 200,\n maxHeight: maxHeightProp = 0.8,\n restingHeight: restingHeightProp,\n mode = 'overlay',\n variant = 'elevated',\n width = '100%',\n title,\n headerActions,\n dismissible = true,\n closeThreshold = 0.25,\n children,\n className,\n}: FloatingSheetProps) {\n const s = styles;\n\n // Controlled / uncontrolled open state\n const isControlled = openProp !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const isOpen = isControlled ? openProp : internalOpen;\n\n const setOpen = useCallback(\n (value: boolean) => {\n if (!isControlled) setInternalOpen(value);\n onOpenChange?.(value);\n },\n [isControlled, onOpenChange],\n );\n\n // Container measurement via ResizeObserver\n const containerRef = useRef<HTMLElement | null>(null);\n const sheetRef = useRef<HTMLDivElement>(null);\n const [containerHeight, setContainerHeight] = useState(0);\n\n useEffect(() => {\n const parent = sheetRef.current?.parentElement;\n if (!parent) return;\n containerRef.current = parent;\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n setContainerHeight(entry.contentRect.height);\n }\n });\n observer.observe(parent);\n setContainerHeight(parent.getBoundingClientRect().height);\n\n return () => observer.disconnect();\n }, []);\n\n // Resolve min/max to px\n const minHeightPx = useMemo(\n () => resolveSize(minHeightProp, containerHeight),\n [minHeightProp, containerHeight],\n );\n const maxHeightPx = useMemo(\n () => resolveSize(maxHeightProp, containerHeight),\n [maxHeightProp, containerHeight],\n );\n const restingHeightPx = useMemo(\n () =>\n restingHeightProp !== undefined\n ? clamp(resolveSize(restingHeightProp, containerHeight), minHeightPx, maxHeightPx)\n : minHeightPx,\n [restingHeightProp, containerHeight, minHeightPx, maxHeightPx],\n );\n\n // Snap points\n const hasSnapPoints = !!snapPointsProp && snapPointsProp.length > 0;\n const { snapPointHeights, findActiveIndex, getSnapRelease } = useSnapPoints({\n closeThreshold,\n containerHeight,\n containerRef,\n maxHeightPx,\n minHeightPx,\n snapPoints: snapPointsProp ?? [],\n });\n\n // Compute the \"resting\" height for the current open + snap state\n const restingHeight = useMemo(() => {\n if (!containerHeight) return 0;\n if (hasSnapPoints && activeSnapPointProp !== undefined) {\n const resolved = resolveSize(activeSnapPointProp, containerHeight);\n return clamp(resolved, minHeightPx, maxHeightPx);\n }\n if (hasSnapPoints && snapPointHeights.length > 0) {\n return snapPointHeights[0];\n }\n return restingHeightPx;\n }, [\n containerHeight,\n hasSnapPoints,\n activeSnapPointProp,\n snapPointHeights,\n minHeightPx,\n maxHeightPx,\n restingHeightPx,\n ]);\n\n const [height, setHeight] = useState(isOpen ? restingHeight : 0);\n const [isAnimating, setIsAnimating] = useState(false);\n // Keeps sheet visible during close animation (height → 0)\n const [isClosing, setIsClosing] = useState(false);\n const heightBeforeDrag = useRef(0);\n const prevOpenRef = useRef(isOpen);\n\n // Handle open/close transitions\n useEffect(() => {\n const wasOpen = prevOpenRef.current;\n prevOpenRef.current = isOpen;\n\n if (isOpen && !wasOpen) {\n // Opening: animate from 0 → resting height\n setIsClosing(false);\n setIsAnimating(true);\n setHeight(restingHeight);\n const timer = setTimeout(() => setIsAnimating(false), ANIMATION_MS);\n return () => clearTimeout(timer);\n }\n\n if (!isOpen && wasOpen) {\n // Closing: animate from current height → 0, then hide\n setIsClosing(true);\n setIsAnimating(true);\n setHeight(0);\n const timer = setTimeout(() => {\n setIsAnimating(false);\n setIsClosing(false);\n }, ANIMATION_MS);\n return () => clearTimeout(timer);\n }\n }, [isOpen]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Sync height when resting height changes (container resize, snap point change)\n useEffect(() => {\n if (isOpen && !isDragging) {\n setHeight(restingHeight);\n }\n }, [restingHeight]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Drag handlers\n const onDragChange = useCallback(\n (draggedDistance: number) => {\n const newHeight = heightBeforeDrag.current + draggedDistance;\n\n if (hasSnapPoints) {\n const highest = snapPointHeights.at(-1) ?? maxHeightPx;\n const lowest = snapPointHeights[0] ?? minHeightPx;\n\n if (newHeight > highest) {\n const overshoot = newHeight - highest;\n setHeight(highest + dampenValue(overshoot));\n } else if (newHeight < lowest) {\n const undershoot = lowest - newHeight;\n setHeight(Math.max(0, lowest - dampenValue(undershoot)));\n } else {\n setHeight(newHeight);\n }\n } else {\n setHeight(clamp(newHeight, 0, maxHeightPx));\n }\n },\n [hasSnapPoints, snapPointHeights, maxHeightPx, minHeightPx],\n );\n\n const onDragEnd = useCallback(\n (draggedDistance: number, velocity: number) => {\n setIsAnimating(true);\n const currentHeight = heightBeforeDrag.current + draggedDistance;\n\n if (hasSnapPoints) {\n const activeIndex = findActiveIndex(heightBeforeDrag.current);\n const result = getSnapRelease({\n activeIndex,\n currentHeight,\n dismissible,\n draggedDistance,\n velocity,\n });\n\n if (result.type === 'dismiss') {\n setIsClosing(true);\n setHeight(0);\n const timer = setTimeout(() => {\n setOpen(false);\n setIsAnimating(false);\n setIsClosing(false);\n }, ANIMATION_MS);\n return () => clearTimeout(timer);\n }\n\n setHeight(result.height);\n const originalSnapValue = snapPointsProp?.find(\n (sp) =>\n resolveSize(sp, containerHeight) === result.height ||\n clamp(resolveSize(sp, containerHeight), minHeightPx, maxHeightPx) === result.height,\n );\n if (originalSnapValue !== undefined) {\n onSnapPointChange?.(originalSnapValue);\n }\n } else {\n if (dismissible && currentHeight < minHeightPx * closeThreshold) {\n setIsClosing(true);\n setHeight(0);\n const timer = setTimeout(() => {\n setOpen(false);\n setIsAnimating(false);\n setIsClosing(false);\n }, ANIMATION_MS);\n return () => clearTimeout(timer);\n }\n setHeight(clamp(currentHeight, minHeightPx, maxHeightPx));\n }\n\n setTimeout(() => setIsAnimating(false), ANIMATION_MS);\n },\n [\n hasSnapPoints,\n findActiveIndex,\n getSnapRelease,\n dismissible,\n snapPointsProp,\n containerHeight,\n minHeightPx,\n maxHeightPx,\n closeThreshold,\n setOpen,\n onSnapPointChange,\n ],\n );\n\n const { isDragging, handleProps } = useSheetDrag({\n enabled: isOpen ?? false,\n onDragChange,\n onDragEnd,\n });\n\n // Record height at drag start\n useEffect(() => {\n if (isDragging) {\n heightBeforeDrag.current = height;\n }\n }, [isDragging]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const isVisible = isOpen || isClosing || height > 0;\n const shouldAnimate = !isDragging && isAnimating;\n const inlineOverflowUp =\n mode === 'inline' && isVisible ? Math.max(0, height - restingHeightPx) : 0;\n\n return (\n <div\n data-floating-sheet=\"\"\n data-state={isOpen ? 'open' : 'closed'}\n ref={sheetRef}\n className={cx(\n s.root,\n variant === 'embedded' ? s.embedded : s.elevated,\n mode === 'overlay' ? s.overlay : s.inline,\n mode === 'overlay' ? s.overlayRadius : s.inlineRadius,\n shouldAnimate && s.transition,\n !isVisible && s.hidden,\n className,\n )}\n style={{\n height: isVisible ? height : 0,\n marginTop: inlineOverflowUp ? -inlineOverflowUp : undefined,\n width,\n }}\n >\n <FloatingSheetHeader\n handleProps={handleProps}\n headerActions={headerActions}\n isDragging={isDragging}\n title={title}\n />\n <div className={s.content}>{children}</div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { styles } from "./style.mjs";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { cx } from "antd-style";
4
+ //#region src/base-ui/FloatingSheet/FloatingSheetHeader.tsx
5
+ function FloatingSheetHeader({ title, headerActions, isDragging, handleProps }) {
6
+ const s = styles;
7
+ return /* @__PURE__ */ jsxs("div", {
8
+ className: cx(s.header, isDragging && s.headerDragging),
9
+ ...handleProps,
10
+ children: [/* @__PURE__ */ jsx("div", { className: s.handle }), /* @__PURE__ */ jsxs("div", {
11
+ className: s.headerContent,
12
+ children: [title && /* @__PURE__ */ jsx("div", {
13
+ className: s.headerTitle,
14
+ children: title
15
+ }), headerActions && /* @__PURE__ */ jsx("div", {
16
+ className: s.headerActions,
17
+ "data-no-drag": "",
18
+ children: headerActions
19
+ })]
20
+ })]
21
+ });
22
+ }
23
+ //#endregion
24
+ export { FloatingSheetHeader };
25
+
26
+ //# sourceMappingURL=FloatingSheetHeader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FloatingSheetHeader.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/FloatingSheetHeader.tsx"],"sourcesContent":["import { cx } from 'antd-style';\nimport { type ReactNode } from 'react';\n\nimport { styles } from './style';\n\ninterface FloatingSheetHeaderProps {\n handleProps: {\n onMouseDown: (e: React.MouseEvent<HTMLDivElement>) => void;\n };\n headerActions?: ReactNode;\n isDragging: boolean;\n title?: ReactNode;\n}\n\nexport function FloatingSheetHeader({\n title,\n headerActions,\n isDragging,\n handleProps,\n}: FloatingSheetHeaderProps) {\n const s = styles;\n\n return (\n <div className={cx(s.header, isDragging && s.headerDragging)} {...handleProps}>\n <div className={s.handle} />\n <div className={s.headerContent}>\n {title && <div className={s.headerTitle}>{title}</div>}\n {headerActions && (\n <div className={s.headerActions} data-no-drag=\"\">\n {headerActions}\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAcA,SAAgB,oBAAoB,EAClC,OACA,eACA,YACA,eAC2B;CAC3B,MAAM,IAAI;AAEV,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,EAAE,QAAQ,cAAc,EAAE,eAAe;EAAE,GAAI;YAAlE,CACE,oBAAC,OAAD,EAAK,WAAW,EAAE,QAAU,CAAA,EAC5B,qBAAC,OAAD;GAAK,WAAW,EAAE;aAAlB,CACG,SAAS,oBAAC,OAAD;IAAK,WAAW,EAAE;cAAc;IAAY,CAAA,EACrD,iBACC,oBAAC,OAAD;IAAK,WAAW,EAAE;IAAe,gBAAa;cAC3C;IACG,CAAA,CAEJ;KACF"}
@@ -0,0 +1,30 @@
1
+ //#region src/base-ui/FloatingSheet/helpers.ts
2
+ /**
3
+ * Clamp a value between min and max.
4
+ */
5
+ function clamp(value, min, max) {
6
+ return Math.min(Math.max(value, min), max);
7
+ }
8
+ /**
9
+ * Rubber-band damping for drag resistance beyond snap bounds.
10
+ * Returns 0 when v <= 0, near 1:1 for small v, then grows logarithmically.
11
+ */
12
+ function dampenValue(v) {
13
+ if (v <= 0) return 0;
14
+ return 8 * Math.log1p(v / 8);
15
+ }
16
+ /**
17
+ * Resolve a size value to pixels.
18
+ * 0 < v <= 1 → fraction of containerHeight.
19
+ * v > 1 → pixel value.
20
+ * v === 0 → 0.
21
+ */
22
+ function resolveSize(value, containerHeight) {
23
+ if (value === 0) return 0;
24
+ if (value > 0 && value <= 1) return value * containerHeight;
25
+ return value;
26
+ }
27
+ //#endregion
28
+ export { clamp, dampenValue, resolveSize };
29
+
30
+ //# sourceMappingURL=helpers.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/helpers.ts"],"sourcesContent":["/**\n * Clamp a value between min and max.\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Rubber-band damping for drag resistance beyond snap bounds.\n * Returns 0 when v <= 0, near 1:1 for small v, then grows logarithmically.\n */\nexport function dampenValue(v: number): number {\n if (v <= 0) return 0;\n return 8 * Math.log1p(v / 8);\n}\n\n/**\n * Resolve a size value to pixels.\n * 0 < v <= 1 → fraction of containerHeight.\n * v > 1 → pixel value.\n * v === 0 → 0.\n */\nexport function resolveSize(value: number, containerHeight: number): number {\n if (value === 0) return 0;\n if (value > 0 && value <= 1) return value * containerHeight;\n return value;\n}\n"],"mappings":";;;;AAGA,SAAgB,MAAM,OAAe,KAAa,KAAqB;AACrE,QAAO,KAAK,IAAI,KAAK,IAAI,OAAO,IAAI,EAAE,IAAI;;;;;;AAO5C,SAAgB,YAAY,GAAmB;AAC7C,KAAI,KAAK,EAAG,QAAO;AACnB,QAAO,IAAI,KAAK,MAAM,IAAI,EAAE;;;;;;;;AAS9B,SAAgB,YAAY,OAAe,iBAAiC;AAC1E,KAAI,UAAU,EAAG,QAAO;AACxB,KAAI,QAAQ,KAAK,SAAS,EAAG,QAAO,QAAQ;AAC5C,QAAO"}
@@ -0,0 +1,2 @@
1
+ import { FloatingSheetProps } from "./type.mjs";
2
+ import { FloatingSheet } from "./FloatingSheet.mjs";
@@ -0,0 +1,94 @@
1
+ import { createStaticStyles } from "antd-style";
2
+ //#region src/base-ui/FloatingSheet/style.ts
3
+ const transitionDuration = "0.3s";
4
+ const transitionEasing = "cubic-bezier(0.32, 0.72, 0, 1)";
5
+ const styles = createStaticStyles(({ css, cssVar }) => ({
6
+ root: css`
7
+ overflow: hidden;
8
+ display: flex;
9
+ flex-direction: column;
10
+ background: ${cssVar.colorBgContainer};
11
+ `,
12
+ overlayRadius: css`
13
+ border-radius: 12px 12px 0 0;
14
+ `,
15
+ inlineRadius: css`
16
+ border-radius: 12px;
17
+ `,
18
+ elevated: css`
19
+ box-shadow: ${cssVar.boxShadowSecondary};
20
+ `,
21
+ embedded: css`
22
+ border: 1px solid ${cssVar.colorBorderSecondary};
23
+ box-shadow: none;
24
+ `,
25
+ overlay: css`
26
+ position: absolute;
27
+ z-index: 10;
28
+ inset-block-end: 0;
29
+ inset-inline: 0;
30
+ `,
31
+ inline: css`
32
+ position: relative;
33
+ z-index: 1;
34
+ flex-shrink: 0;
35
+ `,
36
+ transition: css`
37
+ transition:
38
+ height ${transitionDuration} ${transitionEasing},
39
+ margin-block-start ${transitionDuration} ${transitionEasing};
40
+ `,
41
+ hidden: css`
42
+ visibility: hidden;
43
+ `,
44
+ header: css`
45
+ cursor: grab;
46
+ user-select: none;
47
+
48
+ display: flex;
49
+ flex-direction: column;
50
+ flex-shrink: 0;
51
+ align-items: center;
52
+
53
+ padding-block: 8px 4px;
54
+ padding-inline: 16px;
55
+ `,
56
+ headerDragging: css`
57
+ cursor: grabbing;
58
+ `,
59
+ handle: css`
60
+ width: 32px;
61
+ height: 4px;
62
+ margin-block-end: 8px;
63
+ border-radius: 2px;
64
+
65
+ background: ${cssVar.colorBorderSecondary};
66
+ `,
67
+ headerContent: css`
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: space-between;
71
+
72
+ width: 100%;
73
+ min-height: 24px;
74
+ `,
75
+ headerTitle: css`
76
+ flex: 1;
77
+ min-width: 0;
78
+ `,
79
+ headerActions: css`
80
+ display: flex;
81
+ flex-shrink: 0;
82
+ gap: 4px;
83
+ align-items: center;
84
+ `,
85
+ content: css`
86
+ overflow: auto;
87
+ flex: 1;
88
+ min-height: 0;
89
+ `
90
+ }));
91
+ //#endregion
92
+ export { styles };
93
+
94
+ //# sourceMappingURL=style.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/style.ts"],"sourcesContent":["import { createStaticStyles } from 'antd-style';\n\nconst transitionDuration = '0.3s';\nconst transitionEasing = 'cubic-bezier(0.32, 0.72, 0, 1)';\n\nexport const styles = createStaticStyles(({ css, cssVar }) => ({\n root: css`\n overflow: hidden;\n display: flex;\n flex-direction: column;\n background: ${cssVar.colorBgContainer};\n `,\n overlayRadius: css`\n border-radius: 12px 12px 0 0;\n `,\n inlineRadius: css`\n border-radius: 12px;\n `,\n elevated: css`\n box-shadow: ${cssVar.boxShadowSecondary};\n `,\n embedded: css`\n border: 1px solid ${cssVar.colorBorderSecondary};\n box-shadow: none;\n `,\n overlay: css`\n position: absolute;\n z-index: 10;\n inset-block-end: 0;\n inset-inline: 0;\n `,\n inline: css`\n position: relative;\n z-index: 1;\n flex-shrink: 0;\n `,\n transition: css`\n transition:\n height ${transitionDuration} ${transitionEasing},\n margin-block-start ${transitionDuration} ${transitionEasing};\n `,\n hidden: css`\n visibility: hidden;\n `,\n header: css`\n cursor: grab;\n user-select: none;\n\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n align-items: center;\n\n padding-block: 8px 4px;\n padding-inline: 16px;\n `,\n headerDragging: css`\n cursor: grabbing;\n `,\n handle: css`\n width: 32px;\n height: 4px;\n margin-block-end: 8px;\n border-radius: 2px;\n\n background: ${cssVar.colorBorderSecondary};\n `,\n headerContent: css`\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n width: 100%;\n min-height: 24px;\n `,\n headerTitle: css`\n flex: 1;\n min-width: 0;\n `,\n headerActions: css`\n display: flex;\n flex-shrink: 0;\n gap: 4px;\n align-items: center;\n `,\n content: css`\n overflow: auto;\n flex: 1;\n min-height: 0;\n `,\n}));\n"],"mappings":";;AAEA,MAAM,qBAAqB;AAC3B,MAAM,mBAAmB;AAEzB,MAAa,SAAS,oBAAoB,EAAE,KAAK,cAAc;CAC7D,MAAM,GAAG;;;;kBAIO,OAAO,iBAAiB;;CAExC,eAAe,GAAG;;;CAGlB,cAAc,GAAG;;;CAGjB,UAAU,GAAG;kBACG,OAAO,mBAAmB;;CAE1C,UAAU,GAAG;wBACS,OAAO,qBAAqB;;;CAGlD,SAAS,GAAG;;;;;;CAMZ,QAAQ,GAAG;;;;;CAKX,YAAY,GAAG;;eAEF,mBAAmB,GAAG,iBAAiB;2BAC3B,mBAAmB,GAAG,iBAAiB;;CAEhE,QAAQ,GAAG;;;CAGX,QAAQ,GAAG;;;;;;;;;;;;CAYX,gBAAgB,GAAG;;;CAGnB,QAAQ,GAAG;;;;;;kBAMK,OAAO,qBAAqB;;CAE5C,eAAe,GAAG;;;;;;;;CAQlB,aAAa,GAAG;;;;CAIhB,eAAe,GAAG;;;;;;CAMlB,SAAS,GAAG;;;;;CAKb,EAAE"}
@@ -0,0 +1,26 @@
1
+ import { ReactNode } from "react";
2
+
3
+ //#region src/base-ui/FloatingSheet/type.d.ts
4
+ interface FloatingSheetProps {
5
+ activeSnapPoint?: number;
6
+ children?: ReactNode;
7
+ className?: string;
8
+ closeThreshold?: number;
9
+ defaultOpen?: boolean;
10
+ dismissible?: boolean;
11
+ headerActions?: ReactNode;
12
+ maxHeight?: number;
13
+ minHeight?: number;
14
+ mode?: 'overlay' | 'inline';
15
+ onOpenChange?: (open: boolean) => void;
16
+ onSnapPointChange?: (snapPoint: number) => void;
17
+ open?: boolean;
18
+ restingHeight?: number;
19
+ snapPoints?: number[];
20
+ title?: ReactNode;
21
+ variant?: 'elevated' | 'embedded';
22
+ width?: number | string;
23
+ }
24
+ //#endregion
25
+ export { FloatingSheetProps };
26
+ //# sourceMappingURL=type.d.mts.map
@@ -0,0 +1,51 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ //#region src/base-ui/FloatingSheet/useSheetDrag.ts
3
+ function useSheetDrag({ onDragChange, onDragEnd, enabled }) {
4
+ const [isDragging, setIsDragging] = useState(false);
5
+ const startY = useRef(0);
6
+ const startTime = useRef(0);
7
+ const draggingRef = useRef(false);
8
+ const onDragChangeRef = useRef(onDragChange);
9
+ const onDragEndRef = useRef(onDragEnd);
10
+ onDragChangeRef.current = onDragChange;
11
+ onDragEndRef.current = onDragEnd;
12
+ useEffect(() => {
13
+ if (!draggingRef.current) return;
14
+ const onMouseMove = (e) => {
15
+ e.preventDefault();
16
+ const draggedDistance = startY.current - e.clientY;
17
+ onDragChangeRef.current(draggedDistance);
18
+ };
19
+ const onMouseUp = (e) => {
20
+ draggingRef.current = false;
21
+ setIsDragging(false);
22
+ const draggedDistance = startY.current - e.clientY;
23
+ const elapsed = Date.now() - startTime.current;
24
+ const velocity = elapsed > 0 ? Math.abs(draggedDistance) / elapsed : 0;
25
+ onDragEndRef.current(draggedDistance, velocity);
26
+ };
27
+ document.addEventListener("mousemove", onMouseMove);
28
+ document.addEventListener("mouseup", onMouseUp);
29
+ return () => {
30
+ document.removeEventListener("mousemove", onMouseMove);
31
+ document.removeEventListener("mouseup", onMouseUp);
32
+ };
33
+ }, [isDragging]);
34
+ return {
35
+ isDragging,
36
+ handleProps: { onMouseDown: useCallback((event) => {
37
+ if (!enabled) return;
38
+ if (event.button !== 0) return;
39
+ if (event.target.closest?.("[data-no-drag]")) return;
40
+ event.preventDefault();
41
+ startY.current = event.clientY;
42
+ startTime.current = Date.now();
43
+ draggingRef.current = true;
44
+ setIsDragging(true);
45
+ }, [enabled]) }
46
+ };
47
+ }
48
+ //#endregion
49
+ export { useSheetDrag };
50
+
51
+ //# sourceMappingURL=useSheetDrag.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSheetDrag.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/useSheetDrag.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface UseSheetDragOptions {\n enabled: boolean;\n onDragChange: (draggedDistance: number) => void;\n onDragEnd: (draggedDistance: number, velocity: number) => void;\n}\n\nexport function useSheetDrag({ onDragChange, onDragEnd, enabled }: UseSheetDragOptions) {\n const [isDragging, setIsDragging] = useState(false);\n const startY = useRef(0);\n const startTime = useRef(0);\n const draggingRef = useRef(false);\n\n // Store latest callbacks in refs to avoid stale closures in document listeners\n const onDragChangeRef = useRef(onDragChange);\n const onDragEndRef = useRef(onDragEnd);\n onDragChangeRef.current = onDragChange;\n onDragEndRef.current = onDragEnd;\n\n useEffect(() => {\n if (!draggingRef.current) return;\n\n const onMouseMove = (e: MouseEvent) => {\n e.preventDefault();\n const draggedDistance = startY.current - e.clientY;\n onDragChangeRef.current(draggedDistance);\n };\n\n const onMouseUp = (e: MouseEvent) => {\n draggingRef.current = false;\n setIsDragging(false);\n\n const draggedDistance = startY.current - e.clientY;\n const elapsed = Date.now() - startTime.current;\n const velocity = elapsed > 0 ? Math.abs(draggedDistance) / elapsed : 0;\n onDragEndRef.current(draggedDistance, velocity);\n };\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n return () => {\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n }, [isDragging]);\n\n const onMouseDown = useCallback(\n (event: React.MouseEvent<HTMLDivElement>) => {\n if (!enabled) return;\n if (event.button !== 0) return; // left click only\n\n const target = event.target as HTMLElement;\n if (target.closest?.('[data-no-drag]')) return;\n\n event.preventDefault(); // prevent text selection during drag\n\n startY.current = event.clientY;\n startTime.current = Date.now();\n draggingRef.current = true;\n setIsDragging(true);\n },\n [enabled],\n );\n\n return {\n isDragging,\n handleProps: {\n onMouseDown,\n },\n };\n}\n"],"mappings":";;AAQA,SAAgB,aAAa,EAAE,cAAc,WAAW,WAAgC;CACtF,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,SAAS,OAAO,EAAE;CACxB,MAAM,YAAY,OAAO,EAAE;CAC3B,MAAM,cAAc,OAAO,MAAM;CAGjC,MAAM,kBAAkB,OAAO,aAAa;CAC5C,MAAM,eAAe,OAAO,UAAU;AACtC,iBAAgB,UAAU;AAC1B,cAAa,UAAU;AAEvB,iBAAgB;AACd,MAAI,CAAC,YAAY,QAAS;EAE1B,MAAM,eAAe,MAAkB;AACrC,KAAE,gBAAgB;GAClB,MAAM,kBAAkB,OAAO,UAAU,EAAE;AAC3C,mBAAgB,QAAQ,gBAAgB;;EAG1C,MAAM,aAAa,MAAkB;AACnC,eAAY,UAAU;AACtB,iBAAc,MAAM;GAEpB,MAAM,kBAAkB,OAAO,UAAU,EAAE;GAC3C,MAAM,UAAU,KAAK,KAAK,GAAG,UAAU;GACvC,MAAM,WAAW,UAAU,IAAI,KAAK,IAAI,gBAAgB,GAAG,UAAU;AACrE,gBAAa,QAAQ,iBAAiB,SAAS;;AAGjD,WAAS,iBAAiB,aAAa,YAAY;AACnD,WAAS,iBAAiB,WAAW,UAAU;AAC/C,eAAa;AACX,YAAS,oBAAoB,aAAa,YAAY;AACtD,YAAS,oBAAoB,WAAW,UAAU;;IAEnD,CAAC,WAAW,CAAC;AAoBhB,QAAO;EACL;EACA,aAAa,EACX,aArBgB,aACjB,UAA4C;AAC3C,OAAI,CAAC,QAAS;AACd,OAAI,MAAM,WAAW,EAAG;AAGxB,OADe,MAAM,OACV,UAAU,iBAAiB,CAAE;AAExC,SAAM,gBAAgB;AAEtB,UAAO,UAAU,MAAM;AACvB,aAAU,UAAU,KAAK,KAAK;AAC9B,eAAY,UAAU;AACtB,iBAAc,KAAK;KAErB,CAAC,QAAQ,CACV,EAME;EACF"}
@@ -0,0 +1,69 @@
1
+ import { clamp, resolveSize } from "./helpers.mjs";
2
+ import { useMemo } from "react";
3
+ //#region src/base-ui/FloatingSheet/useSnapPoints.ts
4
+ const VELOCITY_THRESHOLD = .4;
5
+ const DRAG_DISTANCE_RATIO = .4;
6
+ function useSnapPoints({ closeThreshold, snapPoints, containerHeight, minHeightPx, maxHeightPx }) {
7
+ const snapPointHeights = useMemo(() => {
8
+ if (!containerHeight) return [];
9
+ const resolved = snapPoints.map((sp) => clamp(resolveSize(sp, containerHeight), minHeightPx, maxHeightPx)).sort((a, b) => a - b);
10
+ return [...new Set(resolved)];
11
+ }, [
12
+ snapPoints,
13
+ containerHeight,
14
+ minHeightPx,
15
+ maxHeightPx
16
+ ]);
17
+ function findClosestSnapPoint(height) {
18
+ if (snapPointHeights.length === 0) return clamp(height, minHeightPx, maxHeightPx);
19
+ return snapPointHeights.reduce((prev, curr) => Math.abs(curr - height) < Math.abs(prev - height) ? curr : prev);
20
+ }
21
+ function findActiveIndex(height) {
22
+ const closest = findClosestSnapPoint(height);
23
+ return snapPointHeights.indexOf(closest);
24
+ }
25
+ function getSnapRelease({ currentHeight, activeIndex, draggedDistance, velocity, dismissible }) {
26
+ const isFirst = activeIndex === 0;
27
+ const isLast = activeIndex === snapPointHeights.length - 1;
28
+ const isDraggingUp = draggedDistance > 0;
29
+ const highestSnapPoint = snapPointHeights.at(-1) ?? maxHeightPx;
30
+ const lowestSnapPoint = snapPointHeights[0] ?? minHeightPx;
31
+ const nextHigherSnapPoint = snapPointHeights[Math.min(activeIndex + 1, snapPointHeights.length - 1)] ?? highestSnapPoint;
32
+ const nextLowerSnapPoint = snapPointHeights[Math.max(activeIndex - 1, 0)] ?? lowestSnapPoint;
33
+ const sheetHeight = snapPointHeights[activeIndex] ?? currentHeight;
34
+ if (velocity > VELOCITY_THRESHOLD && Math.abs(draggedDistance) < sheetHeight * DRAG_DISTANCE_RATIO) if (isDraggingUp) {
35
+ if (isLast) return {
36
+ type: "snap",
37
+ height: highestSnapPoint
38
+ };
39
+ return {
40
+ type: "snap",
41
+ height: nextHigherSnapPoint
42
+ };
43
+ } else {
44
+ if (isFirst) return dismissible ? { type: "dismiss" } : {
45
+ type: "snap",
46
+ height: lowestSnapPoint
47
+ };
48
+ return {
49
+ type: "snap",
50
+ height: nextLowerSnapPoint
51
+ };
52
+ }
53
+ if (dismissible && isFirst && !isDraggingUp && currentHeight < lowestSnapPoint * closeThreshold) return { type: "dismiss" };
54
+ return {
55
+ type: "snap",
56
+ height: findClosestSnapPoint(currentHeight)
57
+ };
58
+ }
59
+ return {
60
+ snapPointHeights,
61
+ findClosestSnapPoint,
62
+ findActiveIndex,
63
+ getSnapRelease
64
+ };
65
+ }
66
+ //#endregion
67
+ export { useSnapPoints };
68
+
69
+ //# sourceMappingURL=useSnapPoints.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSnapPoints.mjs","names":[],"sources":["../../../src/base-ui/FloatingSheet/useSnapPoints.ts"],"sourcesContent":["import { type RefObject, useMemo } from 'react';\n\nimport { clamp, resolveSize } from './helpers';\n\nconst VELOCITY_THRESHOLD = 0.4;\nconst DRAG_DISTANCE_RATIO = 0.4;\n\ninterface UseSnapPointsOptions {\n closeThreshold: number;\n containerHeight: number;\n containerRef: RefObject<HTMLElement | null>;\n maxHeightPx: number;\n minHeightPx: number;\n snapPoints: number[];\n}\n\ninterface SnapReleaseParams {\n activeIndex: number;\n currentHeight: number;\n dismissible: boolean;\n draggedDistance: number; // positive = upward (growing), negative = downward (shrinking)\n velocity: number;\n}\n\ntype SnapReleaseResult = { type: 'snap'; height: number } | { type: 'dismiss' };\n\nexport function useSnapPoints({\n closeThreshold,\n snapPoints,\n containerHeight,\n minHeightPx,\n maxHeightPx,\n}: UseSnapPointsOptions) {\n const snapPointHeights = useMemo(() => {\n if (!containerHeight) return [];\n\n const resolved = snapPoints\n .map((sp) => clamp(resolveSize(sp, containerHeight), minHeightPx, maxHeightPx))\n .sort((a, b) => a - b);\n\n // Remove duplicates\n return [...new Set(resolved)];\n }, [snapPoints, containerHeight, minHeightPx, maxHeightPx]);\n\n function findClosestSnapPoint(height: number): number {\n if (snapPointHeights.length === 0) return clamp(height, minHeightPx, maxHeightPx);\n\n return snapPointHeights.reduce((prev, curr) =>\n Math.abs(curr - height) < Math.abs(prev - height) ? curr : prev,\n );\n }\n\n function findActiveIndex(height: number): number {\n const closest = findClosestSnapPoint(height);\n return snapPointHeights.indexOf(closest);\n }\n\n function getSnapRelease({\n currentHeight,\n activeIndex,\n draggedDistance,\n velocity,\n dismissible,\n }: SnapReleaseParams): SnapReleaseResult {\n const isFirst = activeIndex === 0;\n const isLast = activeIndex === snapPointHeights.length - 1;\n const isDraggingUp = draggedDistance > 0;\n const highestSnapPoint = snapPointHeights.at(-1) ?? maxHeightPx;\n const lowestSnapPoint = snapPointHeights[0] ?? minHeightPx;\n const nextHigherSnapPoint =\n snapPointHeights[Math.min(activeIndex + 1, snapPointHeights.length - 1)] ?? highestSnapPoint;\n const nextLowerSnapPoint = snapPointHeights[Math.max(activeIndex - 1, 0)] ?? lowestSnapPoint;\n const sheetHeight = snapPointHeights[activeIndex] ?? currentHeight;\n\n // High velocity handling\n if (\n velocity > VELOCITY_THRESHOLD &&\n Math.abs(draggedDistance) < sheetHeight * DRAG_DISTANCE_RATIO\n ) {\n if (isDraggingUp) {\n // Fling upward: go to next higher snap, cap at highest\n if (isLast) return { type: 'snap', height: highestSnapPoint };\n\n return { type: 'snap', height: nextHigherSnapPoint };\n } else {\n // Fling downward: go to next lower snap, or dismiss if at lowest\n if (isFirst) {\n return dismissible ? { type: 'dismiss' } : { type: 'snap', height: lowestSnapPoint };\n }\n\n return { type: 'snap', height: nextLowerSnapPoint };\n }\n }\n\n if (\n dismissible &&\n isFirst &&\n !isDraggingUp &&\n currentHeight < lowestSnapPoint * closeThreshold\n ) {\n return { type: 'dismiss' };\n }\n\n // Low velocity: snap to closest\n const closest = findClosestSnapPoint(currentHeight);\n return { type: 'snap', height: closest };\n }\n\n return {\n snapPointHeights,\n findClosestSnapPoint,\n findActiveIndex,\n getSnapRelease,\n };\n}\n"],"mappings":";;;AAIA,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAqB5B,SAAgB,cAAc,EAC5B,gBACA,YACA,iBACA,aACA,eACuB;CACvB,MAAM,mBAAmB,cAAc;AACrC,MAAI,CAAC,gBAAiB,QAAO,EAAE;EAE/B,MAAM,WAAW,WACd,KAAK,OAAO,MAAM,YAAY,IAAI,gBAAgB,EAAE,aAAa,YAAY,CAAC,CAC9E,MAAM,GAAG,MAAM,IAAI,EAAE;AAGxB,SAAO,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC;IAC5B;EAAC;EAAY;EAAiB;EAAa;EAAY,CAAC;CAE3D,SAAS,qBAAqB,QAAwB;AACpD,MAAI,iBAAiB,WAAW,EAAG,QAAO,MAAM,QAAQ,aAAa,YAAY;AAEjF,SAAO,iBAAiB,QAAQ,MAAM,SACpC,KAAK,IAAI,OAAO,OAAO,GAAG,KAAK,IAAI,OAAO,OAAO,GAAG,OAAO,KAC5D;;CAGH,SAAS,gBAAgB,QAAwB;EAC/C,MAAM,UAAU,qBAAqB,OAAO;AAC5C,SAAO,iBAAiB,QAAQ,QAAQ;;CAG1C,SAAS,eAAe,EACtB,eACA,aACA,iBACA,UACA,eACuC;EACvC,MAAM,UAAU,gBAAgB;EAChC,MAAM,SAAS,gBAAgB,iBAAiB,SAAS;EACzD,MAAM,eAAe,kBAAkB;EACvC,MAAM,mBAAmB,iBAAiB,GAAG,GAAG,IAAI;EACpD,MAAM,kBAAkB,iBAAiB,MAAM;EAC/C,MAAM,sBACJ,iBAAiB,KAAK,IAAI,cAAc,GAAG,iBAAiB,SAAS,EAAE,KAAK;EAC9E,MAAM,qBAAqB,iBAAiB,KAAK,IAAI,cAAc,GAAG,EAAE,KAAK;EAC7E,MAAM,cAAc,iBAAiB,gBAAgB;AAGrD,MACE,WAAW,sBACX,KAAK,IAAI,gBAAgB,GAAG,cAAc,oBAE1C,KAAI,cAAc;AAEhB,OAAI,OAAQ,QAAO;IAAE,MAAM;IAAQ,QAAQ;IAAkB;AAE7D,UAAO;IAAE,MAAM;IAAQ,QAAQ;IAAqB;SAC/C;AAEL,OAAI,QACF,QAAO,cAAc,EAAE,MAAM,WAAW,GAAG;IAAE,MAAM;IAAQ,QAAQ;IAAiB;AAGtF,UAAO;IAAE,MAAM;IAAQ,QAAQ;IAAoB;;AAIvD,MACE,eACA,WACA,CAAC,gBACD,gBAAgB,kBAAkB,eAElC,QAAO,EAAE,MAAM,WAAW;AAK5B,SAAO;GAAE,MAAM;GAAQ,QADP,qBAAqB,cAAc;GACX;;AAG1C,QAAO;EACL;EACA;EACA;EACA;EACD"}
@@ -19,6 +19,8 @@ import { ScrollAreaProps } from "./ScrollArea/type.mjs";
19
19
  import { ScrollArea } from "./ScrollArea/ScrollArea.mjs";
20
20
  import { ToastAPI, ToastInstance, ToastOptions, ToastPosition, ToastPromiseOptions, ToastProps, ToastType } from "./Toast/type.mjs";
21
21
  import { ToastHost, ToastHostProps, toast, useToast } from "./Toast/imperative.mjs";
22
+ import { FloatingSheetProps } from "./FloatingSheet/type.mjs";
23
+ import { FloatingSheet } from "./FloatingSheet/FloatingSheet.mjs";
22
24
  import { ModalBackdrop, ModalBackdropProps, ModalClose, ModalCloseProps, ModalContent, ModalContentProps, ModalDescription, ModalDescriptionProps, ModalFooter, ModalFooterProps, ModalHeader, ModalHeaderProps, ModalPopup, ModalPopupProps, ModalPortal, ModalPortalProps, ModalRoot, ModalRootProps, ModalTitle, ModalTitleProps, ModalTrigger, ModalTriggerProps, ModalViewport, ModalViewportProps, useModalActions, useModalOpen } from "./Modal/atoms.mjs";
23
25
  import { backdropTransition, modalMotionConfig } from "./Modal/constants.mjs";
24
26
  import { BaseModalProps, ImperativeModalProps, ModalComponentProps, ModalConfirmConfig, ModalContextValue, ModalInstance } from "./Modal/type.mjs";
@@ -32,4 +34,4 @@ import { SwitchChangeEventHandler, SwitchClassNames, SwitchClickEventHandler, Sw
32
34
  import { styles } from "./Switch/style.mjs";
33
35
  import { SwitchIcon, SwitchRoot, SwitchThumb, useSwitchContext } from "./Switch/atoms.mjs";
34
36
  import { Switch } from "./Switch/Switch.mjs";
35
- export { BaseModalProps, type ContextMenuCheckboxItem, ContextMenuHost, type ContextMenuItem, ContextMenuTrigger, DropdownItem, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuCheckboxItemIndicator, DropdownMenuCheckboxItemPrimitive, DropdownMenuCheckboxItemProps, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuGroupLabelProps, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemContentProps, DropdownMenuItemDesc, DropdownMenuItemDescProps, DropdownMenuItemExtra, DropdownMenuItemExtraProps, DropdownMenuItemIcon, DropdownMenuItemIconProps, DropdownMenuItemLabel, DropdownMenuItemLabelGroup, DropdownMenuItemLabelGroupProps, DropdownMenuItemLabelProps, DropdownMenuItemProps, DropdownMenuPlacement, DropdownMenuPopup, DropdownMenuPopupProps, DropdownMenuPortal, DropdownMenuPortalProps, DropdownMenuPositioner, DropdownMenuPositionerProps, DropdownMenuProps, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuSeparatorProps, DropdownMenuSubmenuArrow, DropdownMenuSubmenuArrowProps, DropdownMenuSubmenuRoot, DropdownMenuSubmenuTrigger, DropdownMenuSubmenuTriggerProps, DropdownMenuSwitchItem, DropdownMenuSwitchItemProps, DropdownMenuSwitchItem$1 as DropdownMenuSwitchItemType, DropdownMenuTrigger, DropdownMenuTriggerProps, IconSpaceMode, ImperativeModalProps, Modal, ModalBackdrop, ModalBackdropProps, ModalClose, ModalCloseProps, ModalComponentProps, ModalConfirmConfig, ModalContent, ModalContentProps, ModalContext, ModalContextValue, ModalDescription, ModalDescriptionProps, ModalFooter, ModalFooterProps, ModalHeader, ModalHeaderProps, ModalHost, ModalHostProps, ModalInstance, ModalPopup, ModalPopupProps, ModalPortal, ModalPortalProps, ModalRoot, ModalRootProps, ModalSystem, ModalTitle, ModalTitleProps, ModalTrigger, ModalTriggerProps, ModalViewport, ModalViewportProps, PopoverArrow, PopoverArrowAtomProps, PopoverArrowIcon, PopoverBackdrop, PopoverBackdropProps, PopoverContextValue, PopoverGroup, PopoverPlacement, PopoverPopup, PopoverPopupAtomProps, PopoverPopupProps, PopoverPortal, PopoverPortalAtomProps, PopoverPortalProps, PopoverPositioner, PopoverPositionerAtomProps, PopoverPositionerProps, PopoverProps, PopoverProvider, PopoverRoot, PopoverTrigger, PopoverTriggerComponentProps, PopoverTriggerElement, PopoverTriggerElementProps, PopoverViewport, PopoverViewportAtomProps, ScrollArea, ScrollAreaContent, ScrollAreaContentProps, ScrollAreaCorner, ScrollAreaCornerProps, ScrollAreaProps, ScrollAreaRoot, ScrollAreaRootProps, ScrollAreaScrollbar, ScrollAreaScrollbarProps, ScrollAreaThumb, ScrollAreaThumbProps, ScrollAreaViewport, ScrollAreaViewportProps, Select, SelectArrow, SelectArrowProps, SelectBackdrop, SelectBehaviorVariant, SelectClassNames, SelectGroup, SelectGroupLabel, SelectGroupLabelProps, SelectGroupProps, SelectIcon, SelectIconProps, SelectIndicatorVariant, SelectItem, SelectItemIndicator, SelectItemIndicatorProps, SelectItemProps, SelectItemText, SelectItemTextProps, SelectList, SelectListProps, SelectOption, SelectOptionGroup, SelectOptions, SelectPopup, SelectPopupProps, SelectPortal, SelectPortalProps, SelectPositioner, SelectPositionerProps, SelectProps, SelectRoot, SelectScrollDownArrow, SelectScrollDownArrowProps, SelectScrollUpArrow, SelectScrollUpArrowProps, SelectSeparator, SelectSize, SelectTrigger, SelectTriggerProps, SelectValue, SelectValueProps, SelectVariant, Switch, SwitchChangeEventHandler, SwitchClassNames, SwitchClickEventHandler, SwitchContextType, SwitchIcon, SwitchIconPosition, SwitchIconProps, SwitchProps, SwitchRoot, SwitchRootProps, SwitchSize, SwitchStyles, SwitchThumb, SwitchThumbProps, type ToastAPI, ToastHost, type ToastHostProps, type ToastInstance, type ToastOptions, type ToastPosition, type ToastPromiseOptions, type ToastProps, type ToastType, backdropTransition, closeContextMenu, confirmModal, createModal, createModalSystem, modalMotionConfig, parseTrigger, renderDropdownMenuItems, showContextMenu, styles as switchStyles, toast, updateContextMenuItems, useModalActions, useModalContext, useModalOpen, usePopoverContext, usePopoverPortalContainer, useSwitchContext, useToast };
37
+ export { BaseModalProps, type ContextMenuCheckboxItem, ContextMenuHost, type ContextMenuItem, ContextMenuTrigger, DropdownItem, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuCheckboxItemIndicator, DropdownMenuCheckboxItemPrimitive, DropdownMenuCheckboxItemProps, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuGroupLabelProps, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemContentProps, DropdownMenuItemDesc, DropdownMenuItemDescProps, DropdownMenuItemExtra, DropdownMenuItemExtraProps, DropdownMenuItemIcon, DropdownMenuItemIconProps, DropdownMenuItemLabel, DropdownMenuItemLabelGroup, DropdownMenuItemLabelGroupProps, DropdownMenuItemLabelProps, DropdownMenuItemProps, DropdownMenuPlacement, DropdownMenuPopup, DropdownMenuPopupProps, DropdownMenuPortal, DropdownMenuPortalProps, DropdownMenuPositioner, DropdownMenuPositionerProps, DropdownMenuProps, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuSeparatorProps, DropdownMenuSubmenuArrow, DropdownMenuSubmenuArrowProps, DropdownMenuSubmenuRoot, DropdownMenuSubmenuTrigger, DropdownMenuSubmenuTriggerProps, DropdownMenuSwitchItem, DropdownMenuSwitchItemProps, DropdownMenuSwitchItem$1 as DropdownMenuSwitchItemType, DropdownMenuTrigger, DropdownMenuTriggerProps, FloatingSheet, FloatingSheetProps, IconSpaceMode, ImperativeModalProps, Modal, ModalBackdrop, ModalBackdropProps, ModalClose, ModalCloseProps, ModalComponentProps, ModalConfirmConfig, ModalContent, ModalContentProps, ModalContext, ModalContextValue, ModalDescription, ModalDescriptionProps, ModalFooter, ModalFooterProps, ModalHeader, ModalHeaderProps, ModalHost, ModalHostProps, ModalInstance, ModalPopup, ModalPopupProps, ModalPortal, ModalPortalProps, ModalRoot, ModalRootProps, ModalSystem, ModalTitle, ModalTitleProps, ModalTrigger, ModalTriggerProps, ModalViewport, ModalViewportProps, PopoverArrow, PopoverArrowAtomProps, PopoverArrowIcon, PopoverBackdrop, PopoverBackdropProps, PopoverContextValue, PopoverGroup, PopoverPlacement, PopoverPopup, PopoverPopupAtomProps, PopoverPopupProps, PopoverPortal, PopoverPortalAtomProps, PopoverPortalProps, PopoverPositioner, PopoverPositionerAtomProps, PopoverPositionerProps, PopoverProps, PopoverProvider, PopoverRoot, PopoverTrigger, PopoverTriggerComponentProps, PopoverTriggerElement, PopoverTriggerElementProps, PopoverViewport, PopoverViewportAtomProps, ScrollArea, ScrollAreaContent, ScrollAreaContentProps, ScrollAreaCorner, ScrollAreaCornerProps, ScrollAreaProps, ScrollAreaRoot, ScrollAreaRootProps, ScrollAreaScrollbar, ScrollAreaScrollbarProps, ScrollAreaThumb, ScrollAreaThumbProps, ScrollAreaViewport, ScrollAreaViewportProps, Select, SelectArrow, SelectArrowProps, SelectBackdrop, SelectBehaviorVariant, SelectClassNames, SelectGroup, SelectGroupLabel, SelectGroupLabelProps, SelectGroupProps, SelectIcon, SelectIconProps, SelectIndicatorVariant, SelectItem, SelectItemIndicator, SelectItemIndicatorProps, SelectItemProps, SelectItemText, SelectItemTextProps, SelectList, SelectListProps, SelectOption, SelectOptionGroup, SelectOptions, SelectPopup, SelectPopupProps, SelectPortal, SelectPortalProps, SelectPositioner, SelectPositionerProps, SelectProps, SelectRoot, SelectScrollDownArrow, SelectScrollDownArrowProps, SelectScrollUpArrow, SelectScrollUpArrowProps, SelectSeparator, SelectSize, SelectTrigger, SelectTriggerProps, SelectValue, SelectValueProps, SelectVariant, Switch, SwitchChangeEventHandler, SwitchClassNames, SwitchClickEventHandler, SwitchContextType, SwitchIcon, SwitchIconPosition, SwitchIconProps, SwitchProps, SwitchRoot, SwitchRootProps, SwitchSize, SwitchStyles, SwitchThumb, SwitchThumbProps, type ToastAPI, ToastHost, type ToastHostProps, type ToastInstance, type ToastOptions, type ToastPosition, type ToastPromiseOptions, type ToastProps, type ToastType, backdropTransition, closeContextMenu, confirmModal, createModal, createModalSystem, modalMotionConfig, parseTrigger, renderDropdownMenuItems, showContextMenu, styles as switchStyles, toast, updateContextMenuItems, useModalActions, useModalContext, useModalOpen, usePopoverContext, usePopoverPortalContainer, useSwitchContext, useToast };
@@ -13,6 +13,7 @@ import PopoverGroup from "./Popover/PopoverGroup.mjs";
13
13
  import { ScrollAreaContent, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from "./ScrollArea/atoms.mjs";
14
14
  import { ScrollArea } from "./ScrollArea/ScrollArea.mjs";
15
15
  import { ToastHost, toast, useToast } from "./Toast/imperative.mjs";
16
+ import { FloatingSheet } from "./FloatingSheet/FloatingSheet.mjs";
16
17
  import { backdropTransition, modalMotionConfig } from "./Modal/constants.mjs";
17
18
  import { ModalBackdrop, ModalClose, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalPopup, ModalPortal, ModalRoot, ModalTitle, ModalTrigger, ModalViewport, useModalActions, useModalOpen } from "./Modal/atoms.mjs";
18
19
  import { ModalContext, useModalContext } from "./Modal/context.mjs";
@@ -23,4 +24,4 @@ import Select from "./Select/Select.mjs";
23
24
  import { styles } from "./Switch/style.mjs";
24
25
  import { SwitchIcon, SwitchRoot, SwitchThumb, useSwitchContext } from "./Switch/atoms.mjs";
25
26
  import Switch from "./Switch/Switch.mjs";
26
- export { ContextMenuHost, ContextMenuTrigger, DropdownMenu, DropdownMenuCheckboxItemIndicator, DropdownMenuCheckboxItemPrimitive, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemDesc, DropdownMenuItemExtra, DropdownMenuItemIcon, DropdownMenuItemLabel, DropdownMenuItemLabelGroup, DropdownMenuPopup, DropdownMenuPortal, DropdownMenuPositioner, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuSubmenuArrow, DropdownMenuSubmenuRoot, DropdownMenuSubmenuTrigger, DropdownMenuSwitchItem, DropdownMenuTrigger, Modal, ModalBackdrop, ModalClose, ModalContent, ModalContext, ModalDescription, ModalFooter, ModalHeader, ModalHost, ModalPopup, ModalPortal, ModalRoot, ModalTitle, ModalTrigger, ModalViewport, PopoverArrow, PopoverArrowIcon, PopoverBackdrop, PopoverGroup, PopoverPopup, PopoverPortal, PopoverPositioner, PopoverProvider, PopoverRoot, PopoverTriggerElement, PopoverViewport, ScrollArea, ScrollAreaContent, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport, Select, SelectArrow, SelectBackdrop, SelectGroup, SelectGroupLabel, SelectIcon, SelectItem, SelectItemIndicator, SelectItemText, SelectList, SelectPopup, SelectPortal, SelectPositioner, SelectRoot, SelectScrollDownArrow, SelectScrollUpArrow, SelectSeparator, SelectTrigger, SelectValue, Switch, SwitchIcon, SwitchRoot, SwitchThumb, ToastHost, backdropTransition, closeContextMenu, confirmModal, createModal, createModalSystem, modalMotionConfig, parseTrigger, renderDropdownMenuItems, showContextMenu, styles as switchStyles, toast, updateContextMenuItems, useModalActions, useModalContext, useModalOpen, usePopoverContext, usePopoverPortalContainer, useSwitchContext, useToast };
27
+ export { ContextMenuHost, ContextMenuTrigger, DropdownMenu, DropdownMenuCheckboxItemIndicator, DropdownMenuCheckboxItemPrimitive, DropdownMenuGroup, DropdownMenuGroupLabel, DropdownMenuItem, DropdownMenuItemContent, DropdownMenuItemDesc, DropdownMenuItemExtra, DropdownMenuItemIcon, DropdownMenuItemLabel, DropdownMenuItemLabelGroup, DropdownMenuPopup, DropdownMenuPortal, DropdownMenuPositioner, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuSubmenuArrow, DropdownMenuSubmenuRoot, DropdownMenuSubmenuTrigger, DropdownMenuSwitchItem, DropdownMenuTrigger, FloatingSheet, Modal, ModalBackdrop, ModalClose, ModalContent, ModalContext, ModalDescription, ModalFooter, ModalHeader, ModalHost, ModalPopup, ModalPortal, ModalRoot, ModalTitle, ModalTrigger, ModalViewport, PopoverArrow, PopoverArrowIcon, PopoverBackdrop, PopoverGroup, PopoverPopup, PopoverPortal, PopoverPositioner, PopoverProvider, PopoverRoot, PopoverTriggerElement, PopoverViewport, ScrollArea, ScrollAreaContent, ScrollAreaCorner, ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport, Select, SelectArrow, SelectBackdrop, SelectGroup, SelectGroupLabel, SelectIcon, SelectItem, SelectItemIndicator, SelectItemText, SelectList, SelectPopup, SelectPortal, SelectPositioner, SelectRoot, SelectScrollDownArrow, SelectScrollUpArrow, SelectSeparator, SelectTrigger, SelectValue, Switch, SwitchIcon, SwitchRoot, SwitchThumb, ToastHost, backdropTransition, closeContextMenu, confirmModal, createModal, createModalSystem, modalMotionConfig, parseTrigger, renderDropdownMenuItems, showContextMenu, styles as switchStyles, toast, updateContextMenuItems, useModalActions, useModalContext, useModalOpen, usePopoverContext, usePopoverPortalContainer, useSwitchContext, useToast };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/ui",
3
- "version": "5.8.0",
3
+ "version": "5.9.1",
4
4
  "description": "Lobe UI is an open-source UI component library for building AIGC web apps",
5
5
  "keywords": [
6
6
  "lobehub",