@sybilion/uilib 1.3.78 → 1.3.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/esm/components/ui/Chat/Chat.js +2 -2
  2. package/dist/esm/components/ui/Chat/Chat.styl.js +1 -1
  3. package/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.js +6 -6
  4. package/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.styl.js +2 -2
  5. package/dist/esm/components/ui/Chat/ChatEmptyState/ChatEmptyState.styl.js +1 -1
  6. package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.js +2 -2
  7. package/dist/esm/components/ui/Chat/ChatPresets/ChatPresets.styl.js +1 -1
  8. package/dist/esm/components/ui/Chat/ChatSheet/ChatSelector.js +2 -2
  9. package/dist/esm/components/ui/Chat/ChatSheet/ChatSheet.js +2 -1
  10. package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +6 -5
  11. package/dist/esm/components/ui/FileChip/FileChip.js +4 -0
  12. package/dist/esm/contexts/chat-context.js +36 -4
  13. package/dist/esm/index.js +1 -0
  14. package/dist/esm/types/src/components/ui/Chat/Chat.d.ts +1 -1
  15. package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +10 -0
  16. package/dist/esm/types/src/components/ui/Chat/ChatChrome/ChatChrome.d.ts +1 -1
  17. package/dist/esm/types/src/components/ui/Chat/ChatChrome/ChatChrome.types.d.ts +4 -0
  18. package/dist/esm/types/src/components/ui/Chat/ChatMessage/ChatMessage.d.ts +1 -1
  19. package/dist/esm/types/src/components/ui/Chat/ChatSheet/ChatSelector.d.ts +2 -1
  20. package/dist/esm/types/src/components/ui/Chat/ChatSheet/ChatSheet.d.ts +1 -1
  21. package/dist/esm/types/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.d.ts +5 -3
  22. package/dist/esm/types/src/components/ui/Chat/index.d.ts +3 -1
  23. package/dist/esm/types/src/components/ui/FileChip/FileChip.types.d.ts +1 -1
  24. package/dist/esm/types/src/contexts/chat-context.d.ts +16 -4
  25. package/package.json +1 -1
  26. package/src/components/ui/Chat/Chat.styl +4 -1
  27. package/src/components/ui/Chat/Chat.tsx +2 -1
  28. package/src/components/ui/Chat/Chat.types.ts +12 -0
  29. package/src/components/ui/Chat/ChatChrome/ChatChrome.styl +20 -10
  30. package/src/components/ui/Chat/ChatChrome/ChatChrome.styl.d.ts +2 -0
  31. package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +12 -4
  32. package/src/components/ui/Chat/ChatChrome/ChatChrome.types.ts +4 -0
  33. package/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.styl +1 -2
  34. package/src/components/ui/Chat/ChatMessage/ChatMessage.tsx +9 -1
  35. package/src/components/ui/Chat/ChatPresets/ChatPresets.styl +5 -4
  36. package/src/components/ui/Chat/ChatSheet/ChatSelector.tsx +3 -1
  37. package/src/components/ui/Chat/ChatSheet/ChatSheet.tsx +2 -0
  38. package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +24 -5
  39. package/src/components/ui/Chat/index.ts +4 -0
  40. package/src/components/ui/FileChip/FileChip.tsx +11 -0
  41. package/src/components/ui/FileChip/FileChip.types.ts +1 -1
  42. package/src/contexts/chat-context.tsx +57 -5
  43. package/src/docs/pages/ChatPage.styl +6 -0
  44. package/src/docs/pages/ChatPage.styl.d.ts +7 -0
  45. package/src/docs/pages/ChatPage.tsx +30 -87
  46. package/src/docs/pages/ChatSlashCommandsPage.tsx +4 -4
@@ -7,8 +7,8 @@ import { ChatMessage } from './ChatMessage/ChatMessage.js';
7
7
  import { ChatPrompt } from './ChatPrompt/ChatPrompt.js';
8
8
  import { ChatSelector } from './ChatSheet/ChatSelector.js';
9
9
 
10
- function Chat({ children, className, isEmpty, scopeId, onChatDeleted, onNewChat, ...props }) {
11
- return (jsxs("div", { className: cn(S.root, className, isEmpty && S.isEmpty), ...props, children: [scopeId ? (jsx("div", { className: S.header, children: jsx(ChatSelector, { id: scopeId, onChatDeleted: onChatDeleted, onNewChat: onNewChat }) })) : null, children] }));
10
+ function Chat({ children, className, isEmpty, scopeId, onChatDeleted, onNewChat, hideChatSelector = false, ...props }) {
11
+ return (jsxs("div", { className: cn(S.root, className, isEmpty && S.isEmpty), ...props, children: [scopeId && !hideChatSelector ? (jsx("div", { className: S.header, children: jsx(ChatSelector, { id: scopeId, onChatDeleted: onChatDeleted, onNewChat: onNewChat }) })) : null, children] }));
12
12
  }
13
13
  Chat.Prompt = ChatPrompt;
14
14
  Chat.Message = ChatMessage;
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".Chat_root__IWt99{background-color:var(--background);display:flex;flex-direction:column;height:100%;min-height:0}.Chat_header__ZjwP-{align-items:center;display:flex;flex-shrink:0;min-height:64px;padding:var(--p-2) var(--p-6) 0;padding-right:var(--p-12)}.Chat_isEmpty__b4ViB{padding-bottom:170px}";
3
+ var css_248z = ".Chat_root__IWt99{background-color:var(--background);display:flex;flex:1;flex-direction:column;height:100%;min-height:0}.Chat_header__ZjwP-{align-items:center;display:flex;flex-shrink:0;min-height:64px;padding:var(--p-2) var(--p-6) 0;padding-right:var(--p-12)}.Chat_isEmpty__b4ViB{flex:1;min-height:0;overflow:hidden}";
4
4
  var S = {"root":"Chat_root__IWt99","header":"Chat_header__ZjwP-","isEmpty":"Chat_isEmpty__b4ViB"};
5
5
  styleInject(css_248z);
6
6
 
@@ -1,4 +1,4 @@
1
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import cn from 'classnames';
3
3
  import { useMemo, useState, useCallback, useEffect } from 'react';
4
4
  import { displayLabelForBranchKeyFromMessages, humanizeBranchKey } from '../ChatMessage/presetScript.js';
@@ -14,7 +14,7 @@ import { filterToTextAttachments, isAttachmentsDropzoneEnabled, buildAcceptAttr
14
14
  import { extractChatAttachmentItems } from '../chatAttachmentExtract.js';
15
15
  import S from './ChatChrome.styl.js';
16
16
 
17
- function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, loadingLabel, scriptContinueLabel, onScriptContinue, renderMessageChart, showSyntheticBranchButtons, unusedBranchKeys, showInlinePresets, isLastMessageFromUser, scrollRef, effectiveScopeId, onPromptSubmit, onChatDeleted, onNewChat, promptPrefill, footerClassName, emptyState, allowedAttachments, allowPdfAttachments = false, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, promptPlaceholder, }) {
17
+ function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, loadingLabel, scriptContinueLabel, onScriptContinue, renderMessageChart, renderSystemMessage, showSyntheticBranchButtons, unusedBranchKeys, showInlinePresets, isLastMessageFromUser, scrollRef, effectiveScopeId, onPromptSubmit, onChatDeleted, onNewChat, promptPrefill, footerClassName, emptyState, allowedAttachments, allowPdfAttachments = false, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, promptPlaceholder, hideChatSelector = false, }) {
18
18
  const filteredAllowedAttachments = useMemo(() => filterToTextAttachments(allowedAttachments), [allowedAttachments]);
19
19
  const attachmentsDropzoneEnabled = isAttachmentsDropzoneEnabled(allowedAttachments, allowPdfAttachments);
20
20
  const attachmentAccept = useMemo(() => buildAcceptAttr(filteredAllowedAttachments, allowPdfAttachments), [filteredAllowedAttachments, allowPdfAttachments]);
@@ -74,18 +74,18 @@ function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPr
74
74
  showInlinePresets,
75
75
  showSyntheticBranchButtons,
76
76
  ]);
