@dbx-tools/ui-mastra 0.1.24 → 0.3.2
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/package.json
CHANGED
|
@@ -26,19 +26,19 @@
|
|
|
26
26
|
"shiki": "^3.0.0",
|
|
27
27
|
"sql-formatter": "^15.6.9",
|
|
28
28
|
"streamdown": "^2.5.0",
|
|
29
|
-
"@dbx-tools/shared-core": "0.
|
|
30
|
-
"@dbx-tools/shared-genie": "0.
|
|
31
|
-
"@dbx-tools/
|
|
32
|
-
"@dbx-tools/shared-
|
|
33
|
-
"@dbx-tools/
|
|
34
|
-
"@dbx-tools/ui-branding": "0.
|
|
29
|
+
"@dbx-tools/shared-core": "0.3.2",
|
|
30
|
+
"@dbx-tools/shared-genie": "0.3.2",
|
|
31
|
+
"@dbx-tools/ui-appkit": "0.3.2",
|
|
32
|
+
"@dbx-tools/shared-mastra": "0.3.2",
|
|
33
|
+
"@dbx-tools/shared-model": "0.3.2",
|
|
34
|
+
"@dbx-tools/ui-branding": "0.3.2"
|
|
35
35
|
},
|
|
36
36
|
"main": "index.ts",
|
|
37
37
|
"license": "UNLICENSED",
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
-
"version": "0.
|
|
41
|
+
"version": "0.3.2",
|
|
42
42
|
"types": "index.ts",
|
|
43
43
|
"type": "module",
|
|
44
44
|
"exports": {
|
package/src/react/chat-view.tsx
CHANGED
|
@@ -100,6 +100,7 @@ export const ChatView = ({
|
|
|
100
100
|
models,
|
|
101
101
|
model,
|
|
102
102
|
onModelChange,
|
|
103
|
+
defaultModelName,
|
|
103
104
|
onLoadMore,
|
|
104
105
|
isLoadingMore = false,
|
|
105
106
|
hasMore = false,
|
|
@@ -285,11 +286,22 @@ export const ChatView = ({
|
|
|
285
286
|
// otherwise as static text.
|
|
286
287
|
const showModelDisplay = Boolean(onModelChange);
|
|
287
288
|
const modelChangeable = Boolean(models && models.length > 0);
|
|
288
|
-
//
|
|
289
|
-
//
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
models?.find((m) => m.name ===
|
|
289
|
+
// Human-readable name for an endpoint id, using the catalogue's
|
|
290
|
+
// `displayName` when present, else the raw id.
|
|
291
|
+
const modelLabel = (name?: string): string | undefined => {
|
|
292
|
+
if (!name) return undefined;
|
|
293
|
+
return models?.find((m) => m.name === name)?.displayName || name;
|
|
294
|
+
};
|
|
295
|
+
// The "Server default" option names the actual endpoint the server falls
|
|
296
|
+
// back to (from `defaultModelName`) when known: "Server default (Claude
|
|
297
|
+
// Sonnet 4.6)". Plain "Server default" when the server didn't report one.
|
|
298
|
+
const defaultOptionLabel = (() => {
|
|
299
|
+
const named = modelLabel(defaultModelName);
|
|
300
|
+
return named ? `Server default (${named})` : "Server default";
|
|
301
|
+
})();
|
|
302
|
+
// Label the current model by its human-readable name when a model is
|
|
303
|
+
// pinned, else the default-option label (which names the server default).
|
|
304
|
+
const currentModelLabel = modelLabel(model) || defaultOptionLabel;
|
|
293
305
|
const showClear = Boolean(onClear);
|
|
294
306
|
const showExport = Boolean(onExportConversation);
|
|
295
307
|
// The conversation sidebar turns on once the host wires both the
|
|
@@ -680,10 +692,12 @@ export const ChatView = ({
|
|
|
680
692
|
size="sm"
|
|
681
693
|
className="h-7 w-auto max-w-[200px] gap-1 rounded-full px-2.5 text-xs [&_svg]:size-3"
|
|
682
694
|
>
|
|
683
|
-
<SelectValue placeholder=
|
|
695
|
+
<SelectValue placeholder={defaultOptionLabel} />
|
|
684
696
|
</SelectTrigger>
|
|
685
697
|
<SelectContent>
|
|
686
|
-
<SelectItem value={DEFAULT_MODEL_VALUE}>
|
|
698
|
+
<SelectItem value={DEFAULT_MODEL_VALUE}>
|
|
699
|
+
{defaultOptionLabel}
|
|
700
|
+
</SelectItem>
|
|
687
701
|
{models!.map((m) => (
|
|
688
702
|
<SelectItem key={m.name} value={m.name}>
|
|
689
703
|
{m.displayName || m.name}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
TooltipContent,
|
|
9
9
|
TooltipTrigger,
|
|
10
10
|
} from "@dbx-tools/ui-appkit/react";
|
|
11
|
-
import { DownloadIcon, FileTextIcon
|
|
11
|
+
import { DownloadIcon, FileTextIcon } from "lucide-react";
|
|
12
12
|
import type { ExportFormat } from "../support/export";
|
|
13
13
|
|
|
14
14
|
// Shared export affordance: a download button that opens a small menu of
|
|
@@ -22,7 +22,6 @@ const FORMATS: ReadonlyArray<{
|
|
|
22
22
|
Icon: typeof DownloadIcon;
|
|
23
23
|
}> = [
|
|
24
24
|
{ format: "pdf", label: "PDF", Icon: DownloadIcon },
|
|
25
|
-
{ format: "print", label: "Print", Icon: PrinterIcon },
|
|
26
25
|
{ format: "markdown", label: "Markdown", Icon: FileTextIcon },
|
|
27
26
|
];
|
|
28
27
|
|
package/src/react/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { exportChat, type EmbedResolver, type ExportFormat } from "../support/ex
|
|
|
10
10
|
import { useBrand } from "@dbx-tools/ui-branding/react";
|
|
11
11
|
import {
|
|
12
12
|
useMastraClient,
|
|
13
|
+
useMastraDefaultModel,
|
|
13
14
|
useMastraModels,
|
|
14
15
|
useMastraSuggestions,
|
|
15
16
|
useMastraThreads,
|
|
@@ -347,6 +348,10 @@ export const useMastraChat = (
|
|
|
347
348
|
// hidden and skips the catalogue fetch entirely.
|
|
348
349
|
const showModelPicker = Boolean(options.showModelPicker);
|
|
349
350
|
const { models } = useMastraModels(showModelPicker);
|
|
351
|
+
// The endpoint the active agent falls back to when no model is pinned, so
|
|
352
|
+
// the picker can name its "Server default" option. Fetched only when the
|
|
353
|
+
// picker is shown; `null` when the agent's model is dynamic.
|
|
354
|
+
const defaultModelName = useMastraDefaultModel(agentId, showModelPicker);
|
|
350
355
|
// Starter suggestions: an explicit `options.suggestions` always
|
|
351
356
|
// wins (including `[]` to force none) and is rendered verbatim;
|
|
352
357
|
// otherwise auto-source the agent's Genie space sample questions.
|
|
@@ -1333,6 +1338,7 @@ export const useMastraChat = (
|
|
|
1333
1338
|
models: showModelPicker ? models : undefined,
|
|
1334
1339
|
model,
|
|
1335
1340
|
onModelChange: showModelPicker ? setModel : undefined,
|
|
1341
|
+
defaultModelName: showModelPicker ? (defaultModelName ?? undefined) : undefined,
|
|
1336
1342
|
onLoadMore: loadOlderHistory,
|
|
1337
1343
|
isLoadingMore,
|
|
1338
1344
|
hasMore: activeSession.hasMoreHistory,
|
package/src/react/types.ts
CHANGED
|
@@ -116,6 +116,13 @@ export type ChatViewProps = {
|
|
|
116
116
|
/** Currently selected model name; empty string means "use server default". */
|
|
117
117
|
model?: string;
|
|
118
118
|
onModelChange?: (model: string) => void;
|
|
119
|
+
/**
|
|
120
|
+
* The serving-endpoint id the server falls back to when no model is pinned.
|
|
121
|
+
* When set, the picker's "Server default" option names it (e.g. "Server
|
|
122
|
+
* default (Claude Sonnet 4.6)"), using the matching `models` entry's
|
|
123
|
+
* `displayName` when available.
|
|
124
|
+
*/
|
|
125
|
+
defaultModelName?: string;
|
|
119
126
|
/**
|
|
120
127
|
* Optional infinite-scroll-up handler. Fired when the user scrolls
|
|
121
128
|
* within `TOP_LOAD_MORE_THRESHOLD_PX` of the top of the
|
package/src/support/export.ts
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* - `"pdf"` - a print-ready HTML document rendered in a hidden iframe
|
|
9
9
|
* with the browser's print dialog triggered, so the user
|
|
10
10
|
* saves a real PDF ("Save as PDF") with no popup tab.
|
|
11
|
-
* - `"print"` - the same document routed to a paper printer.
|
|
12
11
|
* - `"markdown"` - a `.md` file download.
|
|
13
12
|
*
|
|
14
13
|
* The document can be brand-styled (logo, colors, font) via the optional
|
|
@@ -37,10 +36,9 @@ import { normalizeChartOption } from "./chart-option";
|
|
|
37
36
|
* Output formats {@link exportChat} can produce.
|
|
38
37
|
* - `"pdf"` - Save-as-PDF via a hidden print iframe (dialog defaults to
|
|
39
38
|
* "Save as PDF"); no new tab.
|
|
40
|
-
* - `"print"` - same rendered document + print dialog, for a paper printer.
|
|
41
39
|
* - `"markdown"` - a `.md` file download.
|
|
42
40
|
*/
|
|
43
|
-
export type ExportFormat = "pdf" | "
|
|
41
|
+
export type ExportFormat = "pdf" | "markdown";
|
|
44
42
|
|
|
45
43
|
/**
|
|
46
44
|
* Optional brand styling for the exported document. Plain data (no React /
|
|
@@ -102,14 +100,13 @@ const CHART_HEIGHT_PX = 380;
|
|
|
102
100
|
const PRINT_SETTLE_MS = 300;
|
|
103
101
|
|
|
104
102
|
/**
|
|
105
|
-
* Export `messages` as a PDF
|
|
103
|
+
* Export `messages` as a PDF or a Markdown file.
|
|
106
104
|
*
|
|
107
|
-
* `"pdf"`
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* export - charts included - still lands.
|
|
105
|
+
* `"pdf"` renders a branded, self-contained HTML document and drives it
|
|
106
|
+
* through a hidden `<iframe>` + `print()` - so the browser's print dialog
|
|
107
|
+
* (defaulting to "Save as PDF") opens directly, with no popup tab. If the
|
|
108
|
+
* iframe path can't run (e.g. no DOM body), the document is downloaded as a
|
|
109
|
+
* self-contained `.html` file so the export - charts included - still lands.
|
|
113
110
|
*/
|
|
114
111
|
export async function exportChat(options: ExportChatOptions): Promise<void> {
|
|
115
112
|
const { messages, format, resolver } = options;
|
|
@@ -123,8 +120,8 @@ export async function exportChat(options: ExportChatOptions): Promise<void> {
|
|
|
123
120
|
return;
|
|
124
121
|
}
|
|
125
122
|
|
|
126
|
-
// pdf
|
|
127
|
-
//
|
|
123
|
+
// pdf: build the branded document, then print it from a hidden iframe so
|
|
124
|
+
// the Save-as-PDF dialog opens without a stray tab.
|
|
128
125
|
const html = await buildHtmlDocument(messages, resolver, title, userLabel, options.brand);
|
|
129
126
|
printViaHiddenIframe(html, `${stem}.html`);
|
|
130
127
|
}
|
|
@@ -174,6 +174,24 @@ export class MastraPluginClient extends MastraClient {
|
|
|
174
174
|
return payload.endpoints;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Fetch the static default serving-endpoint id `agentId` falls back to
|
|
179
|
+
* when no model is pinned, from `GET ${basePath}/default-model`. Returns
|
|
180
|
+
* `null` when the agent resolves its model dynamically at call time (or the
|
|
181
|
+
* agent id is unknown) - there's nothing static to advertise. The picker
|
|
182
|
+
* uses this to name its "Server default" option. Defaults to the server's
|
|
183
|
+
* default agent when `agentId` is omitted.
|
|
184
|
+
*/
|
|
185
|
+
async defaultModel(agentId?: string, signal?: AbortSignal): Promise<string | null> {
|
|
186
|
+
const query = agentId ? `?agentId=${encodeURIComponent(agentId)}` : "";
|
|
187
|
+
const payload = await this.#getJson(
|
|
188
|
+
`${this.basePath}${routes.MASTRA_ROUTES.defaultModel}${query}`,
|
|
189
|
+
wire.DefaultModelResponseSchema,
|
|
190
|
+
signal,
|
|
191
|
+
);
|
|
192
|
+
return payload.model;
|
|
193
|
+
}
|
|
194
|
+
|
|
177
195
|
/**
|
|
178
196
|
* Fetch the curated starter questions for `agentId`'s Genie space
|
|
179
197
|
* from `GET ${basePath}/suggestions`. Empty when the agent has no
|
|
@@ -533,6 +551,38 @@ export const useMastraModels = (
|
|
|
533
551
|
return { models, loading, error };
|
|
534
552
|
};
|
|
535
553
|
|
|
554
|
+
/**
|
|
555
|
+
* Fetch the static default serving-endpoint id the given agent (or the
|
|
556
|
+
* server's default agent) falls back to when no model is pinned, via
|
|
557
|
+
* `client.defaultModel(agentId)`. `null` while loading, when the agent's
|
|
558
|
+
* model is dynamic, or on error - the picker then shows a plain "Server
|
|
559
|
+
* default". Pass `enabled: false` to skip the fetch (e.g. picker hidden).
|
|
560
|
+
*/
|
|
561
|
+
export const useMastraDefaultModel = (
|
|
562
|
+
agentId?: string,
|
|
563
|
+
enabled = true,
|
|
564
|
+
): string | null => {
|
|
565
|
+
const client = useMastraClient();
|
|
566
|
+
const [model, setModel] = useState<string | null>(null);
|
|
567
|
+
|
|
568
|
+
useEffect(() => {
|
|
569
|
+
if (!enabled) return;
|
|
570
|
+
const controller = new AbortController();
|
|
571
|
+
client
|
|
572
|
+
.defaultModel(agentId, controller.signal)
|
|
573
|
+
.then((m) => {
|
|
574
|
+
if (!controller.signal.aborted) setModel(m);
|
|
575
|
+
})
|
|
576
|
+
.catch(() => {
|
|
577
|
+
// Non-critical: a missing default just yields a plain "Server default".
|
|
578
|
+
if (!controller.signal.aborted) setModel(null);
|
|
579
|
+
});
|
|
580
|
+
return () => controller.abort();
|
|
581
|
+
}, [client, agentId, enabled]);
|
|
582
|
+
|
|
583
|
+
return model;
|
|
584
|
+
};
|
|
585
|
+
|
|
536
586
|
/**
|
|
537
587
|
* Fetch the curated starter questions for an agent's Genie space via
|
|
538
588
|
* `client.suggestions(agentId)`. These are the `sample_questions` an
|