diff --git a/packages/react-ui/package.json b/packages/react-ui/package.json index 55d747712..df27c52f3 100644 --- a/packages/react-ui/package.json +++ b/packages/react-ui/package.json @@ -2,7 +2,7 @@ "type": "module", "name": "@openuidev/react-ui", "license": "MIT", - "version": "0.9.19", + "version": "0.9.20", "description": "Component library for Generative UI SDK", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -66,8 +66,8 @@ "ci": "pnpm run lint:check && pnpm run format:check" }, "peerDependencies": { - "@openuidev/react-lang": "workspace:^", "@openuidev/react-headless": "workspace:^", + "@openuidev/react-lang": "workspace:^", "react": ">=19.0.0", "react-dom": ">=19.0.0", "zustand": "^4.5.5" @@ -77,6 +77,7 @@ "@radix-ui/react-accordion": "^1.2.2", "@radix-ui/react-aspect-ratio": "^1.1.1", "@radix-ui/react-checkbox": "^1.1.3", + "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.7", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-radio-group": "^1.2.2", diff --git a/packages/react-ui/src/components/BottomTray/Thread.tsx b/packages/react-ui/src/components/BottomTray/Thread.tsx index 68f67925d..712e9f190 100644 --- a/packages/react-ui/src/components/BottomTray/Thread.tsx +++ b/packages/react-ui/src/components/BottomTray/Thread.tsx @@ -191,12 +191,14 @@ export const RenderMessage = memo( allMessages, assistantMessage: CustomAssistantMessage, userMessage: CustomUserMessage, + isStreaming, }: { message: Message; className?: string; allMessages: Message[]; assistantMessage?: AssistantMessageComponent; userMessage?: UserMessageComponent; + isStreaming: boolean; }) => { if (message.role === "tool") { return null; @@ -204,7 +206,7 @@ export const RenderMessage = memo( if (message.role === "assistant") { if (CustomAssistantMessage) { - return ; + return ; } return ( @@ -252,7 +254,7 @@ export const Messages = ({ return (
- {messages.map((message) => { + {messages.map((message, i) => { return ( ); diff --git a/packages/react-ui/src/components/Charts/index.ts b/packages/react-ui/src/components/Charts/index.ts index f9f64af1a..914a4e0a2 100644 --- a/packages/react-ui/src/components/Charts/index.ts +++ b/packages/react-ui/src/components/Charts/index.ts @@ -1,13 +1,10 @@ -export { AreaChart as ScrollableAreaChart } from "./AreaChart"; +export * from "./AreaChart"; export * from "./AreaChartCondensed"; -export { AreaChartCondensed as AreaChart } from "./AreaChartCondensed"; -export { BarChart as ScrollableBarChart } from "./BarChart"; +export * from "./BarChart"; export * from "./BarChartCondensed"; -export { BarChartCondensed as BarChart } from "./BarChartCondensed"; export * from "./HorizontalBarChart"; -export { LineChart as ScrollableLineChart } from "./LineChart"; +export * from "./LineChart"; export * from "./LineChartCondensed"; -export { LineChartCondensed as LineChart } from "./LineChartCondensed"; export * from "./MiniAreaChart"; export * from "./MiniBarChart"; export * from "./MiniLineChart"; diff --git a/packages/react-ui/src/components/CopilotShell/Thread.tsx b/packages/react-ui/src/components/CopilotShell/Thread.tsx index 99e70bdc3..28f0bdcc6 100644 --- a/packages/react-ui/src/components/CopilotShell/Thread.tsx +++ b/packages/react-ui/src/components/CopilotShell/Thread.tsx @@ -189,12 +189,14 @@ export const RenderMessage = memo( allMessages, assistantMessage: CustomAssistantMessage, userMessage: CustomUserMessage, + isStreaming, }: { message: Message; className?: string; allMessages: Message[]; assistantMessage?: AssistantMessageComponent; userMessage?: UserMessageComponent; + isStreaming: boolean; }) => { if (message.role === "tool") { return null; @@ -202,7 +204,7 @@ export const RenderMessage = memo( if (message.role === "assistant") { if (CustomAssistantMessage) { - return ; + return ; } return ( @@ -250,7 +252,7 @@ export const Messages = ({ return (
- {messages.map((message) => { + {messages.map((message, i) => { return ( ); diff --git a/packages/react-ui/src/components/MarkDownRenderer/MarkDownRenderer.tsx b/packages/react-ui/src/components/MarkDownRenderer/MarkDownRenderer.tsx index fe5b14003..fd2f4ec31 100644 --- a/packages/react-ui/src/components/MarkDownRenderer/MarkDownRenderer.tsx +++ b/packages/react-ui/src/components/MarkDownRenderer/MarkDownRenderer.tsx @@ -41,6 +41,7 @@ export interface MarkDownRendererProps { textMarkdown: string; options?: Options; className?: string; + isStreaming?: boolean; } export const MarkDownRenderer = memo((props: MarkDownRendererProps) => { @@ -93,6 +94,7 @@ export const MarkDownRenderer = memo((props: MarkDownRendererProps) => { className={clsx( props["variant"] && variantStyles[props["variant"] as keyof typeof variantStyles], "openui-markdown-renderer", + props.isStreaming && "openui-markdown-renderer--streaming", props.className, )} > diff --git a/packages/react-ui/src/components/OpenUIChat/ComposedBottomTray.tsx b/packages/react-ui/src/components/OpenUIChat/ComposedBottomTray.tsx index 0200ee24d..148fb6fc0 100644 --- a/packages/react-ui/src/components/OpenUIChat/ComposedBottomTray.tsx +++ b/packages/react-ui/src/components/OpenUIChat/ComposedBottomTray.tsx @@ -13,6 +13,7 @@ import { WelcomeScreen, } from "../BottomTray"; import { CustomComposerAdapter } from "./CustomComposerAdapter"; +import { ShareThread } from "./ShareThread"; import type { SharedChatUIProps } from "./types"; import { isChatEmpty, isWelcomeComponent } from "./utils"; import { withChatProvider } from "./withChatProvider"; @@ -82,6 +83,7 @@ const BottomTrayInner = ({ userMessage, composer: ComposerComponent, headerActions, + generateShareLink, }: BottomTraySpecificProps) => { const [uncontrolledIsOpen, setUncontrolledIsOpen] = useState(defaultOpen); @@ -94,6 +96,10 @@ const BottomTrayInner = ({ onOpenChange?.(newIsOpen); }; + const shareButton = generateShareLink ? ( + + ) : null; + return ( <> handleOpenChange(!isOpen)} isOpen={isOpen}> @@ -104,7 +110,15 @@ const BottomTrayInner = ({ -
handleOpenChange(false)} rightChildren={headerActions} /> +
handleOpenChange(false)} + rightChildren={ + <> + {shareButton} + {headerActions} + + } + /> { + const shareButton = generateShareLink ? ( + + ) : null; + return ( -
+
+ {shareButton} + {headerActions} + + } + /> { + const shareButton = generateShareLink ? ( + + ) : null; + return ( @@ -100,8 +106,20 @@ const FullScreenInner = ({ - - {threadHeader && {threadHeader}} + + {shareButton} + {mobileHeaderActions} + + } + /> + {(threadHeader || shareButton) && ( + + {threadHeader} + {shareButton} + + )} Promise; + /** Title for the share modal. Defaults to `"Share chat"`. */ + modalTitle?: string; + /** Custom trigger element. When omitted, a default share button is rendered. */ + customTrigger?: ReactNode; +} + +/** + * Share button that opens a modal for generating and copying a shareable link. + * Renders nothing when there are no messages to share. + * + * @category Components + */ +export const ShareThread = ({ generateShareLink, modalTitle, customTrigger }: ShareThreadProps) => { + const { layout } = useLayoutContext() || {}; + const isMobile = layout === "mobile"; + const { portalThemeClassName } = useTheme(); + + const { hasMessages, getShareThreadLink, shouldDisableShareButton } = useShareThread({ + generateShareLink, + }); + + if (!hasMessages) return null; + + return ( + + ) + } + generateLink={getShareThreadLink} + themeClassName={portalThemeClassName} + /> + ); +}; + +ShareThread.displayName = "ShareThread"; + +type DefaultShareButtonProps = { + isMobile: boolean; + shouldDisableShareButton?: boolean; +} & React.ButtonHTMLAttributes; + +const DefaultShareButton = React.forwardRef( + ({ isMobile, shouldDisableShareButton, ...props }, ref) => { + return isMobile ? ( + } + size="medium" + icon={} + variant="secondary" + disabled={shouldDisableShareButton} + {...props} + /> + ) : ( + + ); + }, +); + +DefaultShareButton.displayName = "DefaultShareButton"; diff --git a/packages/react-ui/src/components/OpenUIChat/ShareThreadModal.tsx b/packages/react-ui/src/components/OpenUIChat/ShareThreadModal.tsx new file mode 100644 index 000000000..7444aadae --- /dev/null +++ b/packages/react-ui/src/components/OpenUIChat/ShareThreadModal.tsx @@ -0,0 +1,190 @@ +import * as Dialog from "@radix-ui/react-dialog"; +import clsx from "clsx"; +import { Check, Copy, Link, Loader2, X } from "lucide-react"; +import { forwardRef, type ReactNode, useCallback, useState } from "react"; +import { useLayoutContext } from "../../context/LayoutContext"; +import { Button } from "../Button"; +import { IconButton } from "../IconButton"; +import { Input } from "../Input"; +import { useTheme } from "../ThemeProvider/ThemeProvider"; +import "./shareThread.scss"; + +/** + * Props for {@link ShareThreadModal}. + * + * @category Components + */ +export interface ShareThreadModalProps { + /** Modal title. Defaults to `"Share chat"`. */ + title?: string; + /** The trigger element that opens the modal. */ + trigger: ReactNode; + /** Async function that returns a shareable URL. */ + generateLink: () => Promise; + /** Theme class name for portal targeting. */ + themeClassName?: string; +} + +const getErrorMessage = (error: Error) => { + if (error instanceof DOMException && error.name === "NotAllowedError") { + return "Clipboard access denied. Please allow clipboard access in your browser settings, or copy the link manually from the text area above."; + } else if (error instanceof DOMException && error.name === "NotSupportedError") { + return "Clipboard not supported. Please copy the link manually from the text area above."; + } else { + return "Failed to copy to clipboard. Please copy the link manually from the text area above."; + } +}; + +/** + * Modal dialog for generating and copying a shareable link. + * + * @category Components + */ +export const ShareThreadModal = forwardRef( + ({ title, trigger, generateLink, themeClassName }, _ref) => { + const { portalThemeClassName } = useTheme(); + const { layout } = useLayoutContext() || {}; + const isMobile = layout === "mobile"; + + const [isOpen, setIsOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [generatedLink, setGeneratedLink] = useState(null); + const [hasCopied, setHasCopied] = useState(false); + const [clipboardError, setClipboardError] = useState(null); + + const handleGenerateLink = useCallback(async () => { + setIsLoading(true); + try { + const link = await generateLink(); + setGeneratedLink(link); + } catch (_error) { + // Consumer handles errors in their generateLink callback + } finally { + setIsLoading(false); + } + }, [generateLink]); + + const handleCopy = useCallback(async () => { + if (!generatedLink) return; + setClipboardError(null); + + if (!navigator.clipboard) { + setClipboardError( + "Clipboard access not available. Please copy the link manually from the text area above.", + ); + return; + } + + try { + await navigator.clipboard.writeText(generatedLink); + setHasCopied(true); + setTimeout(() => setHasCopied(false), 2000); + } catch (error) { + console.warn("Copy to clipboard failed:", error); + setClipboardError(getErrorMessage(error as Error)); + } + }, [generatedLink]); + + const handleOnOpenChange = useCallback((open: boolean) => { + setIsOpen(open); + if (!open) { + setTimeout(() => { + setIsLoading(false); + setGeneratedLink(null); + setHasCopied(false); + setClipboardError(null); + }, 300); + } + }, []); + + const renderActionButton = () => { + if (isLoading) { + return ( + + ); + } + + if (generatedLink) { + return ( + + ); + } + + return ( + + ); + }; + + return ( + + {trigger} + + + +
+ + {title ?? "Share chat"} + + } + variant="tertiary" + size="small" + onClick={() => handleOnOpenChange(false)} + className="openui-share-thread-modal__close-button" + /> +
+ +
+

+ This conversation may include personal information. Take a moment to check the + content before sharing the link. +

+ +
+
+ +
+ {renderActionButton()} +
+
+ + {clipboardError && ( +

{clipboardError}

+ )} +
+
+
+
+
+ ); + }, +); + +ShareThreadModal.displayName = "ShareThreadModal"; diff --git a/packages/react-ui/src/components/OpenUIChat/index.ts b/packages/react-ui/src/components/OpenUIChat/index.ts index 2afe1c22f..ddbe429ee 100644 --- a/packages/react-ui/src/components/OpenUIChat/index.ts +++ b/packages/react-ui/src/components/OpenUIChat/index.ts @@ -2,6 +2,10 @@ export { BottomTray } from "./ComposedBottomTray"; export { Copilot } from "./ComposedCopilot"; export { FullScreen } from "./ComposedStandalone"; export { GenUIUserMessage } from "./GenUIUserMessage"; +export { ShareThread } from "./ShareThread"; +export type { ShareThreadProps } from "./ShareThread"; +export { ShareThreadModal } from "./ShareThreadModal"; +export type { ShareThreadModalProps } from "./ShareThreadModal"; export type { AssistantMessageComponent, ComposerComponent, diff --git a/packages/react-ui/src/components/OpenUIChat/openUIChat.scss b/packages/react-ui/src/components/OpenUIChat/openUIChat.scss index adb45f588..d3b49825c 100644 --- a/packages/react-ui/src/components/OpenUIChat/openUIChat.scss +++ b/packages/react-ui/src/components/OpenUIChat/openUIChat.scss @@ -1 +1,2 @@ @forward "./composedBottomTray.scss"; +@forward "./shareThread.scss"; diff --git a/packages/react-ui/src/components/OpenUIChat/shareThread.scss b/packages/react-ui/src/components/OpenUIChat/shareThread.scss new file mode 100644 index 000000000..8328a1047 --- /dev/null +++ b/packages/react-ui/src/components/OpenUIChat/shareThread.scss @@ -0,0 +1,111 @@ +@use "../../cssUtils" as cssUtils; + +.openui-share-thread-modal { + &__overlay { + position: absolute; + inset: 0; + z-index: 9999; + background-color: cssUtils.$overlay; + } + + &__content { + position: absolute; + left: 50%; + top: 50%; + width: 100%; + max-width: 32rem; + transform: translate(-50%, -50%); + border-radius: cssUtils.$radius-2xl; + background-color: cssUtils.$elevated; + padding: cssUtils.$space-l; + box-shadow: cssUtils.$shadow-m; + border: 1px solid cssUtils.$border-interactive; + z-index: 10001; + + &--mobile { + max-width: 20rem; + padding: cssUtils.$space-l; + } + } + + &__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: cssUtils.$space-m; + } + + &__title { + color: cssUtils.$text-neutral-primary; + @include cssUtils.typography(heading, small); + margin: 0; + } + + &__body { + color: cssUtils.$text-neutral-primary; + display: flex; + flex-direction: column; + gap: cssUtils.$space-m; + } + + &__description { + @include cssUtils.typography(body, small); + color: cssUtils.$text-neutral-secondary; + margin: 0; + } + + &__input-section { + display: flex; + flex-direction: column; + gap: cssUtils.$space-s; + } + + &__input-wrapper { + display: flex; + gap: cssUtils.$space-s; + padding: cssUtils.$space-xs; + border: 1px solid cssUtils.$border-default; + border-radius: cssUtils.$radius-l; + background-color: cssUtils.$elevated-strong; + color: cssUtils.$text-neutral-primary; + + &:focus-within { + border-color: cssUtils.$border-interactive; + outline: 2px solid cssUtils.$border-interactive; + outline-offset: -1px; + } + } + + &__input { + padding: 0 cssUtils.$space-xs; + border: none; + background: transparent; + @include cssUtils.typography(body, default); + } + + &__button-container { + display: flex; + align-items: flex-start; + flex-shrink: 0; + } + + &__loading-icon { + height: 1rem; + width: 1rem; + animation: openui-share-spin 1s linear infinite; + } + + &__error-message { + @include cssUtils.typography(body, small); + color: cssUtils.$text-danger-primary; + } +} + +@keyframes openui-share-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/packages/react-ui/src/components/OpenUIChat/stories/OpenUIChat.stories.tsx b/packages/react-ui/src/components/OpenUIChat/stories/OpenUIChat.stories.tsx index a1dce08c2..a94d7be66 100644 --- a/packages/react-ui/src/components/OpenUIChat/stories/OpenUIChat.stories.tsx +++ b/packages/react-ui/src/components/OpenUIChat/stories/OpenUIChat.stories.tsx @@ -75,6 +75,11 @@ const LONG_STARTERS: ConversationStartersConfig = { ], }; +const mockGenerateShareLink = async (threadId: string) => { + await new Promise((r) => setTimeout(r, 1000)); + return `https://example.com/shared/${threadId}`; +}; + const mockProcessMessage = async ({ messages }: { messages: Message[] }) => { const lastMsg = messages[messages.length - 1]; const content = @@ -145,6 +150,19 @@ export const StandaloneLongStarters = { ), }; +export const StandaloneWithShareLink = { + render: () => ( +
+ +
+ ), +}; + export const StandaloneWithThreadHeader = { render: () => (
@@ -201,6 +219,19 @@ export const CopilotWithWelcome = { ), }; +export const CopilotWithShareLink = { + render: () => ( +
+ +
+ ), +}; + export const CopilotWithHeaderActions = { render: () => (
@@ -283,6 +314,17 @@ export const BottomTrayLongStarters = { ), }; +export const BottomTrayWithShareLink = { + render: () => ( + + ), +}; + export const BottomTrayWithHeaderActions = { render: () => ( Promise; } diff --git a/packages/react-ui/src/components/OpenUIChat/useShareThread.ts b/packages/react-ui/src/components/OpenUIChat/useShareThread.ts new file mode 100644 index 000000000..237eedd07 --- /dev/null +++ b/packages/react-ui/src/components/OpenUIChat/useShareThread.ts @@ -0,0 +1,28 @@ +import { useThread, useThreadList } from "@openuidev/react-headless"; +import { useCallback } from "react"; + +/** + * Hook for sharing conversation threads by threadId. + * The consumer's backend looks up messages by threadId. + * + * @category Hooks + */ +export const useShareThread = ({ + generateShareLink, +}: { + generateShareLink: (threadId: string) => Promise; +}) => { + const { isRunning, isLoadingMessages, messages } = useThread(); + const { selectedThreadId } = useThreadList(); + + const getShareThreadLink = useCallback(async () => { + if (!selectedThreadId) throw new Error("No thread selected"); + return generateShareLink(selectedThreadId); + }, [generateShareLink, selectedThreadId]); + + return { + shouldDisableShareButton: isRunning || isLoadingMessages || !selectedThreadId, + hasMessages: messages.length > 0, + getShareThreadLink, + }; +}; diff --git a/packages/react-ui/src/components/Shell/Thread.tsx b/packages/react-ui/src/components/Shell/Thread.tsx index bbc6c2e25..5ccdce1bb 100644 --- a/packages/react-ui/src/components/Shell/Thread.tsx +++ b/packages/react-ui/src/components/Shell/Thread.tsx @@ -4,6 +4,7 @@ import clsx from "clsx"; import React, { memo, useRef } from "react"; import { useLayoutContext } from "../../context/LayoutContext"; import { ScrollVariant, useScrollToBottom } from "../../hooks/useScrollToBottom"; +import { separateContentAndContext } from "../../utils/contentParser"; import { ArtifactOverlay, ArtifactPortalTarget } from "../_shared/artifact"; import { useShellStore } from "../_shared/store"; import type { AssistantMessageComponent, UserMessageComponent } from "../_shared/types"; @@ -223,7 +224,9 @@ const UserMessageContent = ({ message }: { message: Message }) => { if (message.role !== "user") return null; const content = message.content; if (typeof content === "string") { - return <>{content}; + // Strip XML wrapper tags (, ) so the bubble shows clean text + const { content: humanText } = separateContentAndContext(content); + return <>{humanText}; } // InputContent[] — render text parts return ( @@ -256,12 +259,14 @@ export const RenderMessage = memo( allMessages, assistantMessage: CustomAssistantMessage, userMessage: CustomUserMessage, + isStreaming, }: { message: Message; className?: string; allMessages: Message[]; assistantMessage?: AssistantMessageComponent; userMessage?: UserMessageComponent; + isStreaming: boolean; }) => { if (message.role === "tool") { // Tool messages are rendered inline with their parent assistant message @@ -270,7 +275,7 @@ export const RenderMessage = memo( if (message.role === "assistant") { if (CustomAssistantMessage) { - return ; + return ; } return ( @@ -335,7 +340,7 @@ export const Messages = ({ return (
- {messages.map((message) => { + {messages.map((message, i) => { return ( ); diff --git a/packages/react-ui/src/components/_shared/types/index.ts b/packages/react-ui/src/components/_shared/types/index.ts index 9fd648af9..869f4ea5f 100644 --- a/packages/react-ui/src/components/_shared/types/index.ts +++ b/packages/react-ui/src/components/_shared/types/index.ts @@ -14,6 +14,7 @@ import type { AssistantMessage, UserMessage } from "@openuidev/react-headless"; */ export type AssistantMessageComponent = React.ComponentType<{ message: AssistantMessage; + isStreaming: boolean; }>; /** diff --git a/packages/react-ui/src/genui-lib/Charts/AreaChartCondensed.ts b/packages/react-ui/src/genui-lib/Charts/AreaChartCondensed.ts index 394c23b99..4f5010898 100644 --- a/packages/react-ui/src/genui-lib/Charts/AreaChartCondensed.ts +++ b/packages/react-ui/src/genui-lib/Charts/AreaChartCondensed.ts @@ -3,7 +3,7 @@ import { defineComponent } from "@openuidev/react-lang"; import React from "react"; import { z } from "zod"; -import { AreaChart as AreaChartCondensedComponent } from "../../components/Charts"; +import { AreaChartCondensed as AreaChartCondensedComponent } from "../../components/Charts"; import { buildChartData, hasAllProps } from "../helpers"; import { SeriesSchema } from "./Series"; diff --git a/packages/react-ui/src/genui-lib/Charts/BarChartCondensed.ts b/packages/react-ui/src/genui-lib/Charts/BarChartCondensed.ts index c9b4db761..92af8b6e8 100644 --- a/packages/react-ui/src/genui-lib/Charts/BarChartCondensed.ts +++ b/packages/react-ui/src/genui-lib/Charts/BarChartCondensed.ts @@ -3,7 +3,7 @@ import { defineComponent } from "@openuidev/react-lang"; import React from "react"; import { z } from "zod"; -import { BarChart as BarChartCondensedComponent } from "../../components/Charts"; +import { BarChartCondensed as BarChartCondensedComponent } from "../../components/Charts"; import { buildChartData, hasAllProps } from "../helpers"; import { SeriesSchema } from "./Series"; diff --git a/packages/react-ui/src/genui-lib/Charts/LineChartCondensed.ts b/packages/react-ui/src/genui-lib/Charts/LineChartCondensed.ts index 431ba3d34..9e7cd61f6 100644 --- a/packages/react-ui/src/genui-lib/Charts/LineChartCondensed.ts +++ b/packages/react-ui/src/genui-lib/Charts/LineChartCondensed.ts @@ -3,7 +3,7 @@ import { defineComponent } from "@openuidev/react-lang"; import React from "react"; import { z } from "zod"; -import { LineChart as LineChartCondensedComponent } from "../../components/Charts"; +import { LineChartCondensed as LineChartCondensedComponent } from "../../components/Charts"; import { buildChartData, hasAllProps } from "../helpers"; import { SeriesSchema } from "./Series"; diff --git a/packages/react-ui/src/genui-lib/Table/index.tsx b/packages/react-ui/src/genui-lib/Table/index.tsx index 2dfb85534..eff61d4ad 100644 --- a/packages/react-ui/src/genui-lib/Table/index.tsx +++ b/packages/react-ui/src/genui-lib/Table/index.tsx @@ -10,28 +10,51 @@ import { TableHeader as OpenUITableHeader, TableRow as OpenUITableRow, } from "../../components/Table"; -import { asArray } from "../helpers"; import { ColSchema } from "./schema"; export { ColSchema } from "./schema"; +// ── Col ─────────────────────────────────────────────────────────────────────── + export const Col = defineComponent({ name: "Col", props: ColSchema, - description: "Column definition", + description: 'Column definition. Use type="action" for columns with Button/ButtonGroup actions.', component: () => null, }); +// ── Helpers ─────────────────────────────────────────────────────────────────── + +/** Normalize a row from the Renderer (may be array, {cells:[...]}, or {children:[...]}) */ +function toArray(v: unknown): unknown[] { + if (Array.isArray(v)) return v; + if (v && typeof v === "object") { + const obj = v as Record; + if ("cells" in obj && Array.isArray(obj["cells"])) return obj["cells"] as unknown[]; + if ("children" in obj && Array.isArray(obj["children"])) return obj["children"] as unknown[]; + } + return []; +} + +/** Map column type to alignment */ +function colAlign(type?: string): "left" | "right" { + return type === "number" ? "right" : "left"; +} + +// ── Table ───────────────────────────────────────────────────────────────────── + export const Table = defineComponent({ name: "Table", props: z.object({ columns: z.array(Col.ref), - rows: z.array(z.array(z.union([z.string(), z.number(), z.boolean()]))), + rows: z.array( + z.array(z.union([z.string(), z.number(), z.boolean(), z.record(z.string(), z.any())])), + ), }), description: "Data table", component: ({ props, renderNode }) => { const columns = props.columns ?? []; - const rows = asArray(props.rows) as unknown[][]; + const rawRows = (props.rows ?? []) as unknown[]; if (!columns.length) return null; @@ -40,22 +63,33 @@ export const Table = defineComponent({ {columns.map((c, i) => ( - {c.props.label} + + {c.props.label} + ))} - {rows.map((row, ri) => { - const cells = asArray(row); + {rawRows.map((rawRow, ri) => { + const cells = toArray(rawRow); return ( - {cells.map((cell, ci) => ( - - {typeof cell === "object" && cell !== null - ? renderNode(cell) - : String(cell ?? "")} - - ))} + {cells.map((cell: unknown, ci: number) => { + const col = columns[ci]; + const align = col ? colAlign(col.props.type) : "left"; + + return ( + + {typeof cell === "object" && cell !== null + ? renderNode(cell) + : String(cell ?? "")} + + ); + })} ); })} diff --git a/packages/react-ui/src/genui-lib/Table/schema.ts b/packages/react-ui/src/genui-lib/Table/schema.ts index c40291c8e..4d8e32edd 100644 --- a/packages/react-ui/src/genui-lib/Table/schema.ts +++ b/packages/react-ui/src/genui-lib/Table/schema.ts @@ -3,4 +3,5 @@ import { z } from "zod"; export const ColSchema = z.object({ label: z.string(), type: z.enum(["string", "number", "action"]).optional(), + icon: z.any().optional(), }); diff --git a/packages/react-ui/src/genui-lib/TextContent/index.tsx b/packages/react-ui/src/genui-lib/TextContent/index.tsx index cfbf2683e..658e613e7 100644 --- a/packages/react-ui/src/genui-lib/TextContent/index.tsx +++ b/packages/react-ui/src/genui-lib/TextContent/index.tsx @@ -1,6 +1,6 @@ "use client"; -import { defineComponent } from "@openuidev/react-lang"; +import { defineComponent, useIsStreaming } from "@openuidev/react-lang"; import React from "react"; import { MarkDownRenderer } from "../../components/MarkDownRenderer"; import { TextContentSchema } from "./schema"; @@ -30,9 +30,10 @@ export const TextContent = defineComponent({ "--openui-text-body-default-letter-spacing": `var(${varName}-letter-spacing)`, } as React.CSSProperties); const text = props.text == null ? "" : String(props.text); + const isStreaming = useIsStreaming(); return (
- +
); }, diff --git a/packages/react-ui/src/index.ts b/packages/react-ui/src/index.ts index fac42b290..fc82c883f 100644 --- a/packages/react-ui/src/index.ts +++ b/packages/react-ui/src/index.ts @@ -68,6 +68,8 @@ export { defaultLightTheme, swatchTokens, } from "./components/ThemeProvider"; +export type { ThemeProps } from "./components/ThemeProvider/ThemeProvider"; +export type { ThemeMode } from "./components/ThemeProvider/types"; export * from "./components/ToolCall"; export * from "./components/ToolResult"; diff --git a/packages/react-ui/src/utils/contentParser.ts b/packages/react-ui/src/utils/contentParser.ts index 9b2daa206..31361e41e 100644 --- a/packages/react-ui/src/utils/contentParser.ts +++ b/packages/react-ui/src/utils/contentParser.ts @@ -26,7 +26,7 @@ export function separateContentAndContext(raw: string): { content = raw.slice(0, contextMatch.index!).trimEnd(); } - const contentMatch = content.match(/^([\s\S]*)<\/content>\s*$/); + const contentMatch = content.match(/^]*>([\s\S]*)<\/content>\s*$/); if (contentMatch) { content = contentMatch[1] ?? content; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d18e02ed..78e5ea8c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,13 +61,13 @@ importers: version: 0.68.17 fumadocs-core: specifier: 16.6.5 - version: 16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6) + version: 16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6) fumadocs-mdx: specifier: 14.2.8 - version: 14.2.8(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react@19.2.4)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.0)) + version: 14.2.8(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react@19.2.4)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.0)) fumadocs-ui: specifier: 16.6.5 - version: 16.6.5(@takumi-rs/image-response@0.68.17)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1) + version: 16.6.5(@takumi-rs/image-response@0.68.17)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1) gpt-tokenizer: specifier: ^3.4.0 version: 3.4.0 @@ -79,7 +79,7 @@ importers: version: 12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next: specifier: 16.1.6 - version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2) + version: 16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -149,7 +149,7 @@ importers: version: 0.575.0(react@19.2.3) next: specifier: 16.1.6 - version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2) + version: 16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2) openai: specifier: ^6.22.0 version: 6.22.0(ws@8.18.2)(zod@4.3.6) @@ -207,7 +207,7 @@ importers: version: 0.575.0(react@19.2.3) next: specifier: 16.1.6 - version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2) + version: 16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2) openai: specifier: ^6.22.0 version: 6.22.0(ws@8.18.2)(zod@4.3.6) @@ -252,7 +252,7 @@ importers: dependencies: next: specifier: ^15.2.3 - version: 15.5.12(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2) + version: 15.5.12(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2) openai: specifier: ^4.90.0 version: 4.104.0(ws@8.18.2)(zod@4.3.6) @@ -347,7 +347,7 @@ importers: version: 0.562.0(react@19.2.3) next: specifier: 16.1.6 - version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2) + version: 16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2) openai: specifier: ^6.22.0 version: 6.22.0(ws@8.18.2)(zod@4.3.6) @@ -480,7 +480,7 @@ importers: version: 0.575.0(react@19.2.3) next: specifier: 16.1.6 - version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2) + version: 16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2) openai: specifier: ^6.22.0 version: 6.22.0(ws@8.18.2)(zod@4.3.6) @@ -571,7 +571,7 @@ importers: version: 0.575.0(react@19.2.3) next: specifier: 16.1.6 - version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2) + version: 16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2) react: specifier: 19.2.3 version: 19.2.3 @@ -709,6 +709,9 @@ importers: '@radix-ui/react-checkbox': specifier: ^1.1.3 version: 1.3.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-dialog': + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@radix-ui/react-dropdown-menu': specifier: ^2.1.7 version: 2.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -13359,7 +13362,7 @@ snapshots: aria-hidden: 1.2.6 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - react-remove-scroll: 2.7.1(@types/react@19.2.14)(react@19.2.4) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.4) optionalDependencies: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) @@ -18065,7 +18068,7 @@ snapshots: '@next/eslint-plugin-next': 16.1.6 eslint: 9.29.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.29.0(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.29.0(jiti@2.6.1)) @@ -18092,7 +18095,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -18107,14 +18110,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3) eslint: 9.29.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -18129,7 +18132,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.29.0(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.29.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)))(eslint@9.29.0(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -18615,7 +18618,7 @@ snapshots: fsevents@2.3.3: optional: true - fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6): + fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6): dependencies: '@formatjs/intl-localematcher': 0.8.1 '@orama/orama': 3.1.18 @@ -18647,21 +18650,21 @@ snapshots: '@types/mdast': 4.0.4 '@types/react': 19.2.14 lucide-react: 0.570.0(react@19.2.4) - next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2) + next: 16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) zod: 4.3.6 transitivePeerDependencies: - supports-color - fumadocs-mdx@14.2.8(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react@19.2.4)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.0)): + fumadocs-mdx@14.2.8(@types/mdast@4.0.4)(@types/mdx@2.0.13)(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react@19.2.4)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.0)): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.1.0 chokidar: 5.0.0 esbuild: 0.27.3 estree-util-value-to-estree: 3.5.0 - fumadocs-core: 16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6) + fumadocs-core: 16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6) js-yaml: 4.1.1 mdast-util-mdx: 3.0.0 mdast-util-to-markdown: 2.1.2 @@ -18678,13 +18681,13 @@ snapshots: '@types/mdast': 4.0.4 '@types/mdx': 2.0.13 '@types/react': 19.2.14 - next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2) + next: 16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2) react: 19.2.4 vite: 7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.89.2)(terser@5.43.0)(tsx@4.20.3)(yaml@2.8.0) transitivePeerDependencies: - supports-color - fumadocs-ui@16.6.5(@takumi-rs/image-response@0.68.17)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1): + fumadocs-ui@16.6.5(@takumi-rs/image-response@0.68.17)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(fumadocs-core@16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(tailwindcss@4.2.1): dependencies: '@fumadocs/tailwind': 0.0.2(tailwindcss@4.2.1) '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -18698,7 +18701,7 @@ snapshots: '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.4) '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) class-variance-authority: 0.7.1 - fumadocs-core: 16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6) + fumadocs-core: 16.6.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@0.570.0(react@19.2.4))(next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6) lucide-react: 0.570.0(react@19.2.4) motion: 12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next-themes: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -18713,7 +18716,7 @@ snapshots: optionalDependencies: '@takumi-rs/image-response': 0.68.17 '@types/react': 19.2.14 - next: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2) + next: 16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2) transitivePeerDependencies: - '@emotion/is-prop-valid' - '@types/react-dom' @@ -20611,7 +20614,7 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - next@15.5.12(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2): + next@15.5.12(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2): dependencies: '@next/env': 15.5.12 '@swc/helpers': 0.5.15 @@ -20619,7 +20622,7 @@ snapshots: postcss: 8.4.31 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4) + styled-jsx: 5.1.6(react@19.2.4) optionalDependencies: '@next/swc-darwin-arm64': 15.5.12 '@next/swc-darwin-x64': 15.5.12 @@ -20637,7 +20640,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2): + next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(sass@1.89.2): dependencies: '@next/env': 16.1.6 '@swc/helpers': 0.5.15 @@ -20646,7 +20649,7 @@ snapshots: postcss: 8.4.31 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.3) + styled-jsx: 5.1.6(react@19.2.3) optionalDependencies: '@next/swc-darwin-arm64': 16.1.6 '@next/swc-darwin-x64': 16.1.6 @@ -20664,7 +20667,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2): + next@16.1.6(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.89.2): dependencies: '@next/env': 16.1.6 '@swc/helpers': 0.5.15 @@ -20673,7 +20676,7 @@ snapshots: postcss: 8.4.31 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4) + styled-jsx: 5.1.6(react@19.2.4) optionalDependencies: '@next/swc-darwin-arm64': 16.1.6 '@next/swc-darwin-x64': 16.1.6 @@ -22390,19 +22393,15 @@ snapshots: dependencies: inline-style-parser: 0.2.4 - styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.3): + styled-jsx@5.1.6(react@19.2.3): dependencies: client-only: 0.0.1 react: 19.2.3 - optionalDependencies: - '@babel/core': 7.29.0 - styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.4): + styled-jsx@5.1.6(react@19.2.4): dependencies: client-only: 0.0.1 react: 19.2.4 - optionalDependencies: - '@babel/core': 7.29.0 styleq@0.1.3: {}