@sybilion/uilib 1.3.20 → 1.3.22
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/ChatSheet/useChatPanelChromeModel.js +21 -4
- package/dist/esm/components/ui/Dialog/Dialog.js +10 -2
- package/dist/esm/constants/appMount.js +5 -0
- package/dist/esm/types/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.types.d.ts +9 -1
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.d.ts +2 -2
- package/dist/esm/types/src/components/ui/Chat/index.d.ts +1 -0
- package/dist/esm/types/src/constants/appMount.d.ts +4 -0
- package/package.json +1 -1
- package/src/components/ui/Chat/ChatEmptyState/ChatEmptyState.types.ts +17 -1
- package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +23 -4
- package/src/components/ui/Chat/index.ts +5 -0
- package/src/components/ui/Dialog/Dialog.tsx +19 -2
- package/src/constants/appMount.ts +5 -0
- package/src/docs/index.tsx +2 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
3
3
|
import { MessageRole, GENERATING_DASHBOARD_SYSTEM_TEXT } from '../Chat.types.js';
|
|
4
|
-
import { isGraphIntakeAssistantStepComplete, matchUserTextToQuickReply, parseScriptLine, textHasQuickReplyMarkers, branchKeysUsedFromChatHistory, branchKeysUsedByUserMessages, extractQuickReplyLabelKeyPairsFromText, entryBranchKeyBeforeLastAssistant
|
|
4
|
+
import { isGraphIntakeAssistantStepComplete, matchUserTextToQuickReply, isPresetScriptGraph, branchesFromPresetScriptGraph, parseScriptLine, textHasQuickReplyMarkers, branchKeysUsedFromChatHistory, branchKeysUsedByUserMessages, extractQuickReplyLabelKeyPairsFromText, entryBranchKeyBeforeLastAssistant } from '../ChatMessage/presetScript.js';
|
|
5
5
|
import { buildChatSendMessagePayload, displayTextFromSendPayload } from '../buildChatSendMessagePayload.js';
|
|
6
6
|
import { usedPresetIdsFromMessages, formatChatTranscript } from '../chat-preset-utils.js';
|
|
7
7
|
import { useChatsForScopeId, useChat, useChatOutboundPending, useSyncChatPanelBusy, isChatEmpty } from '../../../../contexts/chat-context.js';
|
|
@@ -425,7 +425,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
425
425
|
onMessage,
|
|
426
426
|
onScriptComplete,
|
|
427
427
|
]);
|
|
428
|
-
const submitPreset = async (preset) => {
|
|
428
|
+
const submitPreset = useCallback(async (preset) => {
|
|
429
429
|
const script = preset.script;
|
|
430
430
|
const scriptGraph = isPresetScriptGraph(script);
|
|
431
431
|
const hasLinearScript = Array.isArray(script) && script.length > 0;
|
|
@@ -518,7 +518,24 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
518
518
|
finally {
|
|
519
519
|
setLocalUiBusy(false);
|
|
520
520
|
}
|
|
521
|
-
}
|
|
521
|
+
}, [
|
|
522
|
+
currentChatId,
|
|
523
|
+
endLocalDemoFlow,
|
|
524
|
+
handlePromptSubmit,
|
|
525
|
+
addMessage,
|
|
526
|
+
presetsWithFreeform,
|
|
527
|
+
]);
|
|
528
|
+
const resolvedEmptyState = useMemo(() => {
|
|
529
|
+
if (!emptyState)
|
|
530
|
+
return undefined;
|
|
531
|
+
const { additionalContent, ...rest } = emptyState;
|
|
532
|
+
return {
|
|
533
|
+
...rest,
|
|
534
|
+
additionalContent: typeof additionalContent === 'function'
|
|
535
|
+
? additionalContent({ submitPreset })
|
|
536
|
+
: additionalContent,
|
|
537
|
+
};
|
|
538
|
+
}, [emptyState, submitPreset]);
|
|
522
539
|
const activeScript = currentChatId
|
|
523
540
|
? scriptByChatId[currentChatId]
|
|
524
541
|
: undefined;
|
|
@@ -765,7 +782,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
765
782
|
onPromptSubmit: handlePromptSubmit,
|
|
766
783
|
onChatDeleted: endLocalDemoFlow,
|
|
767
784
|
promptPrefill: promptLinkPrefill,
|
|
768
|
-
emptyState,
|
|
785
|
+
emptyState: resolvedEmptyState,
|
|
769
786
|
allowedAttachments,
|
|
770
787
|
allowPdfAttachments,
|
|
771
788
|
onAttachmentsDropped,
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import cn from 'classnames';
|
|
3
|
-
import React__default, { useState, useRef, useId, useEffect } from 'react';
|
|
3
|
+
import React__default, { useState, useRef, useId, useEffect, useLayoutEffect } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import { Card, CardHeader, CardContent, CardFooter } from '../Card/Card.js';
|
|
6
|
+
import { APP_MODAL_ID } from '../../../constants/appMount.js';
|
|
6
7
|
import { XIcon } from '@phosphor-icons/react';
|
|
7
8
|
import S from './Dialog.styl.js';
|
|
8
9
|
|
|
9
10
|
function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disableCloseButton, icon, content, contentClassName, footer, footerClassName, footerAlignment = 'center', size = 'default', className, noScroll, maxHeight, autoScrollBottom, width, }) {
|
|
10
11
|
const [isMounted, setIsMounted] = useState(false);
|
|
12
|
+
const [modalContainer, setModalContainer] = useState(null);
|
|
11
13
|
const [isAnimating, setIsAnimating] = useState(false);
|
|
12
14
|
const dialogRef = useRef(null);
|
|
13
15
|
const triggerRef = useRef(null);
|
|
@@ -19,6 +21,9 @@ function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disabl
|
|
|
19
21
|
setIsMounted(true);
|
|
20
22
|
return () => setIsMounted(false);
|
|
21
23
|
}, []);
|
|
24
|
+
useLayoutEffect(() => {
|
|
25
|
+
setModalContainer(document.getElementById(APP_MODAL_ID));
|
|
26
|
+
}, []);
|
|
22
27
|
// Handle open/close animations
|
|
23
28
|
useEffect(() => {
|
|
24
29
|
if (open) {
|
|
@@ -120,7 +125,10 @@ function Dialog({ open, onOpenChange, disabled, trigger, title, subtitle, disabl
|
|
|
120
125
|
: null,
|
|
121
126
|
}, children: [(title || subtitle || icon) && (jsx(CardHeader, { icon: icon, title: title, description: subtitle })), !disableCloseButton && (jsx("button", { className: S.dialogClose, onClick: handleCloseClick, "aria-label": "Close dialog", type: "button", children: jsx(XIcon, { size: 16 }) })), content && (jsx(CardContent, { className: contentClassName, noScroll: noScroll, autoScrollBottom: autoScrollBottom, children: content })), footer && (jsx(CardFooter, { className: cn(S.footer, S[`align-${footerAlignment}`], footerClassName), children: footer }))] })] }));
|
|
122
127
|
const onTriggerClick = !disabled ? () => onOpenChange(true) : undefined;
|
|
123
|
-
return (jsxs(Fragment, { children: [trigger && jsx("div", { onClick: onTriggerClick, children: trigger }), open &&
|
|
128
|
+
return (jsxs(Fragment, { children: [trigger && jsx("div", { onClick: onTriggerClick, children: trigger }), open &&
|
|
129
|
+
!disabled &&
|
|
130
|
+
modalContainer &&
|
|
131
|
+
createPortal(dialogContent, modalContainer)] }));
|
|
124
132
|
}
|
|
125
133
|
|
|
126
134
|
export { Dialog, S as DialogStyles };
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import type { ChatPreset } from '#uilib/components/ui/Chat/Chat.types';
|
|
2
|
+
export type ChatEmptyStateContext = {
|
|
3
|
+
submitPreset: (preset: ChatPreset) => void | Promise<void>;
|
|
4
|
+
};
|
|
1
5
|
export interface ChatEmptyStateProps {
|
|
2
6
|
icon?: React.ReactNode;
|
|
3
7
|
title?: string;
|
|
4
8
|
description?: string;
|
|
5
|
-
/** Extra block below description (
|
|
9
|
+
/** Extra block below description (resolved before render in `ChatEmptyState`). */
|
|
6
10
|
additionalContent?: React.ReactNode;
|
|
7
11
|
}
|
|
12
|
+
/** Passed to `ChatSheet` / `useChatPanelChromeModel`; function form resolved before render. */
|
|
13
|
+
export interface ChatEmptyStateConfig extends Omit<ChatEmptyStateProps, 'additionalContent'> {
|
|
14
|
+
additionalContent?: React.ReactNode | ((ctx: ChatEmptyStateContext) => React.ReactNode);
|
|
15
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ChatPreset, type ScriptCompletePayload } from '#uilib/components/ui/Chat/Chat.types';
|
|
2
2
|
import type { ChatChromeProps } from '../ChatChrome';
|
|
3
3
|
import type { ChatAttachmentDropItem } from '../ChatChrome/ChatChrome.types';
|
|
4
|
-
import type {
|
|
4
|
+
import type { ChatEmptyStateConfig } from '../ChatEmptyState/ChatEmptyState.types';
|
|
5
5
|
export type UseChatPanelChromeModelInput = {
|
|
6
6
|
/** When true, skip sidebar chat slot, URL `?chat=`, and portal behavior (e.g. page main content). */
|
|
7
7
|
embedAsPage: boolean;
|
|
@@ -16,7 +16,7 @@ export type UseChatPanelChromeModelInput = {
|
|
|
16
16
|
/** Renders `[CHART]` tokens in assistant messages. */
|
|
17
17
|
renderMessageChart?: () => React.ReactNode;
|
|
18
18
|
/** Forwarded to `ChatChrome` when the thread is empty. */
|
|
19
|
-
emptyState?:
|
|
19
|
+
emptyState?: ChatEmptyStateConfig;
|
|
20
20
|
/** MIME types / extensions for text-only chat attachments (filtered by uilib allowlist). */
|
|
21
21
|
allowedAttachments?: readonly string[];
|
|
22
22
|
/** When true, PDF drops are accepted and parsed to plain text. */
|
|
@@ -12,6 +12,7 @@ export type { UseChatPanelChromeModelInput, UseChatPanelChromeModelResult, } fro
|
|
|
12
12
|
export { ChatMessage } from './ChatMessage';
|
|
13
13
|
export { ChatPrompt } from './ChatPrompt';
|
|
14
14
|
export { ChatPresets } from './ChatPresets';
|
|
15
|
+
export type { ChatEmptyStateConfig, ChatEmptyStateContext, ChatEmptyStateProps, } from './ChatEmptyState/ChatEmptyState.types';
|
|
15
16
|
export type { Chat as ChatType, ChatAttachmentDropItem, ChatSendMessagePayload, ChatProps, ChatPreset as ChatPresetType, Message, UserTextFileAttachment, } from './Chat.types';
|
|
16
17
|
export { MessageRole } from './Chat.types';
|
|
17
18
|
export { CsvIcon } from '../../icons/CsvIcon/CsvIcon';
|
package/package.json
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import type { ChatPreset } from '#uilib/components/ui/Chat/Chat.types';
|
|
2
|
+
|
|
3
|
+
export type ChatEmptyStateContext = {
|
|
4
|
+
submitPreset: (preset: ChatPreset) => void | Promise<void>;
|
|
5
|
+
};
|
|
6
|
+
|
|
1
7
|
export interface ChatEmptyStateProps {
|
|
2
8
|
icon?: React.ReactNode;
|
|
3
9
|
title?: string;
|
|
4
10
|
description?: string;
|
|
5
|
-
/** Extra block below description (
|
|
11
|
+
/** Extra block below description (resolved before render in `ChatEmptyState`). */
|
|
6
12
|
additionalContent?: React.ReactNode;
|
|
7
13
|
}
|
|
14
|
+
|
|
15
|
+
/** Passed to `ChatSheet` / `useChatPanelChromeModel`; function form resolved before render. */
|
|
16
|
+
export interface ChatEmptyStateConfig extends Omit<
|
|
17
|
+
ChatEmptyStateProps,
|
|
18
|
+
'additionalContent'
|
|
19
|
+
> {
|
|
20
|
+
additionalContent?:
|
|
21
|
+
| React.ReactNode
|
|
22
|
+
| ((ctx: ChatEmptyStateContext) => React.ReactNode);
|
|
23
|
+
}
|
|
@@ -45,6 +45,7 @@ import { useSidebar } from '../../Sidebar/Sidebar';
|
|
|
45
45
|
import { Chat } from '../Chat';
|
|
46
46
|
import type { ChatChromeProps } from '../ChatChrome';
|
|
47
47
|
import type { ChatAttachmentDropItem } from '../ChatChrome/ChatChrome.types';
|
|
48
|
+
import type { ChatEmptyStateConfig } from '../ChatEmptyState/ChatEmptyState.types';
|
|
48
49
|
import type { ChatEmptyStateProps } from '../ChatEmptyState/ChatEmptyState.types';
|
|
49
50
|
|
|
50
51
|
export type UseChatPanelChromeModelInput = {
|
|
@@ -61,7 +62,7 @@ export type UseChatPanelChromeModelInput = {
|
|
|
61
62
|
/** Renders `[CHART]` tokens in assistant messages. */
|
|
62
63
|
renderMessageChart?: () => React.ReactNode;
|
|
63
64
|
/** Forwarded to `ChatChrome` when the thread is empty. */
|
|
64
|
-
emptyState?:
|
|
65
|
+
emptyState?: ChatEmptyStateConfig;
|
|
65
66
|
/** MIME types / extensions for text-only chat attachments (filtered by uilib allowlist). */
|
|
66
67
|
allowedAttachments?: readonly string[];
|
|
67
68
|
/** When true, PDF drops are accepted and parsed to plain text. */
|
|
@@ -615,7 +616,7 @@ export function useChatPanelChromeModel({
|
|
|
615
616
|
],
|
|
616
617
|
);
|
|
617
618
|
|
|
618
|
-
const submitPreset = async (preset: ChatPreset) => {
|
|
619
|
+
const submitPreset = useCallback(async (preset: ChatPreset) => {
|
|
619
620
|
const script = preset.script;
|
|
620
621
|
const scriptGraph = isPresetScriptGraph(script);
|
|
621
622
|
const hasLinearScript = Array.isArray(script) && script.length > 0;
|
|
@@ -705,7 +706,25 @@ export function useChatPanelChromeModel({
|
|
|
705
706
|
} finally {
|
|
706
707
|
setLocalUiBusy(false);
|
|
707
708
|
}
|
|
708
|
-
}
|
|
709
|
+
}, [
|
|
710
|
+
currentChatId,
|
|
711
|
+
endLocalDemoFlow,
|
|
712
|
+
handlePromptSubmit,
|
|
713
|
+
addMessage,
|
|
714
|
+
presetsWithFreeform,
|
|
715
|
+
]);
|
|
716
|
+
|
|
717
|
+
const resolvedEmptyState = useMemo((): ChatEmptyStateProps | undefined => {
|
|
718
|
+
if (!emptyState) return undefined;
|
|
719
|
+
const { additionalContent, ...rest } = emptyState;
|
|
720
|
+
return {
|
|
721
|
+
...rest,
|
|
722
|
+
additionalContent:
|
|
723
|
+
typeof additionalContent === 'function'
|
|
724
|
+
? additionalContent({ submitPreset })
|
|
725
|
+
: additionalContent,
|
|
726
|
+
};
|
|
727
|
+
}, [emptyState, submitPreset]);
|
|
709
728
|
|
|
710
729
|
const activeScript = currentChatId
|
|
711
730
|
? scriptByChatId[currentChatId]
|
|
@@ -1017,7 +1036,7 @@ export function useChatPanelChromeModel({
|
|
|
1017
1036
|
onPromptSubmit: handlePromptSubmit,
|
|
1018
1037
|
onChatDeleted: endLocalDemoFlow,
|
|
1019
1038
|
promptPrefill: promptLinkPrefill,
|
|
1020
|
-
emptyState,
|
|
1039
|
+
emptyState: resolvedEmptyState,
|
|
1021
1040
|
allowedAttachments,
|
|
1022
1041
|
allowPdfAttachments,
|
|
1023
1042
|
onAttachmentsDropped,
|
|
@@ -25,6 +25,11 @@ export type {
|
|
|
25
25
|
export { ChatMessage } from './ChatMessage';
|
|
26
26
|
export { ChatPrompt } from './ChatPrompt';
|
|
27
27
|
export { ChatPresets } from './ChatPresets';
|
|
28
|
+
export type {
|
|
29
|
+
ChatEmptyStateConfig,
|
|
30
|
+
ChatEmptyStateContext,
|
|
31
|
+
ChatEmptyStateProps,
|
|
32
|
+
} from './ChatEmptyState/ChatEmptyState.types';
|
|
28
33
|
export type {
|
|
29
34
|
Chat as ChatType,
|
|
30
35
|
ChatAttachmentDropItem,
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import cn from 'classnames';
|
|
2
|
-
import React, {
|
|
2
|
+
import React, {
|
|
3
|
+
useEffect,
|
|
4
|
+
useId,
|
|
5
|
+
useLayoutEffect,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from 'react';
|
|
3
9
|
import { createPortal } from 'react-dom';
|
|
4
10
|
|
|
5
11
|
import {
|
|
@@ -8,6 +14,7 @@ import {
|
|
|
8
14
|
CardFooter,
|
|
9
15
|
CardHeader,
|
|
10
16
|
} from '#uilib/components/ui/Card/Card';
|
|
17
|
+
import { APP_MODAL_ID } from '#uilib/constants/appMount';
|
|
11
18
|
import { XIcon } from '@phosphor-icons/react';
|
|
12
19
|
|
|
13
20
|
import S from './Dialog.styl';
|
|
@@ -35,6 +42,9 @@ export function Dialog({
|
|
|
35
42
|
width,
|
|
36
43
|
}: DialogProps) {
|
|
37
44
|
const [isMounted, setIsMounted] = useState(false);
|
|
45
|
+
const [modalContainer, setModalContainer] = useState<HTMLElement | null>(
|
|
46
|
+
null,
|
|
47
|
+
);
|
|
38
48
|
const [isAnimating, setIsAnimating] = useState(false);
|
|
39
49
|
const dialogRef = useRef<HTMLDivElement>(null);
|
|
40
50
|
const triggerRef = useRef<HTMLElement | null>(null);
|
|
@@ -48,6 +58,10 @@ export function Dialog({
|
|
|
48
58
|
return () => setIsMounted(false);
|
|
49
59
|
}, []);
|
|
50
60
|
|
|
61
|
+
useLayoutEffect(() => {
|
|
62
|
+
setModalContainer(document.getElementById(APP_MODAL_ID));
|
|
63
|
+
}, []);
|
|
64
|
+
|
|
51
65
|
// Handle open/close animations
|
|
52
66
|
useEffect(() => {
|
|
53
67
|
if (open) {
|
|
@@ -228,7 +242,10 @@ export function Dialog({
|
|
|
228
242
|
return (
|
|
229
243
|
<>
|
|
230
244
|
{trigger && <div onClick={onTriggerClick}>{trigger}</div>}
|
|
231
|
-
{open &&
|
|
245
|
+
{open &&
|
|
246
|
+
!disabled &&
|
|
247
|
+
modalContainer &&
|
|
248
|
+
createPortal(dialogContent, modalContainer)}
|
|
232
249
|
</>
|
|
233
250
|
);
|
|
234
251
|
}
|
package/src/docs/index.tsx
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { createRoot } from 'react-dom/client';
|
|
2
2
|
import { BrowserRouter } from 'react-router-dom';
|
|
3
3
|
|
|
4
|
+
import { APP_ROOT_ID } from '#uilib/constants/appMount';
|
|
4
5
|
import { DEFAULT_THEME_ACTIVE_COLOR } from '#uilib/docs/lib/theme';
|
|
5
6
|
|
|
6
7
|
import App from './App/App';
|
|
7
8
|
import { ThemeProvider } from './contexts/theme-context';
|
|
8
9
|
|
|
9
|
-
const elem = document.getElementById(
|
|
10
|
+
const elem = document.getElementById(APP_ROOT_ID) as HTMLElement;
|
|
10
11
|
const root = createRoot(elem);
|
|
11
12
|
|
|
12
13
|
root.render(
|