77
- return (jsxs("div", { className: S.root, children: [showResizeHandle && resizeHandle ? (jsx(PanelResizeHandle, { edge: "leading", isActive: resizeHandle.isActive, startWidthPx: resizeHandle.startWidthPx, getShellWidth: resizeHandle.getShellWidth, onDragWidth: resizeHandle.onDragWidth, onDragComplete: resizeHandle.onDragComplete, className: cn(SidebarStem.sidebarResizeHandle, S.chatResizeHandle) })) : null, jsx("div", { className: S.panelHeader, children: onClose ? (jsx(Button, { type: "button", variant: "ghost", icon: true, className: S.panelClose, "aria-label": "Close chat", onClick: onClose, children: jsx(X, { className: "size-4" }) })) : null }), jsxs("div", { className: S.content, children: [attachmentsDropzoneEnabled ? (jsx(DropZone, { accept: attachmentAccept, label: "Drop text files to attach", multiple: true, ghost: true, overlayScope: "container", disabled: promptDisabled, className: S.attachmentDropzone, onFiles: handleAttachmentFiles })) : null, jsxs(Chat, { isEmpty: isEmpty, scopeId: effectiveScopeId, onChatDeleted: onChatDeleted, onNewChat: onNewChat, children: [isEmpty ? (jsxs(Fragment, { children: [jsx(Chat.EmptyState, { ...emptyState }), renderPresets('fixed')] })) : (jsx("div", { className: S.scrollWrapper, children: jsxs(Scroll, { y: true, yScrollbarClassName: S.scrollbar, className: S.scroll, innerClassName: S.scrollInner, offset: { y: { before: 56, after: 180 } }, fadeSize: "m", autoHide: true, ref: scrollRef, children: [messages.map((msg, index, arr) => {
77
+ return (jsxs("div", { className: S.root, children: [showResizeHandle && resizeHandle ? (jsx(PanelResizeHandle, { edge: "leading", isActive: resizeHandle.isActive, startWidthPx: resizeHandle.startWidthPx, getShellWidth: resizeHandle.getShellWidth, onDragWidth: resizeHandle.onDragWidth, onDragComplete: resizeHandle.onDragComplete, className: cn(SidebarStem.sidebarResizeHandle, S.chatResizeHandle) })) : null, jsx("div", { className: S.panelHeader, children: onClose ? (jsx(Button, { type: "button", variant: "ghost", icon: true, className: S.panelClose, "aria-label": "Close chat", onClick: onClose, children: jsx(X, { className: "size-4" }) })) : null }), jsxs("div", { className: S.content, children: [attachmentsDropzoneEnabled ? (jsx(DropZone, { accept: attachmentAccept, label: "Drop text files to attach", multiple: true, ghost: true, overlayScope: "container", disabled: promptDisabled, className: S.attachmentDropzone, onFiles: handleAttachmentFiles })) : null, jsxs(Chat, { isEmpty: isEmpty, scopeId: effectiveScopeId, onChatDeleted: onChatDeleted, onNewChat: onNewChat, hideChatSelector: hideChatSelector, children: [isEmpty ? (jsx("div", { className: S.emptyBody, children: jsx(Chat.EmptyState, { ...emptyState }) })) : (jsx("div", { className: S.scrollWrapper, children: jsxs(Scroll, { y: true, yScrollbarClassName: S.scrollbar, className: S.scroll, innerClassName: S.scrollInner, offset: { y: { before: 56, after: 24 } }, fadeSize: "m", autoHide: true, ref: scrollRef, children: [messages.map((msg, index, arr) => {
78
78
  const isLast = index === arr.length - 1;
79
- return (jsx(Chat.Message, { role: msg.role, text: msg.text, inProgress: msg.inProgress, userTextFileAttachments: msg.userTextFileAttachments, onQuickReply: onQuickReply, suppressedQuickReplyKeys: suppressedQuickReplyKeys, quickReplyDisabled: isLoading, quickReplyHidden: Boolean(loadingLabel), isLastMessage: isLast, scriptContinue: isLast && scriptContinueLabel
79
+ return (jsx(Chat.Message, { message: msg, role: msg.role, text: msg.text, inProgress: msg.inProgress, userTextFileAttachments: msg.userTextFileAttachments, onQuickReply: onQuickReply, suppressedQuickReplyKeys: suppressedQuickReplyKeys, quickReplyDisabled: isLoading, quickReplyHidden: Boolean(loadingLabel), isLastMessage: isLast, scriptContinue: isLast && scriptContinueLabel
80
80
  ? { label: scriptContinueLabel }
81
81
  : undefined, onScriptContinue: isLast && scriptContinueLabel
82
82
  ? onScriptContinue
83
- : undefined, renderMessageChart: renderMessageChart }, msg.id));
83
+ : undefined, renderMessageChart: renderMessageChart, renderSystemMessage: renderSystemMessage }, msg.id));
84
84
  }), showSyntheticBranchButtons ? (jsx("div", { className: S.branchRow, children: unusedBranchKeys.map(key => {
85
85
  const label = displayLabelForBranchKeyFromMessages(key, messages) ??
86
86
  humanizeBranchKey(key);
87
87
  return (jsx("span", { className: S.branchBtnWrap, children: jsxs(Button, { type: "button", variant: "outline", size: "sm", disabled: isLoading, onClick: () => onQuickReply(key, label), children: [jsx(PaperPlaneRightIcon, {}), label] }) }, key));
88
- }) })) : null, showInlinePresets && renderPresets('inline'), isLoading && (isLastMessageFromUser || loadingLabel) && (jsx(TextShimmer, { duration: 1, spread: 5, className: S.loader, children: loadingLabel ?? 'Thinking...' }))] }) })), jsxs("div", { className: cn(S.footer, footerClassName), children: [isEmpty ? (jsx("div", { className: S.notice, children: "Forecast Assistant can make mistakes." })) : null, jsx(Chat.Prompt, { onSubmit: handlePromptSubmitWithAttachments, disabled: promptDisabled, attachments: pendingAttachments, onRemoveAttachment: handleRemoveAttachment, prefillMessage: promptPrefill ?? undefined, placeholder: promptPlaceholder, slashCommandItems: slashCommandItems, onSlashItemCommand: onSlashItemCommand, attachmentAccept: attachmentsDropzoneEnabled ? attachmentAccept : undefined, onAttachmentFiles: attachmentsDropzoneEnabled ? handleAttachmentFiles : undefined })] })] })] })] }));
88
+ }) })) : null, showInlinePresets && renderPresets('inline'), isLoading && (isLastMessageFromUser || loadingLabel) && (jsx(TextShimmer, { duration: 1, spread: 5, className: S.loader, children: loadingLabel ?? 'Thinking...' }))] }) })), isEmpty ? (jsx("div", { className: S.fixedPresets, children: renderPresets('fixed') })) : null, jsxs("div", { className: cn(S.footer, footerClassName), children: [isEmpty ? (jsx("div", { className: S.notice, children: "Forecast Assistant can make mistakes." })) : null, jsx(Chat.Prompt, { onSubmit: handlePromptSubmitWithAttachments, disabled: promptDisabled, attachments: pendingAttachments, onRemoveAttachment: handleRemoveAttachment, prefillMessage: promptPrefill ?? undefined, placeholder: promptPlaceholder, slashCommandItems: slashCommandItems, onSlashItemCommand: onSlashItemCommand, attachmentAccept: attachmentsDropzoneEnabled ? attachmentAccept : undefined, onAttachmentFiles: attachmentsDropzoneEnabled ? handleAttachmentFiles : undefined })] })] })] })] }));
89
89
  }
