@miiflow/assistant-ui 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +209 -46
- package/dist/{WelcomeScreen-EGO8Qt2y.d.ts → WelcomeScreen-CSzlU0Yx.d.ts} +41 -9
- package/dist/{avatar-CD685KQV.d.ts → avatar-B_AvYfE8.d.ts} +4 -1
- package/dist/chunk-7QNDCHY7.js +2 -0
- package/dist/chunk-7QNDCHY7.js.map +1 -0
- package/dist/chunk-BINTGT2Q.js +5 -0
- package/dist/chunk-BINTGT2Q.js.map +1 -0
- package/dist/chunk-HNHLLH6W.js +2 -0
- package/dist/chunk-HNHLLH6W.js.map +1 -0
- package/dist/{chunk-WG77GQR3.js → chunk-ISMTV5BU.js} +2 -2
- package/dist/{chunk-WG77GQR3.js.map → chunk-ISMTV5BU.js.map} +1 -1
- package/dist/chunk-LDTAZ4GT.js +2 -0
- package/dist/chunk-LDTAZ4GT.js.map +1 -0
- package/dist/chunk-OU5ITGKX.js +2 -0
- package/dist/chunk-OU5ITGKX.js.map +1 -0
- package/dist/chunk-W7HQXA2Z.js +146 -0
- package/dist/chunk-W7HQXA2Z.js.map +1 -0
- package/dist/client/index.d.ts +66 -5
- package/dist/client/index.js +6 -6
- package/dist/client/index.js.map +1 -1
- package/dist/composer/index.d.ts +37 -3
- package/dist/composer/index.js +1 -1
- package/dist/context/index.d.ts +10 -3
- package/dist/context/index.js +1 -1
- package/dist/hooks/index.d.ts +11 -141
- package/dist/hooks/index.js +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/{message-D0oMw3tR.d.ts → message-DTNTKSQr.d.ts} +1 -1
- package/dist/primitives/index.d.ts +3 -3
- package/dist/primitives/index.js +1 -1
- package/dist/{streaming-CmQo_OOA.d.ts → streaming-BfLEgW5u.d.ts} +81 -3
- package/dist/styled/index.d.ts +410 -11
- package/dist/styled/index.js +1 -1
- package/dist/styles-no-preflight.css +1 -1
- package/dist/styles.css +3 -1
- package/dist/use-branding-css-vars-CR2tjSV8.d.ts +155 -0
- package/package.json +2 -1
- package/dist/chunk-4WWJTYYA.js +0 -2
- package/dist/chunk-4WWJTYYA.js.map +0 -1
- package/dist/chunk-6RN7SUWT.js +0 -2
- package/dist/chunk-6RN7SUWT.js.map +0 -1
- package/dist/chunk-D2PFIJNZ.js +0 -2
- package/dist/chunk-D2PFIJNZ.js.map +0 -1
- package/dist/chunk-HYFMOOLH.js +0 -133
- package/dist/chunk-HYFMOOLH.js.map +0 -1
- package/dist/chunk-NKLA5PPB.js +0 -2
- package/dist/chunk-NKLA5PPB.js.map +0 -1
- package/dist/chunk-ZP4FGAAH.js +0 -2
- package/dist/chunk-ZP4FGAAH.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/primitives/message.tsx","../src/primitives/message-list.tsx","../src/primitives/message-composer.tsx","../src/primitives/avatar.tsx","../src/primitives/streaming-text.tsx","../src/primitives/suggested-actions.tsx","../src/primitives/typing-indicator.tsx"],"names":["MessageContext","createContext","useMessage","context","useContext","Message","forwardRef","message","viewerRole","children","props","ref","isViewer","isStreaming","jsx","MessageContent","MessageTimestamp","format","date","formatted","MessageList","autoScroll","onIsAtBottomChange","onScrollToBottomRef","containerRef","scrollToBottom","isAtBottom","useAutoScroll","node","ComposerContext","useComposer","MessageComposer","onSubmit","disabled","className","composer","useMessageComposer","handleFormSubmit","e","contextValue","ComposerInput","content","handleContentChange","handleKeyDown","inputRef","ComposerSubmit","canSubmit","isSubmitting","getInitials","name","parts","Avatar","src","alt","role","fallback","initials","Fragment","StreamingText","showCursor","cursor","jsxs","SuggestedActionsContext","useSuggestedActions","SuggestedActions","actions","onSelect","ActionButton","action","onClick","TypingIndicator","dotCount","_","i"],"mappings":"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","file":"chunk-7QNDCHY7.js","sourcesContent":["import { createContext, forwardRef, useContext, type HTMLAttributes, type ReactNode } from \"react\";\nimport type { MessageData, ParticipantRole } from \"../types\";\n\ninterface MessageContextValue {\n\tmessage: MessageData;\n\tisViewer: boolean;\n\tisStreaming: boolean;\n}\n\nconst MessageContext = createContext<MessageContextValue | null>(null);\n\n/**\n * Hook to access the current message context.\n * Must be used within a Message component.\n */\nexport function useMessage() {\n\tconst context = useContext(MessageContext);\n\tif (!context) {\n\t\tthrow new Error(\"useMessage must be used within a Message component\");\n\t}\n\treturn context;\n}\n\nexport interface MessageProps extends HTMLAttributes<HTMLDivElement> {\n\t/** The message data */\n\tmessage: MessageData;\n\t/** The viewer's role (used to determine alignment) */\n\tviewerRole?: ParticipantRole;\n\t/** Children to render inside the message */\n\tchildren: ReactNode;\n}\n\n/**\n * Headless Message primitive.\n * Provides message context to children and data attributes for styling.\n */\nexport const Message = forwardRef<HTMLDivElement, MessageProps>(\n\t({ message, viewerRole = \"user\", children, ...props }, ref) => {\n\t\t// Case-insensitive comparison for role matching\n\t\tconst isViewer = (message.participant?.role || \"\").toLowerCase() === (viewerRole || \"\").toLowerCase();\n\t\tconst isStreaming = message.isStreaming ?? false;\n\n\t\treturn (\n\t\t\t<MessageContext.Provider value={{ message, isViewer, isStreaming }}>\n\t\t\t\t<div\n\t\t\t\t\tref={ref}\n\t\t\t\t\tdata-role={message.participant?.role}\n\t\t\t\t\tdata-viewer={isViewer}\n\t\t\t\t\tdata-streaming={isStreaming}\n\t\t\t\t\t{...props}>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t</MessageContext.Provider>\n\t\t);\n\t},\n);\n\nMessage.displayName = \"Message\";\n\nexport interface MessageContentProps extends HTMLAttributes<HTMLDivElement> {\n\t/** Custom content to render instead of message text */\n\tchildren?: ReactNode;\n}\n\n/**\n * Renders the message content.\n * By default renders the message's textContent.\n */\nexport const MessageContent = forwardRef<HTMLDivElement, MessageContentProps>(({ children, ...props }, ref) => {\n\tconst { message } = useMessage();\n\n\treturn (\n\t\t<div ref={ref} {...props}>\n\t\t\t{children ?? message.textContent}\n\t\t</div>\n\t);\n});\n\nMessageContent.displayName = \"MessageContent\";\n\nexport interface MessageTimestampProps extends HTMLAttributes<HTMLSpanElement> {\n\t/** Custom date formatter */\n\tformat?: (date: Date) => string;\n}\n\n/**\n * Renders the message timestamp.\n */\nexport const MessageTimestamp = forwardRef<HTMLSpanElement, MessageTimestampProps>(({ format, ...props }, ref) => {\n\tconst { message } = useMessage();\n\tconst date = typeof message.createdAt === \"string\" ? new Date(message.createdAt) : message.createdAt;\n\n\tconst formatted = format\n\t\t? format(date)\n\t\t: date.toLocaleTimeString(\"en-US\", {\n\t\t\t\thour: \"numeric\",\n\t\t\t\tminute: \"2-digit\",\n\t\t\t\thour12: true,\n\t\t\t});\n\n\treturn (\n\t\t<span ref={ref} {...props}>\n\t\t\t{formatted}\n\t\t</span>\n\t);\n});\n\nMessageTimestamp.displayName = \"MessageTimestamp\";\n\nexport { MessageContext };\n","import { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\nimport { useAutoScroll } from \"../hooks/use-auto-scroll\";\n\nexport interface MessageListProps extends HTMLAttributes<HTMLDivElement> {\n\t/** Messages to render */\n\tchildren: ReactNode;\n\t/** Whether to auto-scroll to bottom on new messages */\n\tautoScroll?: boolean;\n\t/** Whether the viewport is currently at the bottom (exposed for scroll-to-bottom button) */\n\tisAtBottom?: boolean;\n\t/** Callback to receive scroll state updates */\n\tonIsAtBottomChange?: (isAtBottom: boolean) => void;\n\t/** Callback to receive scrollToBottom function */\n\tonScrollToBottomRef?: (scrollToBottom: (behavior?: ScrollBehavior) => void) => void;\n}\n\n/**\n * Headless MessageList primitive.\n * Provides a scrollable container with auto-scroll behavior.\n * Exposes isAtBottom and scrollToBottom for scroll-to-bottom button integration.\n */\nexport const MessageList = forwardRef<HTMLDivElement, MessageListProps>(\n\t({ children, autoScroll = true, onIsAtBottomChange, onScrollToBottomRef, ...props }, ref) => {\n\t\tconst { containerRef, scrollToBottom, isAtBottom } = useAutoScroll<HTMLDivElement>({\n\t\t\tenabled: autoScroll,\n\t\t});\n\n\t\t// Notify parent of scroll state changes\n\t\tconst prevIsAtBottomRef = { current: isAtBottom };\n\t\tif (prevIsAtBottomRef.current !== isAtBottom) {\n\t\t\tonIsAtBottomChange?.(isAtBottom);\n\t\t}\n\n\t\t// Expose scrollToBottom to parent\n\t\tonScrollToBottomRef?.(scrollToBottom);\n\n\t\t// Merge refs\n\t\tconst mergedRef = (node: HTMLDivElement | null) => {\n\t\t\t(containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n\t\t\tif (typeof ref === \"function\") {\n\t\t\t\tref(node);\n\t\t\t} else if (ref) {\n\t\t\t\tref.current = node;\n\t\t\t}\n\t\t};\n\n\t\treturn (\n\t\t\t<div ref={mergedRef} {...props}>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t);\n\t},\n);\n\nMessageList.displayName = \"MessageList\";\n","import {\n createContext,\n useContext,\n forwardRef,\n type ReactNode,\n type TextareaHTMLAttributes,\n type ButtonHTMLAttributes,\n} from \"react\";\nimport { useMessageComposer } from \"../hooks/use-message-composer\";\n\ninterface ComposerContextValue {\n content: string;\n isSubmitting: boolean;\n canSubmit: boolean;\n error: string | null;\n handleContentChange: (value: string) => void;\n handleSubmit: () => Promise<void>;\n handleKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;\n inputRef: React.RefObject<HTMLTextAreaElement | null>;\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null);\n\n/**\n * Hook to access the composer context.\n * Must be used within a MessageComposer component.\n */\nexport function useComposer() {\n const context = useContext(ComposerContext);\n if (!context) {\n throw new Error(\"useComposer must be used within a MessageComposer component\");\n }\n return context;\n}\n\nexport interface MessageComposerProps {\n /** Callback when message is submitted */\n onSubmit: (content: string, attachments?: File[]) => Promise<void>;\n /** Whether the composer is disabled */\n disabled?: boolean;\n /** Whether the current conversation is mid-response. Blocks submitting a\n * second, concurrent turn into the same conversation. */\n isStreaming?: boolean;\n /** Children to render inside the composer */\n children: ReactNode;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Headless MessageComposer primitive.\n * Provides composer state and behavior to children.\n */\nexport const MessageComposer = forwardRef<HTMLFormElement, MessageComposerProps>(\n ({ onSubmit, disabled = false, isStreaming = false, children, className }, ref) => {\n const composer = useMessageComposer({ onSubmit, disabled, isStreaming });\n\n const handleFormSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n composer.handleSubmit();\n };\n\n const contextValue: ComposerContextValue = {\n ...composer,\n canSubmit: Boolean(composer.canSubmit),\n };\n\n return (\n <ComposerContext.Provider value={contextValue}>\n <form\n ref={ref}\n className={className}\n onSubmit={handleFormSubmit}\n data-submitting={composer.isSubmitting}\n data-can-submit={composer.canSubmit}\n >\n {children}\n </form>\n </ComposerContext.Provider>\n );\n }\n);\n\nMessageComposer.displayName = \"MessageComposer\";\n\nexport interface ComposerInputProps\n extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, \"onChange\" | \"value\"> {}\n\n/**\n * Text input for the message composer.\n */\nexport const ComposerInput = forwardRef<HTMLTextAreaElement, ComposerInputProps>(\n (props, ref) => {\n const { content, handleContentChange, handleKeyDown, inputRef } = useComposer();\n\n // Merge refs\n const mergedRef = (node: HTMLTextAreaElement | null) => {\n (inputRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n };\n\n return (\n <textarea\n ref={mergedRef}\n value={content}\n onChange={(e) => handleContentChange(e.target.value)}\n onKeyDown={handleKeyDown}\n {...props}\n />\n );\n }\n);\n\nComposerInput.displayName = \"ComposerInput\";\n\nexport interface ComposerSubmitProps extends ButtonHTMLAttributes<HTMLButtonElement> {}\n\n/**\n * Submit button for the message composer.\n */\nexport const ComposerSubmit = forwardRef<HTMLButtonElement, ComposerSubmitProps>(\n ({ children, disabled, ...props }, ref) => {\n const { canSubmit, isSubmitting } = useComposer();\n\n return (\n <button\n ref={ref}\n type=\"submit\"\n disabled={disabled || !canSubmit}\n data-submitting={isSubmitting}\n {...props}\n >\n {children}\n </button>\n );\n }\n);\n\nComposerSubmit.displayName = \"ComposerSubmit\";\n\nexport { ComposerContext };\n","import { forwardRef, type HTMLAttributes } from \"react\";\nimport type { ParticipantRole } from \"../types\";\n\nexport interface AvatarProps extends HTMLAttributes<HTMLDivElement> {\n /** Name for fallback initials */\n name?: string;\n /** Image URL */\n src?: string;\n /** Alt text for image */\n alt?: string;\n /** Participant role for styling */\n role?: ParticipantRole;\n /** Fallback content when no image or name */\n fallback?: React.ReactNode;\n}\n\n/**\n * Get initials from a name.\n */\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/);\n if (parts.length === 1) {\n return parts[0].charAt(0).toUpperCase();\n }\n return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();\n}\n\n/**\n * Headless Avatar primitive.\n * Renders an image or fallback initials.\n */\nexport const Avatar = forwardRef<HTMLDivElement, AvatarProps>(\n ({ name, src, alt, role, fallback, children, ...props }, ref) => {\n const initials = name ? getInitials(name) : null;\n const altText = alt ?? name ?? role ?? \"Avatar\";\n\n return (\n <div ref={ref} data-role={role} {...props}>\n {children ?? (\n <>\n {src ? (\n <img src={src} alt={altText} style={{ width: \"100%\", height: \"100%\" }} />\n ) : (\n fallback ?? initials ?? role?.charAt(0).toUpperCase()\n )}\n </>\n )}\n </div>\n );\n }\n);\n\nAvatar.displayName = \"Avatar\";\n","import { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nexport interface StreamingTextProps extends HTMLAttributes<HTMLDivElement> {\n /** The content to display */\n content: string;\n /** Whether the text is currently streaming */\n isStreaming?: boolean;\n /** Show a cursor indicator while streaming */\n showCursor?: boolean;\n /** Custom cursor element */\n cursor?: ReactNode;\n /** Children override (takes precedence over content) */\n children?: ReactNode;\n}\n\n/**\n * Headless StreamingText primitive.\n * Renders text with streaming indicator support.\n */\nexport const StreamingText = forwardRef<HTMLDivElement, StreamingTextProps>(\n (\n { content, isStreaming = false, showCursor = true, cursor, children, ...props },\n ref\n ) => {\n const defaultCursor = (\n <span\n aria-hidden=\"true\"\n style={{\n display: \"inline-block\",\n width: \"2px\",\n height: \"1em\",\n backgroundColor: \"currentColor\",\n marginLeft: \"2px\",\n verticalAlign: \"text-bottom\",\n animation: \"blink 1s step-end infinite\",\n }}\n />\n );\n\n return (\n <div ref={ref} data-streaming={isStreaming} {...props}>\n {children ?? content}\n {isStreaming && showCursor && (cursor ?? defaultCursor)}\n </div>\n );\n }\n);\n\nStreamingText.displayName = \"StreamingText\";\n","import {\n createContext,\n useContext,\n forwardRef,\n type ReactNode,\n type ButtonHTMLAttributes,\n} from \"react\";\nimport type { SuggestedAction } from \"../types\";\n\ninterface SuggestedActionsContextValue {\n actions: SuggestedAction[];\n onSelect: (action: SuggestedAction) => void;\n}\n\nconst SuggestedActionsContext = createContext<SuggestedActionsContextValue | null>(null);\n\n/**\n * Hook to access suggested actions context.\n */\nexport function useSuggestedActions() {\n const context = useContext(SuggestedActionsContext);\n if (!context) {\n throw new Error(\n \"useSuggestedActions must be used within a SuggestedActions component\"\n );\n }\n return context;\n}\n\nexport interface SuggestedActionsProps {\n /** List of suggested actions */\n actions: SuggestedAction[];\n /** Callback when an action is selected */\n onSelect: (action: SuggestedAction) => void;\n /** Children to render */\n children: ReactNode;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Headless SuggestedActions primitive.\n * Provides context for rendering action buttons.\n */\nexport const SuggestedActions = forwardRef<HTMLDivElement, SuggestedActionsProps>(\n ({ actions, onSelect, children, className }, ref) => {\n if (actions.length === 0) {\n return null;\n }\n\n return (\n <SuggestedActionsContext.Provider value={{ actions, onSelect }}>\n <div ref={ref} role=\"group\" aria-label=\"Suggested actions\" className={className}>\n {children}\n </div>\n </SuggestedActionsContext.Provider>\n );\n }\n);\n\nSuggestedActions.displayName = \"SuggestedActions\";\n\nexport interface ActionButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** The action this button represents */\n action: SuggestedAction;\n /** Children to render inside button */\n children?: ReactNode;\n}\n\n/**\n * Button for a suggested action.\n */\nexport const ActionButton = forwardRef<HTMLButtonElement, ActionButtonProps>(\n ({ action, children, onClick, ...props }, ref) => {\n const { onSelect } = useSuggestedActions();\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n if (!e.defaultPrevented) {\n onSelect(action);\n }\n };\n\n return (\n <button ref={ref} type=\"button\" onClick={handleClick} {...props}>\n {children ?? action.label}\n </button>\n );\n }\n);\n\nActionButton.displayName = \"ActionButton\";\n\nexport { SuggestedActionsContext };\n","import { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nexport interface TypingIndicatorProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom content to show while typing */\n children?: ReactNode;\n /** Number of dots to show */\n dotCount?: number;\n}\n\n/**\n * Headless TypingIndicator primitive.\n * Shows animated dots or custom content.\n */\nexport const TypingIndicator = forwardRef<HTMLDivElement, TypingIndicatorProps>(\n ({ children, dotCount = 3, ...props }, ref) => {\n return (\n <div ref={ref} role=\"status\" aria-label=\"Assistant is typing\" {...props}>\n {children ?? (\n <span aria-hidden=\"true\">\n {Array.from({ length: dotCount }).map((_, i) => (\n <span\n key={i}\n style={{\n display: \"inline-block\",\n width: \"6px\",\n height: \"6px\",\n borderRadius: \"50%\",\n backgroundColor: \"currentColor\",\n marginRight: i < dotCount - 1 ? \"4px\" : 0,\n animation: `typing 1.4s infinite ease-in-out`,\n animationDelay: `${i * 0.2}s`,\n }}\n />\n ))}\n </span>\n )}\n </div>\n );\n }\n);\n\nTypingIndicator.displayName = \"TypingIndicator\";\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import {$getSelection,$isRangeSelection,$getRoot,CLEAR_HISTORY_COMMAND,KEY_ENTER_COMMAND,COMMAND_PRIORITY_HIGH,DecoratorNode,$createParagraphNode,$createLineBreakNode,$createTextNode,$isTextNode,$isElementNode}from'lexical';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {useLexicalComposerContext}from'@lexical/react/LexicalComposerContext';import {LexicalTypeaheadMenuPlugin,MenuOption}from'@lexical/react/LexicalTypeaheadMenuPlugin';import {forwardRef,useMemo,useRef,useEffect,useCallback,useImperativeHandle,useState,useLayoutEffect}from'react';import {createPortal}from'react-dom';import {LexicalComposer}from'@lexical/react/LexicalComposer';import {ContentEditable}from'@lexical/react/LexicalContentEditable';import {LexicalErrorBoundary}from'@lexical/react/LexicalErrorBoundary';import {HistoryPlugin}from'@lexical/react/LexicalHistoryPlugin';import {OnChangePlugin}from'@lexical/react/LexicalOnChangePlugin';import {RichTextPlugin}from'@lexical/react/LexicalRichTextPlugin';import {useReducedMotion,AnimatePresence,motion}from'framer-motion';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';var oe="color-mix(in srgb, currentColor 16%, transparent)",re="color-mix(in srgb, currentColor 8%, transparent)",ie="color-mix(in srgb, currentColor 6%, transparent)",ae="color-mix(in srgb, currentColor 12%, transparent)",J={flexShrink:0,display:"inline-flex",alignItems:"center",fontSize:10,fontWeight:700,letterSpacing:.5,textTransform:"uppercase",padding:"2px 5px",borderRadius:3,background:oe,lineHeight:1};function M({id:t,kind:e,label:o,description:d,icon:i,tag:r,variant:p="chip",selected:l=false,onMouseEnter:u,onMouseDown:g,htmlId:m}){let h=o??t,T=r!==void 0?jsx("span",{style:{display:"inline-flex",flexShrink:0,alignItems:"center"},children:r}):jsx("span",{style:J,children:e});return p==="chip"?jsxs("span",{"data-chat-command-chip":"true","data-command-id":t,"data-command-kind":e,title:`${e}: ${t}`,style:{display:"inline-flex",alignItems:"center",gap:5,verticalAlign:"middle",transform:"translateY(-1px)",background:re,borderRadius:999,padding:"2px 9px 2px 4px",margin:"0 1px",whiteSpace:"nowrap",fontWeight:500,lineHeight:1},children:[T,i&&jsx("span",{style:{display:"inline-flex",flexShrink:0},children:i}),jsx("span",{children:h})]}):jsxs("div",{role:"option","aria-selected":l,id:m,onMouseEnter:u,onMouseDown:g,"data-command-id":t,"data-command-kind":e,style:{display:"flex",alignItems:"center",gap:8,padding:"6px 8px",borderRadius:6,cursor:"pointer",background:l?ae:"transparent",transition:"background 80ms ease"},onMouseOver:n=>{l||(n.currentTarget.style.background=ie);},onMouseOut:n=>{l||(n.currentTarget.style.background="transparent");},children:[jsx("span",{style:J,children:e}),i&&jsx("span",{style:{display:"inline-flex",flexShrink:0},children:i}),jsxs("div",{style:{flex:1,minWidth:0,lineHeight:1.3},children:[jsx("div",{style:{fontSize:12,fontWeight:500,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:h}),d&&jsx("div",{style:{fontSize:11,opacity:.65,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:d})]})]})}var A="data-chat-command-token",I="/",N=class t extends DecoratorNode{constructor(e,o,d,i=I,r){super(r),this.__id=e,this.__kind=o,this.__label=d,this.__prefix=i;}static getType(){return "command-token"}static clone(e){return new t(e.__id,e.__kind,e.__label,e.__prefix,e.__key)}static importJSON(e){return new t(e.commandId,e.commandKind,e.commandLabel,e.commandPrefix??I)}exportJSON(){return {...super.exportJSON(),commandId:this.__id,commandKind:this.__kind,commandLabel:this.__label,commandPrefix:this.__prefix,type:"command-token",version:2}}getCommandId(){return this.__id}getCommandKind(){return this.__kind}getCommandLabel(){return this.__label}getCommandPrefix(){return this.__prefix}getEncodedText(){return `${this.__prefix}${this.__id}:${this.__kind}`}createDOM(){let e=document.createElement("span");return e.setAttribute(A,"true"),e}updateDOM(){return false}exportDOM(){let e=document.createElement("span");return e.setAttribute(A,"true"),e.setAttribute("data-command-id",this.__id),e.setAttribute("data-command-kind",this.__kind),e.setAttribute("data-command-prefix",this.__prefix),e.textContent=this.getEncodedText(),{element:e}}static importDOM(){return {span:e=>e.hasAttribute(A)?{conversion:de,priority:1}:null}}decorate(){return jsx("span",{contentEditable:false,style:{userSelect:"none"},children:jsx(M,{id:this.__id,kind:this.__kind,label:this.__label,variant:"chip"})})}isInline(){return true}isKeyboardSelectable(){return true}getTextContent(){return this.getEncodedText()}};function de(t){let e=t.getAttribute("data-command-id"),o=t.getAttribute("data-command-kind");if(!e||!o)return null;let d=t.getAttribute("data-command-prefix")??I,i=t.textContent?.replace(new RegExp(`^[\\${d}]`),"")??e;return {node:S(e,o,i,d)}}function S(t,e,o,d=I){return new N(t,e,o,d)}function L(t){return t instanceof N}var D=/(^|\s|\(|\[)([/@])([^\n:]+?):([\w-]+)(?=$|[\s.,!?)\]])/g;function K(t){let e=[];D.lastIndex=0;let o;for(;(o=D.exec(t))!==null;){let d=o[1]??"",i=o[2],r=o[3],p=o[4],l=o.index+d.length,u=o[0].slice(d.length);e.push({id:r,kind:p,prefix:i,raw:u,index:l,endIndex:l+u.length});}return e}var ye="/",Te=75,G=class extends MenuOption{constructor(e){super(`${e.kind}:${e.id}`),this.command=e;}};function ke(t){let e=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`(^|\\s|\\()(${e}((?:[^${e}\\s]){0,${Te}}))$`)}function F({commandProvider:t,menuRenderer:e,onMenuStateChange:o}){let[d]=useLexicalComposerContext(),[i,r]=useState(null),[p,l]=useState([]),u=t?.trigger??ye,g=useMemo(()=>ke(u),[u]);useEffect(()=>{o?.(i!=null);},[i,o]),useEffect(()=>{if(!t||i==null){l([]);return}let s=false;return Promise.resolve(t.fetch(i)).then(a=>{s||l(a);}),()=>{s=true;}},[t,i]);let m=useMemo(()=>p.map(s=>new G(s)),[p]),h=useCallback(s=>{let a=g.exec(s);if(!a)return null;let c=a[1]??"",x=a[3]??"";return {leadOffset:a.index+c.length,matchingString:x,replaceableString:a[2]??""}},[g]),T=t?.singletonKinds,n=useCallback((s,a,c)=>{d.update(()=>{let x=s.command.kind;if(T?.includes(x)??false){let C=[],R=k=>{if(L(k)&&k.getCommandKind()===x){C.push({node:k,index:0});return}if($isElementNode(k))for(let _ of k.getChildren())R(_);};R($getRoot());for(let k of C){let _=k.node.getNextSibling();if(k.node.remove(),_&&$isTextNode(_)){let W=_.getTextContent();if(W.startsWith(" ")){let Y=W.slice(1);Y.length===0?_.remove():_.setTextContent(Y);}}}}let E=S(s.command.id,x,s.command.label,u),f=$createTextNode(" ");a&&a.replace(E).insertAfter(f),f.select(),c();});},[d,u,T]);return t?jsx(LexicalTypeaheadMenuPlugin,{onQueryChange:r,onSelectOption:n,triggerFn:h,options:m,menuRenderFn:(s,{selectedIndex:a,selectOptionAndCleanUp:c,setHighlightedIndex:x})=>e?jsx(Fragment,{children:e({anchorElement:s.current,options:p,selectedIndex:a??0,setSelectedIndex:x,selectOption:v=>{let E=m.find(f=>f.command.id===v.id&&f.command.kind===v.kind);E&&c(E);},closeMenu:()=>r(null)})}):jsx(be,{anchorElement:s.current,options:m,selectedIndex:a??0,setHighlightedIndex:x,selectOptionAndCleanUp:c,visible:p.length>0||i!=null,queryString:i})}):null}function be({anchorElement:t,options:e,selectedIndex:o,setHighlightedIndex:d,selectOptionAndCleanUp:i,visible:r,queryString:p}){let l=useRef(null),[u,g]=useState(false),m=6;if(useEffect(()=>{let n=l.current;if(!n||e.length===0)return;let s=n.querySelector(`#command-typeahead-item-${o}`);if(!s)return;let a=n.getBoundingClientRect(),c=s.getBoundingClientRect();c.top<a.top?n.scrollTop-=a.top-c.top:c.bottom>a.bottom&&(n.scrollTop+=c.bottom-a.bottom);},[o,e.length]),useLayoutEffect(()=>{if(!t||!r||!l.current)return;let n=()=>{let s=l.current;if(!s)return;let a=t.getBoundingClientRect(),c=s.offsetHeight,v=window.innerHeight-a.top,f=a.top-a.height,C=c+m>v,R=c+m<=f;g(C&&R);};return n(),window.addEventListener("resize",n),window.addEventListener("scroll",n,true),()=>{window.removeEventListener("resize",n),window.removeEventListener("scroll",n,true);}},[t,r,e.length]),!t||!r)return null;let h=t.offsetHeight||20,T=u?`translateY(calc(-100% - ${h+m}px))`:"none";return createPortal(jsx("div",{ref:l,role:"listbox",style:{position:"absolute",top:0,left:0,transform:T,zIndex:99999,minWidth:320,maxWidth:520,maxHeight:280,overflowY:"auto",borderRadius:8,border:"1px solid rgba(0,0,0,0.1)",background:"#ffffff",boxShadow:"0 8px 24px rgba(0,0,0,0.15)",padding:4,fontSize:12,color:"#1f2937"},children:e.length===0?jsxs("div",{style:{padding:"6px 10px",fontSize:11,color:"#6b7280"},children:["No matching commands",p?` for "${p}"`:""]}):e.map((n,s)=>{let a=s===o,c=n.command;return jsx(M,{variant:"row",id:c.id,kind:c.kind,label:c.label,description:c.description,icon:c.icon,selected:a,htmlId:`command-typeahead-item-${s}`,onMouseEnter:()=>d(s),onMouseDown:x=>{x.preventDefault(),x.stopPropagation(),d(s),i(n);}},n.key)})}),t)}function P(t,e){let o=$getRoot();o.clear();let d=(t??"").split(`
|
|
2
|
+
|
|
3
|
+
`);for(let i of d){let r=$createParagraphNode();i.split(`
|
|
4
|
+
`).forEach((l,u)=>{u>0&&r.append($createLineBreakNode());let g=0;for(let m of K(l))m.index>g&&r.append($createTextNode(l.slice(g,m.index))),r.append(S(m.id,m.kind,e?.(m.id,m.kind)??m.id,m.prefix)),g=m.endIndex;g<l.length&&r.append($createTextNode(l.slice(g)));}),o.append(r);}}function O(...t){return twMerge(clsx(t))}var Je={paragraph:"chat-composer-paragraph"};function ne(t){let e="",o=[];return t.getEditorState().read(()=>{let d=$getRoot();e=d.getTextContent();let i=r=>{if(L(r)&&o.push({id:r.getCommandId(),kind:r.getCommandKind(),label:r.getCommandLabel()}),$isElementNode(r))for(let p of r.getChildren())i(p);};i(d);}),{text:e,tokens:o}}var Ue=forwardRef(function({placeholder:e="Type a message...",disabled:o=false,className:d,inputClassName:i,placeholderClassName:r,children:p,onChange:l,onSubmit:u,submitOnEnter:g=true,initialContent:m,resolveTokenLabel:h,commandProvider:T,commandProviders:n},s){let a=useMemo(()=>({namespace:"ChatComposer",theme:Je,nodes:[N],editable:!o,editorState:m?()=>P(m,h):void 0,onError:c=>{console.error("[chat-ui composer]",c);}}),[]);return jsx(LexicalComposer,{initialConfig:a,children:jsx(Xe,{placeholder:e,disabled:o,className:d,inputClassName:i,placeholderClassName:r,onChange:l,onSubmit:u,submitOnEnter:g,resolveTokenLabel:h,commandProvider:T,commandProviders:n,imperativeHandle:s,children:p})})});function Xe({placeholder:t,disabled:e,className:o,inputClassName:d,placeholderClassName:i,children:r,onChange:p,onSubmit:l,submitOnEnter:u=true,resolveTokenLabel:g,commandProvider:m,commandProviders:h,imperativeHandle:T}){let[n]=useLexicalComposerContext(),s=useReducedMotion(),a=useRef(l);a.current=l;let c=useRef(false);useEffect(()=>{n.setEditable(!e);},[n,e]);let x=useCallback(()=>{a.current?.(ne(n));},[n]);useImperativeHandle(T,()=>({clear:()=>{n.update(()=>{$getRoot().clear();});},submit:x,focus:()=>n.focus(),setContent:f=>{n.update(()=>{P(f,g),$getRoot().selectEnd();}),n.dispatchCommand(CLEAR_HISTORY_COMMAND,void 0);},insertText:f=>{n.focus(()=>{n.update(()=>{let C=$getSelection();$isRangeSelection(C)||($getRoot().selectEnd(),C=$getSelection()),$isRangeSelection(C)&&C.insertText(f);});});}}),[n,x,g]),useEffect(()=>{if(u)return n.registerCommand(KEY_ENTER_COMMAND,f=>{if(c.current)return false;let C=f;return C?.shiftKey?false:(C?.preventDefault(),x(),true)},COMMAND_PRIORITY_HIGH)},[n,x,u]);let v=useCallback(f=>{c.current=f;},[]),E=useCallback(f=>{if(!p)return;let C=ne(n);p(C);},[n,p]);return jsxs("div",{className:O("relative w-full",o),children:[jsx("div",{className:"relative",children:jsx(RichTextPlugin,{contentEditable:jsx(ContentEditable,{className:O("outline-none w-full text-sm leading-relaxed","min-h-[24px] max-h-[200px] overflow-y-auto","text-gray-900 dark:text-zinc-100",e&&"opacity-50 cursor-not-allowed",d)}),placeholder:jsx("div",{className:O("pointer-events-none absolute inset-0 flex items-center overflow-hidden","text-sm text-gray-400 dark:text-zinc-500 select-none",i),children:s?jsx("span",{className:"truncate",children:t}):jsx(AnimatePresence,{mode:"wait",initial:false,children:jsx(motion.span,{className:"truncate",initial:{opacity:0,y:4},animate:{opacity:1,y:0},exit:{opacity:0,y:-4},transition:{duration:.18,ease:[.4,0,.2,1]},children:t},t)})}),ErrorBoundary:LexicalErrorBoundary})}),jsx(OnChangePlugin,{onChange:E}),jsx(HistoryPlugin,{}),(h&&h.length>0?h:m?[m]:[]).map((C,R)=>jsx(F,{commandProvider:C,onMenuStateChange:v},`${C.trigger??"/"}-${R}`)),r]})}export{O as a,M as b,N as c,S as d,L as e,D as f,K as g,F as h,P as i,Ue as j};//# sourceMappingURL=chunk-BINTGT2Q.js.map
|
|
5
|
+
//# sourceMappingURL=chunk-BINTGT2Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/composer/CommandTokenView.tsx","../src/composer/CommandTokenNode.tsx","../src/composer/CommandTokenPlugin.tsx","../src/composer/hydrate.ts","../src/utils/cn.ts","../src/composer/LexicalChatInput.tsx"],"names":["TAG_BG","CHIP_BG","ROW_HOVER_BG","ROW_SELECTED_BG","tagStyle","CommandTokenView","id","kind","label","description","icon","tag","variant","selected","onMouseEnter","onMouseDown","htmlId","display","tagNode","jsx","jsxs","e","TOKEN_DATA_ATTR","DEFAULT_PREFIX","CommandTokenNode","_CommandTokenNode","DecoratorNode","prefix","key","node","serialized","span","element","domNode","convertCommandTokenElement","$createCommandTokenNode","$isCommandTokenNode","COMMAND_TOKEN_REGEX","findInlineCommandTokens","text","matches","m","leading","start","raw","DEFAULT_TRIGGER","QUERY_LENGTH_LIMIT","CommandTypeaheadOption","MenuOption","command","buildTriggerRegex","trigger","escaped","CommandTokenPlugin","commandProvider","menuRenderer","onMenuStateChange","editor","useLexicalComposerContext","queryString","setQueryString","useState","results","setResults","triggerRegex","useMemo","useEffect","cancelled","cmds","options","cmd","checkForMatch","useCallback","match","matchingString","singletonKinds","onSelectOption","selectedOption","nodeToReplace","closeMenu","stale","walk","n","$isElementNode","c","$getRoot","entry","next","$isTextNode","nextText","trimmed","tokenNode","trailingSpace","$createTextNode","LexicalTypeaheadMenuPlugin","anchorElementRef","selectedIndex","selectOptionAndCleanUp","setHighlightedIndex","Fragment","option","o","DefaultCommandMenu","anchorElement","visible","menuRef","useRef","flipped","setFlipped","VERTICAL_GAP","menu","selectedEl","menuRect","itemRect","useLayoutEffect","reposition","menuEl","anchorRect","menuHeight","spaceBelow","spaceAbove","wouldOverflowBottom","fitsAbove","anchorHeight","transform","createPortal","i","isSelected","$hydrateFromEncodedText","resolveLabel","root","paragraphs","paragraphText","paragraph","$createParagraphNode","line","lineIndex","$createLineBreakNode","cursor","cn","inputs","twMerge","clsx","EDITOR_THEME","readPayload","tokens","collect","child","LexicalChatInput","forwardRef","placeholder","disabled","className","inputClassName","placeholderClassName","children","onChange","onSubmit","submitOnEnter","initialContent","resolveTokenLabel","commandProviders","ref","initialConfig","error","LexicalComposer","ChatInputBody","imperativeHandle","prefersReducedMotion","useReducedMotion","onSubmitRef","commandMenuOpenRef","submit","useImperativeHandle","CLEAR_HISTORY_COMMAND","selection","$getSelection","$isRangeSelection","KEY_ENTER_COMMAND","event","keyboard","COMMAND_PRIORITY_HIGH","handleCommandMenuStateChange","isOpen","handleChange","_state","payload","RichTextPlugin","ContentEditable","AnimatePresence","motion","LexicalErrorBoundary","OnChangePlugin","HistoryPlugin","p"],"mappings":"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","file":"chunk-BINTGT2Q.js","sourcesContent":["import type { CSSProperties, ReactNode } from \"react\";\n\n/**\n * Single source of truth for slash-command token visuals.\n *\n * Used in three places:\n * - Typeahead popover row (variant=\"row\")\n * - Inline chip in the editor (variant=\"chip\", inside CommandTokenNode.decorate)\n * - Inline chip in message history (variant=\"chip\", inside MarkdownContent)\n *\n * Renders a kind tag + label so the user gets the same visual identity at\n * every step: pick → type → send → re-read. Adapts to the surrounding text\n * color via `currentColor` so it survives light bubbles, dark bubbles, and\n * any branded chat surface without bespoke palettes.\n */\nexport interface CommandTokenViewProps {\n /** Slug-style id (wire format `/<id>:<kind>`). */\n id: string;\n /** Token category, e.g. \"skill\", \"mode\". Rendered as the leading tag. */\n kind: string;\n /** Display name; falls back to `id` if not provided. */\n label?: string;\n /** Optional secondary line; only rendered for variant=\"row\". */\n description?: string;\n /** Optional leading icon (slotted before the label, after the tag). */\n icon?: ReactNode;\n /** Optional override for the leading tag (where `kind` renders by default).\n * When set, replaces the uppercase kind pill — e.g. swap \"AD-ACCOUNT\" for\n * the platform logo. */\n tag?: ReactNode;\n /** Compact inline pill (\"chip\") vs. full popover row (\"row\"). */\n variant?: \"chip\" | \"row\";\n /** When true, applies the row's \"selected\" styling. */\n selected?: boolean;\n /** Optional click handler (popover row uses it; chip doesn't by default). */\n onMouseEnter?: () => void;\n onMouseDown?: (e: React.MouseEvent) => void;\n /** Pass-through id for ARIA wiring (used by typeahead). */\n htmlId?: string;\n}\n\nconst TAG_BG = \"color-mix(in srgb, currentColor 16%, transparent)\";\nconst CHIP_BG = \"color-mix(in srgb, currentColor 8%, transparent)\";\nconst ROW_HOVER_BG = \"color-mix(in srgb, currentColor 6%, transparent)\";\nconst ROW_SELECTED_BG = \"color-mix(in srgb, currentColor 12%, transparent)\";\n\nconst tagStyle: CSSProperties = {\n flexShrink: 0,\n display: \"inline-flex\",\n alignItems: \"center\",\n fontSize: 10,\n fontWeight: 700,\n letterSpacing: 0.5,\n textTransform: \"uppercase\",\n padding: \"2px 5px\",\n borderRadius: 3,\n background: TAG_BG,\n lineHeight: 1,\n};\n\nexport function CommandTokenView({\n id,\n kind,\n label,\n description,\n icon,\n tag,\n variant = \"chip\",\n selected = false,\n onMouseEnter,\n onMouseDown,\n htmlId,\n}: CommandTokenViewProps) {\n const display = label ?? id;\n\n // When the caller supplies a tag node (e.g. a platform logo for an\n // ad-account chip), render it raw — no uppercase pill background. The\n // default kind text still uses tagStyle.\n const tagNode =\n tag !== undefined ? (\n <span style={{ display: \"inline-flex\", flexShrink: 0, alignItems: \"center\" }}>{tag}</span>\n ) : (\n <span style={tagStyle}>{kind}</span>\n );\n\n if (variant === \"chip\") {\n return (\n <span\n data-chat-command-chip=\"true\"\n data-command-id={id}\n data-command-kind={kind}\n title={`${kind}: ${id}`}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 5,\n verticalAlign: \"middle\",\n // Optical correction: the pill otherwise reads slightly below adjacent text.\n transform: \"translateY(-1px)\",\n background: CHIP_BG,\n borderRadius: 999,\n padding: \"2px 9px 2px 4px\",\n margin: \"0 1px\",\n whiteSpace: \"nowrap\",\n fontWeight: 500,\n lineHeight: 1,\n }}\n >\n {tagNode}\n {icon && (\n <span style={{ display: \"inline-flex\", flexShrink: 0 }}>{icon}</span>\n )}\n <span>{display}</span>\n </span>\n );\n }\n\n // variant === \"row\"\n return (\n <div\n role=\"option\"\n aria-selected={selected}\n id={htmlId}\n onMouseEnter={onMouseEnter}\n onMouseDown={onMouseDown}\n data-command-id={id}\n data-command-kind={kind}\n style={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n padding: \"6px 8px\",\n borderRadius: 6,\n cursor: \"pointer\",\n background: selected ? ROW_SELECTED_BG : \"transparent\",\n transition: \"background 80ms ease\",\n }}\n onMouseOver={(e) => {\n if (!selected) {\n (e.currentTarget as HTMLDivElement).style.background = ROW_HOVER_BG;\n }\n }}\n onMouseOut={(e) => {\n if (!selected) {\n (e.currentTarget as HTMLDivElement).style.background = \"transparent\";\n }\n }}\n >\n <span style={tagStyle}>{kind}</span>\n {icon && (\n <span style={{ display: \"inline-flex\", flexShrink: 0 }}>{icon}</span>\n )}\n <div style={{ flex: 1, minWidth: 0, lineHeight: 1.3 }}>\n <div\n style={{\n fontSize: 12,\n fontWeight: 500,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {display}\n </div>\n {description && (\n <div\n style={{\n fontSize: 11,\n opacity: 0.65,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {description}\n </div>\n )}\n </div>\n </div>\n );\n}\n","import {\n DecoratorNode,\n type DOMConversionMap,\n type DOMConversionOutput,\n type DOMExportOutput,\n type LexicalNode,\n type NodeKey,\n type SerializedLexicalNode,\n type Spread,\n} from \"lexical\";\nimport type { ReactNode } from \"react\";\n\nimport { CommandTokenView } from \"./CommandTokenView\";\n\nexport type SerializedCommandTokenNode = Spread<\n {\n commandId: string;\n commandKind: string;\n commandLabel: string;\n /** Trigger character that produced the chip (e.g. \"/\" or \"@\"). Optional in\n * v1 payloads — defaulted to \"/\" on import for backwards compatibility. */\n commandPrefix?: string;\n type: \"command-token\";\n version: 1 | 2;\n },\n SerializedLexicalNode\n>;\n\nconst TOKEN_DATA_ATTR = \"data-chat-command-token\";\nconst DEFAULT_PREFIX = \"/\";\n\nexport class CommandTokenNode extends DecoratorNode<ReactNode> {\n __id: string;\n __kind: string;\n __label: string;\n __prefix: string;\n\n constructor(\n id: string,\n kind: string,\n label: string,\n prefix: string = DEFAULT_PREFIX,\n key?: NodeKey,\n ) {\n super(key);\n this.__id = id;\n this.__kind = kind;\n this.__label = label;\n this.__prefix = prefix;\n }\n\n static getType(): string {\n return \"command-token\";\n }\n\n static clone(node: CommandTokenNode): CommandTokenNode {\n return new CommandTokenNode(\n node.__id,\n node.__kind,\n node.__label,\n node.__prefix,\n node.__key,\n );\n }\n\n static importJSON(serialized: SerializedCommandTokenNode): CommandTokenNode {\n return new CommandTokenNode(\n serialized.commandId,\n serialized.commandKind,\n serialized.commandLabel,\n serialized.commandPrefix ?? DEFAULT_PREFIX,\n );\n }\n\n exportJSON(): SerializedCommandTokenNode {\n return {\n ...super.exportJSON(),\n commandId: this.__id,\n commandKind: this.__kind,\n commandLabel: this.__label,\n commandPrefix: this.__prefix,\n type: \"command-token\",\n version: 2,\n };\n }\n\n getCommandId(): string {\n return this.__id;\n }\n\n getCommandKind(): string {\n return this.__kind;\n }\n\n getCommandLabel(): string {\n return this.__label;\n }\n\n getCommandPrefix(): string {\n return this.__prefix;\n }\n\n /** Wire format. Backend parses `<prefix><id>:<kind>` from the message body. */\n getEncodedText(): string {\n return `${this.__prefix}${this.__id}:${this.__kind}`;\n }\n\n createDOM(): HTMLElement {\n const span = document.createElement(\"span\");\n span.setAttribute(TOKEN_DATA_ATTR, \"true\");\n return span;\n }\n\n updateDOM(): false {\n return false;\n }\n\n exportDOM(): DOMExportOutput {\n const element = document.createElement(\"span\");\n element.setAttribute(TOKEN_DATA_ATTR, \"true\");\n element.setAttribute(\"data-command-id\", this.__id);\n element.setAttribute(\"data-command-kind\", this.__kind);\n element.setAttribute(\"data-command-prefix\", this.__prefix);\n element.textContent = this.getEncodedText();\n return { element };\n }\n\n static importDOM(): DOMConversionMap | null {\n return {\n span: (domNode: HTMLElement) => {\n if (!domNode.hasAttribute(TOKEN_DATA_ATTR)) return null;\n return { conversion: convertCommandTokenElement, priority: 1 };\n },\n };\n }\n\n decorate(): ReactNode {\n return (\n <span contentEditable={false} style={{ userSelect: \"none\" }}>\n <CommandTokenView\n id={this.__id}\n kind={this.__kind}\n label={this.__label}\n variant=\"chip\"\n />\n </span>\n );\n }\n\n isInline(): boolean {\n return true;\n }\n\n isKeyboardSelectable(): boolean {\n return true;\n }\n\n /** Plain-text projection (clipboard, root.getTextContent, etc.). */\n getTextContent(): string {\n return this.getEncodedText();\n }\n}\n\nfunction convertCommandTokenElement(domNode: HTMLElement): DOMConversionOutput | null {\n const id = domNode.getAttribute(\"data-command-id\");\n const kind = domNode.getAttribute(\"data-command-kind\");\n if (!id || !kind) return null;\n const prefix = domNode.getAttribute(\"data-command-prefix\") ?? DEFAULT_PREFIX;\n const label =\n domNode.textContent?.replace(new RegExp(`^[\\\\${prefix}]`), \"\") ?? id;\n return { node: $createCommandTokenNode(id, kind, label, prefix) };\n}\n\nexport function $createCommandTokenNode(\n id: string,\n kind: string,\n label: string,\n prefix: string = DEFAULT_PREFIX,\n): CommandTokenNode {\n return new CommandTokenNode(id, kind, label, prefix);\n}\n\nexport function $isCommandTokenNode(\n node: LexicalNode | null | undefined,\n): node is CommandTokenNode {\n return node instanceof CommandTokenNode;\n}\n\n/**\n * Matches `<prefix><id>:<kind>` substrings emitted by `CommandTokenNode.getEncodedText()`,\n * where `<prefix>` is `/` (skill / mode chips) or `@` (ad-account chips).\n *\n * Bounded by start-of-string, whitespace, or an opening bracket on the left,\n * and end-of-string, whitespace, or punctuation on the right — so it doesn't\n * fire on URLs like `https://example.com/path:foo` or emails like\n * `user@host.com:port`.\n *\n * The id capture allows spaces (skill names may have them); the kind capture\n * allows word chars and hyphens (e.g. `ad-account`).\n */\nexport const COMMAND_TOKEN_REGEX =\n /(^|\\s|\\(|\\[)([/@])([^\\n:]+?):([\\w-]+)(?=$|[\\s.,!?)\\]])/g;\n\nexport interface InlineCommandTokenMatch {\n id: string;\n kind: string;\n /** Trigger character that introduced the chip (`/` or `@`). */\n prefix: string;\n /** The full matched substring including the leading prefix (no leading boundary). */\n raw: string;\n /** Index in the original string where the leading prefix sits. */\n index: number;\n /** End index (exclusive) of the matched token. */\n endIndex: number;\n}\n\n/**\n * Find all `<prefix><id>:<kind>` tokens in a plain-text string.\n *\n * Returns matches in document order. Use this when rendering message\n * history to replace token substrings with a visual chip.\n */\nexport function findInlineCommandTokens(text: string): InlineCommandTokenMatch[] {\n const matches: InlineCommandTokenMatch[] = [];\n COMMAND_TOKEN_REGEX.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = COMMAND_TOKEN_REGEX.exec(text)) !== null) {\n const leading = m[1] ?? \"\";\n const prefix = m[2];\n const id = m[3];\n const kind = m[4];\n const start = m.index + leading.length;\n const raw = m[0].slice(leading.length);\n matches.push({\n id,\n kind,\n prefix,\n raw,\n index: start,\n endIndex: start + raw.length,\n });\n }\n return matches;\n}\n","import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport {\n LexicalTypeaheadMenuPlugin,\n MenuOption,\n type MenuTextMatch,\n} from \"@lexical/react/LexicalTypeaheadMenuPlugin\";\nimport {\n $createTextNode,\n $getRoot,\n $isElementNode,\n $isTextNode,\n type ElementNode,\n type LexicalNode,\n type TextNode,\n} from \"lexical\";\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { $createCommandTokenNode, $isCommandTokenNode } from \"./CommandTokenNode\";\nimport { CommandTokenView } from \"./CommandTokenView\";\nimport type { ChatComposerCommand, CommandProvider } from \"./types\";\n\nconst DEFAULT_TRIGGER = \"/\";\nconst QUERY_LENGTH_LIMIT = 75;\n\nclass CommandTypeaheadOption extends MenuOption {\n command: ChatComposerCommand;\n\n constructor(command: ChatComposerCommand) {\n super(`${command.kind}:${command.id}`);\n this.command = command;\n }\n}\n\nfunction buildTriggerRegex(trigger: string): RegExp {\n // Escape the trigger char for use in a character class.\n const escaped = trigger.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n // Match: leading boundary (start, whitespace, or open-paren), the trigger,\n // then up to QUERY_LENGTH_LIMIT chars that are not whitespace.\n return new RegExp(\n `(^|\\\\s|\\\\()(${escaped}((?:[^${escaped}\\\\s]){0,${QUERY_LENGTH_LIMIT}}))$`,\n );\n}\n\nexport interface CommandTokenPluginProps {\n /** The data source for the typeahead. When null, the plugin is inert. */\n commandProvider?: CommandProvider | null;\n /**\n * Optional override for the typeahead UI. When supplied, replaces the\n * default Tailwind popover (use this to render an MUI/host-themed menu).\n */\n menuRenderer?: (params: {\n anchorElement: HTMLElement | null;\n options: ChatComposerCommand[];\n selectedIndex: number;\n setSelectedIndex: (index: number) => void;\n selectOption: (option: ChatComposerCommand) => void;\n closeMenu: () => void;\n }) => React.ReactNode;\n /**\n * Notified whenever the typeahead menu opens or closes. Use this to gate\n * keyboard handlers (e.g. don't submit on Enter while the menu is open).\n */\n onMenuStateChange?: (isOpen: boolean) => void;\n}\n\nexport function CommandTokenPlugin({\n commandProvider,\n menuRenderer,\n onMenuStateChange,\n}: CommandTokenPluginProps): React.JSX.Element | null {\n const [editor] = useLexicalComposerContext();\n const [queryString, setQueryString] = useState<string | null>(null);\n const [results, setResults] = useState<ChatComposerCommand[]>([]);\n\n const trigger = commandProvider?.trigger ?? DEFAULT_TRIGGER;\n const triggerRegex = useMemo(() => buildTriggerRegex(trigger), [trigger]);\n\n useEffect(() => {\n onMenuStateChange?.(queryString != null);\n }, [queryString, onMenuStateChange]);\n\n useEffect(() => {\n if (!commandProvider || queryString == null) {\n setResults([]);\n return;\n }\n let cancelled = false;\n Promise.resolve(commandProvider.fetch(queryString)).then((cmds) => {\n if (!cancelled) setResults(cmds);\n });\n return () => {\n cancelled = true;\n };\n }, [commandProvider, queryString]);\n\n const options = useMemo(\n () => results.map((cmd) => new CommandTypeaheadOption(cmd)),\n [results],\n );\n\n const checkForMatch = useCallback(\n (text: string): MenuTextMatch | null => {\n const match = triggerRegex.exec(text);\n if (!match) return null;\n const leading = match[1] ?? \"\";\n const matchingString = match[3] ?? \"\";\n return {\n leadOffset: match.index + leading.length,\n matchingString,\n replaceableString: match[2] ?? \"\",\n };\n },\n [triggerRegex],\n );\n\n const singletonKinds = commandProvider?.singletonKinds;\n\n const onSelectOption = useCallback(\n (\n selectedOption: CommandTypeaheadOption,\n nodeToReplace: TextNode | null,\n closeMenu: () => void,\n ) => {\n editor.update(() => {\n const kind = selectedOption.command.kind;\n const isSingleton = singletonKinds?.includes(kind) ?? false;\n\n if (isSingleton) {\n // Remove any existing chip of the same kind. Walking once with a\n // collected list (rather than mutating during traversal) keeps the\n // tree stable while we iterate.\n const stale: { node: ReturnType<typeof $createCommandTokenNode>; index: number }[] = [];\n const walk = (n: LexicalNode) => {\n if ($isCommandTokenNode(n) && n.getCommandKind() === kind) {\n stale.push({ node: n, index: 0 });\n return;\n }\n if ($isElementNode(n)) {\n for (const c of (n as ElementNode).getChildren()) walk(c);\n }\n };\n walk($getRoot());\n for (const entry of stale) {\n // If the chip is followed by a single space, drop that too so\n // we don't leave an orphan space where the chip used to sit.\n const next = entry.node.getNextSibling();\n entry.node.remove();\n if (next && $isTextNode(next)) {\n const nextText = next.getTextContent();\n if (nextText.startsWith(\" \")) {\n const trimmed = nextText.slice(1);\n if (trimmed.length === 0) next.remove();\n else next.setTextContent(trimmed);\n }\n }\n }\n }\n\n const tokenNode = $createCommandTokenNode(\n selectedOption.command.id,\n kind,\n selectedOption.command.label,\n trigger,\n );\n const trailingSpace = $createTextNode(\" \");\n if (nodeToReplace) {\n nodeToReplace.replace(tokenNode).insertAfter(trailingSpace);\n }\n trailingSpace.select();\n closeMenu();\n });\n },\n [editor, trigger, singletonKinds],\n );\n\n if (!commandProvider) return null;\n\n return (\n <LexicalTypeaheadMenuPlugin<CommandTypeaheadOption>\n onQueryChange={setQueryString}\n onSelectOption={onSelectOption}\n triggerFn={checkForMatch}\n options={options}\n menuRenderFn={(\n anchorElementRef,\n { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex },\n ) => {\n if (menuRenderer) {\n return (\n <>\n {menuRenderer({\n anchorElement: anchorElementRef.current,\n options: results,\n selectedIndex: selectedIndex ?? 0,\n setSelectedIndex: setHighlightedIndex,\n selectOption: (option) => {\n const match = options.find(\n (o) => o.command.id === option.id && o.command.kind === option.kind,\n );\n if (match) selectOptionAndCleanUp(match);\n },\n closeMenu: () => setQueryString(null),\n })}\n </>\n );\n }\n return (\n <DefaultCommandMenu\n anchorElement={anchorElementRef.current}\n options={options}\n selectedIndex={selectedIndex ?? 0}\n setHighlightedIndex={setHighlightedIndex}\n selectOptionAndCleanUp={selectOptionAndCleanUp}\n visible={results.length > 0 || queryString != null}\n queryString={queryString}\n />\n );\n }}\n />\n );\n}\n\nfunction DefaultCommandMenu({\n anchorElement,\n options,\n selectedIndex,\n setHighlightedIndex,\n selectOptionAndCleanUp,\n visible,\n queryString,\n}: {\n anchorElement: HTMLElement | null;\n options: CommandTypeaheadOption[];\n selectedIndex: number;\n setHighlightedIndex: (index: number) => void;\n selectOptionAndCleanUp: (option: CommandTypeaheadOption) => void;\n visible: boolean;\n queryString: string | null;\n}) {\n const menuRef = useRef<HTMLDivElement>(null);\n const [flipped, setFlipped] = useState(false);\n const VERTICAL_GAP = 6;\n\n useEffect(() => {\n const menu = menuRef.current;\n if (!menu || options.length === 0) return;\n const selectedEl = menu.querySelector<HTMLElement>(\n `#command-typeahead-item-${selectedIndex}`,\n );\n if (!selectedEl) return;\n const menuRect = menu.getBoundingClientRect();\n const itemRect = selectedEl.getBoundingClientRect();\n if (itemRect.top < menuRect.top) {\n menu.scrollTop -= menuRect.top - itemRect.top;\n } else if (itemRect.bottom > menuRect.bottom) {\n menu.scrollTop += itemRect.bottom - menuRect.bottom;\n }\n }, [selectedIndex, options.length]);\n\n useLayoutEffect(() => {\n if (!anchorElement || !visible || !menuRef.current) return;\n const reposition = () => {\n const menuEl = menuRef.current;\n if (!menuEl) return;\n const anchorRect = anchorElement.getBoundingClientRect();\n const menuHeight = menuEl.offsetHeight;\n const viewportHeight = window.innerHeight;\n // Lexical positions the anchor right below the cursor; anchorRect.top is\n // where the menu would render by default. anchorRect.height is the\n // cursor-line height (~20px), so the cursor's top is anchorRect.top - height.\n const spaceBelow = viewportHeight - anchorRect.top;\n const cursorTop = anchorRect.top - anchorRect.height;\n const spaceAbove = cursorTop;\n const wouldOverflowBottom = menuHeight + VERTICAL_GAP > spaceBelow;\n const fitsAbove = menuHeight + VERTICAL_GAP <= spaceAbove;\n setFlipped(wouldOverflowBottom && fitsAbove);\n };\n reposition();\n window.addEventListener(\"resize\", reposition);\n window.addEventListener(\"scroll\", reposition, true);\n return () => {\n window.removeEventListener(\"resize\", reposition);\n window.removeEventListener(\"scroll\", reposition, true);\n };\n }, [anchorElement, visible, options.length]);\n\n if (!anchorElement || !visible) return null;\n\n const anchorHeight = anchorElement.offsetHeight || 20;\n const transform = flipped\n ? `translateY(calc(-100% - ${anchorHeight + VERTICAL_GAP}px))`\n : \"none\";\n\n return createPortal(\n <div\n ref={menuRef}\n role=\"listbox\"\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n transform,\n zIndex: 99999,\n minWidth: 320,\n maxWidth: 520,\n maxHeight: 280,\n overflowY: \"auto\",\n borderRadius: 8,\n border: \"1px solid rgba(0,0,0,0.1)\",\n background: \"#ffffff\",\n boxShadow: \"0 8px 24px rgba(0,0,0,0.15)\",\n padding: 4,\n fontSize: 12,\n color: \"#1f2937\",\n }}\n >\n {options.length === 0 ? (\n <div style={{ padding: \"6px 10px\", fontSize: 11, color: \"#6b7280\" }}>\n No matching commands{queryString ? ` for \"${queryString}\"` : \"\"}\n </div>\n ) : (\n options.map((option, i) => {\n const isSelected = i === selectedIndex;\n const cmd = option.command;\n return (\n <CommandTokenView\n key={option.key}\n variant=\"row\"\n id={cmd.id}\n kind={cmd.kind}\n label={cmd.label}\n description={cmd.description}\n icon={cmd.icon}\n selected={isSelected}\n htmlId={`command-typeahead-item-${i}`}\n onMouseEnter={() => setHighlightedIndex(i)}\n onMouseDown={(e) => {\n e.preventDefault();\n e.stopPropagation();\n setHighlightedIndex(i);\n selectOptionAndCleanUp(option);\n }}\n />\n );\n })\n )}\n </div>,\n anchorElement,\n );\n}\n","import {\n $createLineBreakNode,\n $createParagraphNode,\n $createTextNode,\n $getRoot,\n type ParagraphNode,\n} from \"lexical\";\n\nimport { $createCommandTokenNode, findInlineCommandTokens } from \"./CommandTokenNode\";\n\nexport type ResolveTokenLabel = (id: string, kind: string) => string | undefined;\n\n/**\n * Rebuild editor content from the plain-text projection the editor itself\n * emits (`root.getTextContent()` / `ChatComposerSubmitPayload.text`).\n *\n * The exact inverse of Lexical's projection: paragraphs are joined with\n * `\"\\n\\n\"` and a `LineBreakNode` is `\"\\n\"`, so `\"\\n\\n\"` splits paragraphs and\n * `\"\\n\"` inside one becomes a line break. `<prefix><id>:<kind>` substrings\n * become chips again; `resolveLabel` supplies a display label for ids that\n * are opaque (ad accounts) — it falls back to the id.\n *\n * Must run inside `editor.update()` (or as `initialConfig.editorState`).\n */\nexport function $hydrateFromEncodedText(text: string, resolveLabel?: ResolveTokenLabel): void {\n const root = $getRoot();\n root.clear();\n const paragraphs = (text ?? \"\").split(\"\\n\\n\");\n for (const paragraphText of paragraphs) {\n const paragraph: ParagraphNode = $createParagraphNode();\n const lines = paragraphText.split(\"\\n\");\n lines.forEach((line, lineIndex) => {\n if (lineIndex > 0) paragraph.append($createLineBreakNode());\n let cursor = 0;\n for (const match of findInlineCommandTokens(line)) {\n if (match.index > cursor) {\n paragraph.append($createTextNode(line.slice(cursor, match.index)));\n }\n paragraph.append(\n $createCommandTokenNode(\n match.id,\n match.kind,\n resolveLabel?.(match.id, match.kind) ?? match.id,\n match.prefix,\n ),\n );\n cursor = match.endIndex;\n }\n if (cursor < line.length) {\n paragraph.append($createTextNode(line.slice(cursor)));\n }\n });\n root.append(paragraph);\n }\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Utility function to merge Tailwind CSS classes with proper precedence.\n * Combines clsx for conditional classes with tailwind-merge for conflict resolution.\n *\n * @example\n * cn(\"px-4 py-2\", \"px-6\") // => \"py-2 px-6\"\n * cn(\"text-red-500\", isActive && \"text-blue-500\") // => \"text-blue-500\" when isActive\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { LexicalComposer } from \"@lexical/react/LexicalComposer\";\nimport { LexicalComposerContext, useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport { ContentEditable } from \"@lexical/react/LexicalContentEditable\";\nimport { LexicalErrorBoundary } from \"@lexical/react/LexicalErrorBoundary\";\nimport { HistoryPlugin } from \"@lexical/react/LexicalHistoryPlugin\";\nimport { OnChangePlugin } from \"@lexical/react/LexicalOnChangePlugin\";\nimport { RichTextPlugin } from \"@lexical/react/LexicalRichTextPlugin\";\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport {\n $getRoot,\n $getSelection,\n $isElementNode,\n $isRangeSelection,\n CLEAR_HISTORY_COMMAND,\n COMMAND_PRIORITY_HIGH,\n KEY_ENTER_COMMAND,\n type EditorState,\n type LexicalEditor,\n type LexicalNode,\n} from \"lexical\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"../utils/cn\";\nimport { CommandTokenNode, $isCommandTokenNode } from \"./CommandTokenNode\";\nimport { CommandTokenPlugin } from \"./CommandTokenPlugin\";\nimport { $hydrateFromEncodedText, type ResolveTokenLabel } from \"./hydrate\";\nimport type {\n ChatComposerSubmitPayload,\n ChatComposerToken,\n CommandProvider,\n} from \"./types\";\n\nconst EDITOR_THEME = {\n paragraph: \"chat-composer-paragraph\",\n};\n\nexport interface LexicalChatInputHandle {\n /** Clear the editor contents and reset history. */\n clear: () => void;\n /** Programmatically submit the current editor contents. */\n submit: () => void;\n /** Focus the editor. */\n focus: () => void;\n /**\n * Insert text at the caret (or at the end when there is no selection) and\n * focus the editor. Used by toolbar buttons to type trigger characters\n * like \"/\" or \"@\" so the matching typeahead opens.\n */\n insertText: (text: string) => void;\n /**\n * Replace the editor contents with the plain-text projection of an earlier\n * payload (`/id:kind` substrings become chips again) and reset history.\n */\n setContent: (text: string) => void;\n}\n\nexport interface LexicalChatInputProps {\n placeholder?: string;\n disabled?: boolean;\n className?: string;\n /** Tailwind classes applied to the inner ContentEditable element. */\n inputClassName?: string;\n /** Tailwind classes applied to the placeholder overlay (e.g. font sizing). */\n placeholderClassName?: string;\n /** Slot for additional Lexical plugins (mounted inside LexicalComposer). */\n children?: ReactNode;\n /**\n * Called whenever the editor's plain-text projection changes. Use this to\n * gate a parent-rendered submit button on `text.trim().length > 0`.\n */\n onChange?: (payload: ChatComposerSubmitPayload) => void;\n /** Called on submit — Enter without Shift (when `submitOnEnter`) or `submit()`. */\n onSubmit?: (payload: ChatComposerSubmitPayload) => void | Promise<void>;\n /**\n * Whether Enter (without Shift) submits. Default true — the chat contract.\n * Set false when the editor is a form FIELD (a schedule's message, a saved\n * prompt): Enter then inserts a paragraph and Shift+Enter a line break, and\n * the owner reads the value from `onChange`.\n */\n submitOnEnter?: boolean;\n /**\n * Content to hydrate on mount, in the same plain-text projection the editor\n * emits (`payload.text`). Read ONCE — remount with a `key` to reset.\n */\n initialContent?: string;\n /** Display label for a rehydrated chip whose id is opaque; defaults to the id. */\n resolveTokenLabel?: ResolveTokenLabel;\n /**\n * Optional slash-command typeahead. When omitted, the editor behaves like\n * a plain rich-text input.\n */\n commandProvider?: CommandProvider | null;\n /**\n * Multiple typeahead providers, one per trigger character (e.g. `/` for\n * skills + modes, `@` for ad accounts). When supplied, this takes\n * precedence over the singular `commandProvider`. Each provider mounts its\n * own `CommandTokenPlugin` instance.\n */\n commandProviders?: CommandProvider[];\n}\n\nfunction readPayload(editor: LexicalEditor): ChatComposerSubmitPayload {\n let text = \"\";\n const tokens: ChatComposerToken[] = [];\n editor.getEditorState().read(() => {\n const root = $getRoot();\n text = root.getTextContent();\n const collect = (node: LexicalNode) => {\n if ($isCommandTokenNode(node)) {\n tokens.push({\n id: node.getCommandId(),\n kind: node.getCommandKind(),\n label: node.getCommandLabel(),\n });\n }\n if ($isElementNode(node)) {\n for (const child of node.getChildren()) collect(child);\n }\n };\n collect(root);\n });\n return { text, tokens };\n}\n\nexport const LexicalChatInput = forwardRef<LexicalChatInputHandle, LexicalChatInputProps>(\n function LexicalChatInput(\n {\n placeholder = \"Type a message...\",\n disabled = false,\n className,\n inputClassName,\n placeholderClassName,\n children,\n onChange,\n onSubmit,\n submitOnEnter = true,\n initialContent,\n resolveTokenLabel,\n commandProvider,\n commandProviders,\n },\n ref,\n ) {\n const initialConfig = useMemo(\n () => ({\n namespace: \"ChatComposer\",\n theme: EDITOR_THEME,\n nodes: [CommandTokenNode],\n editable: !disabled,\n // Runs once inside the editor's first update — the one place initial\n // content can be seeded without a flash of empty editor.\n editorState: initialContent\n ? () => $hydrateFromEncodedText(initialContent, resolveTokenLabel)\n : undefined,\n onError: (error: Error) => {\n console.error(\"[chat-ui composer]\", error);\n },\n }),\n // The initial config can't change after mount; `disabled` is applied via editor.setEditable below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n\n return (\n <LexicalComposer initialConfig={initialConfig}>\n <ChatInputBody\n placeholder={placeholder}\n disabled={disabled}\n className={className}\n inputClassName={inputClassName}\n placeholderClassName={placeholderClassName}\n onChange={onChange}\n onSubmit={onSubmit}\n submitOnEnter={submitOnEnter}\n resolveTokenLabel={resolveTokenLabel}\n commandProvider={commandProvider}\n commandProviders={commandProviders}\n imperativeHandle={ref}\n >\n {children}\n </ChatInputBody>\n </LexicalComposer>\n );\n },\n);\n\nfunction ChatInputBody({\n placeholder,\n disabled,\n className,\n inputClassName,\n placeholderClassName,\n children,\n onChange,\n onSubmit,\n submitOnEnter = true,\n resolveTokenLabel,\n commandProvider,\n commandProviders,\n imperativeHandle,\n}: LexicalChatInputProps & {\n imperativeHandle: React.ForwardedRef<LexicalChatInputHandle>;\n}) {\n const [editor] = useLexicalComposerContext();\n const prefersReducedMotion = useReducedMotion();\n const onSubmitRef = useRef(onSubmit);\n onSubmitRef.current = onSubmit;\n const commandMenuOpenRef = useRef(false);\n\n useEffect(() => {\n editor.setEditable(!disabled);\n }, [editor, disabled]);\n\n // Emits submit intent unconditionally — including on empty text. Whether a\n // message is actually sendable depends on state this component cannot see\n // (attachments, uploads in flight, streaming), so the owning composer makes\n // that call in its onSubmit. Gating on text here silently swallowed\n // attachment-only messages: the parent enabled its send button on\n // `hasText || files.length`, and the click reached a no-op.\n const submit = useCallback(() => {\n void onSubmitRef.current?.(readPayload(editor));\n }, [editor]);\n\n useImperativeHandle(\n imperativeHandle,\n () => ({\n clear: () => {\n editor.update(() => {\n const root = $getRoot();\n root.clear();\n });\n },\n submit,\n focus: () => editor.focus(),\n setContent: (text: string) => {\n editor.update(() => {\n $hydrateFromEncodedText(text, resolveTokenLabel);\n $getRoot().selectEnd();\n });\n editor.dispatchCommand(CLEAR_HISTORY_COMMAND, undefined);\n },\n insertText: (text: string) => {\n editor.focus(() => {\n editor.update(() => {\n let selection = $getSelection();\n if (!$isRangeSelection(selection)) {\n $getRoot().selectEnd();\n selection = $getSelection();\n }\n if ($isRangeSelection(selection)) {\n selection.insertText(text);\n }\n });\n });\n },\n }),\n [editor, submit, resolveTokenLabel],\n );\n\n // Intercept Enter (without Shift) and trigger submit, unless the\n // command-token typeahead menu is open — in which case the typeahead's\n // own LOW-priority handler takes the key to select an option. In field\n // mode (submitOnEnter=false) nothing is registered, so RichTextPlugin's\n // default Enter = new paragraph applies.\n useEffect(() => {\n if (!submitOnEnter) return;\n return editor.registerCommand(\n KEY_ENTER_COMMAND,\n (event) => {\n if (commandMenuOpenRef.current) return false;\n const keyboard = event as KeyboardEvent | null;\n if (keyboard?.shiftKey) return false;\n keyboard?.preventDefault();\n submit();\n return true;\n },\n COMMAND_PRIORITY_HIGH,\n );\n }, [editor, submit, submitOnEnter]);\n\n const handleCommandMenuStateChange = useCallback((isOpen: boolean) => {\n commandMenuOpenRef.current = isOpen;\n }, []);\n\n const handleChange = useCallback(\n (_state: EditorState) => {\n if (!onChange) return;\n const payload = readPayload(editor);\n onChange(payload);\n },\n [editor, onChange],\n );\n\n return (\n <div className={cn(\"relative w-full\", className)}>\n {/* The placeholder is absolutely positioned; this wrapper (not the padded\n outer div) is its containing block so it overlays the editable text\n exactly, regardless of caller padding. */}\n <div className=\"relative\">\n <RichTextPlugin\n contentEditable={\n <ContentEditable\n className={cn(\n \"outline-none w-full text-sm leading-relaxed\",\n \"min-h-[24px] max-h-[200px] overflow-y-auto\",\n \"text-gray-900 dark:text-zinc-100\",\n disabled && \"opacity-50 cursor-not-allowed\",\n inputClassName,\n )}\n />\n }\n placeholder={\n <div\n className={cn(\n \"pointer-events-none absolute inset-0 flex items-center overflow-hidden\",\n \"text-sm text-gray-400 dark:text-zinc-500 select-none\",\n placeholderClassName,\n )}\n >\n {prefersReducedMotion ? (\n <span className=\"truncate\">{placeholder}</span>\n ) : (\n <AnimatePresence mode=\"wait\" initial={false}>\n <motion.span\n key={placeholder}\n className=\"truncate\"\n initial={{ opacity: 0, y: 4 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -4 }}\n transition={{ duration: 0.18, ease: [0.4, 0, 0.2, 1] }}\n >\n {placeholder}\n </motion.span>\n </AnimatePresence>\n )}\n </div>\n }\n ErrorBoundary={LexicalErrorBoundary}\n />\n </div>\n <OnChangePlugin onChange={handleChange} />\n <HistoryPlugin />\n {(() => {\n // Plural takes precedence; fall back to singular for back-compat.\n // We mount one plugin per provider so different triggers (e.g. `/`\n // and `@`) can each fire their own typeahead. The menu-open ref is\n // shared so submit-on-Enter is gated whichever menu is open.\n const providers =\n commandProviders && commandProviders.length > 0\n ? commandProviders\n : commandProvider\n ? [commandProvider]\n : [];\n return providers.map((p, i) => (\n <CommandTokenPlugin\n key={`${p.trigger ?? \"/\"}-${i}`}\n commandProvider={p}\n onMenuStateChange={handleCommandMenuStateChange}\n />\n ));\n })()}\n {children}\n </div>\n );\n}\n\n// Re-export so consumers that mount their own LexicalComposer can grab the\n// editor instance for advanced wiring.\nexport { useLexicalComposerContext, LexicalComposerContext };\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {createContext,useCallback,useMemo,useContext}from'react';import {jsx}from'react/jsx-runtime';var t=createContext(null);function R({children:e,messages:n,isStreaming:o=false,streamingMessageId:a=null,viewerRole:i="user",onSendMessage:r,onStopStreaming:s,onRetryLastMessage:l,customData:d,onVisualizationAction:u,resolveCommandToken:C,isDarkSurface:c=false}){let g=useCallback(async(h,p)=>{await r(h,p);},[r]),v=useMemo(()=>({messages:n,isStreaming:o,streamingMessageId:a,viewerRole:i,sendMessage:g,stopStreaming:s,retryLastMessage:l,customData:d,onVisualizationAction:u,resolveCommandToken:C,isDarkSurface:c}),[n,o,a,i,g,s,l,d,u,C,c]);return jsx(t.Provider,{value:v,children:e})}function M(){let e=useContext(t);if(!e)throw new Error("useChatContext must be used within a ChatProvider");return e}export{t as a,R as b,M as c};//# sourceMappingURL=chunk-HNHLLH6W.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-HNHLLH6W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context/ChatProvider.tsx"],"names":["ChatContext","createContext","ChatProvider","children","messages","isStreaming","streamingMessageId","viewerRole","onSendMessage","onStopStreaming","onRetryLastMessage","customData","onVisualizationAction","resolveCommandToken","isDarkSurface","sendMessage","useCallback","content","attachments","value","useMemo","jsx","useChatContext","context","useContext"],"mappings":"yGA6CMA,CAAAA,CAAcC,aAAAA,CAAuC,IAAI,EAoBxD,SAASC,CAAAA,CAAa,CAC3B,QAAA,CAAAC,EACA,QAAA,CAAAC,CAAAA,CACA,WAAA,CAAAC,CAAAA,CAAc,KAAA,CACd,kBAAA,CAAAC,CAAAA,CAAqB,IAAA,CACrB,WAAAC,CAAAA,CAAa,MAAA,CACb,aAAA,CAAAC,CAAAA,CACA,eAAA,CAAAC,CAAAA,CACA,kBAAA,CAAAC,CAAAA,CACA,UAAA,CAAAC,CAAAA,CACA,qBAAA,CAAAC,CAAAA,CACA,mBAAA,CAAAC,CAAAA,CACA,aAAA,CAAAC,CAAAA,CAAgB,KAClB,CAAA,CAAsB,CACpB,IAAMC,CAAAA,CAAcC,WAAAA,CAClB,MAAOC,CAAAA,CAAiBC,CAAAA,GAAyB,CAC/C,MAAMV,CAAAA,CAAcS,CAAAA,CAASC,CAAW,EAC1C,CAAA,CACA,CAACV,CAAa,CAChB,CAAA,CAEMW,CAAAA,CAAQC,OAAAA,CACZ,KAAO,CACL,QAAA,CAAAhB,CAAAA,CACA,WAAA,CAAAC,CAAAA,CACA,kBAAA,CAAAC,CAAAA,CACA,UAAA,CAAAC,CAAAA,CACA,WAAA,CAAAQ,CAAAA,CACA,aAAA,CAAeN,EACf,gBAAA,CAAkBC,CAAAA,CAClB,UAAA,CAAAC,CAAAA,CACA,qBAAA,CAAAC,CAAAA,CACA,mBAAA,CAAAC,CAAAA,CACA,cAAAC,CACF,CAAA,CAAA,CACA,CACEV,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAQ,CAAAA,CACAN,EACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CACF,CACF,CAAA,CAEA,OAAOO,GAAAA,CAACrB,CAAAA,CAAY,QAAA,CAAZ,CAAqB,KAAA,CAAOmB,CAAAA,CAAQ,QAAA,CAAAhB,CAAAA,CAAS,CACvD,CAEO,SAASmB,CAAAA,EAAiB,CAC/B,IAAMC,CAAAA,CAAUC,UAAAA,CAAWxB,CAAW,EACtC,GAAI,CAACuB,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,mDAAmD,CAAA,CAErE,OAAOA,CACT","file":"chunk-HNHLLH6W.js","sourcesContent":["import {\n createContext,\n useContext,\n useCallback,\n useMemo,\n type ReactNode,\n} from \"react\";\nimport type { ChatMessage, ParticipantRole, VisualizationActionEvent } from \"../types\";\n\nexport interface ChatContextValue {\n /** List of messages in the conversation */\n messages: ChatMessage[];\n /** Whether a message is currently being streamed */\n isStreaming: boolean;\n /** ID of the message currently being streamed */\n streamingMessageId: string | null;\n /** The viewer's role (determines message alignment) */\n viewerRole: ParticipantRole;\n /** Send a new message */\n sendMessage: (content: string, attachments?: File[]) => Promise<void>;\n /** Stop the current streaming response */\n stopStreaming?: () => void;\n /** Retry the last failed message */\n retryLastMessage?: () => Promise<void>;\n /** Custom data passed through context */\n customData?: Record<string, unknown>;\n /** Callback when user interacts with a visualization (form submit, card action, etc.) */\n onVisualizationAction?: (event: VisualizationActionEvent) => void;\n /** Resolve how to render an inline command-token chip (e.g. an\n * `@<id>:ad-account` mention). Wire format only carries id + kind, so the\n * host app supplies the display info. Returning `tag` replaces the default\n * uppercase kind pill (e.g. a platform logo). Returning `label` overrides\n * the id text. */\n resolveCommandToken?: (\n id: string,\n kind: string,\n ) => { label?: string; tag?: ReactNode } | undefined;\n /** Whether the surface hosting the chat is dark. Drives choices that CSS\n * variables can't express, such as which syntax-highlighting theme a code\n * block uses. The host app must supply this: chat-ui is themed through\n * `--chat-*` variables and the `.dark` class is NOT applied by every\n * consumer, so neither is a reliable signal. Defaults to false (light). */\n isDarkSurface: boolean;\n}\n\nconst ChatContext = createContext<ChatContextValue | null>(null);\n\nexport interface ChatProviderProps {\n children: ReactNode;\n messages: ChatMessage[];\n isStreaming?: boolean;\n streamingMessageId?: string | null;\n viewerRole?: ParticipantRole;\n onSendMessage: (content: string, attachments?: File[]) => Promise<void>;\n onStopStreaming?: () => void;\n onRetryLastMessage?: () => Promise<void>;\n customData?: Record<string, unknown>;\n onVisualizationAction?: (event: VisualizationActionEvent) => void;\n resolveCommandToken?: (\n id: string,\n kind: string,\n ) => { label?: string; tag?: ReactNode } | undefined;\n isDarkSurface?: boolean;\n}\n\nexport function ChatProvider({\n children,\n messages,\n isStreaming = false,\n streamingMessageId = null,\n viewerRole = \"user\",\n onSendMessage,\n onStopStreaming,\n onRetryLastMessage,\n customData,\n onVisualizationAction,\n resolveCommandToken,\n isDarkSurface = false,\n}: ChatProviderProps) {\n const sendMessage = useCallback(\n async (content: string, attachments?: File[]) => {\n await onSendMessage(content, attachments);\n },\n [onSendMessage]\n );\n\n const value = useMemo<ChatContextValue>(\n () => ({\n messages,\n isStreaming,\n streamingMessageId,\n viewerRole,\n sendMessage,\n stopStreaming: onStopStreaming,\n retryLastMessage: onRetryLastMessage,\n customData,\n onVisualizationAction,\n resolveCommandToken,\n isDarkSurface,\n }),\n [\n messages,\n isStreaming,\n streamingMessageId,\n viewerRole,\n sendMessage,\n onStopStreaming,\n onRetryLastMessage,\n customData,\n onVisualizationAction,\n resolveCommandToken,\n isDarkSurface,\n ]\n );\n\n return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;\n}\n\nexport function useChatContext() {\n const context = useContext(ChatContext);\n if (!context) {\n throw new Error(\"useChatContext must be used within a ChatProvider\");\n }\n return context;\n}\n\nexport { ChatContext };\n"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {useState,useRef,useCallback}from'react';function b(r={}){let[c,i]=useState({isStreaming:false,streamingMessageId:null,streamedContent:""}),n=useRef(null),m=useCallback(e=>{i({isStreaming:true,streamingMessageId:e,streamedContent:""}),n.current=new AbortController;},[]),
|
|
2
|
-
//# sourceMappingURL=chunk-
|
|
1
|
+
import {useState,useRef,useCallback}from'react';function b(r={}){let[c,i]=useState({isStreaming:false,streamingMessageId:null,streamedContent:""}),n=useRef(null),m=useCallback(e=>{i({isStreaming:true,streamingMessageId:e,streamedContent:""}),n.current=new AbortController;},[]),d=useCallback(e=>{e.type==="content"&&e.content&&i(u=>({...u,streamedContent:u.streamedContent+e.content})),r.onChunk?.(e);},[r]),p=useCallback(()=>{n.current?.abort();let e=c.streamedContent;i({isStreaming:false,streamingMessageId:null,streamedContent:""}),r.onComplete?.(e);},[c.streamedContent,r]),f=useCallback(e=>{n.current?.abort(),i({isStreaming:false,streamingMessageId:null,streamedContent:""}),r.onError?.(e);},[r]);return {...c,startStreaming:m,appendContent:d,stopStreaming:p,handleError:f,abortSignal:n.current?.signal}}function x({maxCount:r=5,maxFileSize:c=10*1024*1024,allowedTypes:i}={}){let[n,m]=useState([]),d=useCallback(t=>{if(n.length>=r)return {success:false,error:`Maximum ${r} attachments allowed`};if(t.size>c)return {success:false,error:`File size exceeds ${Math.round(c/1024/1024)}MB limit`};if(i&&!i.includes(t.type))return {success:false,error:`File type ${t.type} not allowed`};let o=`${Date.now()}-${Math.random().toString(36).slice(2)}`;return m(a=>[...a,{file:t,id:o,progress:0,status:"pending"}]),{success:true}},[n.length,r,c,i]),p=useCallback(t=>{m(o=>o.filter(a=>a.id!==t));},[]),f=useCallback((t,o)=>{m(a=>a.map(s=>s.id===t?{...s,progress:o,status:o<100?"uploading":"complete"}:s));},[]),e=useCallback((t,o)=>{m(a=>a.map(s=>s.id===t?{...s,status:"error",error:o}:s));},[]),u=useCallback((t,o)=>{m(a=>a.map(s=>s.id===t?{...s,url:o,status:"complete",progress:100}:s));},[]),S=useCallback(()=>{m([]);},[]),C=useCallback(()=>n.map(t=>t.file),[n]);return {attachments:n,addAttachment:d,removeAttachment:p,updateProgress:f,setError:e,setUrl:u,clear:S,getFiles:C,canAddMore:n.length<r}}export{b as a,x as b};//# sourceMappingURL=chunk-ISMTV5BU.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-ISMTV5BU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/use-streaming.ts","../src/hooks/use-attachments.ts"],"names":["useStreaming","options","state","setState","useState","abortControllerRef","useRef","startStreaming","useCallback","messageId","appendContent","chunk","prev","stopStreaming","finalContent","handleError","error","useAttachments","maxCount","maxFileSize","allowedTypes","attachments","setAttachments","addAttachment","file","id","removeAttachment","updateProgress","progress","a","setError","setUrl","url","clear","getFiles"],"mappings":"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","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-streaming.ts","../src/hooks/use-attachments.ts"],"names":["useStreaming","options","state","setState","useState","abortControllerRef","useRef","startStreaming","useCallback","messageId","appendContent","chunk","prev","stopStreaming","finalContent","handleError","error","useAttachments","maxCount","maxFileSize","allowedTypes","attachments","setAttachments","addAttachment","file","id","removeAttachment","updateProgress","progress","a","setError","setUrl","url","clear","getFiles"],"mappings":"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","file":"chunk-ISMTV5BU.js","sourcesContent":["import { useState, useCallback, useRef } from \"react\";\nimport type { StreamingState, StreamChunk, StreamingOptions } from \"../types\";\n\n/**\n * Hook to manage streaming message state.\n */\nexport function useStreaming(options: StreamingOptions = {}) {\n const [state, setState] = useState<StreamingState>({\n isStreaming: false,\n streamingMessageId: null,\n streamedContent: \"\",\n });\n\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const startStreaming = useCallback((messageId: string) => {\n setState({\n isStreaming: true,\n streamingMessageId: messageId,\n streamedContent: \"\",\n });\n abortControllerRef.current = new AbortController();\n }, []);\n\n const appendContent = useCallback(\n (chunk: StreamChunk) => {\n if (chunk.type === \"content\" && chunk.content) {\n setState((prev) => ({\n ...prev,\n streamedContent: prev.streamedContent + chunk.content,\n }));\n }\n options.onChunk?.(chunk);\n },\n [options]\n );\n\n const stopStreaming = useCallback(() => {\n abortControllerRef.current?.abort();\n const finalContent = state.streamedContent;\n\n setState({\n isStreaming: false,\n streamingMessageId: null,\n streamedContent: \"\",\n });\n\n options.onComplete?.(finalContent);\n }, [state.streamedContent, options]);\n\n const handleError = useCallback(\n (error: Error) => {\n abortControllerRef.current?.abort();\n setState({\n isStreaming: false,\n streamingMessageId: null,\n streamedContent: \"\",\n });\n options.onError?.(error);\n },\n [options]\n );\n\n return {\n ...state,\n startStreaming,\n appendContent,\n stopStreaming,\n handleError,\n abortSignal: abortControllerRef.current?.signal,\n };\n}\n","import { useState, useCallback } from \"react\";\n\ninterface AttachmentState {\n file: File;\n id: string;\n progress: number;\n status: \"pending\" | \"uploading\" | \"complete\" | \"error\";\n error?: string;\n url?: string;\n}\n\ninterface UseAttachmentsOptions {\n /** Maximum number of attachments */\n maxCount?: number;\n /** Maximum file size in bytes */\n maxFileSize?: number;\n /** Allowed MIME types */\n allowedTypes?: string[];\n}\n\n/**\n * Hook to manage file attachments with upload progress.\n */\nexport function useAttachments({\n maxCount = 5,\n maxFileSize = 10 * 1024 * 1024,\n allowedTypes,\n}: UseAttachmentsOptions = {}) {\n const [attachments, setAttachments] = useState<AttachmentState[]>([]);\n\n const addAttachment = useCallback(\n (file: File): { success: boolean; error?: string } => {\n // Check count\n if (attachments.length >= maxCount) {\n return { success: false, error: `Maximum ${maxCount} attachments allowed` };\n }\n\n // Check size\n if (file.size > maxFileSize) {\n return {\n success: false,\n error: `File size exceeds ${Math.round(maxFileSize / 1024 / 1024)}MB limit`,\n };\n }\n\n // Check type\n if (allowedTypes && !allowedTypes.includes(file.type)) {\n return { success: false, error: `File type ${file.type} not allowed` };\n }\n\n const id = `${Date.now()}-${Math.random().toString(36).slice(2)}`;\n\n setAttachments((prev) => [\n ...prev,\n {\n file,\n id,\n progress: 0,\n status: \"pending\",\n },\n ]);\n\n return { success: true };\n },\n [attachments.length, maxCount, maxFileSize, allowedTypes]\n );\n\n const removeAttachment = useCallback((id: string) => {\n setAttachments((prev) => prev.filter((a) => a.id !== id));\n }, []);\n\n const updateProgress = useCallback((id: string, progress: number) => {\n setAttachments((prev) =>\n prev.map((a) =>\n a.id === id\n ? { ...a, progress, status: progress < 100 ? \"uploading\" : \"complete\" }\n : a\n )\n );\n }, []);\n\n const setError = useCallback((id: string, error: string) => {\n setAttachments((prev) =>\n prev.map((a) => (a.id === id ? { ...a, status: \"error\", error } : a))\n );\n }, []);\n\n const setUrl = useCallback((id: string, url: string) => {\n setAttachments((prev) =>\n prev.map((a) => (a.id === id ? { ...a, url, status: \"complete\", progress: 100 } : a))\n );\n }, []);\n\n const clear = useCallback(() => {\n setAttachments([]);\n }, []);\n\n const getFiles = useCallback(() => {\n return attachments.map((a) => a.file);\n }, [attachments]);\n\n return {\n attachments,\n addAttachment,\n removeAttachment,\n updateProgress,\n setError,\n setUrl,\n clear,\n getFiles,\n canAddMore: attachments.length < maxCount,\n };\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {useRef,useState,useCallback,useEffect}from'react';function w({enabled:r=true,smooth:f=true,scrollToBottomOnMount:d=true}={}){let s=useRef(null),[g,m]=useState(true),h=useRef(0),n=useRef(null),c=useCallback(t=>{let e=s.current;if(!e)return;let i=t??(f?"smooth":"instant");n.current=i,e.scrollTo({top:e.scrollHeight,behavior:i});},[f]),l=useCallback(t=>Math.abs(t.scrollHeight-t.scrollTop-t.clientHeight)<1||t.scrollHeight<=t.clientHeight,[]),u=useCallback(()=>{let t=s.current;if(!t)return;let e=l(t);!e&&h.current<t.scrollTop||(e&&(n.current=null),(e||n.current===null)&&m(e)),h.current=t.scrollTop;},[l]),p=useCallback(()=>{let t=n.current;if(t)c(t);else if(r){let e=s.current;e&&l(e)&&c("instant");}u();},[r,c,l,u]);return useEffect(()=>{let t=s.current;if(!t||!r)return;let e=new ResizeObserver(()=>{p();});return e.observe(t),()=>e.disconnect()},[r,p]),useEffect(()=>{let t=s.current;if(!t||!r)return;let e=new MutationObserver(i=>{i.some(T=>T.type!=="attributes"||T.attributeName!=="style")&&p();});return e.observe(t,{childList:true,subtree:true,attributes:true,characterData:true}),()=>e.disconnect()},[r,p]),useEffect(()=>{let t=s.current;if(t)return t.addEventListener("scroll",u,{passive:true}),()=>t.removeEventListener("scroll",u)},[u]),useEffect(()=>{r&&d&&(n.current="instant",requestAnimationFrame(()=>{c("instant");}));},[]),{containerRef:s,scrollToBottom:c,isAtBottom:g}}function K({onSubmit:r,disabled:f=false,isStreaming:d=false,maxFileSize:s=10*1024*1024,allowedFileTypes:g}){let[m,h]=useState(""),[n,c]=useState([]),[l,u]=useState(false),[p,t]=useState(null),e=useRef(null),i=useRef(false),M=useCallback(o=>{h(o),t(null);},[]),T=useCallback(o=>{let b=Array.from(o),B=[];for(let a of b){if(a.size>s){t(`File "${a.name}" exceeds maximum size`);continue}if(g&&!g.includes(a.type)){t(`File type "${a.type}" is not allowed`);continue}B.push(a);}c(a=>[...a,...B]);},[s,g]),C=useCallback(o=>{c(b=>b.filter((B,a)=>a!==o));},[]),y=useCallback(async()=>{let o=m.trim();if(!(!o&&n.length===0||f||d||l||i.current)){i.current=true,u(true),t(null);try{await r(o,n.length>0?n:void 0),h(""),c([]),e.current?.focus();}catch(b){t(b instanceof Error?b.message:"Failed to send message");}finally{u(false),i.current=false;}}},[m,n,f,d,l,r]),H=useCallback(o=>{o.key==="Enter"&&!o.shiftKey&&(o.preventDefault(),y());},[y]),O=useCallback(()=>{h(""),c([]),t(null);},[]),F=(m.trim()||n.length>0)&&!f&&!d&&!l;return {content:m,attachments:n,isSubmitting:l,error:p,canSubmit:F,inputRef:e,handleContentChange:M,handleAddAttachment:T,handleRemoveAttachment:C,handleSubmit:y,handleKeyDown:H,clear:O}}export{w as a,K as b};//# sourceMappingURL=chunk-LDTAZ4GT.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-LDTAZ4GT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-auto-scroll.ts","../src/hooks/use-message-composer.ts"],"names":["useAutoScroll","enabled","smooth","scrollToBottomOnMount","containerRef","useRef","isAtBottom","setIsAtBottom","useState","lastScrollTopRef","scrollingToBottomBehaviorRef","scrollToBottom","useCallback","behavior","container","b","computeIsAtBottom","el","handleScroll","newIsAtBottom","handleContentResize","scrollBehavior","useEffect","resizeObserver","mutationObserver","mutations","m","useMessageComposer","onSubmit","disabled","isStreaming","maxFileSize","allowedFileTypes","content","setContent","attachments","setAttachments","isSubmitting","setIsSubmitting","error","setError","inputRef","isSubmittingRef","handleContentChange","value","handleAddAttachment","files","fileArray","validFiles","file","prev","handleRemoveAttachment","index","_","i","handleSubmit","trimmedContent","err","handleKeyDown","e","clear","canSubmit"],"mappings":"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","file":"chunk-LDTAZ4GT.js","sourcesContent":["import { useEffect, useRef, useCallback, useState } from \"react\";\n\nexport interface UseAutoScrollOptions {\n /** Whether auto-scroll is enabled */\n enabled?: boolean;\n /** Smooth scroll behavior */\n smooth?: boolean;\n /** Scroll to bottom when component mounts */\n scrollToBottomOnMount?: boolean;\n}\n\nexport interface UseAutoScrollReturn<T extends HTMLElement> {\n /** Ref to attach to the scrollable container */\n containerRef: React.RefObject<T | null>;\n /** Scroll to bottom programmatically (always forces scroll) */\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n /** Whether the viewport is currently at the bottom */\n isAtBottom: boolean;\n}\n\n/**\n * Hook to automatically scroll to the bottom of a container\n * when new content is added, unless the user has scrolled up.\n *\n * Combines ResizeObserver + MutationObserver to catch all height changes\n * (image loads, panel expansion, streaming text, new messages).\n *\n * Uses a 1px tolerance for isAtBottom (matches assistant-ui) and\n * persists scroll behavior across content resize to prevent\n * the \"content outraces scroll\" bug.\n */\nexport function useAutoScroll<T extends HTMLElement>({\n enabled = true,\n smooth = true,\n scrollToBottomOnMount = true,\n}: UseAutoScrollOptions = {}): UseAutoScrollReturn<T> {\n const containerRef = useRef<T>(null);\n const [isAtBottom, setIsAtBottom] = useState(true);\n const lastScrollTopRef = useRef(0);\n\n // When scrollToBottom() is called, store the behavior.\n // On subsequent resize/mutation callbacks, re-scroll with the same behavior\n // until the bottom is actually reached. This prevents the \"content outraces\n // scroll\" bug where new content pushes the bottom away faster than smooth\n // scrolling can reach it.\n const scrollingToBottomBehaviorRef = useRef<ScrollBehavior | null>(null);\n\n const scrollToBottom = useCallback(\n (behavior?: ScrollBehavior) => {\n const container = containerRef.current;\n if (!container) return;\n\n const b = behavior ?? (smooth ? \"smooth\" : \"instant\");\n scrollingToBottomBehaviorRef.current = b;\n container.scrollTo({ top: container.scrollHeight, behavior: b });\n },\n [smooth],\n );\n\n // Determine if we're at the bottom (1px tolerance, or content fits without scroll)\n const computeIsAtBottom = useCallback((el: HTMLElement): boolean => {\n return (\n Math.abs(el.scrollHeight - el.scrollTop - el.clientHeight) < 1 ||\n el.scrollHeight <= el.clientHeight\n );\n }, []);\n\n // Scroll event handler — updates isAtBottom state\n const handleScroll = useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const newIsAtBottom = computeIsAtBottom(container);\n\n // If user is scrolling UP while we're in the middle of a scrollToBottom,\n // don't flip isAtBottom to false (ignore scroll-down momentum).\n if (!newIsAtBottom && lastScrollTopRef.current < container.scrollTop) {\n // Scroll is moving down — likely from our scrollToBottom call, ignore\n } else {\n if (newIsAtBottom) {\n scrollingToBottomBehaviorRef.current = null;\n }\n\n // Only update state when behavior ref is cleared (i.e. not mid-scroll)\n // or when we actually reached the bottom\n const shouldUpdate =\n newIsAtBottom || scrollingToBottomBehaviorRef.current === null;\n\n if (shouldUpdate) {\n setIsAtBottom(newIsAtBottom);\n }\n }\n\n lastScrollTopRef.current = container.scrollTop;\n }, [computeIsAtBottom]);\n\n // Content resize handler — called by both ResizeObserver and MutationObserver\n const handleContentResize = useCallback(() => {\n const scrollBehavior = scrollingToBottomBehaviorRef.current;\n if (scrollBehavior) {\n // We're in the middle of a scrollToBottom — keep chasing the bottom\n scrollToBottom(scrollBehavior);\n } else if (enabled) {\n // Auto-scroll only if already at bottom\n const container = containerRef.current;\n if (container && computeIsAtBottom(container)) {\n scrollToBottom(\"instant\");\n }\n }\n\n handleScroll();\n }, [enabled, scrollToBottom, computeIsAtBottom, handleScroll]);\n\n // ResizeObserver — catches height changes from image loads, layout shifts, etc.\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !enabled) return;\n\n const resizeObserver = new ResizeObserver(() => {\n handleContentResize();\n });\n\n resizeObserver.observe(container);\n return () => resizeObserver.disconnect();\n }, [enabled, handleContentResize]);\n\n // MutationObserver — catches new messages, streaming text, DOM changes\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !enabled) return;\n\n const mutationObserver = new MutationObserver((mutations) => {\n // Filter out style-only attribute mutations to prevent feedback loops\n // (e.g. components that write styles in response to viewport changes)\n const hasRelevantMutation = mutations.some(\n (m) => m.type !== \"attributes\" || m.attributeName !== \"style\",\n );\n if (hasRelevantMutation) {\n handleContentResize();\n }\n });\n\n mutationObserver.observe(container, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n return () => mutationObserver.disconnect();\n }, [enabled, handleContentResize]);\n\n // Scroll listener\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n container.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => container.removeEventListener(\"scroll\", handleScroll);\n }, [handleScroll]);\n\n // Scroll to bottom on mount\n useEffect(() => {\n if (enabled && scrollToBottomOnMount) {\n // Use instant on mount — no need for smooth animation on initial load\n scrollingToBottomBehaviorRef.current = \"instant\";\n requestAnimationFrame(() => {\n scrollToBottom(\"instant\");\n });\n }\n // Only run on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return {\n containerRef,\n scrollToBottom,\n isAtBottom,\n };\n}\n","import { useState, useCallback, useRef, type KeyboardEvent } from \"react\";\n\ninterface UseMessageComposerOptions {\n /**\n * Callback when message is submitted. MUST resolve once the message has been\n * accepted, not when the assistant has finished responding — the submit lock\n * below is held for the duration of this promise, so a host that keeps it\n * open for a whole turn locks the composer for every conversation it is\n * reused for.\n */\n onSubmit: (content: string, attachments?: File[]) => Promise<void>;\n /** Whether submission is disabled */\n disabled?: boolean;\n /** Whether the current conversation is mid-response. Blocks submitting a\n * second, concurrent turn into the same conversation. */\n isStreaming?: boolean;\n /** Maximum file size in bytes */\n maxFileSize?: number;\n /** Allowed file types (MIME types) */\n allowedFileTypes?: string[];\n}\n\n/**\n * Hook to manage message composer state and behavior.\n */\nexport function useMessageComposer({\n onSubmit,\n disabled = false,\n isStreaming = false,\n maxFileSize = 10 * 1024 * 1024, // 10MB\n allowedFileTypes,\n}: UseMessageComposerOptions) {\n const [content, setContent] = useState(\"\");\n const [attachments, setAttachments] = useState<File[]>([]);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const inputRef = useRef<HTMLTextAreaElement>(null);\n const isSubmittingRef = useRef(false);\n\n const handleContentChange = useCallback((value: string) => {\n setContent(value);\n setError(null);\n }, []);\n\n const handleAddAttachment = useCallback(\n (files: FileList | File[]) => {\n const fileArray = Array.from(files);\n const validFiles: File[] = [];\n\n for (const file of fileArray) {\n // Check file size\n if (file.size > maxFileSize) {\n setError(`File \"${file.name}\" exceeds maximum size`);\n continue;\n }\n\n // Check file type\n if (allowedFileTypes && !allowedFileTypes.includes(file.type)) {\n setError(`File type \"${file.type}\" is not allowed`);\n continue;\n }\n\n validFiles.push(file);\n }\n\n setAttachments((prev) => [...prev, ...validFiles]);\n },\n [maxFileSize, allowedFileTypes]\n );\n\n const handleRemoveAttachment = useCallback((index: number) => {\n setAttachments((prev) => prev.filter((_, i) => i !== index));\n }, []);\n\n const handleSubmit = useCallback(async () => {\n const trimmedContent = content.trim();\n if ((!trimmedContent && attachments.length === 0) || disabled || isStreaming || isSubmitting || isSubmittingRef.current) {\n return;\n }\n\n // Lock immediately to prevent double-send race condition\n isSubmittingRef.current = true;\n setIsSubmitting(true);\n setError(null);\n\n try {\n await onSubmit(trimmedContent, attachments.length > 0 ? attachments : undefined);\n setContent(\"\");\n setAttachments([]);\n inputRef.current?.focus();\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to send message\");\n } finally {\n setIsSubmitting(false);\n isSubmittingRef.current = false;\n }\n }, [content, attachments, disabled, isStreaming, isSubmitting, onSubmit]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>) => {\n // Submit on Enter without Shift\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n },\n [handleSubmit]\n );\n\n const clear = useCallback(() => {\n setContent(\"\");\n setAttachments([]);\n setError(null);\n }, []);\n\n const canSubmit =\n (content.trim() || attachments.length > 0) && !disabled && !isStreaming && !isSubmitting;\n\n return {\n content,\n attachments,\n isSubmitting,\n error,\n canSubmit,\n inputRef,\n handleContentChange,\n handleAddAttachment,\n handleRemoveAttachment,\n handleSubmit,\n handleKeyDown,\n clear,\n };\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {useRef,useEffect,useCallback,useState}from'react';function L(l,o){let r=useRef(null),t=useRef(null);return useEffect(()=>()=>{t.current?.();},[]),useCallback(()=>{if(t.current?.(),!r.current&&l.current){let n=l.current;for(;n;){let{overflowY:u}=getComputedStyle(n);if(u==="scroll"||u==="auto"){r.current=n;break}n=n.parentElement;}}let e=r.current;if(!e)return;let d=e.scrollTop,s=e.style.scrollbarWidth;e.style.scrollbarWidth="none";let c=()=>{e.scrollTop=d;};e.addEventListener("scroll",c);let f=setTimeout(()=>{e.removeEventListener("scroll",c),e.style.scrollbarWidth=s,t.current=null;},o);t.current=()=>{clearTimeout(f),e.removeEventListener("scroll",c),e.style.scrollbarWidth=s;};},[o,l])}var h="(prefers-reduced-motion: reduce)";function y(){let[l,o]=useState(false);return useEffect(()=>{if(typeof window>"u"||!window.matchMedia)return;let r=window.matchMedia(h),t=()=>o(r.matches);return t(),r.addEventListener("change",t),()=>r.removeEventListener("change",t)},[]),l}export{L as a,y as b};//# sourceMappingURL=chunk-OU5ITGKX.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-OU5ITGKX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-scroll-lock.ts","../src/hooks/use-reduced-motion.ts"],"names":["useScrollLock","animatedElementRef","animationDuration","scrollContainerRef","useRef","cleanupRef","useEffect","useCallback","el","overflowY","scrollContainer","scrollPosition","prevScrollbarWidth","resetPosition","timeoutId","QUERY","usePrefersReducedMotion","reduced","setReduced","useState","mq","apply"],"mappings":"0DA0BO,SAASA,CAAAA,CACdC,CAAAA,CACAC,CAAAA,CACA,CACA,IAAMC,CAAAA,CAAqBC,MAAAA,CAA2B,IAAI,CAAA,CACpDC,CAAAA,CAAaD,OAA4B,IAAI,CAAA,CAGnD,OAAAE,SAAAA,CAAU,IACD,IAAM,CACXD,CAAAA,CAAW,OAAA,KACb,CAAA,CACC,EAAE,EAEcE,WAAAA,CAAY,IAAM,CAKnC,GAHAF,CAAAA,CAAW,OAAA,KAGP,CAACF,CAAAA,CAAmB,OAAA,EAAWF,CAAAA,CAAmB,OAAA,CAAS,CAC7D,IAAIO,CAAAA,CAAyBP,CAAAA,CAAmB,OAAA,CAChD,KAAOO,CAAAA,EAAI,CACT,GAAM,CAAE,SAAA,CAAAC,CAAU,CAAA,CAAI,gBAAA,CAAiBD,CAAE,CAAA,CACzC,GAAIC,CAAAA,GAAc,QAAA,EAAYA,CAAAA,GAAc,MAAA,CAAQ,CAClDN,CAAAA,CAAmB,OAAA,CAAUK,CAAAA,CAC7B,KACF,CACAA,CAAAA,CAAKA,EAAG,cACV,CACF,CAEA,IAAME,CAAAA,CAAkBP,CAAAA,CAAmB,QAC3C,GAAI,CAACO,CAAAA,CAAiB,OAGtB,IAAMC,CAAAA,CAAiBD,EAAgB,SAAA,CACjCE,CAAAA,CAAqBF,CAAAA,CAAgB,KAAA,CAAM,cAAA,CAGjDA,CAAAA,CAAgB,MAAM,cAAA,CAAiB,MAAA,CAGvC,IAAMG,CAAAA,CAAgB,IAAM,CAC1BH,EAAgB,SAAA,CAAYC,EAC9B,CAAA,CACAD,CAAAA,CAAgB,gBAAA,CAAiB,QAAA,CAAUG,CAAa,CAAA,CAGxD,IAAMC,EAAY,UAAA,CAAW,IAAM,CACjCJ,CAAAA,CAAgB,mBAAA,CAAoB,QAAA,CAAUG,CAAa,CAAA,CAC3DH,CAAAA,CAAgB,MAAM,cAAA,CAAiBE,CAAAA,CACvCP,CAAAA,CAAW,OAAA,CAAU,KACvB,CAAA,CAAGH,CAAiB,CAAA,CAEpBG,CAAAA,CAAW,OAAA,CAAU,IAAM,CACzB,YAAA,CAAaS,CAAS,CAAA,CACtBJ,CAAAA,CAAgB,mBAAA,CAAoB,QAAA,CAAUG,CAAa,CAAA,CAC3DH,EAAgB,KAAA,CAAM,cAAA,CAAiBE,EACzC,EACF,CAAA,CAAG,CAACV,EAAmBD,CAAkB,CAAC,CAG5C,CCtFA,IAAMc,CAAAA,CAAQ,kCAAA,CAUP,SAASC,CAAAA,EAAmC,CAClD,GAAM,CAACC,CAAAA,CAASC,CAAU,CAAA,CAAIC,QAAAA,CAAS,KAAK,CAAA,CAE5C,OAAAb,SAAAA,CAAU,IAAM,CACf,GAAI,OAAO,MAAA,CAAW,GAAA,EAAe,CAAC,OAAO,UAAA,CAAY,OACzD,IAAMc,CAAAA,CAAK,MAAA,CAAO,UAAA,CAAWL,CAAK,CAAA,CAC5BM,CAAAA,CAAQ,IAAMH,CAAAA,CAAWE,CAAAA,CAAG,OAAO,EACzC,OAAAC,CAAAA,EAAM,CACND,CAAAA,CAAG,gBAAA,CAAiB,QAAA,CAAUC,CAAK,CAAA,CAC5B,IAAMD,CAAAA,CAAG,mBAAA,CAAoB,QAAA,CAAUC,CAAK,CACpD,CAAA,CAAG,EAAE,CAAA,CAEEJ,CACR","file":"chunk-OU5ITGKX.js","sourcesContent":["import { type RefObject, useCallback, useEffect, useRef } from \"react\";\n\n/**\n * Locks scroll position during collapsible/height animations.\n *\n * Prevents viewport jumps when content height changes during animations\n * (e.g. ReasoningPanel expand/collapse). Finds the nearest scrollable\n * ancestor and temporarily locks its scroll position for the animation duration.\n *\n * Adapted from assistant-ui's useScrollLock.\n *\n * @param animatedElementRef - Ref to the animated element\n * @param animationDuration - Lock duration in milliseconds\n * @returns Function to activate the scroll lock (call before toggling)\n *\n * @example\n * ```tsx\n * const panelRef = useRef<HTMLDivElement>(null);\n * const lockScroll = useScrollLock(panelRef, 200);\n *\n * const handleToggle = () => {\n * lockScroll();\n * setIsExpanded(!isExpanded);\n * };\n * ```\n */\nexport function useScrollLock<T extends HTMLElement = HTMLElement>(\n animatedElementRef: RefObject<T | null>,\n animationDuration: number,\n) {\n const scrollContainerRef = useRef<HTMLElement | null>(null);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n cleanupRef.current?.();\n };\n }, []);\n\n const lockScroll = useCallback(() => {\n // Clean up any previous lock\n cleanupRef.current?.();\n\n // Find nearest scrollable ancestor (cached after first find)\n if (!scrollContainerRef.current && animatedElementRef.current) {\n let el: HTMLElement | null = animatedElementRef.current;\n while (el) {\n const { overflowY } = getComputedStyle(el);\n if (overflowY === \"scroll\" || overflowY === \"auto\") {\n scrollContainerRef.current = el;\n break;\n }\n el = el.parentElement;\n }\n }\n\n const scrollContainer = scrollContainerRef.current;\n if (!scrollContainer) return;\n\n // Capture current scroll position\n const scrollPosition = scrollContainer.scrollTop;\n const prevScrollbarWidth = scrollContainer.style.scrollbarWidth;\n\n // Hide scrollbar during lock to prevent visual jank\n scrollContainer.style.scrollbarWidth = \"none\";\n\n // Lock: intercept any scroll events and reset position\n const resetPosition = () => {\n scrollContainer.scrollTop = scrollPosition;\n };\n scrollContainer.addEventListener(\"scroll\", resetPosition);\n\n // Release lock after animation completes\n const timeoutId = setTimeout(() => {\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = prevScrollbarWidth;\n cleanupRef.current = null;\n }, animationDuration);\n\n cleanupRef.current = () => {\n clearTimeout(timeoutId);\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = prevScrollbarWidth;\n };\n }, [animationDuration, animatedElementRef]);\n\n return lockScroll;\n}\n","import { useEffect, useState } from \"react\";\n\nconst QUERY = \"(prefers-reduced-motion: reduce)\";\n\n/**\n * Whether the viewer has asked for reduced motion.\n *\n * The reasoning stream drives shimmer, a live meter and a blinking caret from\n * inline `animation` declarations, and a media query cannot override an inline\n * style — so the decision has to be made in JS and the effect simply not\n * applied. Starts `false` so server rendering and the first client paint agree.\n */\nexport function usePrefersReducedMotion(): boolean {\n\tconst [reduced, setReduced] = useState(false);\n\n\tuseEffect(() => {\n\t\tif (typeof window === \"undefined\" || !window.matchMedia) return;\n\t\tconst mq = window.matchMedia(QUERY);\n\t\tconst apply = () => setReduced(mq.matches);\n\t\tapply();\n\t\tmq.addEventListener(\"change\", apply);\n\t\treturn () => mq.removeEventListener(\"change\", apply);\n\t}, []);\n\n\treturn reduced;\n}\n"]}
|