@sybilion/uilib 1.3.44 → 1.3.46
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/assets/logo.svg +1 -1
- package/dist/esm/components/ui/Chart/Chart.styl.js +1 -1
- package/dist/esm/components/ui/ChartAreaInteractive/ChartAreaInteractive.js +7 -1
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.js +5 -3
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.styl.js +1 -1
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPromptComposer.js +7 -3
- package/dist/esm/components/ui/Chat/ChatPrompt/chatPromptComposerInsert.js +67 -0
- package/dist/esm/components/ui/Logo/Logo.styl.js +1 -1
- package/dist/esm/components/ui/Logo/logo.svg.js +1 -1
- package/dist/esm/components/ui/Page/PageFooter/PageFooter.js +3 -2
- package/dist/esm/components/widgets/DriversComparisonChart/DriversComparisonChart.js +1 -0
- package/dist/esm/components/widgets/PerformanceChart/PerformanceTable.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/tiptap/slash-mention/SlashSuggestionList.styl.js +2 -2
- package/dist/esm/tiptap/slash-mention/createSlashMentionExtension.js +45 -28
- package/dist/esm/types/src/components/ui/Chat/Chat.d.ts +1 -2
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPrompt.d.ts +3 -1
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.d.ts +3 -1
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.types.d.ts +2 -0
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/chatPromptComposerInsert.d.ts +35 -0
- package/dist/esm/types/src/components/ui/Chat/index.d.ts +3 -0
- package/dist/esm/types/src/components/ui/Page/PageFooter/PageFooter.d.ts +1 -2
- package/dist/esm/types/src/tiptap/slash-mention/types.d.ts +7 -2
- package/package.json +1 -1
- package/src/components/ui/Chart/Chart.styl +2 -1
- package/src/components/ui/ChartAreaInteractive/ChartAreaInteractive.tsx +8 -1
- package/src/components/ui/Chat/Chat.types.ts +1 -1
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl +25 -0
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.tsx +94 -71
- package/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.tsx +35 -10
- package/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.types.ts +11 -0
- package/src/components/ui/Chat/ChatPrompt/chatPromptComposerInsert.ts +122 -0
- package/src/components/ui/Chat/index.ts +9 -0
- package/src/components/ui/Logo/Logo.styl +1 -0
- package/src/components/ui/Logo/logo.svg +1 -1
- package/src/components/ui/Page/PageFooter/PageFooter.tsx +2 -3
- package/src/docs/DocsShell.tsx +1 -6
- package/src/docs/pages/ChatSlashCommandsPage.tsx +46 -120
- package/src/tiptap/slash-mention/SlashSuggestionList.styl +4 -0
- package/src/tiptap/slash-mention/SlashSuggestionList.styl.d.ts +1 -0
- package/src/tiptap/slash-mention/createSlashMentionExtension.ts +46 -27
- package/src/tiptap/slash-mention/types.ts +7 -2
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
type SlashCommandItem,
|
|
9
9
|
} from '#uilib/components/ui/Chat';
|
|
10
10
|
import { PageContentSection } from '#uilib/components/ui/Page';
|
|
11
|
-
import type { SlashItemCommandContext } from '#uilib/tiptap/slash-mention/types';
|
|
12
11
|
import { ScrollRef } from '@homecode/ui';
|
|
13
12
|
|
|
14
13
|
import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
|
|
@@ -16,65 +15,40 @@ import { DocsHeaderActions } from '../docsHeaderActions';
|
|
|
16
15
|
|
|
17
16
|
const NO_QUICK_REPLY_KEYS: ReadonlySet<string> = new Set();
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
/** Sample items — default TipTap mention insert; optional `className` / `color` style the chip. */
|
|
19
|
+
const DOCS_SLASH_ITEMS: SlashCommandItem[] = [
|
|
20
|
+
{
|
|
21
|
+
id: 'generate-dashboard',
|
|
22
|
+
label: 'Generate dashboard',
|
|
23
|
+
description: 'Insert /generate-dashboard',
|
|
24
|
+
color: 'var(--brand-color-600)',
|
|
25
|
+
},
|
|
23
26
|
{
|
|
24
|
-
id:
|
|
25
|
-
label:
|
|
26
|
-
description:
|
|
27
|
-
|
|
27
|
+
id: 'performance-chart',
|
|
28
|
+
label: 'Performance chart (bigger font)',
|
|
29
|
+
description: 'Insert /PerformanceChart',
|
|
30
|
+
className: 'docs-slash-mention-accent',
|
|
31
|
+
color: '#16a34a',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'mention-fallback',
|
|
35
|
+
label: 'Mention fallback',
|
|
36
|
+
description: 'Default mention styling only',
|
|
37
|
+
color: 'var(--foreground)',
|
|
28
38
|
},
|
|
29
39
|
];
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const SAMPLE_COMMAND_SUCCESS_TEXT = '✅ Sample command complete.';
|
|
34
|
-
|
|
35
|
-
function makeMessage(
|
|
36
|
-
role: MessageRole,
|
|
37
|
-
text: string,
|
|
38
|
-
options?: { inProgress?: boolean },
|
|
39
|
-
): Message {
|
|
41
|
+
function makeMessage(role: MessageRole, text: string): Message {
|
|
40
42
|
return {
|
|
41
43
|
id: crypto.randomUUID(),
|
|
42
44
|
role,
|
|
43
45
|
text,
|
|
44
46
|
timestamp: Date.now(),
|
|
45
|
-
...(options?.inProgress ? { inProgress: true } : {}),
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
/** Local-state equivalent of chat-context `updateMessageById`. */
|
|
50
|
-
function updateMessageInPlace(
|
|
51
|
-
messages: Message[],
|
|
52
|
-
messageId: string,
|
|
53
|
-
patch: { role?: MessageRole; text?: string; inProgress?: boolean },
|
|
54
|
-
): Message[] {
|
|
55
|
-
return messages.map(message => {
|
|
56
|
-
if (message.id !== messageId) return message;
|
|
57
|
-
const next: Message = { ...message };
|
|
58
|
-
if (patch.role != null) {
|
|
59
|
-
next.role = patch.role;
|
|
60
|
-
}
|
|
61
|
-
if (patch.text != null) {
|
|
62
|
-
next.text = patch.text;
|
|
63
|
-
}
|
|
64
|
-
if (patch.inProgress === true) {
|
|
65
|
-
next.inProgress = true;
|
|
66
|
-
} else if (
|
|
67
|
-
patch.inProgress === false ||
|
|
68
|
-
(patch.role != null && patch.role !== MessageRole.SYSTEM)
|
|
69
|
-
) {
|
|
70
|
-
delete next.inProgress;
|
|
71
|
-
}
|
|
72
|
-
return next;
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
50
|
const ASSISTANT_REPLY_TEXT =
|
|
77
|
-
'Demo reply
|
|
51
|
+
'Demo reply. Slash picks insert inline mention chips; plain text on send uses each item id (e.g. /generate-dashboard).';
|
|
78
52
|
|
|
79
53
|
export default function ChatSlashCommandsPage() {
|
|
80
54
|
const [messages, setMessages] = useState<Message[]>([]);
|
|
@@ -95,84 +69,33 @@ export default function ChatSlashCommandsPage() {
|
|
|
95
69
|
messages.length > 0 &&
|
|
96
70
|
messages[messages.length - 1]?.role === MessageRole.USER;
|
|
97
71
|
|
|
98
|
-
const
|
|
99
|
-
const
|
|
72
|
+
const onSubmit = useCallback((raw: string) => {
|
|
73
|
+
const text = raw.trim();
|
|
74
|
+
if (!text) return;
|
|
75
|
+
|
|
76
|
+
setMessages(prev => [...prev, makeMessage(MessageRole.USER, text)]);
|
|
100
77
|
setIsLoading(true);
|
|
101
|
-
setMessages(prev => [
|
|
102
|
-
...prev,
|
|
103
|
-
{
|
|
104
|
-
id: progressId,
|
|
105
|
-
role: MessageRole.SYSTEM,
|
|
106
|
-
text: SAMPLE_COMMAND_PROGRESS_TEXT,
|
|
107
|
-
timestamp: Date.now(),
|
|
108
|
-
inProgress: true,
|
|
109
|
-
},
|
|
110
|
-
]);
|
|
111
78
|
|
|
112
79
|
if (replyTimeoutRef.current != null) {
|
|
113
80
|
clearTimeout(replyTimeoutRef.current);
|
|
114
81
|
}
|
|
115
82
|
replyTimeoutRef.current = setTimeout(() => {
|
|
116
83
|
replyTimeoutRef.current = null;
|
|
117
|
-
setMessages(prev =>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
inProgress: false,
|
|
122
|
-
}),
|
|
123
|
-
);
|
|
84
|
+
setMessages(prev => [
|
|
85
|
+
...prev,
|
|
86
|
+
makeMessage(MessageRole.ASSISTANT, ASSISTANT_REPLY_TEXT),
|
|
87
|
+
]);
|
|
124
88
|
setIsLoading(false);
|
|
125
|
-
},
|
|
89
|
+
}, 900);
|
|
126
90
|
}, []);
|
|
127
91
|
|
|
128
|
-
const onSlashItemCommand = useCallback(
|
|
129
|
-
({ item }: SlashItemCommandContext) => {
|
|
130
|
-
if (item.id !== DOCS_SAMPLE_COMMAND_ID) {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
queueMicrotask(() => runSampleCommand());
|
|
134
|
-
return true;
|
|
135
|
-
},
|
|
136
|
-
[runSampleCommand],
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
const onSubmit = useCallback(
|
|
140
|
-
(raw: string) => {
|
|
141
|
-
const text = raw.trim();
|
|
142
|
-
if (!text) return;
|
|
143
|
-
|
|
144
|
-
if (text === `/${DOCS_SAMPLE_COMMAND_ID}`) {
|
|
145
|
-
runSampleCommand();
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
setMessages(prev => [...prev, makeMessage(MessageRole.USER, text)]);
|
|
150
|
-
setIsLoading(true);
|
|
151
|
-
|
|
152
|
-
if (replyTimeoutRef.current != null) {
|
|
153
|
-
clearTimeout(replyTimeoutRef.current);
|
|
154
|
-
}
|
|
155
|
-
replyTimeoutRef.current = setTimeout(() => {
|
|
156
|
-
replyTimeoutRef.current = null;
|
|
157
|
-
setMessages(prev => [
|
|
158
|
-
...prev,
|
|
159
|
-
makeMessage(MessageRole.ASSISTANT, ASSISTANT_REPLY_TEXT),
|
|
160
|
-
]);
|
|
161
|
-
setIsLoading(false);
|
|
162
|
-
}, 900);
|
|
163
|
-
},
|
|
164
|
-
[runSampleCommand],
|
|
165
|
-
);
|
|
166
|
-
|
|
167
92
|
return (
|
|
168
93
|
<>
|
|
169
94
|
<AppPageHeader
|
|
170
95
|
breadcrumbs={[{ label: 'Chat' }, { label: 'Chat slash commands' }]}
|
|
171
96
|
title="Chat slash commands"
|
|
172
|
-
subheader={`Slash palette uses TipTap Mention with "/" as the trigger. Pass slashCommandItems from the app
|
|
173
|
-
actions={
|
|
174
|
-
<DocsHeaderActions slashCommandItems={DOCS_SAMPLE_SLASH_ITEMS} />
|
|
175
|
-
}
|
|
97
|
+
subheader={`Slash palette uses TipTap Mention with "/" as the trigger. Pass slashCommandItems from the app — each item inserts an inline mention chip by default. Optional className and color on SlashCommandItem style the chip in the composer.`}
|
|
98
|
+
actions={<DocsHeaderActions slashCommandItems={DOCS_SLASH_ITEMS} />}
|
|
176
99
|
/>
|
|
177
100
|
<PageContentSection>
|
|
178
101
|
<p style={{ marginBottom: 16, fontSize: 14, lineHeight: 1.5 }}>
|
|
@@ -180,13 +103,17 @@ export default function ChatSlashCommandsPage() {
|
|
|
180
103
|
<Link className="underline underline-offset-2" to="/docs/chat">
|
|
181
104
|
Chat
|
|
182
105
|
</Link>{' '}
|
|
183
|
-
shell below
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
or send <kbd className="font-mono">/sample-command</kbd> with Enter.
|
|
106
|
+
shell below. Type <kbd className="font-mono">/</kbd> at line start or
|
|
107
|
+
after a space, then pick a command — the composer inserts a styled
|
|
108
|
+
mention chip. Items can set <kbd className="font-mono">className</kbd>{' '}
|
|
109
|
+
and <kbd className="font-mono">color</kbd> on{' '}
|
|
110
|
+
<kbd className="font-mono">SlashCommandItem</kbd>.
|
|
189
111
|
</p>
|
|
112
|
+
<style>{`
|
|
113
|
+
.docs-slash-mention-accent {
|
|
114
|
+
font-size: 150%;
|
|
115
|
+
}
|
|
116
|
+
`}</style>
|
|
190
117
|
<ChatChrome
|
|
191
118
|
showResizeHandle={false}
|
|
192
119
|
resizeHandle={undefined}
|
|
@@ -207,13 +134,12 @@ export default function ChatSlashCommandsPage() {
|
|
|
207
134
|
effectiveScopeId="docs-chat-slash-inline"
|
|
208
135
|
onPromptSubmit={onSubmit}
|
|
209
136
|
onChatDeleted={() => {}}
|
|
210
|
-
slashCommandItems={
|
|
211
|
-
onSlashItemCommand={onSlashItemCommand}
|
|
137
|
+
slashCommandItems={DOCS_SLASH_ITEMS}
|
|
212
138
|
promptPlaceholder='Ask something or type "/" for demo commands…'
|
|
213
139
|
emptyState={{
|
|
214
|
-
title: 'Try
|
|
140
|
+
title: 'Try slash commands',
|
|
215
141
|
description:
|
|
216
|
-
'Pick
|
|
142
|
+
'Pick a command from the palette to insert a mention chip with optional className and color.',
|
|
217
143
|
}}
|
|
218
144
|
/>
|
|
219
145
|
</PageContentSection>
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
SlashSuggestionList,
|
|
11
11
|
type SlashSuggestionListHandle,
|
|
12
12
|
} from './SlashSuggestionList';
|
|
13
|
+
import S from './SlashSuggestionList.styl';
|
|
13
14
|
import { filterSlashItems } from './defaultChatSlashItems';
|
|
14
15
|
import type {
|
|
15
16
|
CreateSlashMentionExtensionOptions,
|
|
@@ -17,6 +18,30 @@ import type {
|
|
|
17
18
|
SlashSuggestionPlacement,
|
|
18
19
|
} from './types';
|
|
19
20
|
|
|
21
|
+
const SlashMention = Mention.extend({
|
|
22
|
+
addAttributes() {
|
|
23
|
+
return {
|
|
24
|
+
...this.parent?.(),
|
|
25
|
+
className: {
|
|
26
|
+
default: null,
|
|
27
|
+
parseHTML: element => element.getAttribute('data-slash-class'),
|
|
28
|
+
renderHTML: attributes => {
|
|
29
|
+
if (!attributes.className) return {};
|
|
30
|
+
return { 'data-slash-class': attributes.className };
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
color: {
|
|
34
|
+
default: null,
|
|
35
|
+
parseHTML: element => element.getAttribute('data-slash-color'),
|
|
36
|
+
renderHTML: attributes => {
|
|
37
|
+
if (!attributes.color) return {};
|
|
38
|
+
return { 'data-slash-color': attributes.color };
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
20
45
|
const SUGGESTION_GAP_PX = 4;
|
|
21
46
|
|
|
22
47
|
function placeSlashSuggestionPopup(
|
|
@@ -124,28 +149,11 @@ export function slashMentionSuggestionRender(
|
|
|
124
149
|
};
|
|
125
150
|
}
|
|
126
151
|
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
): void {
|
|
131
|
-
if (editor.isDestroyed) return;
|
|
132
|
-
try {
|
|
133
|
-
editor.chain().focus().deleteRange(range).clearContent().run();
|
|
134
|
-
} catch {
|
|
135
|
-
// Editor view may be tearing down during suggestion exit.
|
|
136
|
-
}
|
|
152
|
+
function slashMentionChipStyle(color: string | null): string | undefined {
|
|
153
|
+
if (!color) return undefined;
|
|
154
|
+
return `color: ${color}; background-color: color-mix(in srgb, ${color} 30%, transparent);`;
|
|
137
155
|
}
|
|
138
156
|
|
|
139
|
-
function collapseEditorSelectionEnd(editor: Editor): void {
|
|
140
|
-
if (editor.isDestroyed) return;
|
|
141
|
-
try {
|
|
142
|
-
editor.view?.dom?.ownerDocument?.defaultView
|
|
143
|
-
?.getSelection?.()
|
|
144
|
-
?.collapseToEnd();
|
|
145
|
-
} catch {
|
|
146
|
-
// view.dom throws when editor is not mounted
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
157
|
function insertDefaultMention(
|
|
150
158
|
editor: Editor,
|
|
151
159
|
range: { from: number; to: number },
|
|
@@ -166,6 +174,8 @@ function insertDefaultMention(
|
|
|
166
174
|
id: props.id,
|
|
167
175
|
label: props.label,
|
|
168
176
|
mentionSuggestionChar: slashChar,
|
|
177
|
+
className: props.className ?? null,
|
|
178
|
+
color: props.color ?? null,
|
|
169
179
|
},
|
|
170
180
|
},
|
|
171
181
|
{ type: 'text', text: ' ' },
|
|
@@ -212,27 +222,38 @@ export function createSlashMentionExtension({
|
|
|
212
222
|
current: null,
|
|
213
223
|
};
|
|
214
224
|
|
|
215
|
-
return
|
|
225
|
+
return SlashMention.configure({
|
|
216
226
|
renderText({ node }) {
|
|
217
227
|
return `/${node.attrs.id as string}`;
|
|
218
228
|
},
|
|
219
|
-
renderHTML({ options, node
|
|
220
|
-
const suggestionChar = suggestion?.char ?? slashChar;
|
|
229
|
+
renderHTML({ options, node }) {
|
|
221
230
|
const id =
|
|
222
231
|
typeof node.attrs.id === 'string'
|
|
223
232
|
? node.attrs.id
|
|
224
233
|
: String(node.attrs.id ?? '');
|
|
234
|
+
const label =
|
|
235
|
+
typeof node.attrs.label === 'string' && node.attrs.label.trim() !== ''
|
|
236
|
+
? node.attrs.label
|
|
237
|
+
: id;
|
|
238
|
+
const color =
|
|
239
|
+
typeof node.attrs.color === 'string' ? node.attrs.color : null;
|
|
240
|
+
const className = [S.mention, node.attrs.className]
|
|
241
|
+
.filter(Boolean)
|
|
242
|
+
.join(' ');
|
|
243
|
+
const chipStyle = slashMentionChipStyle(color);
|
|
244
|
+
|
|
225
245
|
return [
|
|
226
246
|
'span',
|
|
227
247
|
mergeAttributes(
|
|
228
248
|
{
|
|
229
249
|
'data-type': 'mention',
|
|
230
250
|
'data-slash-command': id,
|
|
231
|
-
class:
|
|
251
|
+
class: className,
|
|
252
|
+
...(chipStyle ? { style: chipStyle } : {}),
|
|
232
253
|
},
|
|
233
254
|
options.HTMLAttributes,
|
|
234
255
|
),
|
|
235
|
-
|
|
256
|
+
label,
|
|
236
257
|
];
|
|
237
258
|
},
|
|
238
259
|
suggestion: {
|
|
@@ -245,8 +266,6 @@ export function createSlashMentionExtension({
|
|
|
245
266
|
command: ({ editor, range, props }) => {
|
|
246
267
|
const item = props as SlashCommandItem;
|
|
247
268
|
if (onItemCommand?.({ editor, range, item }) === true) {
|
|
248
|
-
clearSlashTriggerEditor(editor, range);
|
|
249
|
-
queueMicrotask(() => collapseEditorSelectionEnd(editor));
|
|
250
269
|
return null;
|
|
251
270
|
}
|
|
252
271
|
insertDefaultMention(editor, range, item, slashChar);
|
|
@@ -4,6 +4,10 @@ export type SlashCommandItem = {
|
|
|
4
4
|
id: string;
|
|
5
5
|
label: string;
|
|
6
6
|
description?: string;
|
|
7
|
+
/** Extra class on the inserted mention chip (merged with `slash-mention`). */
|
|
8
|
+
className?: string;
|
|
9
|
+
/** CSS color for chip text; background is 30% of this color mixed with transparent. */
|
|
10
|
+
color?: string;
|
|
7
11
|
};
|
|
8
12
|
|
|
9
13
|
export type SlashItemCommandContext = {
|
|
@@ -14,8 +18,9 @@ export type SlashItemCommandContext = {
|
|
|
14
18
|
};
|
|
15
19
|
|
|
16
20
|
/**
|
|
17
|
-
* If provided, run when a slash item is picked. Return true to skip mention insert
|
|
18
|
-
*
|
|
21
|
+
* If provided, run when a slash item is picked. Return true to skip default mention insert
|
|
22
|
+
* and handle the composer yourself (e.g. `createChatPromptComposerHandle(editor).insertAtCaret`
|
|
23
|
+
* with `{ replaceRange: range }` to replace `/query` with a custom chip).
|
|
19
24
|
*/
|
|
20
25
|
export type SlashOnItemCommand = (ctx: SlashItemCommandContext) => boolean;
|
|
21
26
|
|