90
90
 
91
91
  export { ChatChrome };
@@ -1,7 +1,7 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChatChrome_root__oh4Ay{border-radius:var(--p-4);display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden;position:relative}.ChatChrome_chatResizeHandle__epfiT{background-color:var(--page-color);border-radius:2.5px;height:calc(100vh + 200px);left:0;opacity:0;position:absolute;right:auto;top:-200px;touch-action:none;transition:opacity .15s ease-out;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:5px;z-index:30}.ChatChrome_chatResizeHandle__epfiT:before{left:0;right:auto}.ChatChrome_panelHeader__Hkfit{align-items:center;display:flex;flex-shrink:0;justify-content:flex-end;min-height:70px;padding:var(--p-2) var(--p-3);position:absolute;width:100%;z-index:100}.ChatChrome_panelClose__DbKxz{flex-shrink:0}.ChatChrome_content__5qFEi{display:flex;flex:1;flex-direction:column;min-height:0;position:relative}.ChatChrome_attachmentDropzone__OC8UI{inset:0;position:absolute;z-index:200}.ChatChrome_scrollWrapper__m4HMu{flex:1;min-height:0;position:relative}.ChatChrome_scroll__oCxoJ{align-items:flex-end;height:100%;max-height:100%;max-width:100%;padding-bottom:var(--p-2);position:absolute;width:100%;z-index:3}.ChatChrome_scrollbar__Hu0aG{right:0!important}.ChatChrome_scrollInner__K9hIy{min-height:100%;padding-top:var(--p-10)}.ChatChrome_scrollInner__K9hIy:after{content:\"\";display:block;height:260px}.ChatChrome_footer__a5Bpp{backdrop-filter:blur(30px);background-color:var(--background-alpha-800);border-top:1px solid var(--border);bottom:0;box-shadow:0 0 20px 16px var(--background);display:flex;flex-direction:column;position:absolute;width:100%;z-index:50}.ChatChrome_notice__JACIw{color:var(--muted-foreground);font-size:var(--text-xs);left:0;margin-bottom:var(--p-1);pointer-events:none;position:absolute;right:0;text-align:center;top:calc(var(--p-7)*-1)}@media (max-width:768px){.ChatChrome_notice__JACIw{font-size:10px}}.ChatChrome_loader__9-lnf{color:var(--muted-foreground);margin:var(--p-2) var(--p-6) var(--p-10)}.ChatChrome_branchRow__NMDNv{display:flex;flex-wrap:wrap;gap:8px;margin-top:var(--p-6);padding:0 var(--p-6);width:100%}.ChatChrome_branchBtnWrap__aOSVP{display:inline-flex;vertical-align:middle}";
4
- var S = {"root":"ChatChrome_root__oh4Ay","chatResizeHandle":"ChatChrome_chatResizeHandle__epfiT","panelHeader":"ChatChrome_panelHeader__Hkfit","panelClose":"ChatChrome_panelClose__DbKxz","content":"ChatChrome_content__5qFEi","attachmentDropzone":"ChatChrome_attachmentDropzone__OC8UI","scrollWrapper":"ChatChrome_scrollWrapper__m4HMu","scroll":"ChatChrome_scroll__oCxoJ","scrollbar":"ChatChrome_scrollbar__Hu0aG","scrollInner":"ChatChrome_scrollInner__K9hIy","footer":"ChatChrome_footer__a5Bpp","notice":"ChatChrome_notice__JACIw","loader":"ChatChrome_loader__9-lnf","branchRow":"ChatChrome_branchRow__NMDNv","branchBtnWrap":"ChatChrome_branchBtnWrap__aOSVP"};
3
+ var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChatChrome_root__oh4Ay{border-radius:var(--p-4);display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden;position:relative}.ChatChrome_chatResizeHandle__epfiT{background-color:var(--page-color);border-radius:2.5px;height:calc(100vh + 200px);left:0;opacity:0;position:absolute;right:auto;top:-200px;touch-action:none;transition:opacity .15s ease-out;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:5px;z-index:30}.ChatChrome_chatResizeHandle__epfiT:before{left:0;right:auto}.ChatChrome_panelHeader__Hkfit{align-items:center;display:flex;flex-shrink:0;justify-content:flex-end;min-height:70px;padding:var(--p-2) var(--p-3);position:absolute;width:100%;z-index:100}.ChatChrome_panelClose__DbKxz{flex-shrink:0}.ChatChrome_content__5qFEi{display:flex;flex:1;flex-direction:column;min-height:0;position:relative}.ChatChrome_attachmentDropzone__OC8UI{inset:0;position:absolute;z-index:200}.ChatChrome_scrollWrapper__m4HMu{flex:1;min-height:0;position:relative}.ChatChrome_scroll__oCxoJ{align-items:flex-end;height:100%;max-height:100%;max-width:100%;padding-bottom:var(--p-2);position:absolute;width:100%;z-index:3}.ChatChrome_scrollbar__Hu0aG{right:0!important}.ChatChrome_scrollInner__K9hIy{min-height:100%;padding-top:var(--p-10)}.ChatChrome_emptyBody__f2NE8{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden}.ChatChrome_emptyBody__f2NE8>*{flex:1;min-height:0;overflow:auto}.ChatChrome_footer__a5Bpp{backdrop-filter:blur(30px);background-color:var(--background-alpha-800);border-top:1px solid var(--border);box-shadow:0 8px 24px 0 var(--background);display:flex;flex-direction:column;flex-shrink:0;position:relative;width:100%;z-index:50}.ChatChrome_fixedPresets__bONhR{flex-shrink:0;position:relative;width:100%;z-index:10}.ChatChrome_notice__JACIw{color:var(--muted-foreground);font-size:var(--text-xs);left:0;margin-bottom:var(--p-1);pointer-events:none;position:absolute;right:0;text-align:center;top:calc(var(--p-7)*-1)}@media (max-width:768px){.ChatChrome_notice__JACIw{font-size:10px}}.ChatChrome_loader__9-lnf{color:var(--muted-foreground);margin:var(--p-2) var(--p-6) var(--p-10)}.ChatChrome_branchRow__NMDNv{display:flex;flex-wrap:wrap;gap:8px;margin-top:var(--p-6);padding:0 var(--p-6);width:100%}.ChatChrome_branchBtnWrap__aOSVP{display:inline-flex;vertical-align:middle}";
4
+ var S = {"root":"ChatChrome_root__oh4Ay","chatResizeHandle":"ChatChrome_chatResizeHandle__epfiT","panelHeader":"ChatChrome_panelHeader__Hkfit","panelClose":"ChatChrome_panelClose__DbKxz","content":"ChatChrome_content__5qFEi","attachmentDropzone":"ChatChrome_attachmentDropzone__OC8UI","scrollWrapper":"ChatChrome_scrollWrapper__m4HMu","scroll":"ChatChrome_scroll__oCxoJ","scrollbar":"ChatChrome_scrollbar__Hu0aG","scrollInner":"ChatChrome_scrollInner__K9hIy","emptyBody":"ChatChrome_emptyBody__f2NE8","footer":"ChatChrome_footer__a5Bpp","fixedPresets":"ChatChrome_fixedPresets__bONhR","notice":"ChatChrome_notice__JACIw","loader":"ChatChrome_loader__9-lnf","branchRow":"ChatChrome_branchRow__NMDNv","branchBtnWrap":"ChatChrome_branchBtnWrap__aOSVP"};
5
5
  styleInject(css_248z);
6
6
 
7
7
  export { S as default };
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".ChatEmptyState_root__j1n-C{align-items:center;color:var(--text-secondary);display:flex;flex:1;flex-direction:column;font-size:var(--text-sm);gap:var(--p-10);justify-content:center;padding:var(--p-6);text-align:center;text-wrap:balance}.ChatEmptyState_icon__YSDgv,.ChatEmptyState_icon__YSDgv>svg{height:32px;width:32px}";
3
+ var css_248z = ".ChatEmptyState_root__j1n-C{align-items:center;color:var(--text-secondary);display:flex;flex:1;flex-direction:column;font-size:var(--text-sm);gap:var(--p-10);justify-content:center;min-height:0;padding:var(--p-6);text-align:center;text-wrap:balance}.ChatEmptyState_icon__YSDgv,.ChatEmptyState_icon__YSDgv>svg{height:32px;width:32px}";
4
4
  var S = {"root":"ChatEmptyState_root__j1n-C","icon":"ChatEmptyState_icon__YSDgv"};
5
5
  styleInject(css_248z);
6
6
 
@@ -10,13 +10,13 @@ import { AgentMessageContent } from './AgentMessageContent.js';
10
10
  import S from './ChatMessage.styl.js';
11
11
  import { UserTextFileAttachmentBubble } from './UserTextFileAttachmentBubble.js';
12
12
 
13
- function ChatMessage({ role, text, inProgress, userTextFileAttachments, onQuickReply, suppressedQuickReplyKeys, quickReplyDisabled, quickReplyHidden, isLastMessage = true, scriptContinue, onScriptContinue, renderMessageChart, }) {
13
+ function ChatMessage({ role, text, inProgress, userTextFileAttachments, onQuickReply, suppressedQuickReplyKeys, quickReplyDisabled, quickReplyHidden, isLastMessage = true, scriptContinue, onScriptContinue, renderMessageChart, message, renderSystemMessage, }) {
14
14
  const fileAttachments = userTextFileAttachmentsFromMessage({
15
15
  userTextFileAttachments,
16
16
  });
17
17
  const isAssistant = role === MessageRole.ASSISTANT;
18
18
  const isSystem = role === MessageRole.SYSTEM;
19
- return (jsx("div", { className: cn(S.root, S[`role-${role}`]), children: isSystem ? (jsx("div", { className: S.text, children: inProgress ? jsx(TextShimmer, { as: "span", children: text }) : text })) : isAssistant ? (jsx(AgentMessageContent, { text: text, onQuickReply: onQuickReply, suppressedQuickReplyKeys: suppressedQuickReplyKeys, quickReplyDisabled: quickReplyDisabled, quickReplyHidden: quickReplyHidden, isLastMessage: isLastMessage, scriptContinue: scriptContinue, onScriptContinue: onScriptContinue, renderMessageChart: renderMessageChart })) : (jsxs("div", { className: S.userColumn, children: [jsx("div", { className: S.text, children: jsx(InteractiveContent, { text: text }) }), fileAttachments.map(attachment => (jsx(UserTextFileAttachmentBubble, { attachment: attachment }, `${attachment.displayName}:${attachment.filename}`)))] })) }));
19
+ return (jsx("div", { className: cn(S.root, S[`role-${role}`]), children: isSystem ? (jsx("div", { className: S.text, children: inProgress ? (jsx(TextShimmer, { as: "span", children: text })) : renderSystemMessage && message ? (renderSystemMessage(message)) : (text) })) : isAssistant ? (jsx(AgentMessageContent, { text: text, onQuickReply: onQuickReply, suppressedQuickReplyKeys: suppressedQuickReplyKeys, quickReplyDisabled: quickReplyDisabled, quickReplyHidden: quickReplyHidden, isLastMessage: isLastMessage, scriptContinue: scriptContinue, onScriptContinue: onScriptContinue, renderMessageChart: renderMessageChart })) : (jsxs("div", { className: S.userColumn, children: [jsx("div", { className: S.text, children: jsx(InteractiveContent, { text: text }) }), fileAttachments.map(attachment => (jsx(UserTextFileAttachmentBubble, { attachment: attachment }, `${attachment.displayName}:${attachment.filename}`)))] })) }));
20
20
  }
21
21
 
22
22
  export { ChatMessage };
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".ChatPresets_root__Cj42o{bottom:160px;width:100%}.ChatPresets_inlineRoot__WXVnu{margin-top:var(--p-6);position:relative;width:100%}.ChatPresets_inner__h14-q{background-color:var(--background);display:flex;flex-wrap:wrap;gap:8px;padding:var(--p-2) var(--p-6) var(--p-3)}.ChatPresets_innerInline__iPM2b{background-color:transparent}.ChatPresets_item__LfX5b{flex-shrink:0;font-size:var(--text-xs);height:auto;line-height:1.4;max-width:300px;min-height:auto;min-width:0;overflow-wrap:anywhere;padding:var(--p-3);text-align:left;white-space:break-spaces!important}";
3
+ var css_248z = ".ChatPresets_root__Cj42o{flex-shrink:0;position:relative;width:100%}.ChatPresets_inlineRoot__WXVnu{margin-top:var(--p-6);position:relative;width:100%}.ChatPresets_inner__h14-q{background-color:var(--background);display:flex;flex-wrap:wrap;gap:8px;min-width:0;padding:var(--p-2) var(--p-6) var(--p-3)}.ChatPresets_innerInline__iPM2b{background-color:transparent}.ChatPresets_item__LfX5b{flex:0 1 auto;font-size:var(--text-xs);height:auto;line-height:1.4;max-width:min(300px,100%);min-height:auto;min-width:0;overflow-wrap:anywhere;padding:var(--p-3);text-align:left;white-space:break-spaces!important}";
4
4
  var S = {"root":"ChatPresets_root__Cj42o","inlineRoot":"ChatPresets_inlineRoot__WXVnu","inner":"ChatPresets_inner__h14-q","innerInline":"ChatPresets_innerInline__iPM2b","item":"ChatPresets_item__LfX5b"};
5
5
  styleInject(css_248z);
6
6
 
@@ -6,7 +6,7 @@ import { Button } from '../../Button/Button.js';
6
6
  import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../../Select/Select.js';
7
7
  import S from './ChatSelector.styl.js';
8
8
 
9
- function ChatSelector({ id, className, onChatDeleted, onNewChat, }) {
9
+ function ChatSelector({ id, wrapperClassName, className, onChatDeleted, onNewChat, }) {
10
10
  const { chats, currentChatId, setCurrentChatId, newChat, deleteChat } = useChatsForScopeId(id);
11
11
  const handleValueChange = (value) => {
12
12
  if (value === 'new') {
@@ -39,7 +39,7 @@ function ChatSelector({ id, className, onChatDeleted, onNewChat, }) {
39
39
  }
40
40
  return 'New chat';
41
41
  };
42
- return (jsxs("div", { className: S.wrapper, children: [jsx("div", { className: S.selectGrow, children: jsxs(Select, { variant: "clear", value: currentChatId?.toString() ?? '', onValueChange: handleValueChange, children: [jsx(SelectTrigger, { size: "sm", className: className ? cn(S.selectTrigger, className) : S.selectTrigger, children: jsx(SelectValue, { placeholder: "Select chat" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "new", children: "+ New Chat" }), chats.map(chat => (jsx(SelectItem, { value: chat.session_id.toString(), selected: chat.session_id === currentChatId, children: getChatDisplayName(chat) }, chat.session_id)))] })] }) }), currentChatId && chats.length > 0 && (jsx(Button, { type: "button", variant: "ghost", size: "sm", className: S.deleteBtn, "aria-label": "Delete chat", onClick: handleDeleteChat, children: jsx(Trash2Icon, { size: 16 }) }))] }));
42
+ return (jsxs("div", { className: cn(S.wrapper, wrapperClassName), children: [jsx("div", { className: S.selectGrow, children: jsxs(Select, { variant: "clear", value: currentChatId?.toString() ?? '', onValueChange: handleValueChange, children: [jsx(SelectTrigger, { size: "sm", className: className ? cn(S.selectTrigger, className) : S.selectTrigger, children: jsx(SelectValue, { placeholder: "Select chat" }) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "new", children: "+ New Chat" }), chats.map(chat => (jsx(SelectItem, { value: chat.session_id.toString(), selected: chat.session_id === currentChatId, children: getChatDisplayName(chat) }, chat.session_id)))] })] }) }), currentChatId && chats.length > 0 && (jsx(Button, { type: "button", variant: "ghost", size: "sm", className: S.deleteBtn, "aria-label": "Delete chat", onClick: handleDeleteChat, children: jsx(Trash2Icon, { size: 16 }) }))] }));
43
43
  }
