@dbx-tools/ui-mastra 0.9.116 → 0.9.117
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/index.ts +1 -1
- package/package.json +6 -6
- package/src/react/_model-selector.tsx +93 -39
- package/src/react/bubbles.tsx +7 -11
- package/src/react/markdown.tsx +3 -2
- package/src/react/tool-pill.tsx +22 -13
package/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
|
|
5
5
|
export const PACKAGE_IDENTIFIER = "@dbx-tools/ui-mastra";
|
|
6
|
-
export const PACKAGE_VERSION = "0.9.
|
|
6
|
+
export const PACKAGE_VERSION = "0.9.117";
|
|
7
7
|
export * as reactBubbles from "./src/react/bubbles.tsx";
|
|
8
8
|
export * as reactChatApprovals from "./src/react/chat-approvals.ts";
|
|
9
9
|
export * as reactChatComposer from "./src/react/chat-composer.tsx";
|
package/package.json
CHANGED
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"react-dom": "~19.2.4"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@dbx-tools/shared-core": "^0.9.
|
|
33
|
-
"@dbx-tools/shared-genie": "^0.9.
|
|
34
|
-
"@dbx-tools/shared-mastra": "^0.9.
|
|
35
|
-
"@dbx-tools/shared-model": "^0.9.
|
|
36
|
-
"@dbx-tools/ui": "^0.9.
|
|
32
|
+
"@dbx-tools/shared-core": "^0.9.117",
|
|
33
|
+
"@dbx-tools/shared-genie": "^0.9.117",
|
|
34
|
+
"@dbx-tools/shared-mastra": "^0.9.117",
|
|
35
|
+
"@dbx-tools/shared-model": "^0.9.117",
|
|
36
|
+
"@dbx-tools/ui": "^0.9.117",
|
|
37
37
|
"@mastra/client-js": "1.50.0",
|
|
38
38
|
"@mastra/core": "1.71.0",
|
|
39
39
|
"@tanstack/react-table": "^8.21.3",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
|
-
"version": "0.9.
|
|
52
|
+
"version": "0.9.117",
|
|
53
53
|
"dbxToolsConfig": {
|
|
54
54
|
"tags": [
|
|
55
55
|
"ui"
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
DropdownMenuTrigger,
|
|
17
17
|
Slider,
|
|
18
18
|
Spinner,
|
|
19
|
+
useIsMobile,
|
|
19
20
|
} from "@dbx-tools/ui/react";
|
|
20
21
|
import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
|
21
22
|
import { useMemo, useState, type ReactNode } from "react";
|
|
@@ -185,6 +186,7 @@ export const ModelSelector = ({
|
|
|
185
186
|
onModelChange,
|
|
186
187
|
actions,
|
|
187
188
|
}: ModelSelectorProps) => {
|
|
189
|
+
const isMobile = useIsMobile();
|
|
188
190
|
const [open, setOpen] = useState(false);
|
|
189
191
|
const [modelPickerOpen, setModelPickerOpen] = useState(false);
|
|
190
192
|
const [activeFamily, setActiveFamily] = useState<string>();
|
|
@@ -242,50 +244,102 @@ export const ModelSelector = ({
|
|
|
242
244
|
)}
|
|
243
245
|
</Button>
|
|
244
246
|
</DropdownMenuTrigger>
|
|
245
|
-
<DropdownMenuContent
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
>
|
|
254
|
-
<DropdownMenuSubTrigger className="mx-1 justify-center rounded-lg bg-accent/60 px-3 py-2 text-xs font-medium">
|
|
255
|
-
{showLoading ? (
|
|
247
|
+
<DropdownMenuContent
|
|
248
|
+
align="end"
|
|
249
|
+
collisionPadding={8}
|
|
250
|
+
className="w-48 max-w-[calc(100vw_-_1rem)]"
|
|
251
|
+
>
|
|
252
|
+
{isMobile && modelPickerOpen ? (
|
|
253
|
+
<>
|
|
254
|
+
{!activeFamily && (
|
|
256
255
|
<>
|
|
257
|
-
<
|
|
258
|
-
|
|
256
|
+
<button
|
|
257
|
+
type="button"
|
|
258
|
+
className="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-xs font-medium outline-none hover:bg-accent focus-visible:bg-accent"
|
|
259
|
+
onClick={() => setModelPickerOpen(false)}
|
|
260
|
+
>
|
|
261
|
+
<ChevronLeftIcon className="size-3" />
|
|
262
|
+
Options
|
|
263
|
+
</button>
|
|
264
|
+
<DropdownMenuSeparator />
|
|
259
265
|
</>
|
|
266
|
+
)}
|
|
267
|
+
<FamilyPicker
|
|
268
|
+
families={families}
|
|
269
|
+
selectedModelId={selectedModelId}
|
|
270
|
+
model={model}
|
|
271
|
+
defaultModelName={defaultModelName}
|
|
272
|
+
showLoading={showLoading}
|
|
273
|
+
activeFamily={activeFamily}
|
|
274
|
+
onActiveFamilyChange={setActiveFamily}
|
|
275
|
+
onModelChange={onModelChange}
|
|
276
|
+
/>
|
|
277
|
+
</>
|
|
278
|
+
) : (
|
|
279
|
+
<>
|
|
280
|
+
{isMobile ? (
|
|
281
|
+
<button
|
|
282
|
+
type="button"
|
|
283
|
+
className="mx-1 flex w-[calc(100%_-_0.5rem)] items-center rounded-lg bg-accent/60 px-3 py-2 text-xs font-medium outline-none hover:bg-accent focus-visible:bg-accent"
|
|
284
|
+
onClick={() => setModelPickerOpen(true)}
|
|
285
|
+
>
|
|
286
|
+
{showLoading ? (
|
|
287
|
+
<>
|
|
288
|
+
<Spinner className="size-3 shrink-0" />
|
|
289
|
+
<span className="sr-only">Loading default model</span>
|
|
290
|
+
</>
|
|
291
|
+
) : (
|
|
292
|
+
<span className="truncate">{triggerLabel}</span>
|
|
293
|
+
)}
|
|
294
|
+
<ChevronRightIcon className="ml-auto size-3 shrink-0 text-muted-foreground" />
|
|
295
|
+
</button>
|
|
260
296
|
) : (
|
|
261
|
-
|
|
297
|
+
<DropdownMenuSub
|
|
298
|
+
open={modelPickerOpen}
|
|
299
|
+
onOpenChange={(next) => {
|
|
300
|
+
if (!next && open) return;
|
|
301
|
+
setModelPickerOpen(next);
|
|
302
|
+
if (!next) setActiveFamily(undefined);
|
|
303
|
+
}}
|
|
304
|
+
>
|
|
305
|
+
<DropdownMenuSubTrigger className="mx-1 justify-center rounded-lg bg-accent/60 px-3 py-2 text-xs font-medium">
|
|
306
|
+
{showLoading ? (
|
|
307
|
+
<>
|
|
308
|
+
<Spinner className="size-3 shrink-0" />
|
|
309
|
+
<span className="sr-only">Loading default model</span>
|
|
310
|
+
</>
|
|
311
|
+
) : (
|
|
312
|
+
triggerLabel
|
|
313
|
+
)}
|
|
314
|
+
</DropdownMenuSubTrigger>
|
|
315
|
+
<DropdownMenuPortal>
|
|
316
|
+
<DropdownMenuSubContent collisionPadding={8} className="w-48">
|
|
317
|
+
<FamilyPicker
|
|
318
|
+
families={families}
|
|
319
|
+
selectedModelId={selectedModelId}
|
|
320
|
+
model={model}
|
|
321
|
+
defaultModelName={defaultModelName}
|
|
322
|
+
showLoading={showLoading}
|
|
323
|
+
activeFamily={activeFamily}
|
|
324
|
+
onActiveFamilyChange={setActiveFamily}
|
|
325
|
+
onModelChange={onModelChange}
|
|
326
|
+
/>
|
|
327
|
+
</DropdownMenuSubContent>
|
|
328
|
+
</DropdownMenuPortal>
|
|
329
|
+
</DropdownMenuSub>
|
|
330
|
+
)}
|
|
331
|
+
{(actions || (reasoningEfforts.length > 0 && onReasoningEffortChange)) && (
|
|
332
|
+
<DropdownMenuSeparator />
|
|
262
333
|
)}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
model={model}
|
|
270
|
-
defaultModelName={defaultModelName}
|
|
271
|
-
showLoading={showLoading}
|
|
272
|
-
activeFamily={activeFamily}
|
|
273
|
-
onActiveFamilyChange={setActiveFamily}
|
|
274
|
-
onModelChange={onModelChange}
|
|
334
|
+
{actions && <div className="px-1">{actions}</div>}
|
|
335
|
+
{reasoningEfforts.length > 0 && onReasoningEffortChange && (
|
|
336
|
+
<ReasoningControl
|
|
337
|
+
efforts={reasoningEfforts}
|
|
338
|
+
value={reasoningEffort}
|
|
339
|
+
onChange={onReasoningEffortChange}
|
|
275
340
|
/>
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
</DropdownMenuSub>
|
|
279
|
-
{(actions || (reasoningEfforts.length > 0 && onReasoningEffortChange)) && (
|
|
280
|
-
<DropdownMenuSeparator />
|
|
281
|
-
)}
|
|
282
|
-
{actions && <div className="px-1">{actions}</div>}
|
|
283
|
-
{reasoningEfforts.length > 0 && onReasoningEffortChange && (
|
|
284
|
-
<ReasoningControl
|
|
285
|
-
efforts={reasoningEfforts}
|
|
286
|
-
value={reasoningEffort}
|
|
287
|
-
onChange={onReasoningEffortChange}
|
|
288
|
-
/>
|
|
341
|
+
)}
|
|
342
|
+
</>
|
|
289
343
|
)}
|
|
290
344
|
</DropdownMenuContent>
|
|
291
345
|
</DropdownMenu>
|
package/src/react/bubbles.tsx
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
CopyIcon,
|
|
23
23
|
MessageSquareIcon,
|
|
24
24
|
RefreshCcwIcon,
|
|
25
|
-
SparklesIcon,
|
|
26
25
|
UserIcon,
|
|
27
26
|
XIcon,
|
|
28
27
|
} from "lucide-react";
|
|
@@ -60,12 +59,12 @@ const getReasoningText = (parts: UIMessage["parts"]): string =>
|
|
|
60
59
|
.filter((p): p is { type: "reasoning"; text: string } => p.type === "reasoning")
|
|
61
60
|
.map((p) => compactReasoningText(p.text))
|
|
62
61
|
.filter(Boolean)
|
|
63
|
-
.join("\n
|
|
62
|
+
.join("\n");
|
|
64
63
|
|
|
65
|
-
const
|
|
64
|
+
const UserAvatar = () => (
|
|
66
65
|
<Avatar className="size-7">
|
|
67
66
|
<AvatarFallback>
|
|
68
|
-
|
|
67
|
+
<UserIcon className="size-4" />
|
|
69
68
|
</AvatarFallback>
|
|
70
69
|
</Avatar>
|
|
71
70
|
);
|
|
@@ -363,7 +362,7 @@ export const AssistantBubble = ({
|
|
|
363
362
|
const toolEvents = mergeToolEvents(toolEventsFromParts(message.parts), events, terminalToolState);
|
|
364
363
|
const reasoning = [getReasoningText(message.parts), genieReasoningText(toolEvents)]
|
|
365
364
|
.filter(Boolean)
|
|
366
|
-
.join("\n
|
|
365
|
+
.join("\n");
|
|
367
366
|
const isReasoningStreaming =
|
|
368
367
|
isLast &&
|
|
369
368
|
status === "streaming" &&
|
|
@@ -405,10 +404,7 @@ export const AssistantBubble = ({
|
|
|
405
404
|
// nothing so the prose flows unaffected. Suggested questions
|
|
406
405
|
// stay gated on settle to avoid pop-in mid-stream.
|
|
407
406
|
return (
|
|
408
|
-
<Item className="items-start gap-
|
|
409
|
-
<ItemMedia>
|
|
410
|
-
<RoleAvatar role="assistant" />
|
|
411
|
-
</ItemMedia>
|
|
407
|
+
<Item className="items-start gap-0 border-none bg-transparent p-0">
|
|
412
408
|
{/*
|
|
413
409
|
* `min-w-0` is required so wide content (markdown tables, long
|
|
414
410
|
* code blocks) can shrink to fit instead of pushing the whole
|
|
@@ -432,7 +428,7 @@ export const AssistantBubble = ({
|
|
|
432
428
|
</CollapsibleTrigger>
|
|
433
429
|
<CollapsibleContent>
|
|
434
430
|
<div className="mt-1 border-l-2 border-border/60 pl-2.5 text-xs text-muted-foreground">
|
|
435
|
-
<ToolMarkdown>{reasoning}</ToolMarkdown>
|
|
431
|
+
<ToolMarkdown className="prose-p:my-0 [&_p]:!my-0">{reasoning}</ToolMarkdown>
|
|
436
432
|
</div>
|
|
437
433
|
</CollapsibleContent>
|
|
438
434
|
</Collapsible>
|
|
@@ -540,7 +536,7 @@ export const UserBubble = ({ message }: { message: UIMessage }) => {
|
|
|
540
536
|
return (
|
|
541
537
|
<Item className="items-start gap-2.5 border-none bg-transparent p-0">
|
|
542
538
|
<ItemMedia>
|
|
543
|
-
<
|
|
539
|
+
<UserAvatar />
|
|
544
540
|
</ItemMedia>
|
|
545
541
|
<ItemContent className="min-w-0">
|
|
546
542
|
<div className="max-w-full rounded-lg bg-muted p-2 text-sm">
|
package/src/react/markdown.tsx
CHANGED
|
@@ -264,7 +264,7 @@ export const AssistantMarkdown = ({
|
|
|
264
264
|
* (bold) gets the foreground color so KPI names still pop against
|
|
265
265
|
* the muted body.
|
|
266
266
|
*/
|
|
267
|
-
export const ToolMarkdown = ({ children }: { children: string }) => (
|
|
267
|
+
export const ToolMarkdown = ({ children, className }: { children: string; className?: string }) => (
|
|
268
268
|
<Streamdown
|
|
269
269
|
components={TOOL_MARKDOWN_COMPONENTS}
|
|
270
270
|
plugins={SHIKI_PLUGIN}
|
|
@@ -272,12 +272,13 @@ export const ToolMarkdown = ({ children }: { children: string }) => (
|
|
|
272
272
|
className={cn(
|
|
273
273
|
"prose prose-sm dark:prose-invert max-w-none break-words",
|
|
274
274
|
"text-xs leading-snug text-muted-foreground",
|
|
275
|
-
"prose-p:my-0.5 prose-p:leading-snug",
|
|
275
|
+
"prose-p:my-0.5 prose-p:leading-snug [&_p]:!leading-snug",
|
|
276
276
|
"prose-ul:my-0.5 prose-ul:pl-4 prose-ol:my-0.5 prose-ol:pl-4",
|
|
277
277
|
"prose-li:my-0 prose-li:leading-snug prose-li:marker:text-muted-foreground/60",
|
|
278
278
|
"prose-headings:my-1 prose-headings:text-xs prose-headings:font-semibold",
|
|
279
279
|
"prose-strong:text-foreground/90 prose-strong:font-medium",
|
|
280
280
|
"prose-code:text-xs prose-code:font-medium",
|
|
281
|
+
className,
|
|
281
282
|
)}
|
|
282
283
|
>
|
|
283
284
|
{children}
|
package/src/react/tool-pill.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
cn,
|
|
12
12
|
} from "@dbx-tools/ui/react";
|
|
13
13
|
import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react";
|
|
14
|
-
import { SourceBlock,
|
|
14
|
+
import { SourceBlock, ToolMarkdown } from "./markdown.tsx";
|
|
15
15
|
import type { ToolEvent, ToolProgress } from "./types.ts";
|
|
16
16
|
|
|
17
17
|
// Consolidated tool-session pill and its Genie progress detail view:
|
|
@@ -304,16 +304,20 @@ const MessageGroupBody = ({
|
|
|
304
304
|
) : null}
|
|
305
305
|
</CollapsibleTrigger>
|
|
306
306
|
<CollapsibleContent>
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
307
|
+
{bucket.query && (
|
|
308
|
+
<>
|
|
309
|
+
<SourceBlock
|
|
310
|
+
source={bucket.query.sql}
|
|
311
|
+
language="sql"
|
|
312
|
+
className="max-h-64 border-t border-border/50 px-2 py-1.5 text-foreground"
|
|
313
|
+
/>
|
|
314
|
+
{bucket.query.description && (
|
|
315
|
+
<div className="border-t border-border/50 px-2 py-1.5">
|
|
312
316
|
<ToolMarkdown>{bucket.query.description}</ToolMarkdown>
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
+
</div>
|
|
318
|
+
)}
|
|
319
|
+
</>
|
|
320
|
+
)}
|
|
317
321
|
</CollapsibleContent>
|
|
318
322
|
</Collapsible>
|
|
319
323
|
))}
|
|
@@ -608,15 +612,18 @@ const ToolCallDetails = ({
|
|
|
608
612
|
"input" in event ? toolInputPresentation(event.toolName, event.input) : undefined;
|
|
609
613
|
const outputPresentation =
|
|
610
614
|
"output" in event ? toolOutputPresentation(event.toolName, event.output) : undefined;
|
|
615
|
+
const isAskGenie = event.toolName.startsWith("ask_genie");
|
|
611
616
|
|
|
612
617
|
return (
|
|
613
618
|
<div className="flex flex-col gap-2">
|
|
614
619
|
{showQuestion && question ? (
|
|
615
|
-
<p className="break-words px-0.5 text-
|
|
620
|
+
<p className="break-words px-0.5 text-[11px] leading-snug text-muted-foreground">
|
|
621
|
+
{question}
|
|
622
|
+
</p>
|
|
616
623
|
) : null}
|
|
617
624
|
{inputPresentation ? (
|
|
618
625
|
<ToolInputSource presentation={inputPresentation} />
|
|
619
|
-
) : "input" in event ? (
|
|
626
|
+
) : "input" in event && !isAskGenie ? (
|
|
620
627
|
<RawToolPayload label="Request" value={event.input} />
|
|
621
628
|
) : null}
|
|
622
629
|
<WebSearchProgressDetails event={event} />
|
|
@@ -667,7 +674,9 @@ const ToolCallRow = ({ event }: { event: ToolEvent }) => {
|
|
|
667
674
|
{verb}
|
|
668
675
|
</span>
|
|
669
676
|
{question && (
|
|
670
|
-
<span className="mt-0.5 block break-words
|
|
677
|
+
<span className="mt-0.5 block break-words text-[11px] leading-snug text-foreground/75">
|
|
678
|
+
{question}
|
|
679
|
+
</span>
|
|
671
680
|
)}
|
|
672
681
|
</span>
|
|
673
682
|
);
|