@vantageos/mosaic 0.1.0
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/index.cjs +699 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +255 -0
- package/dist/index.d.ts +255 -0
- package/dist/index.js +669 -0
- package/dist/index.js.map +1 -0
- package/package.json +83 -0
- package/registry.yaml +44 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var zod = require('zod');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
var reactVirtual = require('@tanstack/react-virtual');
|
|
7
|
+
var DOMPurify = require('dompurify');
|
|
8
|
+
var marked = require('marked');
|
|
9
|
+
var rxjs = require('rxjs');
|
|
10
|
+
var server = require('@mcp-ui/server');
|
|
11
|
+
|
|
12
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
|
|
14
|
+
var DOMPurify__default = /*#__PURE__*/_interopDefault(DOMPurify);
|
|
15
|
+
|
|
16
|
+
// src/i18n/strings.ts
|
|
17
|
+
var EN = {
|
|
18
|
+
"ProgressBar.aria.label": "Progress",
|
|
19
|
+
"ProgressBar.error.invalidProps": "ProgressBar: invalid props",
|
|
20
|
+
"ConfirmDialog.button.confirm": "Confirm",
|
|
21
|
+
"ConfirmDialog.button.cancel": "Cancel",
|
|
22
|
+
"ConfirmDialog.aria.dialog": "Confirmation dialog",
|
|
23
|
+
"TableView.aria.table": "Data table",
|
|
24
|
+
"TableView.pagination.next": "Next page",
|
|
25
|
+
"TableView.pagination.prev": "Previous page",
|
|
26
|
+
"TableView.empty.message": "No data to display",
|
|
27
|
+
"TableView.error.invalidProps": "TableView: invalid props",
|
|
28
|
+
"MarkdownRenderer.aria.content": "Rendered Markdown content",
|
|
29
|
+
"MarkdownRenderer.error.invalidProps": "MarkdownRenderer: invalid props",
|
|
30
|
+
"MarkdownRenderer.error.tooLong": "Content exceeds maximum length",
|
|
31
|
+
"TokenDisplayOnceModal.button.copy": "Copy token",
|
|
32
|
+
"TokenDisplayOnceModal.button.close": "Close",
|
|
33
|
+
"TokenDisplayOnceModal.warning.once": "This token will only be shown once. Copy it now.",
|
|
34
|
+
"TokenDisplayOnceModal.copied": "Copied",
|
|
35
|
+
"StatusBadge.aria.status": "Status",
|
|
36
|
+
"StatusBadge.error.invalidProps": "StatusBadge: invalid props"
|
|
37
|
+
};
|
|
38
|
+
var FR = {
|
|
39
|
+
"ProgressBar.aria.label": "Progression",
|
|
40
|
+
"ProgressBar.error.invalidProps": "ProgressBar : propri\xE9t\xE9s invalides",
|
|
41
|
+
"ConfirmDialog.button.confirm": "Confirmer",
|
|
42
|
+
"ConfirmDialog.button.cancel": "Annuler",
|
|
43
|
+
"ConfirmDialog.aria.dialog": "Bo\xEEte de dialogue de confirmation",
|
|
44
|
+
"TableView.aria.table": "Tableau de donn\xE9es",
|
|
45
|
+
"TableView.pagination.next": "Page suivante",
|
|
46
|
+
"TableView.pagination.prev": "Page pr\xE9c\xE9dente",
|
|
47
|
+
"TableView.empty.message": "Aucune donn\xE9e \xE0 afficher",
|
|
48
|
+
"TableView.error.invalidProps": "TableView : propri\xE9t\xE9s invalides",
|
|
49
|
+
"MarkdownRenderer.aria.content": "Contenu Markdown rendu",
|
|
50
|
+
"MarkdownRenderer.error.invalidProps": "MarkdownRenderer : propri\xE9t\xE9s invalides",
|
|
51
|
+
"MarkdownRenderer.error.tooLong": "Le contenu d\xE9passe la longueur maximale",
|
|
52
|
+
"TokenDisplayOnceModal.button.copy": "Copier le jeton",
|
|
53
|
+
"TokenDisplayOnceModal.button.close": "Fermer",
|
|
54
|
+
"TokenDisplayOnceModal.warning.once": "Ce jeton ne sera affich\xE9 qu'une seule fois. Copiez-le maintenant.",
|
|
55
|
+
"TokenDisplayOnceModal.copied": "Copi\xE9",
|
|
56
|
+
"StatusBadge.aria.status": "Statut",
|
|
57
|
+
"StatusBadge.error.invalidProps": "StatusBadge : propri\xE9t\xE9s invalides"
|
|
58
|
+
};
|
|
59
|
+
var TABLE = {
|
|
60
|
+
en: EN,
|
|
61
|
+
fr: FR
|
|
62
|
+
};
|
|
63
|
+
function t(key, locale = "en") {
|
|
64
|
+
return TABLE[locale]?.[key] ?? TABLE.en[key];
|
|
65
|
+
}
|
|
66
|
+
var ProgressBarPropsSchema = zod.z.object({
|
|
67
|
+
value: zod.z.number().min(0).max(100),
|
|
68
|
+
label: zod.z.string().min(1),
|
|
69
|
+
locale: zod.z.enum(["en", "fr"]).default("en"),
|
|
70
|
+
colorVariant: zod.z.enum(["default", "warning", "danger"]).default("default")
|
|
71
|
+
});
|
|
72
|
+
function validateProps(raw) {
|
|
73
|
+
return ProgressBarPropsSchema.parse(raw);
|
|
74
|
+
}
|
|
75
|
+
function pickLocale(raw) {
|
|
76
|
+
return raw.locale === "fr" ? "fr" : "en";
|
|
77
|
+
}
|
|
78
|
+
var colorVariantClasses = {
|
|
79
|
+
default: "bg-blue-600",
|
|
80
|
+
warning: "bg-amber-500",
|
|
81
|
+
danger: "bg-red-600"
|
|
82
|
+
};
|
|
83
|
+
var containerVariantClasses = {
|
|
84
|
+
default: "progressbar-default",
|
|
85
|
+
warning: "progressbar-warning",
|
|
86
|
+
danger: "progressbar-danger"
|
|
87
|
+
};
|
|
88
|
+
function ProgressBarInner(props) {
|
|
89
|
+
const { value, label, colorVariant } = props;
|
|
90
|
+
const fillClass = colorVariantClasses[colorVariant];
|
|
91
|
+
const containerClass = containerVariantClasses[colorVariant];
|
|
92
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
93
|
+
"div",
|
|
94
|
+
{
|
|
95
|
+
role: "progressbar",
|
|
96
|
+
"aria-valuenow": value,
|
|
97
|
+
"aria-valuemin": 0,
|
|
98
|
+
"aria-valuemax": 100,
|
|
99
|
+
"aria-label": label,
|
|
100
|
+
tabIndex: 0,
|
|
101
|
+
className: `mosaic-progress ${containerClass}`,
|
|
102
|
+
style: { width: "100%" },
|
|
103
|
+
children: [
|
|
104
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
105
|
+
"div",
|
|
106
|
+
{
|
|
107
|
+
"data-testid": "progress-fill",
|
|
108
|
+
className: `mosaic-progress-fill ${fillClass}`,
|
|
109
|
+
style: { width: `${value}%` }
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mosaic-progress-label", children: [
|
|
113
|
+
label,
|
|
114
|
+
": ",
|
|
115
|
+
value,
|
|
116
|
+
"%"
|
|
117
|
+
] })
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
function ProgressBar(raw) {
|
|
123
|
+
try {
|
|
124
|
+
const props = validateProps(raw);
|
|
125
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ProgressBarInner, { ...props });
|
|
126
|
+
} catch {
|
|
127
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", className: "mosaic-progress-error", children: t("ProgressBar.error.invalidProps", pickLocale(raw)) });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
var ConfirmDialogPropsSchema = zod.z.object({
|
|
131
|
+
title: zod.z.string().min(1),
|
|
132
|
+
message: zod.z.string().min(1),
|
|
133
|
+
confirmLabel: zod.z.string().min(1),
|
|
134
|
+
cancelLabel: zod.z.string().min(1),
|
|
135
|
+
variant: zod.z.enum(["default", "danger"]).default("default"),
|
|
136
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
137
|
+
});
|
|
138
|
+
function validateConfirmDialogProps(raw) {
|
|
139
|
+
return ConfirmDialogPropsSchema.parse(raw);
|
|
140
|
+
}
|
|
141
|
+
function ConfirmDialogInner({
|
|
142
|
+
title,
|
|
143
|
+
message,
|
|
144
|
+
confirmLabel,
|
|
145
|
+
cancelLabel,
|
|
146
|
+
variant,
|
|
147
|
+
onConfirm,
|
|
148
|
+
onCancel
|
|
149
|
+
}) {
|
|
150
|
+
const dialogRef = react.useRef(null);
|
|
151
|
+
const titleId = react.useId();
|
|
152
|
+
const messageId = react.useId();
|
|
153
|
+
react.useEffect(() => {
|
|
154
|
+
const dialog = dialogRef.current;
|
|
155
|
+
if (!dialog) return;
|
|
156
|
+
dialog.showModal();
|
|
157
|
+
return () => {
|
|
158
|
+
if (dialog.open) dialog.close();
|
|
159
|
+
};
|
|
160
|
+
}, []);
|
|
161
|
+
function handleKeyDown(e) {
|
|
162
|
+
if (e.key === "Escape") {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
onCancel();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function handleBackdropClick(e) {
|
|
168
|
+
if (e.target === dialogRef.current) {
|
|
169
|
+
onCancel();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const confirmClassName = variant === "danger" ? "bg-red-600 text-white hover:bg-red-700 focus-visible:outline-red-600 px-4 py-2 rounded-md text-sm font-medium transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2" : "bg-gray-900 text-white hover:bg-gray-700 focus-visible:outline-gray-900 px-4 py-2 rounded-md text-sm font-medium transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2";
|
|
173
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
174
|
+
"dialog",
|
|
175
|
+
{
|
|
176
|
+
ref: dialogRef,
|
|
177
|
+
"aria-modal": "true",
|
|
178
|
+
"aria-labelledby": titleId,
|
|
179
|
+
"aria-describedby": messageId,
|
|
180
|
+
onKeyDown: handleKeyDown,
|
|
181
|
+
onClick: handleBackdropClick,
|
|
182
|
+
className: "rounded-lg shadow-xl p-0 backdrop:bg-black/40 w-full max-w-md mx-auto",
|
|
183
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
184
|
+
"div",
|
|
185
|
+
{
|
|
186
|
+
className: "p-6 flex flex-col gap-4",
|
|
187
|
+
onClick: (e) => e.stopPropagation(),
|
|
188
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
189
|
+
children: [
|
|
190
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { id: titleId, className: "text-lg font-semibold text-gray-900", children: title }),
|
|
191
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { id: messageId, className: "text-sm text-gray-600", children: message }),
|
|
192
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3 mt-2", children: [
|
|
193
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
194
|
+
"button",
|
|
195
|
+
{
|
|
196
|
+
type: "button",
|
|
197
|
+
onClick: onCancel,
|
|
198
|
+
className: "px-4 py-2 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-400 transition-colors",
|
|
199
|
+
children: cancelLabel
|
|
200
|
+
}
|
|
201
|
+
),
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onConfirm, className: confirmClassName, children: confirmLabel })
|
|
203
|
+
] })
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
function ConfirmDialog(raw) {
|
|
211
|
+
try {
|
|
212
|
+
const parsed = validateConfirmDialogProps(raw);
|
|
213
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ConfirmDialogInner, { ...parsed, onConfirm: raw.onConfirm, onCancel: raw.onCancel });
|
|
214
|
+
} catch (err) {
|
|
215
|
+
console.error("[ConfirmDialog] Invalid props \u2014 component will not render.", err);
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
var ColumnDefSchema = zod.z.object({
|
|
220
|
+
key: zod.z.string().min(1),
|
|
221
|
+
header: zod.z.string().min(1)
|
|
222
|
+
// render is a runtime function — excluded from Zod, typed separately
|
|
223
|
+
});
|
|
224
|
+
var TableViewPropsSchema = zod.z.object({
|
|
225
|
+
columns: zod.z.array(ColumnDefSchema).min(1, "TableView requires at least one column"),
|
|
226
|
+
virtualizeThreshold: zod.z.number().int().positive().default(100),
|
|
227
|
+
ariaLabel: zod.z.string().min(1),
|
|
228
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
229
|
+
});
|
|
230
|
+
function validateTableViewProps(raw) {
|
|
231
|
+
return TableViewPropsSchema.parse(raw);
|
|
232
|
+
}
|
|
233
|
+
var OVERSCAN = 5;
|
|
234
|
+
function TableView(props) {
|
|
235
|
+
let validatedThreshold = 100;
|
|
236
|
+
try {
|
|
237
|
+
const validated = validateTableViewProps({
|
|
238
|
+
columns: props.columns.map(({ key, header }) => ({ key, header })),
|
|
239
|
+
virtualizeThreshold: props.virtualizeThreshold,
|
|
240
|
+
ariaLabel: props.ariaLabel,
|
|
241
|
+
locale: props.locale
|
|
242
|
+
});
|
|
243
|
+
validatedThreshold = validated.virtualizeThreshold;
|
|
244
|
+
} catch {
|
|
245
|
+
const locale = props.locale === "fr" ? "fr" : "en";
|
|
246
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("TableView.error.invalidProps", locale) });
|
|
247
|
+
}
|
|
248
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TableViewInner, { ...props, virtualizeThreshold: validatedThreshold });
|
|
249
|
+
}
|
|
250
|
+
function TableViewInner({
|
|
251
|
+
columns,
|
|
252
|
+
rows$,
|
|
253
|
+
virtualizeThreshold = 100,
|
|
254
|
+
ariaLabel,
|
|
255
|
+
locale
|
|
256
|
+
}) {
|
|
257
|
+
const resolvedLocale = locale === "fr" ? "fr" : "en";
|
|
258
|
+
const [rows, setRows] = react.useState([]);
|
|
259
|
+
react.useEffect(() => {
|
|
260
|
+
setRows([]);
|
|
261
|
+
const sub = rows$.subscribe((chunk) => {
|
|
262
|
+
setRows((prev) => [...prev, ...chunk]);
|
|
263
|
+
});
|
|
264
|
+
return () => {
|
|
265
|
+
sub.unsubscribe();
|
|
266
|
+
};
|
|
267
|
+
}, [rows$]);
|
|
268
|
+
const isVirtual = rows.length > virtualizeThreshold;
|
|
269
|
+
if (isVirtual) {
|
|
270
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
271
|
+
VirtualTable,
|
|
272
|
+
{
|
|
273
|
+
rows,
|
|
274
|
+
columns,
|
|
275
|
+
ariaLabel,
|
|
276
|
+
locale: resolvedLocale
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
281
|
+
StaticTable,
|
|
282
|
+
{
|
|
283
|
+
rows,
|
|
284
|
+
columns,
|
|
285
|
+
ariaLabel,
|
|
286
|
+
locale: resolvedLocale
|
|
287
|
+
}
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
function renderCell(col, row) {
|
|
291
|
+
if (col.render) {
|
|
292
|
+
return col.render(row);
|
|
293
|
+
}
|
|
294
|
+
return String(row[col.key] ?? "");
|
|
295
|
+
}
|
|
296
|
+
function StaticTable({
|
|
297
|
+
rows,
|
|
298
|
+
columns,
|
|
299
|
+
ariaLabel,
|
|
300
|
+
locale
|
|
301
|
+
}) {
|
|
302
|
+
const emptyMsg = t("TableView.empty.message", locale);
|
|
303
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
304
|
+
"table",
|
|
305
|
+
{
|
|
306
|
+
"aria-label": ariaLabel,
|
|
307
|
+
"aria-rowcount": rows.length,
|
|
308
|
+
style: { width: "100%", borderCollapse: "collapse" },
|
|
309
|
+
children: [
|
|
310
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: col.header }, col.key)) }) }),
|
|
311
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: rows.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: columns.length, "aria-label": emptyMsg, children: emptyMsg }) }) : rows.map((row, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
312
|
+
"tr",
|
|
313
|
+
{
|
|
314
|
+
"aria-rowindex": rowIndex + 2,
|
|
315
|
+
children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { children: renderCell(col, row) }, col.key))
|
|
316
|
+
},
|
|
317
|
+
row.id != null ? String(row.id) : `row-${rowIndex}`
|
|
318
|
+
)) })
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
function VirtualTable({
|
|
324
|
+
rows,
|
|
325
|
+
columns,
|
|
326
|
+
ariaLabel,
|
|
327
|
+
// locale reserved for future virtual-mode empty/pagination strings
|
|
328
|
+
locale: _locale
|
|
329
|
+
}) {
|
|
330
|
+
const scrollRef = react.useRef(null);
|
|
331
|
+
const rowVirtualizer = reactVirtual.useVirtualizer({
|
|
332
|
+
count: rows.length,
|
|
333
|
+
/* v8 ignore next 1 */
|
|
334
|
+
getScrollElement: () => scrollRef.current,
|
|
335
|
+
estimateSize: react.useCallback(() => 40, []),
|
|
336
|
+
overscan: OVERSCAN
|
|
337
|
+
});
|
|
338
|
+
const virtualItems = rowVirtualizer.getVirtualItems();
|
|
339
|
+
const totalSize = rowVirtualizer.getTotalSize();
|
|
340
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
341
|
+
"div",
|
|
342
|
+
{
|
|
343
|
+
"data-virtual": "true",
|
|
344
|
+
ref: scrollRef,
|
|
345
|
+
style: { height: 400, overflow: "auto", position: "relative" },
|
|
346
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
347
|
+
"table",
|
|
348
|
+
{
|
|
349
|
+
"aria-label": ariaLabel,
|
|
350
|
+
"aria-rowcount": rows.length,
|
|
351
|
+
style: { width: "100%", borderCollapse: "collapse" },
|
|
352
|
+
children: [
|
|
353
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: col.header }, col.key)) }) }),
|
|
354
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
355
|
+
"tbody",
|
|
356
|
+
{
|
|
357
|
+
style: {
|
|
358
|
+
height: `${totalSize}px`,
|
|
359
|
+
position: "relative",
|
|
360
|
+
display: "block"
|
|
361
|
+
},
|
|
362
|
+
children: virtualItems.map((virtualRow) => {
|
|
363
|
+
const row = rows[virtualRow.index] ?? {};
|
|
364
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
365
|
+
"tr",
|
|
366
|
+
{
|
|
367
|
+
"aria-rowindex": virtualRow.index + 2,
|
|
368
|
+
style: {
|
|
369
|
+
position: "absolute",
|
|
370
|
+
top: 0,
|
|
371
|
+
left: 0,
|
|
372
|
+
width: "100%",
|
|
373
|
+
transform: `translateY(${virtualRow.start}px)`,
|
|
374
|
+
display: "flex"
|
|
375
|
+
},
|
|
376
|
+
children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx("td", { style: { flex: 1 }, children: renderCell(col, row) }, col.key))
|
|
377
|
+
},
|
|
378
|
+
row.id != null ? String(row.id) : `vrow-${virtualRow.index}`
|
|
379
|
+
);
|
|
380
|
+
})
|
|
381
|
+
}
|
|
382
|
+
)
|
|
383
|
+
]
|
|
384
|
+
}
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
var MarkdownRendererPropsSchema = zod.z.object({
|
|
390
|
+
content: zod.z.string(),
|
|
391
|
+
locale: zod.z.enum(["en", "fr"]),
|
|
392
|
+
maxLength: zod.z.number().positive().default(5e4),
|
|
393
|
+
allowHtml: zod.z.boolean().default(false)
|
|
394
|
+
});
|
|
395
|
+
function validateProps2(raw) {
|
|
396
|
+
return MarkdownRendererPropsSchema.parse(raw);
|
|
397
|
+
}
|
|
398
|
+
var tooLongMessages = {
|
|
399
|
+
en: "Content exceeds maximum length.",
|
|
400
|
+
fr: "Le contenu d\xE9passe la longueur maximale."
|
|
401
|
+
};
|
|
402
|
+
function MarkdownRendererInner({
|
|
403
|
+
content,
|
|
404
|
+
locale,
|
|
405
|
+
maxLength,
|
|
406
|
+
allowHtml
|
|
407
|
+
}) {
|
|
408
|
+
if (content.length > maxLength) {
|
|
409
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", lang: locale, children: tooLongMessages[locale] });
|
|
410
|
+
}
|
|
411
|
+
const source = allowHtml ? content : content.replace(/<[^>]*>/g, "");
|
|
412
|
+
const rawHtml = marked.marked.parse(source, { async: false });
|
|
413
|
+
const cleanHtml = DOMPurify__default.default.sanitize(rawHtml);
|
|
414
|
+
const ariaLabel = locale === "fr" ? "Contenu format\xE9" : "Formatted content";
|
|
415
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
416
|
+
"article",
|
|
417
|
+
{
|
|
418
|
+
"aria-label": ariaLabel,
|
|
419
|
+
lang: locale,
|
|
420
|
+
dangerouslySetInnerHTML: { __html: cleanHtml }
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
function MarkdownRenderer(raw) {
|
|
425
|
+
try {
|
|
426
|
+
const props = validateProps2(raw);
|
|
427
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MarkdownRendererInner, { ...props });
|
|
428
|
+
} catch {
|
|
429
|
+
const locale = raw.locale === "fr" ? "fr" : "en";
|
|
430
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", children: t("MarkdownRenderer.error.invalidProps", locale) });
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
var TokenDisplayOnceModalPropsSchema = zod.z.object({
|
|
434
|
+
token: zod.z.string().min(1),
|
|
435
|
+
title: zod.z.string().min(1),
|
|
436
|
+
warningMessage: zod.z.string().min(1),
|
|
437
|
+
copyLabel: zod.z.string().min(1),
|
|
438
|
+
closeLabel: zod.z.string().min(1),
|
|
439
|
+
locale: zod.z.enum(["en", "fr"]).default("en")
|
|
440
|
+
});
|
|
441
|
+
function validateTokenDisplayOnceModalProps(raw) {
|
|
442
|
+
return TokenDisplayOnceModalPropsSchema.parse(raw);
|
|
443
|
+
}
|
|
444
|
+
function TokenDisplayOnceModalInner({
|
|
445
|
+
token,
|
|
446
|
+
title,
|
|
447
|
+
warningMessage,
|
|
448
|
+
copyLabel,
|
|
449
|
+
closeLabel,
|
|
450
|
+
locale,
|
|
451
|
+
onClose
|
|
452
|
+
}) {
|
|
453
|
+
const dialogRef = react.useRef(null);
|
|
454
|
+
const titleId = react.useId();
|
|
455
|
+
const warningId = react.useId();
|
|
456
|
+
const [copied, setCopied] = react.useState(false);
|
|
457
|
+
const [secureToken] = react.useState(token);
|
|
458
|
+
react.useEffect(() => {
|
|
459
|
+
const dialog = dialogRef.current;
|
|
460
|
+
if (!dialog) return;
|
|
461
|
+
dialog.showModal();
|
|
462
|
+
return () => {
|
|
463
|
+
if (dialog.open) dialog.close();
|
|
464
|
+
};
|
|
465
|
+
}, []);
|
|
466
|
+
async function handleCopy() {
|
|
467
|
+
try {
|
|
468
|
+
await navigator.clipboard.writeText(secureToken);
|
|
469
|
+
setCopied(true);
|
|
470
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
471
|
+
} catch {
|
|
472
|
+
setCopied(false);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
function handleKeyDown(e) {
|
|
476
|
+
if (e.key === "Escape") {
|
|
477
|
+
e.preventDefault();
|
|
478
|
+
onClose();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function handleBackdropClick(e) {
|
|
482
|
+
if (e.target === dialogRef.current) {
|
|
483
|
+
onClose();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
487
|
+
"dialog",
|
|
488
|
+
{
|
|
489
|
+
ref: dialogRef,
|
|
490
|
+
"aria-modal": "true",
|
|
491
|
+
"aria-labelledby": titleId,
|
|
492
|
+
"aria-describedby": warningId,
|
|
493
|
+
onKeyDown: handleKeyDown,
|
|
494
|
+
onClick: handleBackdropClick,
|
|
495
|
+
className: "rounded-lg shadow-xl p-0 backdrop:bg-black/40 w-full max-w-lg mx-auto",
|
|
496
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
497
|
+
"div",
|
|
498
|
+
{
|
|
499
|
+
className: "p-6 flex flex-col gap-4",
|
|
500
|
+
onClick: (e) => e.stopPropagation(),
|
|
501
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
502
|
+
children: [
|
|
503
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { id: titleId, className: "text-lg font-semibold text-gray-900", children: title }),
|
|
504
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
505
|
+
"p",
|
|
506
|
+
{
|
|
507
|
+
id: warningId,
|
|
508
|
+
className: "text-sm text-amber-700 bg-amber-50 border border-amber-200 rounded-md px-3 py-2",
|
|
509
|
+
children: warningMessage
|
|
510
|
+
}
|
|
511
|
+
),
|
|
512
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 bg-gray-50 border border-gray-200 rounded-md px-3 py-2", children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "flex-1 text-sm font-mono text-gray-800 break-all select-all", children: secureToken }) }),
|
|
513
|
+
/* @__PURE__ */ jsxRuntime.jsx("output", { "aria-live": "polite", "aria-atomic": "true", className: "min-h-[1.25rem] block", children: copied && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-green-700 font-medium", children: t("TokenDisplayOnceModal.copied", locale) }) }),
|
|
514
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3 mt-2", children: [
|
|
515
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
516
|
+
"button",
|
|
517
|
+
{
|
|
518
|
+
type: "button",
|
|
519
|
+
onClick: handleCopy,
|
|
520
|
+
className: "px-4 py-2 rounded-md text-sm font-medium bg-gray-100 text-gray-800 hover:bg-gray-200 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-400 transition-colors",
|
|
521
|
+
children: copyLabel
|
|
522
|
+
}
|
|
523
|
+
),
|
|
524
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
525
|
+
"button",
|
|
526
|
+
{
|
|
527
|
+
type: "button",
|
|
528
|
+
onClick: onClose,
|
|
529
|
+
className: "px-4 py-2 rounded-md text-sm font-medium bg-gray-900 text-white hover:bg-gray-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-900 transition-colors",
|
|
530
|
+
children: closeLabel
|
|
531
|
+
}
|
|
532
|
+
)
|
|
533
|
+
] })
|
|
534
|
+
]
|
|
535
|
+
}
|
|
536
|
+
)
|
|
537
|
+
}
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
function TokenDisplayOnceModal(raw) {
|
|
541
|
+
try {
|
|
542
|
+
const parsed = validateTokenDisplayOnceModalProps(raw);
|
|
543
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TokenDisplayOnceModalInner, { ...parsed, onClose: raw.onClose });
|
|
544
|
+
} catch (err) {
|
|
545
|
+
console.warn("[TokenDisplayOnceModal] Invalid props \u2014 component will not render.", err);
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
var StatusBadgePropsSchema = zod.z.object({
|
|
550
|
+
status: zod.z.string(),
|
|
551
|
+
variant: zod.z.enum(["success", "warning", "danger", "info", "neutral"]).default("neutral"),
|
|
552
|
+
label: zod.z.string().optional(),
|
|
553
|
+
locale: zod.z.enum(["en", "fr"])
|
|
554
|
+
});
|
|
555
|
+
function validateProps3(raw) {
|
|
556
|
+
return StatusBadgePropsSchema.parse(raw);
|
|
557
|
+
}
|
|
558
|
+
var variantClasses = {
|
|
559
|
+
success: "bg-green-100 text-green-800 ring-1 ring-green-300",
|
|
560
|
+
warning: "bg-amber-100 text-amber-800 ring-1 ring-amber-300",
|
|
561
|
+
danger: "bg-red-100 text-red-800 ring-1 ring-red-300",
|
|
562
|
+
info: "bg-blue-100 text-blue-800 ring-1 ring-blue-300",
|
|
563
|
+
neutral: "bg-slate-100 text-slate-800 ring-1 ring-slate-300"
|
|
564
|
+
};
|
|
565
|
+
function StatusBadgeInner({ status, variant, label, locale }) {
|
|
566
|
+
const ariaLabel = label ?? status;
|
|
567
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
568
|
+
"output",
|
|
569
|
+
{
|
|
570
|
+
"aria-label": ariaLabel,
|
|
571
|
+
lang: locale,
|
|
572
|
+
className: `inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${variantClasses[variant]}`,
|
|
573
|
+
children: label ?? status
|
|
574
|
+
}
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
function StatusBadge(raw) {
|
|
578
|
+
try {
|
|
579
|
+
const props = validateProps3(raw);
|
|
580
|
+
return /* @__PURE__ */ jsxRuntime.jsx(StatusBadgeInner, { ...props });
|
|
581
|
+
} catch {
|
|
582
|
+
const locale = raw.locale === "fr" ? "fr" : "en";
|
|
583
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", children: t("StatusBadge.error.invalidProps", locale) });
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
function useStreamingHydration(props$) {
|
|
587
|
+
const [state, setState] = react.useState({});
|
|
588
|
+
react.useEffect(() => {
|
|
589
|
+
const sub = props$.subscribe((chunk) => {
|
|
590
|
+
setState((prev) => ({ ...prev, ...chunk }));
|
|
591
|
+
});
|
|
592
|
+
return () => {
|
|
593
|
+
sub.unsubscribe();
|
|
594
|
+
};
|
|
595
|
+
}, [props$]);
|
|
596
|
+
return state;
|
|
597
|
+
}
|
|
598
|
+
var MOSAIC_STREAM_CHUNK_TYPE = "mosaic.stream.chunk";
|
|
599
|
+
function createPostMessageObservable() {
|
|
600
|
+
const subject = new rxjs.Subject();
|
|
601
|
+
if (typeof window !== "undefined") {
|
|
602
|
+
window.addEventListener("message", (event) => {
|
|
603
|
+
const data = event.data;
|
|
604
|
+
if (data?.type === MOSAIC_STREAM_CHUNK_TYPE) {
|
|
605
|
+
subject.next(data.data ?? data.params);
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
return subject.asObservable();
|
|
610
|
+
}
|
|
611
|
+
var MCP_UI_CAPABILITY_KEY = "io.modelcontextprotocol/ui";
|
|
612
|
+
var MOSAIC_MIME_TYPE = "text/html;profile=mcp-app";
|
|
613
|
+
var SCHEMA_BY_NAME = {
|
|
614
|
+
ProgressBar: ProgressBarPropsSchema,
|
|
615
|
+
ConfirmDialog: ConfirmDialogPropsSchema,
|
|
616
|
+
TableView: TableViewPropsSchema,
|
|
617
|
+
MarkdownRenderer: MarkdownRendererPropsSchema,
|
|
618
|
+
TokenDisplayOnceModal: TokenDisplayOnceModalPropsSchema,
|
|
619
|
+
StatusBadge: StatusBadgePropsSchema
|
|
620
|
+
};
|
|
621
|
+
var COMPONENT_KEYS = Object.keys(SCHEMA_BY_NAME);
|
|
622
|
+
var I18N_TITLE = {
|
|
623
|
+
ProgressBar: { en: "Progress", fr: "Progression" },
|
|
624
|
+
ConfirmDialog: { en: "Confirmation", fr: "Confirmation" },
|
|
625
|
+
TableView: { en: "Data table", fr: "Tableau de donn\xE9es" },
|
|
626
|
+
MarkdownRenderer: { en: "Markdown content", fr: "Contenu Markdown" },
|
|
627
|
+
TokenDisplayOnceModal: { en: "Token (shown once)", fr: "Jeton (affich\xE9 une fois)" },
|
|
628
|
+
StatusBadge: { en: "Status", fr: "Statut" }
|
|
629
|
+
};
|
|
630
|
+
function escapeHtml(s) {
|
|
631
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
632
|
+
}
|
|
633
|
+
function fallbackMarkdown(componentName, locale) {
|
|
634
|
+
const heading = I18N_TITLE[componentName][locale];
|
|
635
|
+
const note = locale === "fr" ? "Rendu Markdown de secours \u2014 composant Mosaic charg\xE9 via MCP UI." : "Markdown fallback \u2014 Mosaic component rendered via MCP UI.";
|
|
636
|
+
return `# ${heading}
|
|
637
|
+
|
|
638
|
+
${note}
|
|
639
|
+
`;
|
|
640
|
+
}
|
|
641
|
+
function buildHtml(componentName, props, locale) {
|
|
642
|
+
const propsJson = escapeHtml(JSON.stringify(props));
|
|
643
|
+
const title = escapeHtml(I18N_TITLE[componentName][locale]);
|
|
644
|
+
return `<!DOCTYPE html><html lang="${locale}"><head><meta charset="utf-8"><title>${title}</title></head><body><div id="mosaic-root" data-component="${componentName}" data-props='${propsJson}'></div></body></html>`;
|
|
645
|
+
}
|
|
646
|
+
function createMosaicResource(componentName, props, locale = "en") {
|
|
647
|
+
const schema = SCHEMA_BY_NAME[componentName];
|
|
648
|
+
if (!schema) {
|
|
649
|
+
throw new Error(`createMosaicResource: unknown component '${componentName}'`);
|
|
650
|
+
}
|
|
651
|
+
const validated = schema.parse(props);
|
|
652
|
+
const uri = `ui://mosaic/${componentName.toLowerCase()}`;
|
|
653
|
+
const html = buildHtml(componentName, validated, locale);
|
|
654
|
+
const fallback = fallbackMarkdown(componentName, locale);
|
|
655
|
+
const resource = server.createUIResource({
|
|
656
|
+
uri,
|
|
657
|
+
content: { type: "rawHtml", htmlString: html },
|
|
658
|
+
encoding: "text",
|
|
659
|
+
metadata: {
|
|
660
|
+
ui: {
|
|
661
|
+
resourceUri: uri,
|
|
662
|
+
locale,
|
|
663
|
+
componentName,
|
|
664
|
+
fallback
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
resource.resource.mimeType = MOSAIC_MIME_TYPE;
|
|
669
|
+
return resource;
|
|
670
|
+
}
|
|
671
|
+
var MOSAIC_SUPPORTED_COMPONENTS = COMPONENT_KEYS;
|
|
672
|
+
|
|
673
|
+
exports.ConfirmDialog = ConfirmDialog;
|
|
674
|
+
exports.ConfirmDialogPropsSchema = ConfirmDialogPropsSchema;
|
|
675
|
+
exports.MCP_UI_CAPABILITY_KEY = MCP_UI_CAPABILITY_KEY;
|
|
676
|
+
exports.MOSAIC_MIME_TYPE = MOSAIC_MIME_TYPE;
|
|
677
|
+
exports.MOSAIC_STREAM_CHUNK_TYPE = MOSAIC_STREAM_CHUNK_TYPE;
|
|
678
|
+
exports.MOSAIC_SUPPORTED_COMPONENTS = MOSAIC_SUPPORTED_COMPONENTS;
|
|
679
|
+
exports.MarkdownRenderer = MarkdownRenderer;
|
|
680
|
+
exports.MarkdownRendererPropsSchema = MarkdownRendererPropsSchema;
|
|
681
|
+
exports.ProgressBar = ProgressBar;
|
|
682
|
+
exports.ProgressBarPropsSchema = ProgressBarPropsSchema;
|
|
683
|
+
exports.StatusBadge = StatusBadge;
|
|
684
|
+
exports.StatusBadgePropsSchema = StatusBadgePropsSchema;
|
|
685
|
+
exports.TableView = TableView;
|
|
686
|
+
exports.TableViewPropsSchema = TableViewPropsSchema;
|
|
687
|
+
exports.TokenDisplayOnceModal = TokenDisplayOnceModal;
|
|
688
|
+
exports.TokenDisplayOnceModalPropsSchema = TokenDisplayOnceModalPropsSchema;
|
|
689
|
+
exports.createMosaicResource = createMosaicResource;
|
|
690
|
+
exports.createPostMessageObservable = createPostMessageObservable;
|
|
691
|
+
exports.useStreamingHydration = useStreamingHydration;
|
|
692
|
+
exports.validateConfirmDialogProps = validateConfirmDialogProps;
|
|
693
|
+
exports.validateMarkdownRendererProps = validateProps2;
|
|
694
|
+
exports.validateProps = validateProps;
|
|
695
|
+
exports.validateStatusBadgeProps = validateProps3;
|
|
696
|
+
exports.validateTableViewProps = validateTableViewProps;
|
|
697
|
+
exports.validateTokenDisplayOnceModalProps = validateTokenDisplayOnceModalProps;
|
|
698
|
+
//# sourceMappingURL=index.cjs.map
|
|
699
|
+
//# sourceMappingURL=index.cjs.map
|