44
44
 
45
45
  export { ChatSelector };
@@ -4,7 +4,7 @@ import { Button } from '../../Button/Button.js';
4
4
  import { ChatChrome } from '../ChatChrome/ChatChrome.js';
5
5
  import { useChatPanelChromeModel } from './useChatPanelChromeModel.js';
6
6
 
7
- function ChatSheet({ triggerLabel = 'Open Chat', triggerAriaLabel, actionsRef, renderTrigger, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, transformSendPayload, inline = false, }) {
7
+ function ChatSheet({ triggerLabel = 'Open Chat', triggerAriaLabel, actionsRef, renderTrigger, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, renderSystemMessage, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, transformSendPayload, inline = false, }) {
8
8
  const model = useChatPanelChromeModel({
9
9
  embedAsPage: inline,
10
10
  presets,
@@ -12,6 +12,7 @@ function ChatSheet({ triggerLabel = 'Open Chat', triggerAriaLabel, actionsRef, r
12
12
  onMessage,
13
13
  onScriptComplete,
14
14
  renderMessageChart,
15
+ renderSystemMessage,
15
16
  emptyState,
16
17
  allowedAttachments,
17
18
  allowPdfAttachments,
@@ -22,7 +22,7 @@ const CHAT_NEW_SHORTCUT_KEY = 'o';
22
22
  const CHAT_QUERY_PARAM = 'chat';
23
23
  const CHAT_OPEN_VALUE = 'open';
24
24
  const PROMPT_QUERY_PARAM = 'prompt';
25
- function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, copyHistoryOnNewChat = false, transformSendPayload, }) {
25
+ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, renderSystemMessage, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, copyHistoryOnNewChat = false, transformSendPayload, }) {
26
26
  const effectiveScopeId = scopeId ?? NO_SCOPE_FALLBACK;
27
27
  const isMobile = useIsMobile();
28
28
  const { chatPanelContainer, isOpen: sidebarNavOpen, setOpen: setSidebarNavOpen, sidebarWidthPx, chatWidthPx, setChatWidthPx, getShellWidth, chatPanelOpen: shellChatPanelOpen, setChatPanelOpen, } = useSidebar();
@@ -336,8 +336,8 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
336
336
  }
337
337
  setOutboundLoadingLabel(loadingLabelFromSendPayload(payload));
338
338
  try {
339
- const assistantResponse = await sendMessage(payload);
340
- onMessage?.(displayTextFromSendPayload(payload), assistantResponse);
339
+ const { response: assistantResponse, sessionId } = await sendMessage(payload);
340
+ onMessage?.(displayTextFromSendPayload(payload), assistantResponse, sessionId);
341
341
  }
342
342
  finally {
343
343
  setOutboundLoadingLabel(undefined);
@@ -474,8 +474,8 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
474
474
  }
475
475
  setOutboundLoadingLabel(loadingLabelFromSendPayload(payload));
476
476
  try {
477
- const assistantResponse = await sendMessage(payload);
478
- onMessage?.(displayTextFromSendPayload(payload), assistantResponse);
477
+ const { response: assistantResponse, sessionId } = await sendMessage(payload);
478
+ onMessage?.(displayTextFromSendPayload(payload), assistantResponse, sessionId);
479
479
  }
480
480
  finally {
481
481
  setOutboundLoadingLabel(undefined);
@@ -916,6 +916,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
916
916
  scriptContinueLabel,
917
917
  onScriptContinue,
918
918
  renderMessageChart,
919
+ renderSystemMessage,
919
920
  showSyntheticBranchButtons,
920
921
  unusedBranchKeys,
921
922
  showInlinePresets,
@@ -3,6 +3,7 @@ import cn from 'classnames';
3
3
  import { CsvIcon } from '../../icons/CsvIcon/CsvIcon.js';
4
4
  import { CardHeader, CardAction } from '../Card/Card.js';
5
5
  import { XIcon, FileTextIcon } from '@phosphor-icons/react';
6
+ import { LayoutDashboard } from 'lucide-react';
6
7
  import S from './FileChip.styl.js';
7
8
 
8
9
  const FORMAT_ICON_SIZE = 32;
@@ -10,6 +11,9 @@ function FormatIcon({ format }) {
10
11
  if (format === 'csv') {
11
12
  return jsx(CsvIcon, { size: FORMAT_ICON_SIZE });
12
13
  }
14
+ if (format === 'dashboard') {
15
+ return (jsx(LayoutDashboard, { size: FORMAT_ICON_SIZE, "aria-hidden": true, style: { color: 'var(--muted-foreground)' } }));
16
+ }
13
17
  return (jsx(FileTextIcon, { size: FORMAT_ICON_SIZE, "aria-hidden": true, style: { color: 'var(--muted-foreground)' } }));
14
18
  }
15
19
  function FileChipHeader({ name, format, hint, onRemove, }) {
@@ -86,6 +86,7 @@ function cloneMessagesForNewSession(messages) {
86
86
  .filter(message => !message.inProgress)
87
87
  .map(message => ({
88
88
  ...message,
89
+ ...(message.meta ? { meta: { ...message.meta } } : {}),
89
90
  ...(message.userTextFileAttachments
90
91
  ? {
91
92
  userTextFileAttachments: message.userTextFileAttachments.map(attachment => ({ ...attachment })),
@@ -217,6 +218,7 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
217
218
  ? { userTextFileAttachments: attachments }
218
219
  : {}),
219
220
  ...(options?.inProgress ? { inProgress: true } : {}),
221
+ ...(options?.meta ? { meta: { ...options.meta } } : {}),
220
222
  };
221
223
  setChats(prev => {
222
224
  const scopeChats = prev[scopeId] ?? [];
@@ -277,6 +279,9 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
277
279
  (patch.role != null && patch.role !== MessageRole.SYSTEM)) {
278
280
  delete next.inProgress;
279
281
  }
282
+ if (patch.meta != null) {
283
+ next.meta = { ...next.meta, ...patch.meta };
284
+ }
280
285
  return next;
281
286
  }),
282
287
  };
@@ -290,7 +295,10 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
290
295
  if (userSwitchKey === null)
291
296
  return;
292
297
  addScopeIdToRegistry(scopeId);
293
- const cloned = messages.map(message => ({ ...message }));
298
+ const cloned = messages.map(message => ({
299
+ ...message,
300
+ ...(message.meta ? { meta: { ...message.meta } } : {}),
301
+ }));
294
302
  setChats(prev => {
295
303
  const scopeChats = prev[scopeId] ?? [];
296
304
  const updatedChats = scopeChats.map(chat => {
@@ -303,6 +311,24 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
303
311
  return { ...prev, [scopeId]: updatedChats };
304
312
  });
305
313
  }, [userSwitchKey]);
314
+ const updateChatMeta = useCallback((scopeId, chatId, patch) => {
315
+ if (userSwitchKey === null)
316
+ return;
317
+ setChats(prev => {
318
+ const scopeChats = prev[scopeId] ?? [];
319
+ const updatedChats = scopeChats.map(chat => {
320
+ if (chat.session_id !== chatId)
321
+ return chat;
322
+ return {
323
+ ...chat,
324
+ meta: { ...chat.meta, ...patch },
325
+ };
326
+ });
327
+ const chatsKey = getChatsKey(scopeId);
328
+ LS.set(chatsKey, updatedChats);
329
+ return { ...prev, [scopeId]: updatedChats };
330
+ });
331
+ }, [userSwitchKey]);
306
332
  const sendMessage = useCallback(async (scopeId, message, chatId) => {
307
333
  const targetChatId = chatId ?? getCurrentChatId(scopeId);
308
334
  if (targetChatId === null || targetChatId === '') {
@@ -321,6 +347,9 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
321
347
  beginOutboundPending(scopeId, pendingChatSessionId);
322
348
  try {
323
349
  const data = await sendChatMessageFn(apiPayload, pendingChatSessionId);
350
+ const effectiveSessionId = data.session_id && data.session_id !== pendingChatSessionId
351
+ ? data.session_id
352
+ : pendingChatSessionId;
324
353
  if (data.session_id && data.session_id !== pendingChatSessionId) {
325
354
  setChats(prev => {
326
355
  const scopeChats = prev[scopeId] ?? [];
@@ -333,8 +362,8 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
333
362
  });
334
363
  setCurrentChatId(scopeId, data.session_id);
335
364
  }
336
- addMessage(scopeId, data.session_id ? data.session_id : pendingChatSessionId, MessageRole.ASSISTANT, data.response);
337
- return data.response;
365
+ addMessage(scopeId, effectiveSessionId, MessageRole.ASSISTANT, data.response);
366
+ return { response: data.response, sessionId: effectiveSessionId };
338
367
  }
339
368
  catch (error) {
340
369
  const errorMessage = error instanceof Error
@@ -381,6 +410,7 @@ function ChatProvider({ children, userSwitchKey, sendChatMessage: sendChatMessag
381
410
  removeMessageById,
382
411
  updateMessageById,
383
412
  setChatMessages,
413
+ updateChatMeta,
384
414
  sendMessage,
385
415
  getChatsForScopeId,
386
416
  getCurrentChatId,
@@ -441,7 +471,7 @@ function useSyncChatPanelBusy(isLoading) {
441
471
  }, [isLoading, acquirePanelBusy, releasePanelBusy]);
442
472
  }
443
473
  function useChatsForScopeId(scopeId) {
444
- const { getChatsForScopeId, getCurrentChatId, setCurrentChatId, newChat, addMessage, removeMessageById, updateMessageById, sendMessage, deleteChat, } = useChats();
474
+ const { getChatsForScopeId, getCurrentChatId, setCurrentChatId, newChat, addMessage, removeMessageById, updateMessageById, updateChatMeta, setChatMessages, sendMessage, deleteChat, } = useChats();
445
475
  const chats = getChatsForScopeId(scopeId);
446
476
  const currentChatId = getCurrentChatId(scopeId);
447
477
  const currentChat = useChat(scopeId, currentChatId ?? undefined);
@@ -457,6 +487,8 @@ function useChatsForScopeId(scopeId) {
457
487
  removeMessageById: (chatId, messageId) => removeMessageById(scopeId, chatId, messageId),
458
488
  updateMessageById: (chatId, messageId, patch) => updateMessageById(scopeId, chatId, messageId, patch),
459
489
  sendMessage: (message, chatId) => sendMessage(scopeId, message, chatId),
490
+ updateChatMeta: (chatId, patch) => updateChatMeta(scopeId, chatId, patch),
491
+ setChatMessages: (chatId, messages) => setChatMessages(scopeId, chatId, messages),
460
492
  deleteChat: (sessionId) => deleteChat(scopeId, sessionId),
461
493
  };
462
494
  }
package/dist/esm/index.js CHANGED
@@ -29,6 +29,7 @@ export { ChatChrome } from './components/ui/Chat/ChatChrome/ChatChrome.js';
29
29
  export { TEXT_ATTACHMENT_ACCEPT_PARTS, filterToTextAttachments } from './components/ui/Chat/chatAttachmentAccept.js';
30
30
  export { buildChatSendMessagePayload, displayTextFromSendPayload, normalizeUserTextFileAttachments } from './components/ui/Chat/buildChatSendMessagePayload.js';
31
31
  export { sanitizeAttachmentFilename } from './components/ui/Chat/sanitizeAttachmentFilename.js';
32
+ export { ChatSelector } from './components/ui/Chat/ChatSheet/ChatSelector.js';
32
33
  export { ChatSheet } from './components/ui/Chat/ChatSheet/ChatSheet.js';
33
34
  export { useChatPanelChromeModel } from './components/ui/Chat/ChatSheet/useChatPanelChromeModel.js';
34
35
  export { ChatMessage } from './components/ui/Chat/ChatMessage/ChatMessage.js';
@@ -2,7 +2,7 @@ import { ChatPresets } from '#uilib/components/ui/Chat/ChatPresets';
2
2
  import type { ChatProps } from './Chat.types';
3
3
  import { ChatEmptyState } from './ChatEmptyState/ChatEmptyState';
4
4
  import { ChatMessage } from './ChatMessage';
5
- export declare function Chat({ children, className, isEmpty, scopeId, onChatDeleted, onNewChat, ...props }: ChatProps): import("react/jsx-runtime").JSX.Element;
5
+ export declare function Chat({ children, className, isEmpty, scopeId, onChatDeleted, onNewChat, hideChatSelector, ...props }: ChatProps): import("react/jsx-runtime").JSX.Element;
6
6
  export declare namespace Chat {
7
7
  var Prompt: import("react").ForwardRefExoticComponent<import("./Chat.types").ChatPromptProps & import("react").RefAttributes<import(".").ChatPromptComposerHandle>>;
8
8
  var Message: typeof ChatMessage;
@@ -22,6 +22,8 @@ export type ChatSendMessagePayload = {
22
22
  /** Shimmer label while waiting on the API; defaults to "Thinking...". */
23
23
  loadingLabel?: string;
24
24
  };
25
+ export type ChatMetaValue = string | number | boolean | null;
26
+ export type ChatMeta = Record<string, ChatMetaValue>;
25
27
  export interface Message {
26
28
  id: string;
27
29
  role: MessageRole;
@@ -30,11 +32,13 @@ export interface Message {
30
32
  userTextFileAttachments?: UserTextFileAttachment[];
31
33
  /** SYSTEM-only: transient progress placeholder while work is in flight. */
32
34
  inProgress?: boolean;
35
+ meta?: ChatMeta;
33
36
  }
34
37
  export interface Chat {
35
38
  session_id: string;
36
39
  name: string;
37
40
  messages: Message[];
41
+ meta?: ChatMeta;
38
42
  }
39
43
  export interface ChatPreset {
40
44
  id: string;
@@ -111,6 +115,10 @@ export interface ChatMessageProps {
111
115
  onScriptContinue?: () => void;
112
116
  /** Renders `[CHART]` placeholder in assistant messages (app supplies dataset chart). */
113
117
  renderMessageChart?: () => ReactNode;
118
+ /** Full message for system-role render delegation. */
119
+ message?: Message;
120
+ /** When set, SYSTEM messages render via this callback instead of plain text. */
121
+ renderSystemMessage?: (message: Message) => ReactNode;
114
122
  }
115
123
  export interface ChatProps extends HTMLAttributes<HTMLDivElement> {
116
124
  children: ReactNode;
@@ -121,4 +129,6 @@ export interface ChatProps extends HTMLAttributes<HTMLDivElement> {
121
129
  onChatDeleted?: (sessionId: string) => void;
122
130
  /** "+ New Chat" in the selector; when omitted, starts an empty session. */
123
131
  onNewChat?: () => void;
132
+ /** When true, skip built-in header ChatSelector (e.g. external page-header slot). */
133
+ hideChatSelector?: boolean;
124
134
  }
@@ -1,2 +1,2 @@
1
1
  import type { ChatChromeProps } from './ChatChrome.types';
2
- export declare function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, loadingLabel, scriptContinueLabel, onScriptContinue, renderMessageChart, showSyntheticBranchButtons, unusedBranchKeys, showInlinePresets, isLastMessageFromUser, scrollRef, effectiveScopeId, onPromptSubmit, onChatDeleted, onNewChat, promptPrefill, footerClassName, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, promptPlaceholder, }: ChatChromeProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, loadingLabel, scriptContinueLabel, onScriptContinue, renderMessageChart, renderSystemMessage, showSyntheticBranchButtons, unusedBranchKeys, showInlinePresets, isLastMessageFromUser, scrollRef, effectiveScopeId, onPromptSubmit, onChatDeleted, onNewChat, promptPrefill, footerClassName, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, promptPlaceholder, hideChatSelector, }: ChatChromeProps): import("react/jsx-runtime").JSX.Element;
@@ -28,6 +28,8 @@ export interface ChatChromeProps {
28
28
  scriptContinueLabel: string | undefined;
29
29
  onScriptContinue: (() => void) | undefined;
30
30
  renderMessageChart?: () => React.ReactNode;
31
+ /** When set, SYSTEM messages render via this callback instead of plain text. */
32
+ renderSystemMessage?: (message: Message) => React.ReactNode;
31
33
  showSyntheticBranchButtons: boolean;
32
34
  unusedBranchKeys: string[];
33
35
  showInlinePresets: boolean;
@@ -54,4 +56,6 @@ export interface ChatChromeProps {
54
56
  onSlashItemCommand?: SlashOnItemCommand;
55
57
  /** Composer placeholder forwarded to `Chat.Prompt`. */
56
58
  promptPlaceholder?: string;
59
+ /** When true, skip built-in header ChatSelector (e.g. external page-header slot). */
60
+ hideChatSelector?: boolean;
57
61
  }
@@ -1,2 +1,2 @@
1
1
  import { type ChatMessageProps } from '../Chat.types';
2
- export declare function ChatMessage({ role, text, inProgress, userTextFileAttachments, onQuickReply, suppressedQuickReplyKeys, quickReplyDisabled, quickReplyHidden, isLastMessage, scriptContinue, onScriptContinue, renderMessageChart, }: ChatMessageProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function ChatMessage({ role, text, inProgress, userTextFileAttachments, onQuickReply, suppressedQuickReplyKeys, quickReplyDisabled, quickReplyHidden, isLastMessage, scriptContinue, onScriptContinue, renderMessageChart, message, renderSystemMessage, }: ChatMessageProps): import("react/jsx-runtime").JSX.Element;
@@ -1,8 +1,9 @@
1
1
  export interface ChatSelectorProps {
2
2
  id: string;
3
+ wrapperClassName?: string;
3
4
  className?: string;
4
5
  onChatDeleted?: (sessionId: string) => void;
5
6
  /** When set, used for "+ New Chat" instead of the default empty `newChat()`. */
6
7
  onNewChat?: () => void;
7
8
  }
8
- export declare function ChatSelector({ id, className, onChatDeleted, onNewChat, }: ChatSelectorProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function ChatSelector({ id, wrapperClassName, className, onChatDeleted, onNewChat, }: ChatSelectorProps): import("react/jsx-runtime").JSX.Element;
@@ -21,4 +21,4 @@ export interface ChatSheetProps extends Omit<UseChatPanelChromeModelInput, 'embe
21
21
  */
22
22
  inline?: boolean;
23
23
  }
24
- export declare function ChatSheet({ triggerLabel, triggerAriaLabel, actionsRef, renderTrigger, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, transformSendPayload, inline, }: ChatSheetProps): import("react/jsx-runtime").JSX.Element;
24
+ export declare function ChatSheet({ triggerLabel, triggerAriaLabel, actionsRef, renderTrigger, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, renderSystemMessage, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, transformSendPayload, inline, }: ChatSheetProps): import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,4 @@
1
- import { ChatPreset, type ChatSendMessagePayload, type ScriptCompletePayload } from '#uilib/components/ui/Chat/Chat.types';
1
+ import { ChatPreset, type ChatSendMessagePayload, type Message, type ScriptCompletePayload } from '#uilib/components/ui/Chat/Chat.types';
2
2
  import type { SlashCommandItem, SlashOnItemCommand } from '#uilib/tiptap/slash-mention/types';
3
3
  import type { ChatChromeProps } from '../ChatChrome';
4
4
  import type { ChatAttachmentDropItem } from '../ChatChrome/ChatChrome.types';
@@ -10,11 +10,13 @@ export type UseChatPanelChromeModelInput = {
10
10
  /** Composite chat scope (e.g. `${userId}-${datasetId}`, `${userId}-dashboard`, `${userId}-report-${reportId}`). */
11
11
  scopeId?: string | null;
12
12
  /** Fires after send; second arg is the assistant reply when the API call succeeded. */
13
- onMessage?: (displayText: string, assistantResponse?: string) => void;
13
+ onMessage?: (displayText: string, assistantResponse?: string, chatSessionId?: string) => void;
14
14
  /** Fires when a preset script has no further `[Label|branchKey]` steps (graph leaf or linear script end). */
15
15
  onScriptComplete?: (payload: ScriptCompletePayload) => void;
16
16
  /** Renders `[CHART]` tokens in assistant messages. */
17
17
  renderMessageChart?: () => React.ReactNode;
18
+ /** When set, SYSTEM messages render via this callback instead of plain text. */
19
+ renderSystemMessage?: (message: Message) => React.ReactNode;
18
20
  /** Forwarded to `ChatChrome` when the thread is empty. */
19
21
  emptyState?: ChatEmptyStateConfig;
20
22
  /** MIME types / extensions for text-only chat attachments (filtered by uilib allowlist). */
@@ -44,4 +46,4 @@ export type UseChatPanelChromeModelResult = {
44
46
  openNewChatWithPrefill: (prompt: string) => void;
45
47
  chatPanelContainer: HTMLElement | null;
46
48
  };
47
- export declare function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, copyHistoryOnNewChat, transformSendPayload, }: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult;
49
+ export declare function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, renderSystemMessage, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, copyHistoryOnNewChat, transformSendPayload, }: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult;
@@ -5,6 +5,8 @@ export type { ChatChromeProps, ChatChromeResizeHandleConfig, } from './ChatChrom
5
5
  export { TEXT_ATTACHMENT_ACCEPT_PARTS, filterToTextAttachments, } from './chatAttachmentAccept';
6
6
  export { buildChatSendMessagePayload, displayTextFromSendPayload, normalizeUserTextFileAttachments, } from './buildChatSendMessagePayload';
7
7
  export { sanitizeAttachmentFilename } from './sanitizeAttachmentFilename';
8
+ export { ChatSelector } from './ChatSheet/ChatSelector';
9
+ export type { ChatSelectorProps } from './ChatSheet/ChatSelector';
8
10
  export { ChatSheet } from './ChatSheet/ChatSheet';
9
11
  export { useChatPanelChromeModel } from './ChatSheet/useChatPanelChromeModel';
10
12
  export type { ChatSheetActions, ChatSheetProps } from './ChatSheet/ChatSheet';
@@ -16,7 +18,7 @@ export { CHAT_PROMPT_COMMAND_CHIP_CLASS, chatPromptChipHtml, createChatPromptCom
16
18
  export type { ChatPromptComposerInsertOptions } from './ChatPrompt/ChatPromptComposer.types';
17
19
  export { ChatPresets } from './ChatPresets';
18
20
  export type { ChatEmptyStateConfig, ChatEmptyStateContext, ChatEmptyStateProps, } from './ChatEmptyState/ChatEmptyState.types';
19
- export type { Chat as ChatType, ChatAttachmentDropItem, ChatSendMessagePayload, ChatProps, ChatPreset as ChatPresetType, Message, UserTextFileAttachment, } from './Chat.types';
21
+ export type { Chat as ChatType, ChatAttachmentDropItem, ChatMeta, ChatMetaValue, ChatSendMessagePayload, ChatProps, ChatPreset as ChatPresetType, Message, UserTextFileAttachment, } from './Chat.types';
20
22
  export { MessageRole } from './Chat.types';
21
23
  export type { SlashCommandItem, SlashItemCommandContext, SlashOnItemCommand, } from '#uilib/tiptap/slash-mention/types';
22
24
  export { CsvIcon } from '../../icons/CsvIcon/CsvIcon';
@@ -1,4 +1,4 @@
1
- export type FileChipFormat = 'csv' | 'pdf' | 'text';
1
+ export type FileChipFormat = 'csv' | 'pdf' | 'text' | 'dashboard';
2
2
  export type FileChipProps = {
3
3
  name: string;
4
4
  format: FileChipFormat;
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
- import { type Chat, type ChatSendMessagePayload, type Message, MessageRole, type UserTextFileAttachment } from '#uilib/components/ui/Chat/Chat.types';
2
+ import { type Chat, type ChatMeta, type ChatSendMessagePayload, type Message, MessageRole, type UserTextFileAttachment } from '#uilib/components/ui/Chat/Chat.types';
3
3
  import type { ChatResponse } from '#uilib/types/chat-api.types';
4
4
  export type SendChatMessageFn = (message: string, targetChatId: string) => Promise<ChatResponse>;
5
5
  export type { ChatSendMessagePayload, UserTextFileAttachment, } from '#uilib/components/ui/Chat/Chat.types';
@@ -7,16 +7,22 @@ export type AddChatMessageOptions = {
7
7
  userTextFileAttachments?: UserTextFileAttachment[];
8
8
  /** SYSTEM-only: mark as transient progress placeholder (shimmer until removed). */
9
9
  inProgress?: boolean;
10
+ meta?: ChatMeta;
10
11
  };
11
12
  export type UpdateChatMessagePatch = {
12
13
  role?: MessageRole;
13
14
  text?: string;
14
15
  inProgress?: boolean;
16
+ meta?: ChatMeta;
15
17
  };
16
18
  export type NewChatOptions = {
17
19
  /** When set, seeds the new session's messages (e.g. continue dialog in reports). */
18
20
  seedMessages?: readonly Message[];
19
21
  };
22
+ export type SendMessageResult = {
23
+ response: string;
24
+ sessionId: string;
25
+ };
20
26
  export interface ChatContextType {
21
27
  /** Returns the new session id, or undefined if no user / not created. */
22
28
  newChat: (scopeId: string, options?: NewChatOptions) => string | undefined;
@@ -26,7 +32,9 @@ export interface ChatContextType {
26
32
  updateMessageById: (scopeId: string, chatId: string, messageId: string, patch: UpdateChatMessagePatch) => void;
27
33
  /** Replaces all messages on a session (e.g. seeding from another chat). */
28
34
  setChatMessages: (scopeId: string, chatId: string, messages: Message[]) => void;
29
- sendMessage: (scopeId: string, message: string | ChatSendMessagePayload, chatId?: string) => Promise<string>;
35
+ /** Shallow-merge keys into `chat.meta` and persist. */
36
+ updateChatMeta: (scopeId: string, chatId: string, patch: ChatMeta) => void;
37
+ sendMessage: (scopeId: string, message: string | ChatSendMessagePayload, chatId?: string) => Promise<SendMessageResult>;
30
38
  getChatsForScopeId: (scopeId: string) => Chat[];
31
39
  getCurrentChatId: (scopeId: string) => string | null;
32
40
  deleteChat: (scopeId: string, sessionId: string) => void;
@@ -68,7 +76,9 @@ export declare function useChatsForScopeId(scopeId: string): {
68
76
  addMessage: (chatId: string, role: MessageRole, text: string, options?: AddChatMessageOptions) => string;
69
77
  removeMessageById: (chatId: string, messageId: string) => void;
70
78
  updateMessageById: (chatId: string, messageId: string, patch: UpdateChatMessagePatch) => void;
71
- sendMessage: (message: string | ChatSendMessagePayload, chatId?: string) => Promise<string>;
79
+ sendMessage: (message: string | ChatSendMessagePayload, chatId?: string) => Promise<SendMessageResult>;
80
+ updateChatMeta: (chatId: string, patch: ChatMeta) => void;
81
+ setChatMessages: (chatId: string, messages: Message[]) => void;
72
82
  deleteChat: (sessionId: string) => void;
73
83
  };
74
84
  /** @deprecated Use useChatsForScopeId */
@@ -82,7 +92,9 @@ export declare function useChatsForDataset(scopeId: string): {
82
92
  addMessage: (chatId: string, role: MessageRole, text: string, options?: AddChatMessageOptions) => string;
83
93
  removeMessageById: (chatId: string, messageId: string) => void;
84
94
  updateMessageById: (chatId: string, messageId: string, patch: UpdateChatMessagePatch) => void;
85
- sendMessage: (message: string | ChatSendMessagePayload, chatId?: string) => Promise<string>;
95
+ sendMessage: (message: string | ChatSendMessagePayload, chatId?: string) => Promise<SendMessageResult>;
96
+ updateChatMeta: (chatId: string, patch: ChatMeta) => void;
97
+ setChatMessages: (chatId: string, messages: Message[]) => void;
86
98
  deleteChat: (sessionId: string) => void;
87
99
  };
88
100
  export declare function useCurrentChat(scopeId: string): Chat;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sybilion/uilib",
3
- "version": "1.3.78",
3
+ "version": "1.3.79",
4
4
  "description": "Sybilion Design System — React UI components (Webpack + Stylus)",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -1,6 +1,7 @@
1
1
  .root
2
2
  display flex
3
3
  flex-direction column
4
+ flex 1
4
5
  height 100%
5
6
  min-height 0
6
7
  // max-height 100vh
@@ -15,4 +16,6 @@
15
16
  padding-right var(--p-12) // to not overlap with ChatSheet close button
16
17
 
17
18
  .isEmpty
18
- padding-bottom 170px // goes under prompt
19
+ flex 1
20
+ min-height 0
21
+ overflow hidden
@@ -16,11 +16,12 @@ export function Chat({
16
16
  scopeId,
17
17
  onChatDeleted,
18
18
  onNewChat,
19
+ hideChatSelector = false,
19
20
  ...props
20
21
  }: ChatProps) {
21
22
  return (
22
23
  <div className={cn(S.root, className, isEmpty && S.isEmpty)} {...props}>
23
- {scopeId ? (
24
+ {scopeId && !hideChatSelector ? (
24
25
  <div className={S.header}>
25
26
  <ChatSelector
26
27
  id={scopeId}