@sybilion/uilib 1.2.19 → 1.2.21
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/ChatChrome/ChatChrome.js +1 -1
- package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.js +4 -3
- package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.styl.js +2 -2
- package/dist/esm/components/ui/Chat/ChatMessage/UserCsvAttachmentBubble.js +11 -0
- package/dist/esm/components/ui/Chat/ChatMessage/icons/CsvIcon.js +8 -0
- package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +12 -18
- package/dist/esm/components/ui/Chat/chat-preset-utils.js +12 -3
- package/dist/esm/contexts/chat-context.js +69 -10
- package/dist/esm/index.js +1 -1
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +14 -0
- package/dist/esm/types/src/components/ui/Chat/ChatMessage/ChatMessage.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatMessage/UserCsvAttachmentBubble.d.ts +4 -0
- package/dist/esm/types/src/components/ui/Chat/ChatMessage/icons/CsvIcon.d.ts +3 -0
- package/dist/esm/types/src/components/ui/Chat/index.d.ts +1 -1
- package/dist/esm/types/src/contexts/chat-context.d.ts +21 -7
- package/dist/esm/types/src/docs/pages/ChatUserCsvAttachmentPage.d.ts +1 -0
- package/dist/esm/types/src/utils/downloadTextFile.d.ts +2 -0
- package/dist/esm/utils/downloadTextFile.js +14 -0
- package/package.json +1 -1
- package/src/components/ui/Chat/Chat.types.ts +16 -0
- package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +1 -0
- package/src/components/ui/Chat/ChatMessage/ChatMessage.styl +67 -0
- package/src/components/ui/Chat/ChatMessage/ChatMessage.styl.d.ts +6 -0
- package/src/components/ui/Chat/ChatMessage/ChatMessage.tsx +8 -1
- package/src/components/ui/Chat/ChatMessage/UserCsvAttachmentBubble.tsx +36 -0
- package/src/components/ui/Chat/ChatMessage/icons/CsvIcon.tsx +7 -0
- package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +15 -15
- package/src/components/ui/Chat/chat-preset-utils.ts +12 -6
- package/src/components/ui/Chat/index.ts +3 -1
- package/src/contexts/chat-context.tsx +124 -13
- package/src/docs/pages/ChatUserCsvAttachmentPage.tsx +171 -0
- package/src/docs/registry.ts +6 -0
- package/src/utils/downloadTextFile.ts +16 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Button } from '#uilib/components/ui/Button';
|
|
4
|
+
import {
|
|
5
|
+
ChatChrome,
|
|
6
|
+
type Message,
|
|
7
|
+
MessageRole,
|
|
8
|
+
} from '#uilib/components/ui/Chat';
|
|
9
|
+
import { PageContentSection } from '#uilib/components/ui/Page';
|
|
10
|
+
import { ScrollRef } from '@homecode/ui';
|
|
11
|
+
|
|
12
|
+
import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
|
|
13
|
+
import { DocsHeaderActions } from '../docsHeaderActions';
|
|
14
|
+
|
|
15
|
+
const NO_QUICK_REPLY_KEYS: ReadonlySet<string> = new Set();
|
|
16
|
+
|
|
17
|
+
const SAMPLE_CSV = `Name,Score
|
|
18
|
+
Alice,10
|
|
19
|
+
Bob,20`;
|
|
20
|
+
|
|
21
|
+
const ASSISTANT_ACK =
|
|
22
|
+
'Received your message. Use the file row below the text bubble to download the CSV sample.';
|
|
23
|
+
|
|
24
|
+
const ASSISTANT_REPLY_TYPED =
|
|
25
|
+
'Plain reply for typed messages. Use “Load preset-style…” for the CSV attachment demo.';
|
|
26
|
+
|
|
27
|
+
function makeMessage(role: MessageRole, text: string): Message {
|
|
28
|
+
return {
|
|
29
|
+
id: crypto.randomUUID(),
|
|
30
|
+
role,
|
|
31
|
+
text,
|
|
32
|
+
timestamp: Date.now(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function makeUserMessageWithCsv(
|
|
37
|
+
displayText: string,
|
|
38
|
+
displayName: string,
|
|
39
|
+
): Message {
|
|
40
|
+
return {
|
|
41
|
+
id: crypto.randomUUID(),
|
|
42
|
+
role: MessageRole.USER,
|
|
43
|
+
text: displayText,
|
|
44
|
+
timestamp: Date.now(),
|
|
45
|
+
userCsvAttachment: {
|
|
46
|
+
displayName,
|
|
47
|
+
filename: 'docs-sample.csv',
|
|
48
|
+
content: SAMPLE_CSV,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default function ChatUserCsvAttachmentPage() {
|
|
54
|
+
const [messages, setMessages] = useState<Message[]>([]);
|
|
55
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
56
|
+
const replyTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
57
|
+
const scrollRef = useRef<ScrollRef>(null);
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
return () => {
|
|
61
|
+
if (replyTimeoutRef.current != null) {
|
|
62
|
+
clearTimeout(replyTimeoutRef.current);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}, []);
|
|
66
|
+
|
|
67
|
+
const isEmpty = messages.length === 0 && !isLoading;
|
|
68
|
+
const isLastMessageFromUser =
|
|
69
|
+
messages.length > 0 &&
|
|
70
|
+
messages[messages.length - 1]?.role === MessageRole.USER;
|
|
71
|
+
|
|
72
|
+
const addCsvDemo = useCallback(() => {
|
|
73
|
+
if (isLoading) return;
|
|
74
|
+
const displayText =
|
|
75
|
+
'Open procurement buys analysis "Am I overpaying on my open orders?" Open buys (sample)';
|
|
76
|
+
|
|
77
|
+
setMessages(prev => [
|
|
78
|
+
...prev,
|
|
79
|
+
makeUserMessageWithCsv(displayText, 'Open buys (sample)'),
|
|
80
|
+
]);
|
|
81
|
+
setIsLoading(true);
|
|
82
|
+
|
|
83
|
+
if (replyTimeoutRef.current != null) clearTimeout(replyTimeoutRef.current);
|
|
84
|
+
|
|
85
|
+
replyTimeoutRef.current = setTimeout(() => {
|
|
86
|
+
replyTimeoutRef.current = null;
|
|
87
|
+
setMessages(prev => [
|
|
88
|
+
...prev,
|
|
89
|
+
makeMessage(MessageRole.ASSISTANT, ASSISTANT_ACK),
|
|
90
|
+
]);
|
|
91
|
+
setIsLoading(false);
|
|
92
|
+
}, 500);
|
|
93
|
+
}, [isLoading]);
|
|
94
|
+
|
|
95
|
+
const onSubmit = useCallback(
|
|
96
|
+
(raw: string) => {
|
|
97
|
+
const text = raw.trim();
|
|
98
|
+
if (!text || isLoading) return;
|
|
99
|
+
|
|
100
|
+
setMessages(prev => [...prev, makeMessage(MessageRole.USER, text)]);
|
|
101
|
+
setIsLoading(true);
|
|
102
|
+
|
|
103
|
+
if (replyTimeoutRef.current != null) {
|
|
104
|
+
clearTimeout(replyTimeoutRef.current);
|
|
105
|
+
}
|
|
106
|
+
replyTimeoutRef.current = setTimeout(() => {
|
|
107
|
+
replyTimeoutRef.current = null;
|
|
108
|
+
setMessages(prev => [
|
|
109
|
+
...prev,
|
|
110
|
+
makeMessage(MessageRole.ASSISTANT, ASSISTANT_REPLY_TYPED),
|
|
111
|
+
]);
|
|
112
|
+
setIsLoading(false);
|
|
113
|
+
}, 900);
|
|
114
|
+
},
|
|
115
|
+
[isLoading],
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<>
|
|
120
|
+
<AppPageHeader
|
|
121
|
+
breadcrumbs={[{ label: 'Chat' }, { label: 'User CSV attachment' }]}
|
|
122
|
+
title="Chat — user CSV attachment"
|
|
123
|
+
subheader="USER bubble with separate downloadable CSV row (dashboard preset pattern)."
|
|
124
|
+
actions={<DocsHeaderActions />}
|
|
125
|
+
/>
|
|
126
|
+
<PageContentSection>
|
|
127
|
+
<p style={{ marginBottom: 16 }}>
|
|
128
|
+
<Button
|
|
129
|
+
type="button"
|
|
130
|
+
variant="outline"
|
|
131
|
+
size="sm"
|
|
132
|
+
onClick={addCsvDemo}
|
|
133
|
+
>
|
|
134
|
+
Load preset-style message + CSV attachment
|
|
135
|
+
</Button>
|
|
136
|
+
</p>
|
|
137
|
+
<ChatChrome
|
|
138
|
+
showResizeHandle={false}
|
|
139
|
+
resizeHandle={undefined}
|
|
140
|
+
onClose={undefined}
|
|
141
|
+
isEmpty={isEmpty}
|
|
142
|
+
renderPresets={() => null}
|
|
143
|
+
messages={messages}
|
|
144
|
+
onQuickReply={() => {}}
|
|
145
|
+
suppressedQuickReplyKeys={NO_QUICK_REPLY_KEYS}
|
|
146
|
+
isLoading={isLoading}
|
|
147
|
+
scriptContinueLabel={undefined}
|
|
148
|
+
onScriptContinue={undefined}
|
|
149
|
+
showBranchActionsRow={false}
|
|
150
|
+
showSyntheticBranchButtons={false}
|
|
151
|
+
unusedBranchKeys={[]}
|
|
152
|
+
isScriptComplete={false}
|
|
153
|
+
onGenerateDashboard={undefined}
|
|
154
|
+
generatingDashboard={false}
|
|
155
|
+
onGenerateDashboardClick={() => {}}
|
|
156
|
+
showInlinePresets={false}
|
|
157
|
+
isLastMessageFromUser={isLastMessageFromUser}
|
|
158
|
+
scrollRef={scrollRef}
|
|
159
|
+
effectiveScopeId="docs-chat-user-csv"
|
|
160
|
+
onPromptSubmit={onSubmit}
|
|
161
|
+
onChatDeleted={() => {}}
|
|
162
|
+
emptyState={{
|
|
163
|
+
title: 'CSV attachment demo',
|
|
164
|
+
description:
|
|
165
|
+
'Click the button above or send any message. The CSV row uses the same UI as dashboard presets.',
|
|
166
|
+
}}
|
|
167
|
+
/>
|
|
168
|
+
</PageContentSection>
|
|
169
|
+
</>
|
|
170
|
+
);
|
|
171
|
+
}
|
package/src/docs/registry.ts
CHANGED
|
@@ -96,6 +96,12 @@ export const DOC_REGISTRY: DocEntry[] = [
|
|
|
96
96
|
section: 'Chat',
|
|
97
97
|
load: () => import('./pages/ChatPage'),
|
|
98
98
|
},
|
|
99
|
+
{
|
|
100
|
+
slug: 'chat-user-csv-attachment',
|
|
101
|
+
title: 'Chat user CSV attachment',
|
|
102
|
+
section: 'Chat',
|
|
103
|
+
load: () => import('./pages/ChatUserCsvAttachmentPage'),
|
|
104
|
+
},
|
|
99
105
|
{
|
|
100
106
|
slug: 'checkbox',
|
|
101
107
|
title: 'Checkbox',
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Trigger a browser download for text/binary content (no backend). */
|
|
2
|
+
export function downloadTextFile(
|
|
3
|
+
content: string,
|
|
4
|
+
filename: string,
|
|
5
|
+
mimeType: string,
|
|
6
|
+
): void {
|
|
7
|
+
const blob = new Blob([content], { type: mimeType });
|
|
8
|
+
const url = URL.createObjectURL(blob);
|
|
9
|
+
const link = document.createElement('a');
|
|
10
|
+
link.href = url;
|
|
11
|
+
link.download = filename;
|
|
12
|
+
document.body.appendChild(link);
|
|
13
|
+
link.click();
|
|
14
|
+
document.body.removeChild(link);
|
|
15
|
+
URL.revokeObjectURL(url);
|
|
16
|
+
}
|