@sybilion/uilib 1.3.88 → 1.3.90
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/dist/esm/components/ui/Chat/ChatMessage/AgentMessageContent.helpers.js +29 -21
- package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.js +4 -1
- package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +17 -10
- package/dist/esm/contexts/chat-context.js +180 -205
- package/dist/esm/contexts/chatPersistence.js +6 -18
- package/dist/esm/contexts/chatSessionStorage.js +245 -0
- package/dist/esm/lib/dashboard-spec/jsonDashboardFence.js +80 -0
- package/dist/esm/types/src/components/ui/Chat/ChatMessage/AgentMessageContent.helpers.d.ts +1 -5
- package/dist/esm/types/src/components/ui/Chat/ChatMessage/AgentMessageContent.helpers.test.d.ts +1 -0
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.d.ts +6 -1
- package/dist/esm/types/src/contexts/chat-context.d.ts +6 -0
- package/dist/esm/types/src/contexts/chatPersistence.d.ts +4 -1
- package/dist/esm/types/src/contexts/chatSessionStorage.d.ts +32 -0
- package/dist/esm/types/src/contexts/chatSessionStorage.test.d.ts +1 -0
- package/dist/esm/types/src/lib/dashboard-spec/jsonDashboardFence.d.ts +9 -0
- package/dist/esm/types/src/lib/dashboard-spec/stripJsonDashboardFences.d.ts +1 -2
- package/dist/esm/types/src/lib/dashboard-spec/stripJsonDashboardFences.test.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/ui/Chat/ChatMessage/AgentMessageContent.helpers.test.tsx +25 -0
- package/src/components/ui/Chat/ChatMessage/AgentMessageContent.helpers.tsx +35 -26
- package/src/components/ui/Chat/ChatMessage/ChatMessage.tsx +7 -1
- package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +21 -8
- package/src/contexts/chat-context.tsx +253 -220
- package/src/contexts/chatPersistence.test.ts +11 -0
- package/src/contexts/chatPersistence.ts +22 -6
- package/src/contexts/chatSessionStorage.test.ts +125 -0
- package/src/contexts/chatSessionStorage.ts +321 -0
- package/src/lib/dashboard-spec/jsonDashboardFence.ts +98 -0
- package/src/lib/dashboard-spec/stripJsonDashboardFences.test.ts +84 -0
- package/src/lib/dashboard-spec/stripJsonDashboardFences.ts +5 -6
- package/dist/esm/lib/dashboard-spec/stripJsonDashboardFences.js +0 -7
|
@@ -4,27 +4,6 @@ import logger from '../../../../lib/logger.js';
|
|
|
4
4
|
import S from './ChatMessage.styl.js';
|
|
5
5
|
import S$1 from '../../InteractiveContent/InteractiveContent.styl.js';
|
|
6
6
|
|
|
7
|
-
const injectHeaders = (content) => {
|
|
8
|
-
// Match #, ##, ###, or #### headers at start of line
|
|
9
|
-
const regex = /(^|\n)(#{1,4})\s+(.+?)(?=\n|$)/m;
|
|
10
|
-
const matches = content.match(regex);
|
|
11
|
-
if (!matches)
|
|
12
|
-
return null;
|
|
13
|
-
const level = matches[2].length;
|
|
14
|
-
const headerText = matches[3].replace(/^\*+|\*+$/g, '');
|
|
15
|
-
const Tag = level === 1 ? 'h1' : level === 2 ? 'h2' : level === 3 ? 'h3' : 'h4';
|
|
16
|
-
// Calculate the actual match position and length
|
|
17
|
-
// matches[0] includes the leading \n if present, but we want to replace from the # position
|
|
18
|
-
const hasLeadingNewline = matches[1] === '\n';
|
|
19
|
-
const startIndex = matches.index + (hasLeadingNewline ? 1 : 0);
|
|
20
|
-
// Length is: # markers + space + header text (excluding leading newline)
|
|
21
|
-
const length = matches[2].length + 1 + matches[3].length;
|
|
22
|
-
return {
|
|
23
|
-
elem: jsx(Tag, { children: headerText }),
|
|
24
|
-
index: startIndex,
|
|
25
|
-
length: length,
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
7
|
/** Match sits inside list/table HTML (those blocks use dangerouslySetInnerHTML elsewhere). */
|
|
29
8
|
const isInsideHtmlListOrTable = (content, matchStartIndex) => {
|
|
30
9
|
const before = content.substring(0, matchStartIndex);
|
|
@@ -402,6 +381,35 @@ const injectAnchor = (content) => {
|
|
|
402
381
|
length: matches[0].length,
|
|
403
382
|
};
|
|
404
383
|
};
|
|
384
|
+
const headerTextInjectors = [
|
|
385
|
+
injectAnchor,
|
|
386
|
+
injectMarkdownLink,
|
|
387
|
+
injectHTMLTags,
|
|
388
|
+
injectBold,
|
|
389
|
+
injectItalic,
|
|
390
|
+
injectAutolinkUrl,
|
|
391
|
+
];
|
|
392
|
+
const injectHeaders = (content) => {
|
|
393
|
+
// Match #, ##, ###, or #### headers at start of line
|
|
394
|
+
const regex = /(^|\n)(#{1,4})\s+(.+?)(?=\n|$)/m;
|
|
395
|
+
const matches = content.match(regex);
|
|
396
|
+
if (!matches)
|
|
397
|
+
return null;
|
|
398
|
+
const level = matches[2].length;
|
|
399
|
+
const headerText = matches[3].replace(/^\*+|\*+$/g, '');
|
|
400
|
+
const Tag = level === 1 ? 'h1' : level === 2 ? 'h2' : level === 3 ? 'h3' : 'h4';
|
|
401
|
+
// Calculate the actual match position and length
|
|
402
|
+
// matches[0] includes the leading \n if present, but we want to replace from the # position
|
|
403
|
+
const hasLeadingNewline = matches[1] === '\n';
|
|
404
|
+
const startIndex = matches.index + (hasLeadingNewline ? 1 : 0);
|
|
405
|
+
// Length is: # markers + space + header text (excluding leading newline)
|
|
406
|
+
const length = matches[2].length + 1 + matches[3].length;
|
|
407
|
+
return {
|
|
408
|
+
elem: jsx(Tag, { children: runFormattingPipeline(headerText, headerTextInjectors) }),
|
|
409
|
+
index: startIndex,
|
|
410
|
+
length: length,
|
|
411
|
+
};
|
|
412
|
+
};
|
|
405
413
|
const applyFormatting = (text) => runFormattingPipeline(text, [
|
|
406
414
|
injectHeaders,
|
|
407
415
|
injectAnchor,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import cn from 'classnames';
|
|
3
|
+
import { useMemo } from 'react';
|
|
3
4
|
import { InteractiveContent } from '../../InteractiveContent/InteractiveContent.js';
|
|
4
5
|
import 'lucide-react';
|
|
5
6
|
import '../../InteractiveContent/InteractiveContent.styl.js';
|
|
6
7
|
import { TextShimmer } from '../../TextShimmer/TextShimmer.js';
|
|
8
|
+
import { stripJsonDashboardFences } from '../../../../lib/dashboard-spec/jsonDashboardFence.js';
|
|
7
9
|
import { MessageRole } from '../Chat.types.js';
|
|
8
10
|
import { userTextFileAttachmentsFromMessage } from '../userTextFileAttachments.js';
|
|
9
11
|
import { AgentMessageContent } from './AgentMessageContent.js';
|
|
@@ -16,7 +18,8 @@ function ChatMessage({ role, text, inProgress, userTextFileAttachments, onQuickR
|
|
|
16
18
|
});
|
|
17
19
|
const isAssistant = role === MessageRole.ASSISTANT;
|
|
18
20
|
const isSystem = role === MessageRole.SYSTEM;
|
|
19
|
-
|
|
21
|
+
const assistantDisplayText = useMemo(() => (isAssistant ? stripJsonDashboardFences(text) : text), [isAssistant, text]);
|
|
22
|
+
return (jsx("div", { className: cn(S.root, S[`role-${role}`], className), children: isSystem ? (jsx("div", { className: cn(S.text, textClassName), children: inProgress ? (jsx(TextShimmer, { as: "span", children: text })) : renderSystemMessage && message ? (renderSystemMessage(message)) : (text) })) : isAssistant ? (jsx(AgentMessageContent, { text: assistantDisplayText, textClassName: textClassName, 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: cn(S.text, textClassName && S.textCustom, textClassName), children: jsx(InteractiveContent, { text: text }) }), fileAttachments.map(attachment => (jsx(UserTextFileAttachmentBubble, { attachment: attachment }, `${attachment.displayName}:${attachment.filename}`)))] })) }));
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
export { ChatMessage };
|
|
@@ -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, renderSystemMessage, 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, submitPresetsViaApi = false, }) {
|
|
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();
|
|
@@ -350,7 +350,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
350
350
|
setOutboundLoadingLabel(loadingLabelFromSendPayload(payload));
|
|
351
351
|
try {
|
|
352
352
|
const { response: assistantResponse, sessionId } = await sendMessage(payload);
|
|
353
|
-
onMessage?.(displayTextFromSendPayload(payload), assistantResponse, sessionId);
|
|
353
|
+
await onMessage?.(displayTextFromSendPayload(payload), assistantResponse, sessionId);
|
|
354
354
|
}
|
|
355
355
|
finally {
|
|
356
356
|
setOutboundLoadingLabel(undefined);
|
|
@@ -488,7 +488,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
488
488
|
setOutboundLoadingLabel(loadingLabelFromSendPayload(payload));
|
|
489
489
|
try {
|
|
490
490
|
const { response: assistantResponse, sessionId } = await sendMessage(payload);
|
|
491
|
-
onMessage?.(displayTextFromSendPayload(payload), assistantResponse, sessionId);
|
|
491
|
+
await onMessage?.(displayTextFromSendPayload(payload), assistantResponse, sessionId);
|
|
492
492
|
}
|
|
493
493
|
finally {
|
|
494
494
|
setOutboundLoadingLabel(undefined);
|
|
@@ -523,14 +523,19 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
523
523
|
const scriptGraph = isPresetScriptGraph(script);
|
|
524
524
|
const hasLinearScript = Array.isArray(script) && script.length > 0;
|
|
525
525
|
const hasReplies = preset.replies && Object.keys(preset.replies).length > 0;
|
|
526
|
-
const isLocalDemo =
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
526
|
+
const isLocalDemo = !submitPresetsViaApi &&
|
|
527
|
+
(hasLinearScript ||
|
|
528
|
+
scriptGraph ||
|
|
529
|
+
Boolean(preset.answer?.trim()) ||
|
|
530
|
+
Boolean(hasReplies));
|
|
530
531
|
if (!isLocalDemo) {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
532
|
+
let chatId = currentChatId;
|
|
533
|
+
if (!chatId) {
|
|
534
|
+
chatId = startEmptyNewChat() ?? undefined;
|
|
535
|
+
if (!chatId)
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
endLocalDemoFlow(chatId);
|
|
534
539
|
await handlePromptSubmit(options?.message ?? preset.text);
|
|
535
540
|
return;
|
|
536
541
|
}
|
|
@@ -613,10 +618,12 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
613
618
|
}
|
|
614
619
|
}, [
|
|
615
620
|
currentChatId,
|
|
621
|
+
startEmptyNewChat,
|
|
616
622
|
endLocalDemoFlow,
|
|
617
623
|
handlePromptSubmit,
|
|
618
624
|
addMessage,
|
|
619
625
|
presetsWithFreeform,
|
|
626
|
+
submitPresetsViaApi,
|
|
620
627
|
]);
|
|
621
628
|
const resolvedEmptyState = useMemo(() => {
|
|
622
629
|
if (!emptyState)
|