@frontmcp/ui 1.11.2 → 1.11.3

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.
Files changed (90) hide show
  1. package/bridge/adapters/index.d.ts.map +1 -1
  2. package/bridge/core/adapter-registry.d.ts +1 -1
  3. package/bridge/core/adapter-registry.d.ts.map +1 -1
  4. package/components/Alert/Alert.d.ts.map +1 -1
  5. package/components/Alert/index.js +1 -1
  6. package/components/Avatar/Avatar.d.ts.map +1 -1
  7. package/components/Badge/Badge.d.ts.map +1 -1
  8. package/components/Badge/index.js +1 -1
  9. package/components/Button/Button.d.ts.map +1 -1
  10. package/components/Button/index.js +1 -1
  11. package/components/Card/Card.d.ts +3 -3
  12. package/components/Card/Card.d.ts.map +1 -1
  13. package/components/Card/index.js +4 -4
  14. package/components/List/List.d.ts.map +1 -1
  15. package/components/List/index.js +2 -2
  16. package/components/Loader/Loader.d.ts +1 -1
  17. package/components/Loader/Loader.d.ts.map +1 -1
  18. package/components/Loader/index.js +2 -2
  19. package/components/Modal/Modal.d.ts +1 -1
  20. package/components/Modal/Modal.d.ts.map +1 -1
  21. package/components/Modal/index.js +3 -3
  22. package/components/Select/Select.d.ts.map +1 -1
  23. package/components/Select/index.js +1 -1
  24. package/components/Table/Table.d.ts.map +1 -1
  25. package/components/Table/index.js +1 -1
  26. package/components/TextField/TextField.d.ts.map +1 -1
  27. package/components/index.js +16 -16
  28. package/esm/components/Alert/index.mjs +1 -1
  29. package/esm/components/Badge/index.mjs +1 -1
  30. package/esm/components/Button/index.mjs +1 -1
  31. package/esm/components/Card/index.mjs +4 -4
  32. package/esm/components/List/index.mjs +2 -2
  33. package/esm/components/Loader/index.mjs +2 -2
  34. package/esm/components/Modal/index.mjs +3 -3
  35. package/esm/components/Select/index.mjs +1 -1
  36. package/esm/components/Table/index.mjs +1 -1
  37. package/esm/components/index.mjs +16 -16
  38. package/esm/package.json +2 -2
  39. package/esm/renderer/charts/index.mjs +21 -21
  40. package/esm/renderer/common/index.mjs +2 -2
  41. package/esm/renderer/csv/index.mjs +3 -3
  42. package/esm/renderer/flow/index.mjs +31 -31
  43. package/esm/renderer/html/index.mjs +1 -1
  44. package/esm/renderer/image/index.mjs +3 -3
  45. package/esm/renderer/index.mjs +1584 -1578
  46. package/esm/renderer/maps/index.mjs +16 -16
  47. package/esm/renderer/math/index.mjs +15 -15
  48. package/esm/renderer/mdx/index.mjs +7 -5
  49. package/esm/renderer/media/index.mjs +2 -2
  50. package/esm/renderer/mermaid/index.mjs +3 -3
  51. package/esm/renderer/pdf/index.mjs +4 -4
  52. package/esm/renderer/react/index.mjs +8 -8
  53. package/esm/theme/index.mjs +1 -1
  54. package/package.json +2 -2
  55. package/renderer/auto-detect.d.ts +1 -1
  56. package/renderer/auto-detect.d.ts.map +1 -1
  57. package/renderer/charts/index.d.ts.map +1 -1
  58. package/renderer/charts/index.js +22 -22
  59. package/renderer/common/index.js +1 -1
  60. package/renderer/common/use-lazy-module.d.ts.map +1 -1
  61. package/renderer/common/use-renderer-theme.d.ts.map +1 -1
  62. package/renderer/csv/index.d.ts.map +1 -1
  63. package/renderer/csv/index.js +3 -3
  64. package/renderer/flow/index.d.ts.map +1 -1
  65. package/renderer/flow/index.js +32 -32
  66. package/renderer/html/index.d.ts.map +1 -1
  67. package/renderer/html/index.js +1 -1
  68. package/renderer/image/index.d.ts.map +1 -1
  69. package/renderer/image/index.js +3 -3
  70. package/renderer/index.d.ts.map +1 -1
  71. package/renderer/index.js +1593 -1587
  72. package/renderer/maps/index.d.ts.map +1 -1
  73. package/renderer/maps/index.js +15 -15
  74. package/renderer/math/index.d.ts.map +1 -1
  75. package/renderer/math/index.js +14 -14
  76. package/renderer/mdx/index.d.ts.map +1 -1
  77. package/renderer/mdx/index.js +6 -4
  78. package/renderer/media/index.d.ts.map +1 -1
  79. package/renderer/media/index.js +1 -1
  80. package/renderer/mermaid/index.d.ts.map +1 -1
  81. package/renderer/mermaid/index.js +3 -3
  82. package/renderer/pdf/index.d.ts.map +1 -1
  83. package/renderer/pdf/index.js +3 -3
  84. package/renderer/react/index.d.ts.map +1 -1
  85. package/renderer/react/index.js +8 -8
  86. package/renderer/types.d.ts +1 -1
  87. package/renderer/types.d.ts.map +1 -1
  88. package/theme/FrontMcpThemeProvider.d.ts.map +1 -1
  89. package/theme/create-theme.d.ts.map +1 -1
  90. package/theme/index.js +1 -1
@@ -175,54 +175,12 @@ function ContentViewInner({ content, options }) {
175
175
  return renderContent(content, options);
176
176
  }
177
177
 
178
- // libs/ui/src/renderer/common/use-renderer-theme.ts
179
- import { useMemo } from "react";
180
- import { useTheme } from "@mui/material/styles";
181
- function extractThemeValues(theme) {
182
- const palette = theme.palette;
183
- return {
184
- mode: palette.mode,
185
- primary: palette.primary.main,
186
- secondary: palette.secondary.main,
187
- error: palette.error.main,
188
- warning: palette.warning.main,
189
- success: palette.success.main,
190
- info: palette.info.main,
191
- background: palette.background.default,
192
- paper: palette.background.paper,
193
- textPrimary: palette.text.primary,
194
- textSecondary: palette.text.secondary,
195
- divider: palette.divider,
196
- fontFamily: theme.typography.fontFamily ?? "sans-serif",
197
- monoFontFamily: theme.typography["monoFontFamily"] ?? '"SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", monospace',
198
- fontSize: theme.typography.fontSize ?? 14,
199
- borderRadius: typeof theme.shape.borderRadius === "number" ? theme.shape.borderRadius : 4,
200
- seriesColors: [
201
- palette.primary.main,
202
- palette.secondary.main,
203
- palette.error.main,
204
- palette.warning.main,
205
- palette.success.main,
206
- palette.info.main
207
- ]
208
- };
209
- }
210
- function useRendererTheme() {
211
- const theme = useTheme();
212
- return useMemo(() => extractThemeValues(theme), [theme]);
213
- }
214
-
215
- // libs/ui/src/renderer/common/inject-stylesheet.ts
216
- function injectStylesheet(href, id) {
217
- if (typeof document === "undefined") return;
218
- if (document.getElementById(id)) return;
219
- const link = document.createElement("link");
220
- link.id = id;
221
- link.rel = "stylesheet";
222
- link.href = href;
223
- link.crossOrigin = "anonymous";
224
- document.head.appendChild(link);
225
- }
178
+ // libs/ui/src/renderer/charts/index.ts
179
+ import Alert from "@mui/material/Alert";
180
+ import Box from "@mui/material/Box";
181
+ import { styled } from "@mui/material/styles";
182
+ import Typography from "@mui/material/Typography";
183
+ import React2, { useMemo as useMemo2 } from "react";
226
184
 
227
185
  // libs/ui/src/renderer/common/lazy-import.ts
228
186
  var ESM_SH_BASE = "https://esm.sh/";
@@ -284,7 +242,7 @@ function createLazyImport(moduleId, importer) {
284
242
  }
285
243
 
286
244
  // libs/ui/src/renderer/common/use-lazy-module.ts
287
- import { useState, useEffect } from "react";
245
+ import { useEffect, useState } from "react";
288
246
  function useLazyModule(lazy) {
289
247
  const [, forceUpdate] = useState(0);
290
248
  useEffect(() => {
@@ -300,207 +258,606 @@ function useLazyModule(lazy) {
300
258
  return lazy.get();
301
259
  }
302
260
 
303
- // libs/ui/src/renderer/mdx/index.ts
304
- import React2, { useMemo as useMemo2 } from "react";
305
- import Box from "@mui/material/Box";
306
- import Typography from "@mui/material/Typography";
307
- import Link from "@mui/material/Link";
308
- import MuiTable from "@mui/material/Table";
309
- import TableBody from "@mui/material/TableBody";
310
- import TableCell from "@mui/material/TableCell";
311
- import TableHead from "@mui/material/TableHead";
312
- import TableRow from "@mui/material/TableRow";
313
- import { styled } from "@mui/material/styles";
314
- var lazyReactMarkdown = createLazyImport(
315
- "react-markdown",
316
- async () => {
317
- const mod = await runtimeImportWithFallback(
318
- "react-markdown",
319
- esmShUrl("react-markdown@9", { external: ["react", "react-dom"] })
320
- );
321
- return mod;
322
- }
323
- );
324
- var lazyRemarkGfm = createLazyImport("remark-gfm", async () => {
325
- const mod = await runtimeImportWithFallback("remark-gfm", esmShUrl("remark-gfm@4"));
326
- return mod;
327
- });
328
- var lazyRehypeHighlight = createLazyImport("rehype-highlight", async () => {
329
- const mod = await runtimeImportWithFallback("rehype-highlight", esmShUrl("rehype-highlight@7"));
330
- return mod;
331
- });
332
- var lazyRehypeRaw = createLazyImport("rehype-raw", async () => {
333
- const mod = await runtimeImportWithFallback("rehype-raw", esmShUrl("rehype-raw@7"));
334
- return mod;
335
- });
336
- var MarkdownRoot = styled(Box, {
337
- name: "FrontMcpMarkdown",
338
- slot: "Root"
339
- })(() => ({
340
- lineHeight: 1.7,
341
- "& > *:first-of-type": { marginTop: 0 },
342
- "& > *:last-child": { marginBottom: 0 },
343
- "& img": { maxWidth: "100%", height: "auto" }
344
- }));
345
- var CodeBlock = styled("pre", {
346
- name: "FrontMcpMarkdown",
347
- slot: "Code"
348
- })(({ theme }) => {
349
- const monoFont = String(
350
- theme.typography["monoFontFamily"] ?? '"SF Mono", "Fira Code", "Roboto Mono", monospace'
351
- );
352
- return {
353
- backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[100],
354
- padding: theme.spacing(2),
355
- borderRadius: Number(theme.shape.borderRadius),
356
- overflow: "auto",
357
- fontFamily: monoFont,
358
- fontSize: "0.875rem",
359
- "& code": {
360
- backgroundColor: "transparent",
361
- padding: 0,
362
- fontSize: "inherit",
363
- fontFamily: "inherit"
364
- }
365
- };
366
- });
367
- var InlineCode = styled("code")(({ theme }) => {
368
- const monoFont = String(
369
- theme.typography["monoFontFamily"] ?? '"SF Mono", "Fira Code", "Roboto Mono", monospace'
370
- );
371
- return {
372
- backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[800] : theme.palette.grey[200],
373
- padding: "2px 6px",
374
- borderRadius: Number(theme.shape.borderRadius) / 2,
375
- fontFamily: monoFont,
376
- fontSize: "0.85em"
377
- };
378
- });
379
- var Blockquote = styled("blockquote", {
380
- name: "FrontMcpMarkdown",
381
- slot: "Blockquote"
382
- })(({ theme }) => ({
383
- borderLeft: `4px solid ${theme.palette.primary.main}`,
384
- margin: theme.spacing(2, 0),
385
- padding: theme.spacing(1, 2),
386
- color: theme.palette.text.secondary,
387
- "& > p": { margin: 0 }
388
- }));
389
- function createMuiComponents() {
261
+ // libs/ui/src/renderer/common/use-renderer-theme.ts
262
+ import { useTheme } from "@mui/material/styles";
263
+ import { useMemo } from "react";
264
+ function extractThemeValues(theme) {
265
+ const palette = theme.palette;
390
266
  return {
391
- h1: (props) => React2.createElement(Typography, { ...props, variant: "h4", gutterBottom: true }),
392
- h2: (props) => React2.createElement(Typography, { ...props, variant: "h5", gutterBottom: true }),
393
- h3: (props) => React2.createElement(Typography, { ...props, variant: "h6", gutterBottom: true }),
394
- h4: (props) => React2.createElement(Typography, { ...props, variant: "subtitle1", gutterBottom: true, fontWeight: 600 }),
395
- h5: (props) => React2.createElement(Typography, { ...props, variant: "subtitle2", gutterBottom: true }),
396
- h6: (props) => React2.createElement(Typography, { ...props, variant: "subtitle2", gutterBottom: true }),
397
- p: (props) => React2.createElement(Typography, { ...props, variant: "body1", paragraph: true }),
398
- a: (props) => React2.createElement(Link, { ...props, target: "_blank", rel: "noopener noreferrer" }),
399
- blockquote: (props) => React2.createElement(Blockquote, props),
400
- pre: (props) => React2.createElement(CodeBlock, props),
401
- code: ({ inline, className, children, ...rest }) => {
402
- if (inline) return React2.createElement(InlineCode, rest, children);
403
- return React2.createElement("code", { className, ...rest }, children);
404
- },
405
- table: (props) => React2.createElement(MuiTable, { ...props, size: "small" }),
406
- thead: (props) => React2.createElement(TableHead, props),
407
- tbody: (props) => React2.createElement(TableBody, props),
408
- tr: (props) => React2.createElement(TableRow, { ...props, hover: true }),
409
- th: (props) => React2.createElement(TableCell, { ...props, sx: { fontWeight: 600 } }),
410
- td: (props) => React2.createElement(TableCell, props)
267
+ mode: palette.mode,
268
+ primary: palette.primary.main,
269
+ secondary: palette.secondary.main,
270
+ error: palette.error.main,
271
+ warning: palette.warning.main,
272
+ success: palette.success.main,
273
+ info: palette.info.main,
274
+ background: palette.background.default,
275
+ paper: palette.background.paper,
276
+ textPrimary: palette.text.primary,
277
+ textSecondary: palette.text.secondary,
278
+ divider: palette.divider,
279
+ fontFamily: theme.typography.fontFamily ?? "sans-serif",
280
+ monoFontFamily: theme.typography["monoFontFamily"] ?? '"SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", monospace',
281
+ fontSize: theme.typography.fontSize ?? 14,
282
+ borderRadius: typeof theme.shape.borderRadius === "number" ? theme.shape.borderRadius : 4,
283
+ seriesColors: [
284
+ palette.primary.main,
285
+ palette.secondary.main,
286
+ palette.error.main,
287
+ palette.warning.main,
288
+ palette.success.main,
289
+ palette.info.main
290
+ ]
411
291
  };
412
292
  }
413
- function MarkdownView({ content, className }) {
414
- const components = useMemo2(() => createMuiComponents(), []);
415
- const ReactMarkdownMod = useLazyModule(lazyReactMarkdown);
416
- const remarkGfmMod = lazyRemarkGfm.get();
417
- const rehypeHighlightMod = lazyRehypeHighlight.get();
418
- const rehypeRawMod = lazyRehypeRaw.get();
419
- if (ReactMarkdownMod) {
420
- const ReactMarkdown = ReactMarkdownMod.default;
421
- const remarkPlugins = [];
422
- const rehypePlugins = [];
423
- if (remarkGfmMod) remarkPlugins.push(remarkGfmMod.default);
424
- if (rehypeHighlightMod) rehypePlugins.push(rehypeHighlightMod.default);
425
- if (rehypeRawMod) rehypePlugins.push(rehypeRawMod.default);
426
- return React2.createElement(
427
- MarkdownRoot,
428
- { className },
429
- React2.createElement(ReactMarkdown, {
430
- children: content,
431
- remarkPlugins,
432
- rehypePlugins,
433
- components
434
- })
435
- );
436
- }
437
- return React2.createElement(MarkdownRoot, { className }, ...renderFallback(content));
293
+ function useRendererTheme() {
294
+ const theme = useTheme();
295
+ return useMemo(() => extractThemeValues(theme), [theme]);
438
296
  }
439
- function renderFallback(content) {
440
- const lines = content.split("\n");
441
- const elements = [];
442
- for (let i = 0; i < lines.length; i++) {
443
- const line = lines[i];
444
- if (line.startsWith("# ")) {
445
- elements.push(React2.createElement(Typography, { key: i, variant: "h4", gutterBottom: true }, line.slice(2)));
446
- } else if (line.startsWith("## ")) {
447
- elements.push(React2.createElement(Typography, { key: i, variant: "h5", gutterBottom: true }, line.slice(3)));
448
- } else if (line.startsWith("### ")) {
449
- elements.push(React2.createElement(Typography, { key: i, variant: "h6", gutterBottom: true }, line.slice(4)));
450
- } else if (line.startsWith("- ") || line.startsWith("* ")) {
451
- elements.push(React2.createElement("li", { key: i }, line.slice(2)));
452
- } else if (line.startsWith("**") && line.endsWith("**")) {
453
- elements.push(React2.createElement(Typography, { key: i, variant: "body1", sx: { fontWeight: 700 } }, line.slice(2, -2)));
454
- } else if (line.trim()) {
455
- elements.push(React2.createElement(Typography, { key: i, variant: "body1", sx: { mb: 2 } }, line));
456
- }
457
- }
458
- return elements;
297
+
298
+ // libs/ui/src/renderer/charts/index.ts
299
+ var CHART_TYPE_RE2 = /"type"\s*:\s*"(?:bar|line|area|pie|scatter|radar|composed)"/;
300
+ function isChart(content) {
301
+ const trimmed = content.trim();
302
+ if (trimmed.charCodeAt(0) !== 123) return false;
303
+ return CHART_TYPE_RE2.test(trimmed) && trimmed.includes('"data"');
459
304
  }
460
- lazyReactMarkdown.load().catch(() => {
461
- });
462
- lazyRemarkGfm.load().catch(() => {
463
- });
464
- lazyRehypeHighlight.load().catch(() => {
465
- });
466
- lazyRehypeRaw.load().catch(() => {
305
+ var lazyRecharts = createLazyImport("recharts", async () => {
306
+ const mod = await runtimeImportWithFallback("recharts", esmShUrl("recharts@2", { external: ["react", "react-dom"] }));
307
+ return mod;
467
308
  });
468
- var MdxRenderer = class {
469
- type = "mdx";
470
- priority = 5;
471
- canHandle(content) {
472
- return /^---\s*\n/.test(content) || /import\s+/.test(content) || /<[A-Z]/.test(content) || /^#{1,6}\s+\S/m.test(content) || /^\s*[-*+]\s+\S/m.test(content);
473
- }
474
- render(content, options) {
475
- return React2.createElement(MarkdownView, {
476
- content,
477
- className: options?.className ?? "fmcp-mdx-content"
478
- });
479
- }
480
- };
481
- var mdxRenderer = new MdxRenderer();
482
-
483
- // libs/ui/src/renderer/html/index.ts
484
- import React3, { useMemo as useMemo3, useState as useState2, useEffect as useEffect2 } from "react";
485
- import Box2 from "@mui/material/Box";
486
- import { styled as styled2 } from "@mui/material/styles";
487
- var HtmlRoot = styled2(Box2, {
488
- name: "FrontMcpHtml",
309
+ var ChartRoot = styled(Box, {
310
+ name: "FrontMcpChart",
489
311
  slot: "Root"
490
312
  })(({ theme }) => ({
491
- "& a": { color: theme.palette.primary.main },
492
- "& table": { borderCollapse: "collapse", width: "100%" },
493
- "& th, & td": { border: `1px solid ${theme.palette.divider}`, padding: theme.spacing(1) },
494
- "& pre": {
495
- backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[100],
496
- padding: theme.spacing(1.5),
497
- borderRadius: theme.shape.borderRadius,
498
- overflow: "auto"
499
- }
313
+ width: "100%",
314
+ padding: theme.spacing(2)
500
315
  }));
501
- var purifyModule = null;
502
- var purifyPromise = null;
503
- function loadDOMPurify() {
316
+ function ChartView({ config, className }) {
317
+ const themeValues = useRendererTheme();
318
+ const recharts = useLazyModule(lazyRecharts);
319
+ const colors = useMemo2(() => config.colors ?? themeValues.seriesColors, [config.colors, themeValues.seriesColors]);
320
+ if (!recharts) {
321
+ return React2.createElement(Alert, { severity: "info" }, "Loading chart library...");
322
+ }
323
+ const { ResponsiveContainer, Tooltip, Legend, CartesianGrid, XAxis, YAxis } = recharts;
324
+ const height = config.height ?? 400;
325
+ const xKey = config.xKey ?? "name";
326
+ const yKeys = config.yKeys ?? Object.keys(config.data?.[0] ?? {}).filter((k) => k !== xKey);
327
+ const chartContent = renderChartType(
328
+ recharts,
329
+ config.type,
330
+ config.data,
331
+ xKey,
332
+ yKeys,
333
+ colors,
334
+ themeValues.borderRadius
335
+ );
336
+ return React2.createElement(
337
+ ChartRoot,
338
+ { className },
339
+ config.title && React2.createElement(
340
+ Typography,
341
+ { variant: "subtitle1", gutterBottom: true, sx: { fontWeight: 600, textAlign: "center" } },
342
+ config.title
343
+ ),
344
+ React2.createElement(ResponsiveContainer, { width: "100%", height }, chartContent)
345
+ );
346
+ }
347
+ function renderChartType(rc, type, data, xKey, yKeys, colors, borderRadius) {
348
+ const commonProps = { data };
349
+ const grid = React2.createElement(rc.CartesianGrid, { strokeDasharray: "3 3", opacity: 0.3 });
350
+ const xAxis = React2.createElement(rc.XAxis, { dataKey: xKey });
351
+ const yAxis = React2.createElement(rc.YAxis);
352
+ const tooltip = React2.createElement(rc.Tooltip, { contentStyle: { borderRadius } });
353
+ const legend = React2.createElement(rc.Legend);
354
+ switch (type) {
355
+ case "bar":
356
+ return React2.createElement(
357
+ rc.BarChart,
358
+ commonProps,
359
+ grid,
360
+ xAxis,
361
+ yAxis,
362
+ tooltip,
363
+ legend,
364
+ ...yKeys.map(
365
+ (key, i) => React2.createElement(rc.Bar, {
366
+ key,
367
+ dataKey: key,
368
+ fill: colors[i % colors.length],
369
+ radius: [borderRadius, borderRadius, 0, 0]
370
+ })
371
+ )
372
+ );
373
+ case "line":
374
+ return React2.createElement(
375
+ rc.LineChart,
376
+ commonProps,
377
+ grid,
378
+ xAxis,
379
+ yAxis,
380
+ tooltip,
381
+ legend,
382
+ ...yKeys.map(
383
+ (key, i) => React2.createElement(rc.Line, {
384
+ key,
385
+ type: "monotone",
386
+ dataKey: key,
387
+ stroke: colors[i % colors.length],
388
+ strokeWidth: 2
389
+ })
390
+ )
391
+ );
392
+ case "area":
393
+ return React2.createElement(
394
+ rc.AreaChart,
395
+ commonProps,
396
+ grid,
397
+ xAxis,
398
+ yAxis,
399
+ tooltip,
400
+ legend,
401
+ ...yKeys.map(
402
+ (key, i) => React2.createElement(rc.Area, {
403
+ key,
404
+ type: "monotone",
405
+ dataKey: key,
406
+ stroke: colors[i % colors.length],
407
+ fill: colors[i % colors.length],
408
+ fillOpacity: 0.3
409
+ })
410
+ )
411
+ );
412
+ case "pie":
413
+ return React2.createElement(
414
+ rc.PieChart,
415
+ {},
416
+ tooltip,
417
+ legend,
418
+ React2.createElement(
419
+ rc.Pie,
420
+ { data, dataKey: yKeys[0] ?? "value", nameKey: xKey, cx: "50%", cy: "50%", outerRadius: "80%", label: true },
421
+ ...data.map((_, i) => React2.createElement(rc.Cell, { key: i, fill: colors[i % colors.length] }))
422
+ )
423
+ );
424
+ case "scatter":
425
+ return React2.createElement(
426
+ rc.ScatterChart,
427
+ {},
428
+ grid,
429
+ xAxis,
430
+ yAxis,
431
+ tooltip,
432
+ legend,
433
+ ...yKeys.map(
434
+ (key, i) => React2.createElement(rc.Scatter, { key, name: key, dataKey: key, data, fill: colors[i % colors.length] })
435
+ )
436
+ );
437
+ case "radar":
438
+ return React2.createElement(
439
+ rc.RadarChart,
440
+ { cx: "50%", cy: "50%", outerRadius: "80%", data },
441
+ React2.createElement(rc.PolarGrid),
442
+ React2.createElement(rc.PolarAngleAxis, { dataKey: xKey }),
443
+ React2.createElement(rc.PolarRadiusAxis),
444
+ tooltip,
445
+ legend,
446
+ ...yKeys.map(
447
+ (key, i) => React2.createElement(rc.Radar, {
448
+ key,
449
+ name: key,
450
+ dataKey: key,
451
+ stroke: colors[i % colors.length],
452
+ fill: colors[i % colors.length],
453
+ fillOpacity: 0.3
454
+ })
455
+ )
456
+ );
457
+ case "composed":
458
+ return React2.createElement(
459
+ rc.ComposedChart,
460
+ commonProps,
461
+ grid,
462
+ xAxis,
463
+ yAxis,
464
+ tooltip,
465
+ legend,
466
+ ...yKeys.map((key, i) => {
467
+ if (i % 2 === 0) {
468
+ return React2.createElement(rc.Bar, {
469
+ key,
470
+ dataKey: key,
471
+ fill: colors[i % colors.length],
472
+ radius: [borderRadius, borderRadius, 0, 0]
473
+ });
474
+ }
475
+ return React2.createElement(rc.Line, {
476
+ key,
477
+ type: "monotone",
478
+ dataKey: key,
479
+ stroke: colors[i % colors.length],
480
+ strokeWidth: 2
481
+ });
482
+ })
483
+ );
484
+ default:
485
+ return React2.createElement(rc.BarChart, commonProps, grid, xAxis, yAxis, tooltip, legend);
486
+ }
487
+ }
488
+ lazyRecharts.load().catch(() => {
489
+ });
490
+ var ChartsRenderer = class {
491
+ type = "chart";
492
+ priority = 80;
493
+ canHandle(content) {
494
+ return isChart(content);
495
+ }
496
+ render(content, options) {
497
+ try {
498
+ const config = JSON.parse(content);
499
+ return React2.createElement(ChartView, {
500
+ config,
501
+ className: options?.className ?? "fmcp-chart-content"
502
+ });
503
+ } catch {
504
+ return React2.createElement(Alert, { severity: "error" }, "Invalid chart JSON");
505
+ }
506
+ }
507
+ };
508
+ var chartsRenderer = new ChartsRenderer();
509
+
510
+ // libs/ui/src/renderer/csv/index.ts
511
+ import Box2 from "@mui/material/Box";
512
+ import { styled as styled2 } from "@mui/material/styles";
513
+ import MuiTable from "@mui/material/Table";
514
+ import TableBody from "@mui/material/TableBody";
515
+ import TableCell from "@mui/material/TableCell";
516
+ import TableContainer from "@mui/material/TableContainer";
517
+ import TableHead from "@mui/material/TableHead";
518
+ import TablePagination from "@mui/material/TablePagination";
519
+ import TableRow from "@mui/material/TableRow";
520
+ import TableSortLabel from "@mui/material/TableSortLabel";
521
+ import TextField from "@mui/material/TextField";
522
+ import React3, { useCallback, useMemo as useMemo3, useState as useState2 } from "react";
523
+ function detectDelimiter(content) {
524
+ const firstLine = content.split("\n")[0];
525
+ const commas = (firstLine.match(/,/g) ?? []).length;
526
+ const tabs = (firstLine.match(/\t/g) ?? []).length;
527
+ const semicolons = (firstLine.match(/;/g) ?? []).length;
528
+ if (tabs > commas && tabs > semicolons) return " ";
529
+ if (semicolons > commas) return ";";
530
+ return ",";
531
+ }
532
+ function parseCsv(content, delimiter) {
533
+ return content.trim().split("\n").map((line) => line.split(delimiter).map((cell) => cell.trim()));
534
+ }
535
+ var CsvRoot = styled2(Box2, {
536
+ name: "FrontMcpCsvTable",
537
+ slot: "Root"
538
+ })({
539
+ width: "100%"
540
+ });
541
+ var StyledHeaderCell = styled2(TableCell, {
542
+ name: "FrontMcpCsvTable",
543
+ slot: "Header"
544
+ })(({ theme }) => ({
545
+ fontWeight: 600,
546
+ backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[50],
547
+ whiteSpace: "nowrap"
548
+ }));
549
+ var FilterToolbar = styled2(Box2, {
550
+ name: "FrontMcpCsvTable",
551
+ slot: "Toolbar"
552
+ })(({ theme }) => ({
553
+ padding: theme.spacing(1, 2),
554
+ display: "flex",
555
+ gap: theme.spacing(1)
556
+ }));
557
+ function CsvTableView({ content, className, pageSize = 25 }) {
558
+ const delimiter = useMemo3(() => detectDelimiter(content), [content]);
559
+ const allRows = useMemo3(() => parseCsv(content, delimiter), [content, delimiter]);
560
+ const headers = allRows[0] ?? [];
561
+ const dataRows = useMemo3(() => allRows.slice(1), [allRows]);
562
+ const [page, setPage] = useState2(0);
563
+ const [rowsPerPage, setRowsPerPage] = useState2(pageSize);
564
+ const [sortCol, setSortCol] = useState2(null);
565
+ const [sortDir, setSortDir] = useState2("asc");
566
+ const [filter, setFilter] = useState2("");
567
+ const filteredRows = useMemo3(() => {
568
+ if (!filter) return dataRows;
569
+ const lower = filter.toLowerCase();
570
+ return dataRows.filter((row) => row.some((cell) => cell.toLowerCase().includes(lower)));
571
+ }, [dataRows, filter]);
572
+ const sortedRows = useMemo3(() => {
573
+ if (sortCol === null) return filteredRows;
574
+ const sorted = [...filteredRows];
575
+ sorted.sort((a, b) => {
576
+ const va = a[sortCol] ?? "";
577
+ const vb = b[sortCol] ?? "";
578
+ const numA = Number(va);
579
+ const numB = Number(vb);
580
+ if (!isNaN(numA) && !isNaN(numB)) {
581
+ return sortDir === "asc" ? numA - numB : numB - numA;
582
+ }
583
+ return sortDir === "asc" ? va.localeCompare(vb) : vb.localeCompare(va);
584
+ });
585
+ return sorted;
586
+ }, [filteredRows, sortCol, sortDir]);
587
+ const pageRows = useMemo3(
588
+ () => sortedRows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage),
589
+ [sortedRows, page, rowsPerPage]
590
+ );
591
+ const handleSort = useCallback((colIndex) => {
592
+ setSortCol((prev) => {
593
+ if (prev === colIndex) {
594
+ setSortDir((d) => d === "asc" ? "desc" : "asc");
595
+ return colIndex;
596
+ }
597
+ setSortDir("asc");
598
+ return colIndex;
599
+ });
600
+ }, []);
601
+ const handleChangePage = useCallback((_, newPage) => setPage(newPage), []);
602
+ const handleChangeRowsPerPage = useCallback((e) => {
603
+ setRowsPerPage(parseInt(e.target.value, 10));
604
+ setPage(0);
605
+ }, []);
606
+ return React3.createElement(
607
+ CsvRoot,
608
+ { className },
609
+ React3.createElement(
610
+ FilterToolbar,
611
+ null,
612
+ React3.createElement(TextField, {
613
+ size: "small",
614
+ placeholder: "Filter rows...",
615
+ value: filter,
616
+ onChange: (e) => {
617
+ setFilter(e.target.value);
618
+ setPage(0);
619
+ },
620
+ sx: { minWidth: 200 }
621
+ })
622
+ ),
623
+ React3.createElement(
624
+ TableContainer,
625
+ { sx: { border: 1, borderColor: "divider", borderRadius: 1 } },
626
+ React3.createElement(
627
+ MuiTable,
628
+ { size: "small", stickyHeader: true },
629
+ React3.createElement(
630
+ TableHead,
631
+ null,
632
+ React3.createElement(
633
+ TableRow,
634
+ null,
635
+ headers.map(
636
+ (header, i) => React3.createElement(
637
+ StyledHeaderCell,
638
+ { key: i },
639
+ React3.createElement(
640
+ TableSortLabel,
641
+ {
642
+ active: sortCol === i,
643
+ direction: sortCol === i ? sortDir : "asc",
644
+ onClick: () => handleSort(i)
645
+ },
646
+ header
647
+ )
648
+ )
649
+ )
650
+ )
651
+ ),
652
+ React3.createElement(
653
+ TableBody,
654
+ null,
655
+ pageRows.map(
656
+ (row, ri) => React3.createElement(
657
+ TableRow,
658
+ { key: ri, hover: true },
659
+ row.map((cell, ci) => React3.createElement(TableCell, { key: ci }, cell))
660
+ )
661
+ )
662
+ )
663
+ )
664
+ ),
665
+ sortedRows.length > rowsPerPage && React3.createElement(TablePagination, {
666
+ count: sortedRows.length,
667
+ page,
668
+ rowsPerPage,
669
+ onPageChange: handleChangePage,
670
+ onRowsPerPageChange: handleChangeRowsPerPage,
671
+ rowsPerPageOptions: [10, 25, 50, 100]
672
+ })
673
+ );
674
+ }
675
+ var CsvRenderer = class {
676
+ type = "csv";
677
+ priority = 10;
678
+ canHandle(content) {
679
+ const lines = content.trim().split("\n").slice(0, 5);
680
+ if (lines.length < 2) return false;
681
+ for (const delim of [",", " ", ";"]) {
682
+ const counts = lines.map((line) => line.split(delim).length);
683
+ if (counts[0] > 1 && counts.every((c) => c === counts[0])) return true;
684
+ }
685
+ return false;
686
+ }
687
+ render(content, options) {
688
+ const pageSize = options?.rendererOptions?.["pageSize"];
689
+ return React3.createElement(CsvTableView, {
690
+ content,
691
+ className: options?.className ?? "fmcp-csv-table",
692
+ pageSize
693
+ });
694
+ }
695
+ };
696
+ var csvRenderer = new CsvRenderer();
697
+
698
+ // libs/ui/src/renderer/flow/index.ts
699
+ import Alert2 from "@mui/material/Alert";
700
+ import Box3 from "@mui/material/Box";
701
+ import { styled as styled3 } from "@mui/material/styles";
702
+ import Typography2 from "@mui/material/Typography";
703
+ import React4, { useEffect as useEffect2, useMemo as useMemo4 } from "react";
704
+
705
+ // libs/ui/src/renderer/common/inject-stylesheet.ts
706
+ function injectStylesheet(href, id) {
707
+ if (typeof document === "undefined") return;
708
+ if (document.getElementById(id)) return;
709
+ const link = document.createElement("link");
710
+ link.id = id;
711
+ link.rel = "stylesheet";
712
+ link.href = href;
713
+ link.crossOrigin = "anonymous";
714
+ document.head.appendChild(link);
715
+ }
716
+
717
+ // libs/ui/src/renderer/flow/index.ts
718
+ var XYFLOW_CSS_URL = "https://esm.sh/@xyflow/react@12/dist/style.css";
719
+ var XYFLOW_CSS_ID = "fmcp-xyflow-css";
720
+ function isFlow(content) {
721
+ const trimmed = content.trim();
722
+ if (trimmed.charCodeAt(0) !== 123) return false;
723
+ return trimmed.includes('"nodes"') && trimmed.includes('"edges"');
724
+ }
725
+ var lazyXYFlow = createLazyImport("@xyflow/react", async () => {
726
+ const mod = await runtimeImportWithFallback(
727
+ "@xyflow/react",
728
+ esmShUrl("@xyflow/react@12", { external: ["react", "react-dom"] })
729
+ );
730
+ return mod;
731
+ });
732
+ var FlowRoot = styled3(Box3, {
733
+ name: "FrontMcpFlow",
734
+ slot: "Root"
735
+ })(({ theme }) => ({
736
+ width: "100%",
737
+ borderRadius: theme.shape.borderRadius,
738
+ overflow: "hidden",
739
+ border: `1px solid ${theme.palette.divider}`
740
+ }));
741
+ function FlowView({ config, className }) {
742
+ useEffect2(() => {
743
+ injectStylesheet(XYFLOW_CSS_URL, XYFLOW_CSS_ID);
744
+ }, []);
745
+ const themeValues = useRendererTheme();
746
+ const xyflow = useLazyModule(lazyXYFlow);
747
+ const height = config.height ?? 500;
748
+ const styledNodes = useMemo4(() => {
749
+ return config.nodes.map((node) => ({
750
+ ...node,
751
+ style: {
752
+ background: themeValues.paper,
753
+ color: themeValues.textPrimary,
754
+ border: `1px solid ${themeValues.divider}`,
755
+ borderRadius: themeValues.borderRadius,
756
+ padding: 8,
757
+ fontSize: themeValues.fontSize,
758
+ fontFamily: themeValues.fontFamily,
759
+ ...node.style
760
+ }
761
+ }));
762
+ }, [config.nodes, themeValues]);
763
+ const styledEdges = useMemo4(() => {
764
+ return config.edges.map((edge, i) => ({
765
+ ...edge,
766
+ style: {
767
+ stroke: themeValues.seriesColors[i % themeValues.seriesColors.length],
768
+ strokeWidth: 2,
769
+ ...edge.style
770
+ }
771
+ }));
772
+ }, [config.edges, themeValues]);
773
+ if (!xyflow) {
774
+ return React4.createElement(Alert2, { severity: "info" }, "Loading flow diagram library...");
775
+ }
776
+ const { ReactFlow, Controls, MiniMap, Background } = xyflow;
777
+ return React4.createElement(
778
+ FlowRoot,
779
+ { className },
780
+ config.title && React4.createElement(
781
+ Typography2,
782
+ { variant: "subtitle1", sx: { fontWeight: 600, p: 1.5, borderBottom: 1, borderColor: "divider" } },
783
+ config.title
784
+ ),
785
+ React4.createElement(
786
+ Box3,
787
+ { sx: { height } },
788
+ React4.createElement(
789
+ ReactFlow,
790
+ {
791
+ nodes: styledNodes,
792
+ edges: styledEdges,
793
+ fitView: config.fitView !== false,
794
+ proOptions: { hideAttribution: true }
795
+ },
796
+ React4.createElement(Controls, {
797
+ style: {
798
+ backgroundColor: themeValues.paper,
799
+ borderRadius: themeValues.borderRadius,
800
+ border: `1px solid ${themeValues.divider}`
801
+ }
802
+ }),
803
+ React4.createElement(MiniMap, {
804
+ style: {
805
+ backgroundColor: themeValues.background,
806
+ borderRadius: themeValues.borderRadius
807
+ },
808
+ nodeColor: themeValues.primary
809
+ }),
810
+ Background && React4.createElement(Background, {
811
+ color: themeValues.divider,
812
+ gap: 16
813
+ })
814
+ )
815
+ )
816
+ );
817
+ }
818
+ lazyXYFlow.load().catch(() => {
819
+ });
820
+ var FlowRenderer = class {
821
+ type = "flow";
822
+ priority = 70;
823
+ canHandle(content) {
824
+ return isFlow(content);
825
+ }
826
+ render(content, options) {
827
+ try {
828
+ const config = JSON.parse(content);
829
+ return React4.createElement(FlowView, {
830
+ config,
831
+ className: options?.className ?? "fmcp-flow-content"
832
+ });
833
+ } catch {
834
+ return React4.createElement(Alert2, { severity: "error" }, "Invalid flow JSON");
835
+ }
836
+ }
837
+ };
838
+ var flowRenderer = new FlowRenderer();
839
+
840
+ // libs/ui/src/renderer/html/index.ts
841
+ import Box4 from "@mui/material/Box";
842
+ import { styled as styled4 } from "@mui/material/styles";
843
+ import React5, { useEffect as useEffect3, useMemo as useMemo5, useState as useState3 } from "react";
844
+ var HtmlRoot = styled4(Box4, {
845
+ name: "FrontMcpHtml",
846
+ slot: "Root"
847
+ })(({ theme }) => ({
848
+ "& a": { color: theme.palette.primary.main },
849
+ "& table": { borderCollapse: "collapse", width: "100%" },
850
+ "& th, & td": { border: `1px solid ${theme.palette.divider}`, padding: theme.spacing(1) },
851
+ "& pre": {
852
+ backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[100],
853
+ padding: theme.spacing(1.5),
854
+ borderRadius: theme.shape.borderRadius,
855
+ overflow: "auto"
856
+ }
857
+ }));
858
+ var purifyModule = null;
859
+ var purifyPromise = null;
860
+ function loadDOMPurify() {
504
861
  if (purifyModule) return Promise.resolve(purifyModule);
505
862
  if (purifyPromise) return purifyPromise;
506
863
  purifyPromise = runtimeImportWithFallback("dompurify", esmShUrl("dompurify@3")).then((mod) => {
@@ -523,16 +880,16 @@ function sanitizeSync(html) {
523
880
  }
524
881
  loadDOMPurify();
525
882
  function HtmlView({ html, className }) {
526
- const [purifyReady, setPurifyReady] = useState2(!!purifyModule);
527
- useEffect2(() => {
883
+ const [purifyReady, setPurifyReady] = useState3(!!purifyModule);
884
+ useEffect3(() => {
528
885
  if (!purifyModule) {
529
886
  loadDOMPurify().then((mod) => {
530
887
  if (mod) setPurifyReady(true);
531
888
  });
532
889
  }
533
890
  }, []);
534
- const sanitized = useMemo3(() => sanitizeSync(html), [html, purifyReady]);
535
- return React3.createElement(HtmlRoot, {
891
+ const sanitized = useMemo5(() => sanitizeSync(html), [html, purifyReady]);
892
+ return React5.createElement(HtmlRoot, {
536
893
  className,
537
894
  dangerouslySetInnerHTML: { __html: sanitized }
538
895
  });
@@ -544,7 +901,7 @@ var HtmlRenderer = class {
544
901
  return /^\s*</.test(content) && /<\/\w+>/.test(content);
545
902
  }
546
903
  render(content, options) {
547
- return React3.createElement(HtmlView, {
904
+ return React5.createElement(HtmlView, {
548
905
  html: content,
549
906
  className: options?.className ?? "fmcp-html-content"
550
907
  });
@@ -552,251 +909,158 @@ var HtmlRenderer = class {
552
909
  };
553
910
  var htmlRenderer = new HtmlRenderer();
554
911
 
555
- // libs/ui/src/renderer/react/index.ts
556
- import React4, { useState as useState3, useEffect as useEffect3 } from "react";
557
- import Box3 from "@mui/material/Box";
558
- import Alert from "@mui/material/Alert";
559
-
560
- // libs/ui/src/runtime/babel-runtime.ts
561
- var BABEL_STANDALONE_CDN = "https://esm.sh/@babel/standalone@7";
562
- var BABEL_STANDALONE_FALLBACK_CDN = "https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.10/babel.min.js";
563
- var babelState = { status: "idle" };
564
- async function loadBabel() {
565
- if (babelState.status === "loaded") {
566
- return babelState.babel;
567
- }
568
- if (babelState.status === "loading") {
569
- return babelState.promise;
570
- }
571
- if (babelState.status === "error") {
572
- throw babelState.error;
573
- }
574
- const promise = doLoadBabel();
575
- babelState = { status: "loading", promise };
576
- try {
577
- const babel = await promise;
578
- babelState = { status: "loaded", babel };
579
- return babel;
580
- } catch (error) {
581
- const err = error instanceof Error ? error : new Error(String(error));
582
- babelState = { status: "error", error: err };
583
- throw err;
584
- }
585
- }
586
- async function doLoadBabel() {
587
- const globalBabel = globalThis["Babel"];
588
- if (globalBabel && typeof globalBabel.transform === "function") {
589
- return globalBabel;
590
- }
591
- try {
592
- const module = await import(
593
- /* @vite-ignore */
594
- /* webpackIgnore: true */
595
- BABEL_STANDALONE_CDN
596
- );
597
- if (module && typeof module.transform === "function") {
598
- return module;
599
- }
600
- if (module.default && typeof module.default.transform === "function") {
601
- return module.default;
602
- }
603
- } catch {
604
- }
605
- return new Promise((resolve, reject) => {
606
- if (typeof document === "undefined") {
607
- reject(new Error("Babel Standalone requires a browser environment"));
608
- return;
609
- }
610
- const script = document.createElement("script");
611
- script.src = BABEL_STANDALONE_FALLBACK_CDN;
612
- script.crossOrigin = "anonymous";
613
- script.onload = () => {
614
- const babel = globalThis["Babel"];
615
- if (babel && typeof babel.transform === "function") {
616
- resolve(babel);
617
- } else {
618
- reject(new Error("Babel Standalone loaded but transform function not found"));
619
- }
620
- };
621
- script.onerror = () => {
622
- reject(new Error(`Failed to load Babel Standalone from ${BABEL_STANDALONE_FALLBACK_CDN}`));
623
- };
624
- document.head.appendChild(script);
625
- });
626
- }
627
- async function transpileJsx(source, filename = "component.jsx") {
628
- const babel = await loadBabel();
629
- const result = babel.transform(source, {
630
- presets: ["react"],
631
- filename
632
- });
633
- return result.code;
634
- }
635
-
636
- // libs/ui/src/renderer/react/index.ts
637
- function isReactJsx(content) {
638
- if (/^#{1,6}\s+\S/m.test(content)) return false;
639
- return /(?:function|const|class)\s+\w+/.test(content) && /(?:return|=>)[\s(]*</.test(content);
640
- }
641
- function parseImports(source) {
642
- const results = [];
643
- const defaultRe = /^import\s+(\w+)\s+from\s+['"]([^'"]+)['"][\s;]*$/gm;
644
- let m;
645
- while ((m = defaultRe.exec(source)) !== null) {
646
- results.push({ line: m[0], localName: m[1], specifier: m[2], named: false });
647
- }
648
- const namedRe = /^import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"][\s;]*$/gm;
649
- while ((m = namedRe.exec(source)) !== null) {
650
- const names = m[1].split(",").map((n) => n.trim()).filter(Boolean);
651
- for (const name of names) {
652
- results.push({ line: m[0], localName: name, specifier: m[2], named: true });
653
- }
654
- }
655
- return results;
656
- }
657
- function stripImports(source) {
658
- return source.replace(/^import\s+\w+\s+from\s+['"][^'"]+['"][\s;]*$/gm, "").replace(/^import\s+\{[^}]+\}\s+from\s+['"][^'"]+['"][\s;]*$/gm, "");
659
- }
660
- function rewriteExportDefault(source) {
661
- let result = source.replace(/^export\s+default\s+(function|class)\b/gm, "var __default__ = $1");
662
- result = result.replace(/^export\s+default\s+(\([^)]*\)\s*=>)/gm, "var __default__ = $1");
663
- result = result.replace(/^export\s+default\s+(\w+)\s*;?\s*$/gm, "var __default__ = $1;");
664
- return result;
665
- }
666
- function isReactSpecifier(specifier) {
667
- return specifier === "react" || /^https?:\/\/esm\.sh\/react(?:@[\d.]+)?$/.test(specifier);
668
- }
669
- async function resolveModules(imports) {
670
- const modules = {};
671
- const specifierMap = /* @__PURE__ */ new Map();
672
- for (const imp of imports) {
673
- const existing = specifierMap.get(imp.specifier) ?? [];
674
- existing.push(imp);
675
- specifierMap.set(imp.specifier, existing);
676
- }
677
- for (const [specifier, specImports] of specifierMap) {
678
- let mod;
679
- if (isReactSpecifier(specifier)) {
680
- mod = React4;
681
- } else {
682
- mod = await runtimeImport(specifier);
683
- }
684
- for (const imp of specImports) {
685
- if (imp.named) {
686
- modules[imp.localName] = mod[imp.localName] ?? mod["default"]?.[imp.localName];
687
- } else {
688
- modules[imp.localName] = mod["default"] ?? mod;
689
- }
690
- }
691
- }
692
- return modules;
912
+ // libs/ui/src/renderer/image/index.ts
913
+ import Alert3 from "@mui/material/Alert";
914
+ import Box5 from "@mui/material/Box";
915
+ import Skeleton from "@mui/material/Skeleton";
916
+ import { styled as styled5 } from "@mui/material/styles";
917
+ import Typography3 from "@mui/material/Typography";
918
+ import React6, { useCallback as useCallback2, useEffect as useEffect4, useState as useState4 } from "react";
919
+ var IMAGE_DATA_URI = /^data:image\/(?:png|jpeg|jpg|gif|webp|svg\+xml|avif)[;,]/;
920
+ var IMAGE_URL = /^https?:\/\/[^?#\s]+\.(?:png|jpe?g|gif|webp|svg|avif|ico)(?:\?[^#\s]*)?$/i;
921
+ var IMAGE_SERVICE_URL = /^https?:\/\/(?:picsum\.photos|images\.unsplash\.com|i\.imgur\.com|placekitten\.com|via\.placeholder\.com)\//i;
922
+ function isImage(content) {
923
+ const trimmed = content.trim();
924
+ return IMAGE_DATA_URI.test(trimmed) || IMAGE_URL.test(trimmed) || IMAGE_SERVICE_URL.test(trimmed);
693
925
  }
694
- function ReactJsxView({ source, className }) {
695
- const [Component, setComponent] = useState3(null);
696
- const [error, setError] = useState3(null);
697
- const [loading, setLoading] = useState3(true);
698
- useEffect3(() => {
699
- let cancelled = false;
700
- async function transpileAndRender() {
701
- try {
702
- const imports = parseImports(source);
703
- const modules = await resolveModules(imports);
704
- let code = stripImports(source);
705
- code = rewriteExportDefault(code);
706
- code = await transpileJsx(code, "component.jsx");
707
- code += "\nreturn __default__;";
708
- const argNames = Object.keys(modules);
709
- const argValues = argNames.map((n) => modules[n]);
710
- const factory = new Function(...argNames, code);
711
- const Comp = factory(...argValues);
712
- if (!cancelled) {
713
- setComponent(() => Comp);
714
- setError(null);
715
- setLoading(false);
716
- }
717
- } catch (err) {
718
- if (!cancelled) {
719
- setError(err instanceof Error ? err.message : String(err));
720
- setComponent(null);
721
- setLoading(false);
722
- }
926
+ var ImageRoot = styled5(Box5, {
927
+ name: "FrontMcpImage",
928
+ slot: "Root"
929
+ })({
930
+ display: "inline-block",
931
+ position: "relative",
932
+ maxWidth: "100%"
933
+ });
934
+ var StyledImage = styled5("img", {
935
+ name: "FrontMcpImage",
936
+ slot: "Image"
937
+ })({
938
+ maxWidth: "100%",
939
+ height: "auto",
940
+ display: "block",
941
+ cursor: "pointer",
942
+ borderRadius: "inherit"
943
+ });
944
+ var LightboxBackdrop = styled5(Box5, {
945
+ name: "FrontMcpImage",
946
+ slot: "Lightbox"
947
+ })({
948
+ display: "flex",
949
+ alignItems: "center",
950
+ justifyContent: "center",
951
+ padding: 16
952
+ });
953
+ var LightboxImage = styled5("img")({
954
+ maxWidth: "90vw",
955
+ maxHeight: "90vh",
956
+ objectFit: "contain",
957
+ borderRadius: 8
958
+ });
959
+ function LightboxOverlay({ src, alt, onClose }) {
960
+ return React6.createElement(
961
+ LightboxBackdrop,
962
+ {
963
+ onClick: onClose,
964
+ sx: {
965
+ position: "fixed",
966
+ inset: 0,
967
+ zIndex: 1300,
968
+ bgcolor: "rgba(0,0,0,0.8)"
723
969
  }
724
- }
725
- setLoading(true);
726
- setError(null);
727
- setComponent(null);
728
- transpileAndRender();
729
- return () => {
730
- cancelled = true;
731
- };
732
- }, [source]);
733
- if (loading) {
734
- return React4.createElement(Box3, { className, sx: { p: 2, color: "text.secondary" } }, "Transpiling JSX...");
735
- }
970
+ },
971
+ React6.createElement(LightboxImage, { src, alt })
972
+ );
973
+ }
974
+ function ImageView({ src, alt, caption, className }) {
975
+ const [loaded, setLoaded] = useState4(false);
976
+ const [error, setError] = useState4(false);
977
+ const [lightboxOpen, setLightboxOpen] = useState4(false);
978
+ useEffect4(() => {
979
+ setLoaded(false);
980
+ setError(false);
981
+ setLightboxOpen(false);
982
+ }, [src]);
983
+ const handleLoad = useCallback2(() => setLoaded(true), []);
984
+ const handleError = useCallback2(() => {
985
+ setLoaded(true);
986
+ setError(true);
987
+ }, []);
736
988
  if (error) {
737
- return React4.createElement(
738
- Box3,
739
- { className },
740
- React4.createElement(Alert, { severity: "error", sx: { mb: 1 } }, `JSX render error: ${error}`),
741
- React4.createElement(
742
- "pre",
743
- { style: { fontFamily: "monospace", whiteSpace: "pre-wrap", fontSize: "0.85em" } },
744
- source
745
- )
746
- );
747
- }
748
- if (!Component) {
749
- return React4.createElement(
750
- Box3,
751
- { className },
752
- React4.createElement(Alert, { severity: "warning" }, "No component exported. Use `export default MyComponent`.")
753
- );
989
+ return React6.createElement(Alert3, { severity: "error" }, `Failed to load image: ${src}`);
754
990
  }
755
- return React4.createElement(Box3, { className }, React4.createElement(Component, {}));
991
+ return React6.createElement(
992
+ ImageRoot,
993
+ { className },
994
+ !loaded && React6.createElement(Skeleton, { variant: "rectangular", width: 400, height: 300 }),
995
+ React6.createElement(StyledImage, {
996
+ src,
997
+ alt: alt ?? "Image",
998
+ onLoad: handleLoad,
999
+ onError: handleError,
1000
+ onClick: () => setLightboxOpen(true),
1001
+ style: loaded ? void 0 : { display: "none" }
1002
+ }),
1003
+ caption && React6.createElement(
1004
+ Typography3,
1005
+ { variant: "caption", color: "text.secondary", sx: { mt: 0.5, display: "block", textAlign: "center" } },
1006
+ caption
1007
+ ),
1008
+ lightboxOpen && React6.createElement(LightboxOverlay, {
1009
+ src,
1010
+ alt: alt ?? "Image",
1011
+ onClose: () => setLightboxOpen(false)
1012
+ })
1013
+ );
756
1014
  }
757
- var ReactJsxRenderer = class {
758
- type = "jsx";
759
- priority = 10;
1015
+ var ImageRenderer = class {
1016
+ type = "image";
1017
+ priority = 30;
760
1018
  canHandle(content) {
761
- return isReactJsx(content);
1019
+ return isImage(content);
762
1020
  }
763
1021
  render(content, options) {
764
- return React4.createElement(ReactJsxView, {
765
- source: content,
766
- className: options?.className ?? "fmcp-jsx-content"
1022
+ const caption = options?.rendererOptions?.["caption"];
1023
+ return React6.createElement(ImageView, {
1024
+ src: content.trim(),
1025
+ alt: options?.toolName ?? "Image",
1026
+ caption,
1027
+ className: options?.className ?? "fmcp-image-content"
767
1028
  });
768
1029
  }
769
1030
  };
770
- var reactJsxRenderer = new ReactJsxRenderer();
1031
+ var imageRenderer = new ImageRenderer();
771
1032
 
772
- // libs/ui/src/renderer/pdf/index.ts
773
- import React5, { useState as useState4, useCallback, useMemo as useMemo4 } from "react";
774
- import Box4 from "@mui/material/Box";
775
- import IconButton from "@mui/material/IconButton";
776
- import Typography2 from "@mui/material/Typography";
777
- import TextField from "@mui/material/TextField";
778
- import { styled as styled3 } from "@mui/material/styles";
779
- var lazyReactPdf = createLazyImport("react-pdf", async () => {
1033
+ // libs/ui/src/renderer/maps/index.ts
1034
+ import Alert4 from "@mui/material/Alert";
1035
+ import Box6 from "@mui/material/Box";
1036
+ import Paper from "@mui/material/Paper";
1037
+ import { styled as styled6 } from "@mui/material/styles";
1038
+ import Typography4 from "@mui/material/Typography";
1039
+ import React7, { useEffect as useEffect5 } from "react";
1040
+ var LEAFLET_CSS_URL = "https://esm.sh/leaflet@1.9.4/dist/leaflet.css";
1041
+ var LEAFLET_CSS_ID = "fmcp-leaflet-css";
1042
+ var DEFAULT_TILE_URL = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
1043
+ var DEFAULT_ATTRIBUTION = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
1044
+ var GEOJSON_TYPE_RE2 = /"type"\s*:\s*"(?:FeatureCollection|Feature|Point|LineString|Polygon|MultiPoint|MultiLineString|MultiPolygon|GeometryCollection)"/;
1045
+ function isMap(content) {
1046
+ const trimmed = content.trim();
1047
+ if (trimmed.charCodeAt(0) === 123 && GEOJSON_TYPE_RE2.test(trimmed)) return true;
1048
+ try {
1049
+ const parsed = JSON.parse(trimmed);
1050
+ return !!(parsed.center && Array.isArray(parsed.center) || parsed.markers && Array.isArray(parsed.markers) || parsed.geojson && typeof parsed.geojson === "object");
1051
+ } catch {
1052
+ return false;
1053
+ }
1054
+ }
1055
+ var lazyReactLeaflet = createLazyImport("react-leaflet", async () => {
780
1056
  const mod = await runtimeImportWithFallback(
781
- "react-pdf",
782
- esmShUrl("react-pdf@9", { external: ["react", "react-dom"] })
1057
+ "react-leaflet",
1058
+ esmShUrl("react-leaflet@5", { external: ["react", "react-dom"] })
783
1059
  );
784
- const pdfjs = mod["pdfjs"];
785
- if (pdfjs?.GlobalWorkerOptions) {
786
- pdfjs.GlobalWorkerOptions.workerSrc = `${ESM_SH_BASE}pdfjs-dist@${pdfjs.version ?? "4"}/build/pdf.worker.min.mjs?raw`;
787
- }
788
1060
  return mod;
789
1061
  });
790
- function toDataUri(content) {
791
- if (content.startsWith("data:")) return content;
792
- if (content.startsWith("%PDF-")) {
793
- const base64 = typeof btoa === "function" ? btoa(content) : Buffer.from(content).toString("base64");
794
- return `data:application/pdf;base64,${base64}`;
795
- }
796
- return `data:application/pdf;base64,${content}`;
797
- }
798
- var PdfRoot = styled3(Box4, {
799
- name: "FrontMcpPdf",
1062
+ var MapRoot = styled6(Box6, {
1063
+ name: "FrontMcpMap",
800
1064
  slot: "Root"
801
1065
  })(({ theme }) => ({
802
1066
  width: "100%",
@@ -804,650 +1068,562 @@ var PdfRoot = styled3(Box4, {
804
1068
  overflow: "hidden",
805
1069
  border: `1px solid ${theme.palette.divider}`
806
1070
  }));
807
- var Toolbar = styled3(Box4, {
808
- name: "FrontMcpPdf",
809
- slot: "Toolbar"
810
- })(({ theme }) => ({
811
- display: "flex",
812
- alignItems: "center",
813
- gap: theme.spacing(1),
814
- padding: theme.spacing(1, 2),
815
- borderBottom: `1px solid ${theme.palette.divider}`,
816
- backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[50]
817
- }));
818
- var PageContainer = styled3(Box4, {
819
- name: "FrontMcpPdf",
820
- slot: "PageContainer"
821
- })({
822
- display: "flex",
823
- justifyContent: "center",
824
- overflow: "auto",
825
- maxHeight: 700
826
- });
827
- function PdfView({ content, className, title }) {
828
- const [numPages, setNumPages] = useState4(0);
829
- const [pageNumber, setPageNumber] = useState4(1);
830
- const [scale, setScale] = useState4(1);
831
- const reactPdf = useLazyModule(lazyReactPdf);
832
- const dataUri = useMemo4(() => toDataUri(content), [content]);
833
- const onDocumentLoadSuccess = useCallback(({ numPages: n }) => {
834
- setNumPages(n);
1071
+ function parseMapConfig(content) {
1072
+ const parsed = JSON.parse(content);
1073
+ if (parsed.type && [
1074
+ "FeatureCollection",
1075
+ "Feature",
1076
+ "Point",
1077
+ "LineString",
1078
+ "Polygon",
1079
+ "MultiPoint",
1080
+ "MultiLineString",
1081
+ "MultiPolygon",
1082
+ "GeometryCollection"
1083
+ ].includes(parsed.type)) {
1084
+ return {
1085
+ center: [0, 0],
1086
+ zoom: 2,
1087
+ geojson: parsed
1088
+ };
1089
+ }
1090
+ return parsed;
1091
+ }
1092
+ function MapView({ config, className }) {
1093
+ useEffect5(() => {
1094
+ injectStylesheet(LEAFLET_CSS_URL, LEAFLET_CSS_ID);
835
1095
  }, []);
836
- const goToPrev = useCallback(() => setPageNumber((p) => Math.max(1, p - 1)), []);
837
- const goToNext = useCallback(() => setPageNumber((p) => Math.min(numPages, p + 1)), []);
838
- const zoomIn = useCallback(() => setScale((s) => Math.min(3, s + 0.25)), []);
839
- const zoomOut = useCallback(() => setScale((s) => Math.max(0.25, s - 0.25)), []);
840
- if (!reactPdf) {
841
- return React5.createElement("iframe", {
842
- className: className ?? "fmcp-pdf-content",
843
- src: dataUri,
844
- style: { width: "100%", height: "600px", border: "none" },
845
- title: title ?? "PDF Document"
846
- });
1096
+ const leaflet = useLazyModule(lazyReactLeaflet);
1097
+ const height = config.height ?? 400;
1098
+ const center = config.center ?? [51.505, -0.09];
1099
+ const zoom = config.zoom ?? 13;
1100
+ if (!leaflet) {
1101
+ return React7.createElement(Alert4, { severity: "info" }, "Loading map library...");
847
1102
  }
848
- const { Document, Page } = reactPdf;
849
- return React5.createElement(
850
- PdfRoot,
1103
+ const { MapContainer, TileLayer, Marker, Popup, GeoJSON } = leaflet;
1104
+ const tileUrl = config.tileLayer?.url ?? DEFAULT_TILE_URL;
1105
+ const attribution = config.tileLayer?.attribution ?? DEFAULT_ATTRIBUTION;
1106
+ return React7.createElement(
1107
+ MapRoot,
851
1108
  { className },
852
- React5.createElement(
853
- Toolbar,
854
- null,
855
- React5.createElement(
856
- IconButton,
857
- { size: "small", onClick: goToPrev, disabled: pageNumber <= 1, "aria-label": "Previous page" },
858
- "\u25C0"
859
- ),
860
- React5.createElement(
861
- Typography2,
862
- { variant: "body2", sx: { minWidth: 80, textAlign: "center" } },
863
- `${pageNumber} / ${numPages || "..."}`
864
- ),
865
- React5.createElement(
866
- IconButton,
867
- { size: "small", onClick: goToNext, disabled: pageNumber >= numPages, "aria-label": "Next page" },
868
- "\u25B6"
869
- ),
870
- React5.createElement(Box4, { sx: { flex: 1 } }),
871
- React5.createElement(IconButton, { size: "small", onClick: zoomOut, "aria-label": "Zoom out" }, "\u2212"),
872
- React5.createElement(
873
- Typography2,
874
- { variant: "body2", sx: { minWidth: 50, textAlign: "center" } },
875
- `${Math.round(scale * 100)}%`
876
- ),
877
- React5.createElement(IconButton, { size: "small", onClick: zoomIn, "aria-label": "Zoom in" }, "+"),
878
- React5.createElement(Box4, { sx: { flex: 1 } }),
879
- React5.createElement(TextField, {
880
- size: "small",
881
- type: "number",
882
- value: pageNumber,
883
- onChange: (e) => {
884
- const n = parseInt(e.target.value, 10);
885
- if (n >= 1 && n <= numPages) setPageNumber(n);
886
- },
887
- sx: { width: 70 },
888
- slotProps: { htmlInput: { min: 1, max: numPages, "aria-label": "Go to page" } }
889
- })
1109
+ config.title && React7.createElement(
1110
+ Typography4,
1111
+ { variant: "subtitle1", sx: { fontWeight: 600, p: 1.5, borderBottom: 1, borderColor: "divider" } },
1112
+ config.title
890
1113
  ),
891
- React5.createElement(
892
- PageContainer,
893
- null,
894
- React5.createElement(
895
- Document,
896
- { file: dataUri, onLoadSuccess: onDocumentLoadSuccess },
897
- React5.createElement(Page, { pageNumber, scale, renderTextLayer: false, renderAnnotationLayer: false })
898
- )
1114
+ React7.createElement(
1115
+ MapContainer,
1116
+ {
1117
+ center,
1118
+ zoom,
1119
+ style: { height, width: "100%" },
1120
+ scrollWheelZoom: true
1121
+ },
1122
+ React7.createElement(TileLayer, { url: tileUrl, attribution }),
1123
+ config.markers?.map(
1124
+ (marker, i) => React7.createElement(
1125
+ Marker,
1126
+ { key: i, position: marker.position },
1127
+ marker.popup && React7.createElement(Popup, null, React7.createElement(Paper, { elevation: 0, sx: { p: 1 } }, marker.popup))
1128
+ )
1129
+ ),
1130
+ config.geojson && React7.createElement(GeoJSON, { data: config.geojson })
899
1131
  )
900
1132
  );
901
1133
  }
902
- lazyReactPdf.load().catch(() => {
1134
+ lazyReactLeaflet.load().catch(() => {
903
1135
  });
904
- var PdfRenderer = class {
905
- type = "pdf";
906
- priority = 90;
1136
+ var MapsRenderer = class {
1137
+ type = "map";
1138
+ priority = 60;
907
1139
  canHandle(content) {
908
- return content.startsWith("%PDF-") || content.trim().startsWith("JVBER") || /^data:application\/pdf[;,]/.test(content.trim());
1140
+ return isMap(content);
909
1141
  }
910
1142
  render(content, options) {
911
- return React5.createElement(PdfView, {
912
- content,
913
- className: options?.className ?? "fmcp-pdf-content",
914
- title: options?.toolName ?? "PDF Document"
915
- });
1143
+ try {
1144
+ const config = parseMapConfig(content);
1145
+ return React7.createElement(MapView, {
1146
+ config,
1147
+ className: options?.className ?? "fmcp-map-content"
1148
+ });
1149
+ } catch {
1150
+ return React7.createElement(Alert4, { severity: "error" }, "Invalid map/GeoJSON data");
1151
+ }
916
1152
  }
917
1153
  };
918
- var pdfRenderer = new PdfRenderer();
1154
+ var mapsRenderer = new MapsRenderer();
919
1155
 
920
- // libs/ui/src/renderer/csv/index.ts
921
- import React6, { useState as useState5, useMemo as useMemo5, useCallback as useCallback2 } from "react";
922
- import Box5 from "@mui/material/Box";
923
- import MuiTable2 from "@mui/material/Table";
924
- import TableBody2 from "@mui/material/TableBody";
925
- import TableCell2 from "@mui/material/TableCell";
926
- import TableContainer from "@mui/material/TableContainer";
927
- import TableHead2 from "@mui/material/TableHead";
928
- import TableRow2 from "@mui/material/TableRow";
929
- import TablePagination from "@mui/material/TablePagination";
930
- import TableSortLabel from "@mui/material/TableSortLabel";
931
- import TextField2 from "@mui/material/TextField";
932
- import { styled as styled4 } from "@mui/material/styles";
933
- function detectDelimiter(content) {
934
- const firstLine = content.split("\n")[0];
935
- const commas = (firstLine.match(/,/g) ?? []).length;
936
- const tabs = (firstLine.match(/\t/g) ?? []).length;
937
- const semicolons = (firstLine.match(/;/g) ?? []).length;
938
- if (tabs > commas && tabs > semicolons) return " ";
939
- if (semicolons > commas) return ";";
940
- return ",";
941
- }
942
- function parseCsv(content, delimiter) {
943
- return content.trim().split("\n").map((line) => line.split(delimiter).map((cell) => cell.trim()));
1156
+ // libs/ui/src/renderer/math/index.ts
1157
+ import Alert5 from "@mui/material/Alert";
1158
+ import Box7 from "@mui/material/Box";
1159
+ import { styled as styled7 } from "@mui/material/styles";
1160
+ import React8, { useEffect as useEffect6, useMemo as useMemo6 } from "react";
1161
+ var KATEX_CSS_URL = "https://esm.sh/katex@0.16.11/dist/katex.min.css";
1162
+ var KATEX_CSS_ID = "fmcp-katex-css";
1163
+ var MATH_DISPLAY = /\$\$(?:[^$]|\$(?!\$))+\$\$/s;
1164
+ var MATH_INLINE = /\$[^$\n]+?\$/;
1165
+ var MATH_BRACKET_DISPLAY = /\\\[(?:[^\\]|\\.)*\\\]/s;
1166
+ var MATH_PAREN_INLINE = /\\\((?:[^\\]|\\.)*\\\)/s;
1167
+ var MATH_BEGIN = /\\begin\{(?:equation|align|gather|matrix|pmatrix|bmatrix|cases)\}/;
1168
+ function isMath(content) {
1169
+ const trimmed = content.trim();
1170
+ return MATH_DISPLAY.test(trimmed) || MATH_INLINE.test(trimmed) || MATH_BRACKET_DISPLAY.test(trimmed) || MATH_PAREN_INLINE.test(trimmed) || MATH_BEGIN.test(trimmed);
944
1171
  }
945
- var CsvRoot = styled4(Box5, {
946
- name: "FrontMcpCsvTable",
947
- slot: "Root"
948
- })({
949
- width: "100%"
1172
+ var lazyKatex = createLazyImport("katex", async () => {
1173
+ const mod = await runtimeImportWithFallback("katex", esmShUrl("katex@0.16"));
1174
+ return mod;
950
1175
  });
951
- var StyledHeaderCell = styled4(TableCell2, {
952
- name: "FrontMcpCsvTable",
953
- slot: "Header"
1176
+ var MathRoot = styled7(Box7, {
1177
+ name: "FrontMcpMath",
1178
+ slot: "Root"
954
1179
  })(({ theme }) => ({
955
- fontWeight: 600,
956
- backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[50],
957
- whiteSpace: "nowrap"
1180
+ color: theme.palette.text.primary,
1181
+ "& .katex-display": {
1182
+ margin: theme.spacing(2, 0),
1183
+ textAlign: "center"
1184
+ }
958
1185
  }));
959
- var FilterToolbar = styled4(Box5, {
960
- name: "FrontMcpCsvTable",
961
- slot: "Toolbar"
1186
+ var MathDisplay = styled7(Box7, {
1187
+ name: "FrontMcpMath",
1188
+ slot: "Display"
962
1189
  })(({ theme }) => ({
963
- padding: theme.spacing(1, 2),
964
- display: "flex",
965
- gap: theme.spacing(1)
1190
+ display: "block",
1191
+ textAlign: "center",
1192
+ margin: theme.spacing(2, 0),
1193
+ fontSize: "1.2em",
1194
+ overflow: "auto"
966
1195
  }));
967
- function CsvTableView({ content, className, pageSize = 25 }) {
968
- const delimiter = useMemo5(() => detectDelimiter(content), [content]);
969
- const allRows = useMemo5(() => parseCsv(content, delimiter), [content, delimiter]);
970
- const headers = allRows[0] ?? [];
971
- const dataRows = useMemo5(() => allRows.slice(1), [allRows]);
972
- const [page, setPage] = useState5(0);
973
- const [rowsPerPage, setRowsPerPage] = useState5(pageSize);
974
- const [sortCol, setSortCol] = useState5(null);
975
- const [sortDir, setSortDir] = useState5("asc");
976
- const [filter, setFilter] = useState5("");
977
- const filteredRows = useMemo5(() => {
978
- if (!filter) return dataRows;
979
- const lower = filter.toLowerCase();
980
- return dataRows.filter((row) => row.some((cell) => cell.toLowerCase().includes(lower)));
981
- }, [dataRows, filter]);
982
- const sortedRows = useMemo5(() => {
983
- if (sortCol === null) return filteredRows;
984
- const sorted = [...filteredRows];
985
- sorted.sort((a, b) => {
986
- const va = a[sortCol] ?? "";
987
- const vb = b[sortCol] ?? "";
988
- const numA = Number(va);
989
- const numB = Number(vb);
990
- if (!isNaN(numA) && !isNaN(numB)) {
991
- return sortDir === "asc" ? numA - numB : numB - numA;
992
- }
993
- return sortDir === "asc" ? va.localeCompare(vb) : vb.localeCompare(va);
994
- });
995
- return sorted;
996
- }, [filteredRows, sortCol, sortDir]);
997
- const pageRows = useMemo5(
998
- () => sortedRows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage),
999
- [sortedRows, page, rowsPerPage]
1000
- );
1001
- const handleSort = useCallback2((colIndex) => {
1002
- setSortCol((prev) => {
1003
- if (prev === colIndex) {
1004
- setSortDir((d) => d === "asc" ? "desc" : "asc");
1005
- return colIndex;
1006
- }
1007
- setSortDir("asc");
1008
- return colIndex;
1009
- });
1010
- }, []);
1011
- const handleChangePage = useCallback2((_, newPage) => setPage(newPage), []);
1012
- const handleChangeRowsPerPage = useCallback2((e) => {
1013
- setRowsPerPage(parseInt(e.target.value, 10));
1014
- setPage(0);
1196
+ var MathInline = styled7("span", {
1197
+ name: "FrontMcpMath",
1198
+ slot: "Inline"
1199
+ })({
1200
+ display: "inline"
1201
+ });
1202
+ function parseMathContent(source) {
1203
+ const segments = [];
1204
+ const mathRegex = /(\$\$(?:[^$]|\$(?!\$))+\$\$|\$[^$\n]+?\$|\\\[(?:[^\\]|\\.)*\\\]|\\\((?:[^\\]|\\.)*\\\)|\\begin\{(?:[^\\]|\\.)*\\end\{[^}]+\})/gs;
1205
+ let lastIndex = 0;
1206
+ let match;
1207
+ while ((match = mathRegex.exec(source)) !== null) {
1208
+ if (match.index > lastIndex) {
1209
+ segments.push({ type: "text", content: source.slice(lastIndex, match.index) });
1210
+ }
1211
+ const raw = match[0];
1212
+ if (raw.startsWith("$$")) {
1213
+ segments.push({ type: "display", content: raw.slice(2, -2).trim() });
1214
+ } else if (raw.startsWith("\\[")) {
1215
+ segments.push({ type: "display", content: raw.slice(2, -2).trim() });
1216
+ } else if (raw.startsWith("\\begin")) {
1217
+ segments.push({ type: "display", content: raw });
1218
+ } else if (raw.startsWith("\\(")) {
1219
+ segments.push({ type: "inline", content: raw.slice(2, -2).trim() });
1220
+ } else if (raw.startsWith("$")) {
1221
+ segments.push({ type: "inline", content: raw.slice(1, -1).trim() });
1222
+ }
1223
+ lastIndex = match.index + raw.length;
1224
+ }
1225
+ if (lastIndex < source.length) {
1226
+ segments.push({ type: "text", content: source.slice(lastIndex) });
1227
+ }
1228
+ return segments;
1229
+ }
1230
+ function MathView({ content, className }) {
1231
+ useEffect6(() => {
1232
+ injectStylesheet(KATEX_CSS_URL, KATEX_CSS_ID);
1015
1233
  }, []);
1016
- return React6.createElement(
1017
- CsvRoot,
1018
- { className },
1019
- React6.createElement(
1020
- FilterToolbar,
1021
- null,
1022
- React6.createElement(TextField2, {
1023
- size: "small",
1024
- placeholder: "Filter rows...",
1025
- value: filter,
1026
- onChange: (e) => {
1027
- setFilter(e.target.value);
1028
- setPage(0);
1029
- },
1030
- sx: { minWidth: 200 }
1031
- })
1032
- ),
1033
- React6.createElement(
1034
- TableContainer,
1035
- { sx: { border: 1, borderColor: "divider", borderRadius: 1 } },
1036
- React6.createElement(
1037
- MuiTable2,
1038
- { size: "small", stickyHeader: true },
1039
- React6.createElement(
1040
- TableHead2,
1041
- null,
1042
- React6.createElement(
1043
- TableRow2,
1044
- null,
1045
- headers.map(
1046
- (header, i) => React6.createElement(
1047
- StyledHeaderCell,
1048
- { key: i },
1049
- React6.createElement(
1050
- TableSortLabel,
1051
- {
1052
- active: sortCol === i,
1053
- direction: sortCol === i ? sortDir : "asc",
1054
- onClick: () => handleSort(i)
1055
- },
1056
- header
1057
- )
1058
- )
1059
- )
1060
- )
1061
- ),
1062
- React6.createElement(
1063
- TableBody2,
1064
- null,
1065
- pageRows.map(
1066
- (row, ri) => React6.createElement(
1067
- TableRow2,
1068
- { key: ri, hover: true },
1069
- row.map((cell, ci) => React6.createElement(TableCell2, { key: ci }, cell))
1070
- )
1071
- )
1072
- )
1073
- )
1074
- ),
1075
- sortedRows.length > rowsPerPage && React6.createElement(TablePagination, {
1076
- count: sortedRows.length,
1077
- page,
1078
- rowsPerPage,
1079
- onPageChange: handleChangePage,
1080
- onRowsPerPageChange: handleChangeRowsPerPage,
1081
- rowsPerPageOptions: [10, 25, 50, 100]
1082
- })
1083
- );
1234
+ const katexMod = useLazyModule(lazyKatex);
1235
+ const segments = useMemo6(() => parseMathContent(content), [content]);
1236
+ const renderToString = katexMod?.default?.renderToString ?? katexMod?.renderToString;
1237
+ if (!renderToString) {
1238
+ return React8.createElement(
1239
+ MathRoot,
1240
+ { className },
1241
+ React8.createElement("pre", { style: { fontFamily: "monospace", whiteSpace: "pre-wrap" } }, content)
1242
+ );
1243
+ }
1244
+ const elements = segments.map((seg, i) => {
1245
+ if (seg.type === "text") {
1246
+ return React8.createElement("span", { key: i }, seg.content);
1247
+ }
1248
+ try {
1249
+ const html = renderToString(seg.content, {
1250
+ displayMode: seg.type === "display",
1251
+ throwOnError: false
1252
+ });
1253
+ if (seg.type === "display") {
1254
+ return React8.createElement(MathDisplay, {
1255
+ key: i,
1256
+ dangerouslySetInnerHTML: { __html: html }
1257
+ });
1258
+ }
1259
+ return React8.createElement(MathInline, {
1260
+ key: i,
1261
+ dangerouslySetInnerHTML: { __html: html }
1262
+ });
1263
+ } catch (err) {
1264
+ return React8.createElement(
1265
+ Alert5,
1266
+ { key: i, severity: "error", sx: { my: 1 } },
1267
+ `LaTeX error: ${err instanceof Error ? err.message : String(err)}
1268
+ Source: ${seg.content}`
1269
+ );
1270
+ }
1271
+ });
1272
+ return React8.createElement(MathRoot, { className }, ...elements);
1084
1273
  }
1085
- var CsvRenderer = class {
1086
- type = "csv";
1087
- priority = 10;
1274
+ lazyKatex.load().catch(() => {
1275
+ });
1276
+ var MathRenderer = class {
1277
+ type = "math";
1278
+ priority = 40;
1088
1279
  canHandle(content) {
1089
- const lines = content.trim().split("\n").slice(0, 5);
1090
- if (lines.length < 2) return false;
1091
- for (const delim of [",", " ", ";"]) {
1092
- const counts = lines.map((line) => line.split(delim).length);
1093
- if (counts[0] > 1 && counts.every((c) => c === counts[0])) return true;
1094
- }
1095
- return false;
1280
+ return isMath(content);
1096
1281
  }
1097
1282
  render(content, options) {
1098
- const pageSize = options?.rendererOptions?.["pageSize"];
1099
- return React6.createElement(CsvTableView, {
1283
+ return React8.createElement(MathView, {
1100
1284
  content,
1101
- className: options?.className ?? "fmcp-csv-table",
1102
- pageSize
1285
+ className: options?.className ?? "fmcp-math-content"
1103
1286
  });
1104
1287
  }
1105
- };
1106
- var csvRenderer = new CsvRenderer();
1107
-
1108
- // libs/ui/src/renderer/image/index.ts
1109
- import React7, { useState as useState6, useCallback as useCallback3, useEffect as useEffect4 } from "react";
1110
- import Box6 from "@mui/material/Box";
1111
- import Skeleton from "@mui/material/Skeleton";
1112
- import Alert2 from "@mui/material/Alert";
1113
- import Typography3 from "@mui/material/Typography";
1114
- import { styled as styled5 } from "@mui/material/styles";
1115
- var IMAGE_DATA_URI = /^data:image\/(?:png|jpeg|jpg|gif|webp|svg\+xml|avif)[;,]/;
1116
- var IMAGE_URL = /^https?:\/\/[^?#\s]+\.(?:png|jpe?g|gif|webp|svg|avif|ico)(?:\?[^#\s]*)?$/i;
1117
- var IMAGE_SERVICE_URL = /^https?:\/\/(?:picsum\.photos|images\.unsplash\.com|i\.imgur\.com|placekitten\.com|via\.placeholder\.com)\//i;
1118
- function isImage(content) {
1119
- const trimmed = content.trim();
1120
- return IMAGE_DATA_URI.test(trimmed) || IMAGE_URL.test(trimmed) || IMAGE_SERVICE_URL.test(trimmed);
1121
- }
1122
- var ImageRoot = styled5(Box6, {
1123
- name: "FrontMcpImage",
1124
- slot: "Root"
1125
- })({
1126
- display: "inline-block",
1127
- position: "relative",
1128
- maxWidth: "100%"
1288
+ };
1289
+ var mathRenderer = new MathRenderer();
1290
+
1291
+ // libs/ui/src/renderer/mdx/index.ts
1292
+ import Box8 from "@mui/material/Box";
1293
+ import Link from "@mui/material/Link";
1294
+ import { styled as styled8 } from "@mui/material/styles";
1295
+ import MuiTable2 from "@mui/material/Table";
1296
+ import TableBody2 from "@mui/material/TableBody";
1297
+ import TableCell2 from "@mui/material/TableCell";
1298
+ import TableHead2 from "@mui/material/TableHead";
1299
+ import TableRow2 from "@mui/material/TableRow";
1300
+ import Typography5 from "@mui/material/Typography";
1301
+ import React9, { useMemo as useMemo7 } from "react";
1302
+ var lazyReactMarkdown = createLazyImport(
1303
+ "react-markdown",
1304
+ async () => {
1305
+ const mod = await runtimeImportWithFallback(
1306
+ "react-markdown",
1307
+ esmShUrl("react-markdown@9", { external: ["react", "react-dom"] })
1308
+ );
1309
+ return mod;
1310
+ }
1311
+ );
1312
+ var lazyRemarkGfm = createLazyImport("remark-gfm", async () => {
1313
+ const mod = await runtimeImportWithFallback("remark-gfm", esmShUrl("remark-gfm@4"));
1314
+ return mod;
1129
1315
  });
1130
- var StyledImage = styled5("img", {
1131
- name: "FrontMcpImage",
1132
- slot: "Image"
1133
- })({
1134
- maxWidth: "100%",
1135
- height: "auto",
1136
- display: "block",
1137
- cursor: "pointer",
1138
- borderRadius: "inherit"
1316
+ var lazyRehypeHighlight = createLazyImport("rehype-highlight", async () => {
1317
+ const mod = await runtimeImportWithFallback("rehype-highlight", esmShUrl("rehype-highlight@7"));
1318
+ return mod;
1139
1319
  });
1140
- var LightboxBackdrop = styled5(Box6, {
1141
- name: "FrontMcpImage",
1142
- slot: "Lightbox"
1143
- })({
1144
- display: "flex",
1145
- alignItems: "center",
1146
- justifyContent: "center",
1147
- padding: 16
1320
+ var lazyRehypeRaw = createLazyImport("rehype-raw", async () => {
1321
+ const mod = await runtimeImportWithFallback("rehype-raw", esmShUrl("rehype-raw@7"));
1322
+ return mod;
1148
1323
  });
1149
- var LightboxImage = styled5("img")({
1150
- maxWidth: "90vw",
1151
- maxHeight: "90vh",
1152
- objectFit: "contain",
1153
- borderRadius: 8
1324
+ var MarkdownRoot = styled8(Box8, {
1325
+ name: "FrontMcpMarkdown",
1326
+ slot: "Root"
1327
+ })(() => ({
1328
+ lineHeight: 1.7,
1329
+ "& > *:first-of-type": { marginTop: 0 },
1330
+ "& > *:last-child": { marginBottom: 0 },
1331
+ "& img": { maxWidth: "100%", height: "auto" }
1332
+ }));
1333
+ var CodeBlock = styled8("pre", {
1334
+ name: "FrontMcpMarkdown",
1335
+ slot: "Code"
1336
+ })(({ theme }) => {
1337
+ const monoFont = String(
1338
+ theme.typography["monoFontFamily"] ?? '"SF Mono", "Fira Code", "Roboto Mono", monospace'
1339
+ );
1340
+ return {
1341
+ backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[100],
1342
+ padding: theme.spacing(2),
1343
+ borderRadius: Number(theme.shape.borderRadius),
1344
+ overflow: "auto",
1345
+ fontFamily: monoFont,
1346
+ fontSize: "0.875rem",
1347
+ "& code": {
1348
+ backgroundColor: "transparent",
1349
+ padding: 0,
1350
+ fontSize: "inherit",
1351
+ fontFamily: "inherit"
1352
+ }
1353
+ };
1154
1354
  });
1155
- function LightboxOverlay({ src, alt, onClose }) {
1156
- return React7.createElement(
1157
- LightboxBackdrop,
1158
- {
1159
- onClick: onClose,
1160
- sx: {
1161
- position: "fixed",
1162
- inset: 0,
1163
- zIndex: 1300,
1164
- bgcolor: "rgba(0,0,0,0.8)"
1165
- }
1166
- },
1167
- React7.createElement(LightboxImage, { src, alt })
1355
+ var InlineCode = styled8("code")(({ theme }) => {
1356
+ const monoFont = String(
1357
+ theme.typography["monoFontFamily"] ?? '"SF Mono", "Fira Code", "Roboto Mono", monospace'
1168
1358
  );
1359
+ return {
1360
+ backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[800] : theme.palette.grey[200],
1361
+ padding: "2px 6px",
1362
+ borderRadius: Number(theme.shape.borderRadius) / 2,
1363
+ fontFamily: monoFont,
1364
+ fontSize: "0.85em"
1365
+ };
1366
+ });
1367
+ var Blockquote = styled8("blockquote", {
1368
+ name: "FrontMcpMarkdown",
1369
+ slot: "Blockquote"
1370
+ })(({ theme }) => ({
1371
+ borderLeft: `4px solid ${theme.palette.primary.main}`,
1372
+ margin: theme.spacing(2, 0),
1373
+ padding: theme.spacing(1, 2),
1374
+ color: theme.palette.text.secondary,
1375
+ "& > p": { margin: 0 }
1376
+ }));
1377
+ function createMuiComponents() {
1378
+ return {
1379
+ h1: (props) => React9.createElement(Typography5, { ...props, variant: "h4", gutterBottom: true }),
1380
+ h2: (props) => React9.createElement(Typography5, { ...props, variant: "h5", gutterBottom: true }),
1381
+ h3: (props) => React9.createElement(Typography5, { ...props, variant: "h6", gutterBottom: true }),
1382
+ h4: (props) => React9.createElement(Typography5, { ...props, variant: "subtitle1", gutterBottom: true, fontWeight: 600 }),
1383
+ h5: (props) => React9.createElement(Typography5, { ...props, variant: "subtitle2", gutterBottom: true }),
1384
+ h6: (props) => React9.createElement(Typography5, { ...props, variant: "subtitle2", gutterBottom: true }),
1385
+ p: (props) => React9.createElement(Typography5, { ...props, variant: "body1", paragraph: true }),
1386
+ a: (props) => React9.createElement(Link, { ...props, target: "_blank", rel: "noopener noreferrer" }),
1387
+ blockquote: (props) => React9.createElement(Blockquote, props),
1388
+ pre: (props) => React9.createElement(CodeBlock, props),
1389
+ code: ({ inline, className, children, ...rest }) => {
1390
+ if (inline) return React9.createElement(InlineCode, rest, children);
1391
+ return React9.createElement("code", { className, ...rest }, children);
1392
+ },
1393
+ table: (props) => React9.createElement(MuiTable2, { ...props, size: "small" }),
1394
+ thead: (props) => React9.createElement(TableHead2, props),
1395
+ tbody: (props) => React9.createElement(TableBody2, props),
1396
+ tr: (props) => React9.createElement(TableRow2, { ...props, hover: true }),
1397
+ th: (props) => React9.createElement(TableCell2, { ...props, sx: { fontWeight: 600 } }),
1398
+ td: (props) => React9.createElement(TableCell2, props)
1399
+ };
1169
1400
  }
1170
- function ImageView({ src, alt, caption, className }) {
1171
- const [loaded, setLoaded] = useState6(false);
1172
- const [error, setError] = useState6(false);
1173
- const [lightboxOpen, setLightboxOpen] = useState6(false);
1174
- useEffect4(() => {
1175
- setLoaded(false);
1176
- setError(false);
1177
- setLightboxOpen(false);
1178
- }, [src]);
1179
- const handleLoad = useCallback3(() => setLoaded(true), []);
1180
- const handleError = useCallback3(() => {
1181
- setLoaded(true);
1182
- setError(true);
1183
- }, []);
1184
- if (error) {
1185
- return React7.createElement(Alert2, { severity: "error" }, `Failed to load image: ${src}`);
1401
+ function MarkdownView({ content, className }) {
1402
+ const components = useMemo7(() => createMuiComponents(), []);
1403
+ const ReactMarkdownMod = useLazyModule(lazyReactMarkdown);
1404
+ const remarkGfmMod = lazyRemarkGfm.get();
1405
+ const rehypeHighlightMod = lazyRehypeHighlight.get();
1406
+ const rehypeRawMod = lazyRehypeRaw.get();
1407
+ if (ReactMarkdownMod) {
1408
+ const ReactMarkdown = ReactMarkdownMod.default;
1409
+ const remarkPlugins = [];
1410
+ const rehypePlugins = [];
1411
+ if (remarkGfmMod) remarkPlugins.push(remarkGfmMod.default);
1412
+ if (rehypeHighlightMod) rehypePlugins.push(rehypeHighlightMod.default);
1413
+ if (rehypeRawMod) rehypePlugins.push(rehypeRawMod.default);
1414
+ return React9.createElement(
1415
+ MarkdownRoot,
1416
+ { className },
1417
+ React9.createElement(ReactMarkdown, {
1418
+ children: content,
1419
+ remarkPlugins,
1420
+ rehypePlugins,
1421
+ components
1422
+ })
1423
+ );
1186
1424
  }
1187
- return React7.createElement(
1188
- ImageRoot,
1189
- { className },
1190
- !loaded && React7.createElement(Skeleton, { variant: "rectangular", width: 400, height: 300 }),
1191
- React7.createElement(StyledImage, {
1192
- src,
1193
- alt: alt ?? "Image",
1194
- onLoad: handleLoad,
1195
- onError: handleError,
1196
- onClick: () => setLightboxOpen(true),
1197
- style: loaded ? void 0 : { display: "none" }
1198
- }),
1199
- caption && React7.createElement(
1200
- Typography3,
1201
- { variant: "caption", color: "text.secondary", sx: { mt: 0.5, display: "block", textAlign: "center" } },
1202
- caption
1203
- ),
1204
- lightboxOpen && React7.createElement(LightboxOverlay, {
1205
- src,
1206
- alt: alt ?? "Image",
1207
- onClose: () => setLightboxOpen(false)
1208
- })
1209
- );
1425
+ return React9.createElement(MarkdownRoot, { className }, ...renderFallback(content));
1210
1426
  }
1211
- var ImageRenderer = class {
1212
- type = "image";
1213
- priority = 30;
1427
+ function renderFallback(content) {
1428
+ const lines = content.split("\n");
1429
+ const elements = [];
1430
+ for (let i = 0; i < lines.length; i++) {
1431
+ const line = lines[i];
1432
+ if (line.startsWith("# ")) {
1433
+ elements.push(React9.createElement(Typography5, { key: i, variant: "h4", gutterBottom: true }, line.slice(2)));
1434
+ } else if (line.startsWith("## ")) {
1435
+ elements.push(React9.createElement(Typography5, { key: i, variant: "h5", gutterBottom: true }, line.slice(3)));
1436
+ } else if (line.startsWith("### ")) {
1437
+ elements.push(React9.createElement(Typography5, { key: i, variant: "h6", gutterBottom: true }, line.slice(4)));
1438
+ } else if (line.startsWith("- ") || line.startsWith("* ")) {
1439
+ elements.push(React9.createElement("li", { key: i }, line.slice(2)));
1440
+ } else if (line.startsWith("**") && line.endsWith("**")) {
1441
+ elements.push(
1442
+ React9.createElement(Typography5, { key: i, variant: "body1", sx: { fontWeight: 700 } }, line.slice(2, -2))
1443
+ );
1444
+ } else if (line.trim()) {
1445
+ elements.push(React9.createElement(Typography5, { key: i, variant: "body1", sx: { mb: 2 } }, line));
1446
+ }
1447
+ }
1448
+ return elements;
1449
+ }
1450
+ lazyReactMarkdown.load().catch(() => {
1451
+ });
1452
+ lazyRemarkGfm.load().catch(() => {
1453
+ });
1454
+ lazyRehypeHighlight.load().catch(() => {
1455
+ });
1456
+ lazyRehypeRaw.load().catch(() => {
1457
+ });
1458
+ var MdxRenderer = class {
1459
+ type = "mdx";
1460
+ priority = 5;
1214
1461
  canHandle(content) {
1215
- return isImage(content);
1462
+ return /^---\s*\n/.test(content) || /import\s+/.test(content) || /<[A-Z]/.test(content) || /^#{1,6}\s+\S/m.test(content) || /^\s*[-*+]\s+\S/m.test(content);
1216
1463
  }
1217
1464
  render(content, options) {
1218
- const caption = options?.rendererOptions?.["caption"];
1219
- return React7.createElement(ImageView, {
1220
- src: content.trim(),
1221
- alt: options?.toolName ?? "Image",
1222
- caption,
1223
- className: options?.className ?? "fmcp-image-content"
1465
+ return React9.createElement(MarkdownView, {
1466
+ content,
1467
+ className: options?.className ?? "fmcp-mdx-content"
1224
1468
  });
1225
1469
  }
1226
1470
  };
1227
- var imageRenderer = new ImageRenderer();
1471
+ var mdxRenderer = new MdxRenderer();
1228
1472
 
1229
- // libs/ui/src/renderer/charts/index.ts
1230
- import React8, { useMemo as useMemo6 } from "react";
1231
- import Box7 from "@mui/material/Box";
1232
- import Alert3 from "@mui/material/Alert";
1233
- import Typography4 from "@mui/material/Typography";
1234
- import { styled as styled6 } from "@mui/material/styles";
1235
- var CHART_TYPE_RE2 = /"type"\s*:\s*"(?:bar|line|area|pie|scatter|radar|composed)"/;
1236
- function isChart(content) {
1473
+ // libs/ui/src/renderer/media/index.ts
1474
+ import Box9 from "@mui/material/Box";
1475
+ import Paper2 from "@mui/material/Paper";
1476
+ import { styled as styled9 } from "@mui/material/styles";
1477
+ import React10 from "react";
1478
+ var VIDEO_PATTERNS2 = [
1479
+ /^https?:\/\/[^?#\s]+\.(?:mp4|webm|ogg|mov)(?:\?[^#\s]*)?$/i,
1480
+ /^https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be|vimeo\.com)\//i,
1481
+ /^data:video\//
1482
+ ];
1483
+ var AUDIO_PATTERNS2 = [
1484
+ /^https?:\/\/[^?#\s]+\.(?:mp3|wav|ogg|aac|flac|m4a)(?:\?[^#\s]*)?$/i,
1485
+ /^https?:\/\/(?:www\.)?soundcloud\.com\//i,
1486
+ /^data:audio\//
1487
+ ];
1488
+ function isVideo(content) {
1237
1489
  const trimmed = content.trim();
1238
- if (trimmed.charCodeAt(0) !== 123) return false;
1239
- return CHART_TYPE_RE2.test(trimmed) && trimmed.includes('"data"');
1490
+ return VIDEO_PATTERNS2.some((p) => p.test(trimmed));
1240
1491
  }
1241
- var lazyRecharts = createLazyImport("recharts", async () => {
1242
- const mod = await runtimeImportWithFallback("recharts", esmShUrl("recharts@2", { external: ["react", "react-dom"] }));
1492
+ function isAudio(content) {
1493
+ const trimmed = content.trim();
1494
+ return AUDIO_PATTERNS2.some((p) => p.test(trimmed));
1495
+ }
1496
+ function isMedia(content) {
1497
+ return isVideo(content) || isAudio(content);
1498
+ }
1499
+ var lazyReactPlayer = createLazyImport("react-player", async () => {
1500
+ const mod = await runtimeImportWithFallback(
1501
+ "react-player",
1502
+ esmShUrl("react-player@2", { external: ["react", "react-dom"] })
1503
+ );
1243
1504
  return mod;
1244
1505
  });
1245
- var ChartRoot = styled6(Box7, {
1246
- name: "FrontMcpChart",
1506
+ var MediaRoot = styled9(Paper2, {
1507
+ name: "FrontMcpMedia",
1247
1508
  slot: "Root"
1248
1509
  })(({ theme }) => ({
1249
- width: "100%",
1250
- padding: theme.spacing(2)
1510
+ overflow: "hidden",
1511
+ borderRadius: theme.shape.borderRadius
1251
1512
  }));
1252
- function ChartView({ config, className }) {
1253
- const themeValues = useRendererTheme();
1254
- const recharts = useLazyModule(lazyRecharts);
1255
- const colors = useMemo6(() => config.colors ?? themeValues.seriesColors, [config.colors, themeValues.seriesColors]);
1256
- if (!recharts) {
1257
- return React8.createElement(Alert3, { severity: "info" }, "Loading chart library...");
1513
+ var VideoWrapper = styled9(Box9)({
1514
+ position: "relative",
1515
+ paddingTop: "56.25%",
1516
+ // 16:9 aspect ratio
1517
+ "& > *": {
1518
+ position: "absolute",
1519
+ top: 0,
1520
+ left: 0
1258
1521
  }
1259
- const { ResponsiveContainer, Tooltip, Legend, CartesianGrid, XAxis, YAxis } = recharts;
1260
- const height = config.height ?? 400;
1261
- const xKey = config.xKey ?? "name";
1262
- const yKeys = config.yKeys ?? Object.keys(config.data?.[0] ?? {}).filter((k) => k !== xKey);
1263
- const chartContent = renderChartType(
1264
- recharts,
1265
- config.type,
1266
- config.data,
1267
- xKey,
1268
- yKeys,
1269
- colors,
1270
- themeValues.borderRadius
1271
- );
1272
- return React8.createElement(
1273
- ChartRoot,
1274
- { className },
1275
- config.title && React8.createElement(
1276
- Typography4,
1277
- { variant: "subtitle1", gutterBottom: true, sx: { fontWeight: 600, textAlign: "center" } },
1278
- config.title
1279
- ),
1280
- React8.createElement(ResponsiveContainer, { width: "100%", height }, chartContent)
1281
- );
1282
- }
1283
- function renderChartType(rc, type, data, xKey, yKeys, colors, borderRadius) {
1284
- const commonProps = { data };
1285
- const grid = React8.createElement(rc.CartesianGrid, { strokeDasharray: "3 3", opacity: 0.3 });
1286
- const xAxis = React8.createElement(rc.XAxis, { dataKey: xKey });
1287
- const yAxis = React8.createElement(rc.YAxis);
1288
- const tooltip = React8.createElement(rc.Tooltip, { contentStyle: { borderRadius } });
1289
- const legend = React8.createElement(rc.Legend);
1290
- switch (type) {
1291
- case "bar":
1292
- return React8.createElement(
1293
- rc.BarChart,
1294
- commonProps,
1295
- grid,
1296
- xAxis,
1297
- yAxis,
1298
- tooltip,
1299
- legend,
1300
- ...yKeys.map(
1301
- (key, i) => React8.createElement(rc.Bar, {
1302
- key,
1303
- dataKey: key,
1304
- fill: colors[i % colors.length],
1305
- radius: [borderRadius, borderRadius, 0, 0]
1306
- })
1307
- )
1308
- );
1309
- case "line":
1310
- return React8.createElement(
1311
- rc.LineChart,
1312
- commonProps,
1313
- grid,
1314
- xAxis,
1315
- yAxis,
1316
- tooltip,
1317
- legend,
1318
- ...yKeys.map(
1319
- (key, i) => React8.createElement(rc.Line, {
1320
- key,
1321
- type: "monotone",
1322
- dataKey: key,
1323
- stroke: colors[i % colors.length],
1324
- strokeWidth: 2
1325
- })
1326
- )
1327
- );
1328
- case "area":
1329
- return React8.createElement(
1330
- rc.AreaChart,
1331
- commonProps,
1332
- grid,
1333
- xAxis,
1334
- yAxis,
1335
- tooltip,
1336
- legend,
1337
- ...yKeys.map(
1338
- (key, i) => React8.createElement(rc.Area, {
1339
- key,
1340
- type: "monotone",
1341
- dataKey: key,
1342
- stroke: colors[i % colors.length],
1343
- fill: colors[i % colors.length],
1344
- fillOpacity: 0.3
1345
- })
1346
- )
1347
- );
1348
- case "pie":
1349
- return React8.createElement(
1350
- rc.PieChart,
1351
- {},
1352
- tooltip,
1353
- legend,
1354
- React8.createElement(
1355
- rc.Pie,
1356
- { data, dataKey: yKeys[0] ?? "value", nameKey: xKey, cx: "50%", cy: "50%", outerRadius: "80%", label: true },
1357
- ...data.map((_, i) => React8.createElement(rc.Cell, { key: i, fill: colors[i % colors.length] }))
1358
- )
1359
- );
1360
- case "scatter":
1361
- return React8.createElement(
1362
- rc.ScatterChart,
1363
- {},
1364
- grid,
1365
- xAxis,
1366
- yAxis,
1367
- tooltip,
1368
- legend,
1369
- ...yKeys.map(
1370
- (key, i) => React8.createElement(rc.Scatter, { key, name: key, dataKey: key, data, fill: colors[i % colors.length] })
1371
- )
1372
- );
1373
- case "radar":
1374
- return React8.createElement(
1375
- rc.RadarChart,
1376
- { cx: "50%", cy: "50%", outerRadius: "80%", data },
1377
- React8.createElement(rc.PolarGrid),
1378
- React8.createElement(rc.PolarAngleAxis, { dataKey: xKey }),
1379
- React8.createElement(rc.PolarRadiusAxis),
1380
- tooltip,
1381
- legend,
1382
- ...yKeys.map(
1383
- (key, i) => React8.createElement(rc.Radar, {
1384
- key,
1385
- name: key,
1386
- dataKey: key,
1387
- stroke: colors[i % colors.length],
1388
- fill: colors[i % colors.length],
1389
- fillOpacity: 0.3
1390
- })
1522
+ });
1523
+ var AudioWrapper = styled9(Box9)(({ theme }) => ({
1524
+ padding: theme.spacing(1),
1525
+ "& > *": {
1526
+ width: "100%"
1527
+ }
1528
+ }));
1529
+ function MediaView({ url, isAudioContent, className }) {
1530
+ const reactPlayerMod = useLazyModule(lazyReactPlayer);
1531
+ if (!reactPlayerMod) {
1532
+ if (isAudioContent) {
1533
+ return React10.createElement(
1534
+ MediaRoot,
1535
+ { variant: "outlined", className },
1536
+ React10.createElement(
1537
+ AudioWrapper,
1538
+ null,
1539
+ React10.createElement("audio", { controls: true, src: url, style: { width: "100%" } })
1391
1540
  )
1392
1541
  );
1393
- case "composed":
1394
- return React8.createElement(
1395
- rc.ComposedChart,
1396
- commonProps,
1397
- grid,
1398
- xAxis,
1399
- yAxis,
1400
- tooltip,
1401
- legend,
1402
- ...yKeys.map((key, i) => {
1403
- if (i % 2 === 0) {
1404
- return React8.createElement(rc.Bar, {
1405
- key,
1406
- dataKey: key,
1407
- fill: colors[i % colors.length],
1408
- radius: [borderRadius, borderRadius, 0, 0]
1409
- });
1410
- }
1411
- return React8.createElement(rc.Line, {
1412
- key,
1413
- type: "monotone",
1414
- dataKey: key,
1415
- stroke: colors[i % colors.length],
1416
- strokeWidth: 2
1417
- });
1542
+ }
1543
+ return React10.createElement(
1544
+ MediaRoot,
1545
+ { variant: "outlined", className },
1546
+ React10.createElement(
1547
+ VideoWrapper,
1548
+ null,
1549
+ React10.createElement("video", {
1550
+ controls: true,
1551
+ src: url,
1552
+ style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%" }
1418
1553
  })
1419
- );
1420
- default:
1421
- return React8.createElement(rc.BarChart, commonProps, grid, xAxis, yAxis, tooltip, legend);
1554
+ )
1555
+ );
1556
+ }
1557
+ const ReactPlayer = reactPlayerMod.default;
1558
+ if (isAudioContent) {
1559
+ return React10.createElement(
1560
+ MediaRoot,
1561
+ { variant: "outlined", className },
1562
+ React10.createElement(
1563
+ AudioWrapper,
1564
+ null,
1565
+ React10.createElement(ReactPlayer, {
1566
+ url,
1567
+ controls: true,
1568
+ width: "100%",
1569
+ height: 50
1570
+ })
1571
+ )
1572
+ );
1422
1573
  }
1574
+ return React10.createElement(
1575
+ MediaRoot,
1576
+ { variant: "outlined", className },
1577
+ React10.createElement(
1578
+ VideoWrapper,
1579
+ null,
1580
+ React10.createElement(ReactPlayer, {
1581
+ url,
1582
+ controls: true,
1583
+ width: "100%",
1584
+ height: "100%"
1585
+ })
1586
+ )
1587
+ );
1423
1588
  }
1424
- lazyRecharts.load().catch(() => {
1589
+ lazyReactPlayer.load().catch(() => {
1425
1590
  });
1426
- var ChartsRenderer = class {
1427
- type = "chart";
1428
- priority = 80;
1591
+ var VideoRenderer = class {
1592
+ type = "video";
1593
+ priority = 20;
1429
1594
  canHandle(content) {
1430
- return isChart(content);
1595
+ return isVideo(content);
1431
1596
  }
1432
1597
  render(content, options) {
1433
- try {
1434
- const config = JSON.parse(content);
1435
- return React8.createElement(ChartView, {
1436
- config,
1437
- className: options?.className ?? "fmcp-chart-content"
1438
- });
1439
- } catch {
1440
- return React8.createElement(Alert3, { severity: "error" }, "Invalid chart JSON");
1441
- }
1598
+ return React10.createElement(MediaView, {
1599
+ url: content.trim(),
1600
+ isAudioContent: false,
1601
+ className: options?.className ?? "fmcp-media-content"
1602
+ });
1442
1603
  }
1443
1604
  };
1444
- var chartsRenderer = new ChartsRenderer();
1605
+ var AudioRenderer = class {
1606
+ type = "audio";
1607
+ priority = 20;
1608
+ canHandle(content) {
1609
+ return isAudio(content);
1610
+ }
1611
+ render(content, options) {
1612
+ return React10.createElement(MediaView, {
1613
+ url: content.trim(),
1614
+ isAudioContent: true,
1615
+ className: options?.className ?? "fmcp-media-content"
1616
+ });
1617
+ }
1618
+ };
1619
+ var videoRenderer = new VideoRenderer();
1620
+ var audioRenderer = new AudioRenderer();
1445
1621
 
1446
1622
  // libs/ui/src/renderer/mermaid/index.ts
1447
- import React9, { useEffect as useEffect5, useRef, useState as useState7, useId } from "react";
1448
- import Box8 from "@mui/material/Box";
1449
- import Alert4 from "@mui/material/Alert";
1450
- import { styled as styled7 } from "@mui/material/styles";
1623
+ import Alert6 from "@mui/material/Alert";
1624
+ import Box10 from "@mui/material/Box";
1625
+ import { styled as styled10 } from "@mui/material/styles";
1626
+ import React11, { useEffect as useEffect7, useId, useRef, useState as useState5 } from "react";
1451
1627
  var MERMAID_PATTERN2 = /^\s*(?:graph|sequenceDiagram|classDiagram|stateDiagram|flowchart|erDiagram|gantt|pie|journey|gitGraph)\b/;
1452
1628
  function isMermaid(content) {
1453
1629
  return MERMAID_PATTERN2.test(content.trim());
@@ -1457,7 +1633,7 @@ var lazyMermaid = createLazyImport("mermaid", async () => {
1457
1633
  const mermaid = mod["default"] ?? mod;
1458
1634
  return mermaid;
1459
1635
  });
1460
- var MermaidRoot = styled7(Box8, {
1636
+ var MermaidRoot = styled10(Box10, {
1461
1637
  name: "FrontMcpMermaid",
1462
1638
  slot: "Root"
1463
1639
  })(({ theme }) => ({
@@ -1487,11 +1663,11 @@ function getMermaidThemeVariables(tv) {
1487
1663
  }
1488
1664
  function MermaidView({ definition, className }) {
1489
1665
  const containerRef = useRef(null);
1490
- const [svg, setSvg] = useState7(null);
1491
- const [error, setError] = useState7(null);
1666
+ const [svg, setSvg] = useState5(null);
1667
+ const [error, setError] = useState5(null);
1492
1668
  const themeValues = useRendererTheme();
1493
1669
  const uniqueId = useId().replace(/:/g, "-");
1494
- useEffect5(() => {
1670
+ useEffect7(() => {
1495
1671
  let cancelled = false;
1496
1672
  async function renderDiagram() {
1497
1673
  try {
@@ -1520,341 +1696,75 @@ function MermaidView({ definition, className }) {
1520
1696
  };
1521
1697
  }, [definition, themeValues, uniqueId]);
1522
1698
  if (error) {
1523
- return React9.createElement(
1524
- Box8,
1525
- { className },
1526
- React9.createElement(Alert4, { severity: "error", sx: { mb: 1 } }, `Mermaid parse error: ${error}`),
1527
- React9.createElement(
1528
- "pre",
1529
- { style: { fontFamily: "monospace", whiteSpace: "pre-wrap", fontSize: "0.85em" } },
1530
- definition
1531
- )
1532
- );
1533
- }
1534
- if (!svg) {
1535
- return React9.createElement(
1536
- MermaidRoot,
1537
- { className },
1538
- React9.createElement(Box8, { sx: { color: "text.secondary" } }, "Rendering diagram...")
1539
- );
1540
- }
1541
- return React9.createElement(MermaidRoot, {
1542
- ref: containerRef,
1543
- className,
1544
- dangerouslySetInnerHTML: { __html: svg }
1545
- });
1546
- }
1547
- lazyMermaid.load().catch(() => {
1548
- });
1549
- var MermaidRenderer = class {
1550
- type = "mermaid";
1551
- priority = 50;
1552
- canHandle(content) {
1553
- return isMermaid(content);
1554
- }
1555
- render(content, options) {
1556
- return React9.createElement(MermaidView, {
1557
- definition: content.trim(),
1558
- className: options?.className ?? "fmcp-mermaid-content"
1559
- });
1560
- }
1561
- };
1562
- var mermaidRenderer = new MermaidRenderer();
1563
-
1564
- // libs/ui/src/renderer/flow/index.ts
1565
- import React10, { useEffect as useEffect6, useMemo as useMemo7 } from "react";
1566
- import Box9 from "@mui/material/Box";
1567
- import Alert5 from "@mui/material/Alert";
1568
- import Typography5 from "@mui/material/Typography";
1569
- import { styled as styled8 } from "@mui/material/styles";
1570
- var XYFLOW_CSS_URL = "https://esm.sh/@xyflow/react@12/dist/style.css";
1571
- var XYFLOW_CSS_ID = "fmcp-xyflow-css";
1572
- function isFlow(content) {
1573
- const trimmed = content.trim();
1574
- if (trimmed.charCodeAt(0) !== 123) return false;
1575
- return trimmed.includes('"nodes"') && trimmed.includes('"edges"');
1576
- }
1577
- var lazyXYFlow = createLazyImport("@xyflow/react", async () => {
1578
- const mod = await runtimeImportWithFallback(
1579
- "@xyflow/react",
1580
- esmShUrl("@xyflow/react@12", { external: ["react", "react-dom"] })
1581
- );
1582
- return mod;
1583
- });
1584
- var FlowRoot = styled8(Box9, {
1585
- name: "FrontMcpFlow",
1586
- slot: "Root"
1587
- })(({ theme }) => ({
1588
- width: "100%",
1589
- borderRadius: theme.shape.borderRadius,
1590
- overflow: "hidden",
1591
- border: `1px solid ${theme.palette.divider}`
1592
- }));
1593
- function FlowView({ config, className }) {
1594
- useEffect6(() => {
1595
- injectStylesheet(XYFLOW_CSS_URL, XYFLOW_CSS_ID);
1596
- }, []);
1597
- const themeValues = useRendererTheme();
1598
- const xyflow = useLazyModule(lazyXYFlow);
1599
- const height = config.height ?? 500;
1600
- const styledNodes = useMemo7(() => {
1601
- return config.nodes.map((node) => ({
1602
- ...node,
1603
- style: {
1604
- background: themeValues.paper,
1605
- color: themeValues.textPrimary,
1606
- border: `1px solid ${themeValues.divider}`,
1607
- borderRadius: themeValues.borderRadius,
1608
- padding: 8,
1609
- fontSize: themeValues.fontSize,
1610
- fontFamily: themeValues.fontFamily,
1611
- ...node.style
1612
- }
1613
- }));
1614
- }, [config.nodes, themeValues]);
1615
- const styledEdges = useMemo7(() => {
1616
- return config.edges.map((edge, i) => ({
1617
- ...edge,
1618
- style: {
1619
- stroke: themeValues.seriesColors[i % themeValues.seriesColors.length],
1620
- strokeWidth: 2,
1621
- ...edge.style
1622
- }
1623
- }));
1624
- }, [config.edges, themeValues]);
1625
- if (!xyflow) {
1626
- return React10.createElement(Alert5, { severity: "info" }, "Loading flow diagram library...");
1627
- }
1628
- const { ReactFlow, Controls, MiniMap, Background } = xyflow;
1629
- return React10.createElement(
1630
- FlowRoot,
1631
- { className },
1632
- config.title && React10.createElement(
1633
- Typography5,
1634
- { variant: "subtitle1", sx: { fontWeight: 600, p: 1.5, borderBottom: 1, borderColor: "divider" } },
1635
- config.title
1636
- ),
1637
- React10.createElement(
1638
- Box9,
1639
- { sx: { height } },
1640
- React10.createElement(
1641
- ReactFlow,
1642
- {
1643
- nodes: styledNodes,
1644
- edges: styledEdges,
1645
- fitView: config.fitView !== false,
1646
- proOptions: { hideAttribution: true }
1647
- },
1648
- React10.createElement(Controls, {
1649
- style: {
1650
- backgroundColor: themeValues.paper,
1651
- borderRadius: themeValues.borderRadius,
1652
- border: `1px solid ${themeValues.divider}`
1653
- }
1654
- }),
1655
- React10.createElement(MiniMap, {
1656
- style: {
1657
- backgroundColor: themeValues.background,
1658
- borderRadius: themeValues.borderRadius
1659
- },
1660
- nodeColor: themeValues.primary
1661
- }),
1662
- Background && React10.createElement(Background, {
1663
- color: themeValues.divider,
1664
- gap: 16
1665
- })
1666
- )
1667
- )
1668
- );
1669
- }
1670
- lazyXYFlow.load().catch(() => {
1671
- });
1672
- var FlowRenderer = class {
1673
- type = "flow";
1674
- priority = 70;
1675
- canHandle(content) {
1676
- return isFlow(content);
1677
- }
1678
- render(content, options) {
1679
- try {
1680
- const config = JSON.parse(content);
1681
- return React10.createElement(FlowView, {
1682
- config,
1683
- className: options?.className ?? "fmcp-flow-content"
1684
- });
1685
- } catch {
1686
- return React10.createElement(Alert5, { severity: "error" }, "Invalid flow JSON");
1687
- }
1688
- }
1689
- };
1690
- var flowRenderer = new FlowRenderer();
1691
-
1692
- // libs/ui/src/renderer/math/index.ts
1693
- import React11, { useEffect as useEffect7, useMemo as useMemo8 } from "react";
1694
- import Box10 from "@mui/material/Box";
1695
- import Alert6 from "@mui/material/Alert";
1696
- import { styled as styled9 } from "@mui/material/styles";
1697
- var KATEX_CSS_URL = "https://esm.sh/katex@0.16.11/dist/katex.min.css";
1698
- var KATEX_CSS_ID = "fmcp-katex-css";
1699
- var MATH_DISPLAY = /\$\$(?:[^$]|\$(?!\$))+\$\$/s;
1700
- var MATH_INLINE = /\$[^$\n]+?\$/;
1701
- var MATH_BRACKET_DISPLAY = /\\\[(?:[^\\]|\\.)*\\\]/s;
1702
- var MATH_PAREN_INLINE = /\\\((?:[^\\]|\\.)*\\\)/s;
1703
- var MATH_BEGIN = /\\begin\{(?:equation|align|gather|matrix|pmatrix|bmatrix|cases)\}/;
1704
- function isMath(content) {
1705
- const trimmed = content.trim();
1706
- return MATH_DISPLAY.test(trimmed) || MATH_INLINE.test(trimmed) || MATH_BRACKET_DISPLAY.test(trimmed) || MATH_PAREN_INLINE.test(trimmed) || MATH_BEGIN.test(trimmed);
1707
- }
1708
- var lazyKatex = createLazyImport("katex", async () => {
1709
- const mod = await runtimeImportWithFallback("katex", esmShUrl("katex@0.16"));
1710
- return mod;
1711
- });
1712
- var MathRoot = styled9(Box10, {
1713
- name: "FrontMcpMath",
1714
- slot: "Root"
1715
- })(({ theme }) => ({
1716
- color: theme.palette.text.primary,
1717
- "& .katex-display": {
1718
- margin: theme.spacing(2, 0),
1719
- textAlign: "center"
1720
- }
1721
- }));
1722
- var MathDisplay = styled9(Box10, {
1723
- name: "FrontMcpMath",
1724
- slot: "Display"
1725
- })(({ theme }) => ({
1726
- display: "block",
1727
- textAlign: "center",
1728
- margin: theme.spacing(2, 0),
1729
- fontSize: "1.2em",
1730
- overflow: "auto"
1731
- }));
1732
- var MathInline = styled9("span", {
1733
- name: "FrontMcpMath",
1734
- slot: "Inline"
1735
- })({
1736
- display: "inline"
1737
- });
1738
- function parseMathContent(source) {
1739
- const segments = [];
1740
- const mathRegex = /(\$\$(?:[^$]|\$(?!\$))+\$\$|\$[^$\n]+?\$|\\\[(?:[^\\]|\\.)*\\\]|\\\((?:[^\\]|\\.)*\\\)|\\begin\{(?:[^\\]|\\.)*\\end\{[^}]+\})/gs;
1741
- let lastIndex = 0;
1742
- let match;
1743
- while ((match = mathRegex.exec(source)) !== null) {
1744
- if (match.index > lastIndex) {
1745
- segments.push({ type: "text", content: source.slice(lastIndex, match.index) });
1746
- }
1747
- const raw = match[0];
1748
- if (raw.startsWith("$$")) {
1749
- segments.push({ type: "display", content: raw.slice(2, -2).trim() });
1750
- } else if (raw.startsWith("\\[")) {
1751
- segments.push({ type: "display", content: raw.slice(2, -2).trim() });
1752
- } else if (raw.startsWith("\\begin")) {
1753
- segments.push({ type: "display", content: raw });
1754
- } else if (raw.startsWith("\\(")) {
1755
- segments.push({ type: "inline", content: raw.slice(2, -2).trim() });
1756
- } else if (raw.startsWith("$")) {
1757
- segments.push({ type: "inline", content: raw.slice(1, -1).trim() });
1758
- }
1759
- lastIndex = match.index + raw.length;
1760
- }
1761
- if (lastIndex < source.length) {
1762
- segments.push({ type: "text", content: source.slice(lastIndex) });
1763
- }
1764
- return segments;
1765
- }
1766
- function MathView({ content, className }) {
1767
- useEffect7(() => {
1768
- injectStylesheet(KATEX_CSS_URL, KATEX_CSS_ID);
1769
- }, []);
1770
- const katexMod = useLazyModule(lazyKatex);
1771
- const segments = useMemo8(() => parseMathContent(content), [content]);
1772
- const renderToString = katexMod?.default?.renderToString ?? katexMod?.renderToString;
1773
- if (!renderToString) {
1774
- return React11.createElement(
1775
- MathRoot,
1776
- { className },
1777
- React11.createElement("pre", { style: { fontFamily: "monospace", whiteSpace: "pre-wrap" } }, content)
1778
- );
1779
- }
1780
- const elements = segments.map((seg, i) => {
1781
- if (seg.type === "text") {
1782
- return React11.createElement("span", { key: i }, seg.content);
1783
- }
1784
- try {
1785
- const html = renderToString(seg.content, {
1786
- displayMode: seg.type === "display",
1787
- throwOnError: false
1788
- });
1789
- if (seg.type === "display") {
1790
- return React11.createElement(MathDisplay, {
1791
- key: i,
1792
- dangerouslySetInnerHTML: { __html: html }
1793
- });
1794
- }
1795
- return React11.createElement(MathInline, {
1796
- key: i,
1797
- dangerouslySetInnerHTML: { __html: html }
1798
- });
1799
- } catch (err) {
1800
- return React11.createElement(
1801
- Alert6,
1802
- { key: i, severity: "error", sx: { my: 1 } },
1803
- `LaTeX error: ${err instanceof Error ? err.message : String(err)}
1804
- Source: ${seg.content}`
1805
- );
1806
- }
1699
+ return React11.createElement(
1700
+ Box10,
1701
+ { className },
1702
+ React11.createElement(Alert6, { severity: "error", sx: { mb: 1 } }, `Mermaid parse error: ${error}`),
1703
+ React11.createElement(
1704
+ "pre",
1705
+ { style: { fontFamily: "monospace", whiteSpace: "pre-wrap", fontSize: "0.85em" } },
1706
+ definition
1707
+ )
1708
+ );
1709
+ }
1710
+ if (!svg) {
1711
+ return React11.createElement(
1712
+ MermaidRoot,
1713
+ { className },
1714
+ React11.createElement(Box10, { sx: { color: "text.secondary" } }, "Rendering diagram...")
1715
+ );
1716
+ }
1717
+ return React11.createElement(MermaidRoot, {
1718
+ ref: containerRef,
1719
+ className,
1720
+ dangerouslySetInnerHTML: { __html: svg }
1807
1721
  });
1808
- return React11.createElement(MathRoot, { className }, ...elements);
1809
1722
  }
1810
- lazyKatex.load().catch(() => {
1723
+ lazyMermaid.load().catch(() => {
1811
1724
  });
1812
- var MathRenderer = class {
1813
- type = "math";
1814
- priority = 40;
1725
+ var MermaidRenderer = class {
1726
+ type = "mermaid";
1727
+ priority = 50;
1815
1728
  canHandle(content) {
1816
- return isMath(content);
1729
+ return isMermaid(content);
1817
1730
  }
1818
1731
  render(content, options) {
1819
- return React11.createElement(MathView, {
1820
- content,
1821
- className: options?.className ?? "fmcp-math-content"
1732
+ return React11.createElement(MermaidView, {
1733
+ definition: content.trim(),
1734
+ className: options?.className ?? "fmcp-mermaid-content"
1822
1735
  });
1823
1736
  }
1824
1737
  };
1825
- var mathRenderer = new MathRenderer();
1738
+ var mermaidRenderer = new MermaidRenderer();
1826
1739
 
1827
- // libs/ui/src/renderer/maps/index.ts
1828
- import React12, { useEffect as useEffect8 } from "react";
1740
+ // libs/ui/src/renderer/pdf/index.ts
1829
1741
  import Box11 from "@mui/material/Box";
1830
- import Alert7 from "@mui/material/Alert";
1831
- import Paper from "@mui/material/Paper";
1742
+ import IconButton from "@mui/material/IconButton";
1743
+ import { styled as styled11 } from "@mui/material/styles";
1744
+ import TextField2 from "@mui/material/TextField";
1832
1745
  import Typography6 from "@mui/material/Typography";
1833
- import { styled as styled10 } from "@mui/material/styles";
1834
- var LEAFLET_CSS_URL = "https://esm.sh/leaflet@1.9.4/dist/leaflet.css";
1835
- var LEAFLET_CSS_ID = "fmcp-leaflet-css";
1836
- var DEFAULT_TILE_URL = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
1837
- var DEFAULT_ATTRIBUTION = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
1838
- var GEOJSON_TYPE_RE2 = /"type"\s*:\s*"(?:FeatureCollection|Feature|Point|LineString|Polygon|MultiPoint|MultiLineString|MultiPolygon|GeometryCollection)"/;
1839
- function isMap(content) {
1840
- const trimmed = content.trim();
1841
- if (trimmed.charCodeAt(0) === 123 && GEOJSON_TYPE_RE2.test(trimmed)) return true;
1842
- try {
1843
- const parsed = JSON.parse(trimmed);
1844
- return !!(parsed.center && Array.isArray(parsed.center) || parsed.markers && Array.isArray(parsed.markers) || parsed.geojson && typeof parsed.geojson === "object");
1845
- } catch {
1846
- return false;
1847
- }
1848
- }
1849
- var lazyReactLeaflet = createLazyImport("react-leaflet", async () => {
1746
+ import React12, { useCallback as useCallback3, useMemo as useMemo8, useState as useState6 } from "react";
1747
+ var lazyReactPdf = createLazyImport("react-pdf", async () => {
1850
1748
  const mod = await runtimeImportWithFallback(
1851
- "react-leaflet",
1852
- esmShUrl("react-leaflet@5", { external: ["react", "react-dom"] })
1749
+ "react-pdf",
1750
+ esmShUrl("react-pdf@9", { external: ["react", "react-dom"] })
1853
1751
  );
1752
+ const pdfjs = mod["pdfjs"];
1753
+ if (pdfjs?.GlobalWorkerOptions) {
1754
+ pdfjs.GlobalWorkerOptions.workerSrc = `${ESM_SH_BASE}pdfjs-dist@${pdfjs.version ?? "4"}/build/pdf.worker.min.mjs?raw`;
1755
+ }
1854
1756
  return mod;
1855
1757
  });
1856
- var MapRoot = styled10(Box11, {
1857
- name: "FrontMcpMap",
1758
+ function toDataUri(content) {
1759
+ if (content.startsWith("data:")) return content;
1760
+ if (content.startsWith("%PDF-")) {
1761
+ const base64 = typeof btoa === "function" ? btoa(content) : Buffer.from(content).toString("base64");
1762
+ return `data:application/pdf;base64,${base64}`;
1763
+ }
1764
+ return `data:application/pdf;base64,${content}`;
1765
+ }
1766
+ var PdfRoot = styled11(Box11, {
1767
+ name: "FrontMcpPdf",
1858
1768
  slot: "Root"
1859
1769
  })(({ theme }) => ({
1860
1770
  width: "100%",
@@ -1862,239 +1772,335 @@ var MapRoot = styled10(Box11, {
1862
1772
  overflow: "hidden",
1863
1773
  border: `1px solid ${theme.palette.divider}`
1864
1774
  }));
1865
- function parseMapConfig(content) {
1866
- const parsed = JSON.parse(content);
1867
- if (parsed.type && [
1868
- "FeatureCollection",
1869
- "Feature",
1870
- "Point",
1871
- "LineString",
1872
- "Polygon",
1873
- "MultiPoint",
1874
- "MultiLineString",
1875
- "MultiPolygon",
1876
- "GeometryCollection"
1877
- ].includes(parsed.type)) {
1878
- return {
1879
- center: [0, 0],
1880
- zoom: 2,
1881
- geojson: parsed
1882
- };
1883
- }
1884
- return parsed;
1885
- }
1886
- function MapView({ config, className }) {
1887
- useEffect8(() => {
1888
- injectStylesheet(LEAFLET_CSS_URL, LEAFLET_CSS_ID);
1775
+ var Toolbar = styled11(Box11, {
1776
+ name: "FrontMcpPdf",
1777
+ slot: "Toolbar"
1778
+ })(({ theme }) => ({
1779
+ display: "flex",
1780
+ alignItems: "center",
1781
+ gap: theme.spacing(1),
1782
+ padding: theme.spacing(1, 2),
1783
+ borderBottom: `1px solid ${theme.palette.divider}`,
1784
+ backgroundColor: theme.palette.mode === "dark" ? theme.palette.grey[900] : theme.palette.grey[50]
1785
+ }));
1786
+ var PageContainer = styled11(Box11, {
1787
+ name: "FrontMcpPdf",
1788
+ slot: "PageContainer"
1789
+ })({
1790
+ display: "flex",
1791
+ justifyContent: "center",
1792
+ overflow: "auto",
1793
+ maxHeight: 700
1794
+ });
1795
+ function PdfView({ content, className, title }) {
1796
+ const [numPages, setNumPages] = useState6(0);
1797
+ const [pageNumber, setPageNumber] = useState6(1);
1798
+ const [scale, setScale] = useState6(1);
1799
+ const reactPdf = useLazyModule(lazyReactPdf);
1800
+ const dataUri = useMemo8(() => toDataUri(content), [content]);
1801
+ const onDocumentLoadSuccess = useCallback3(({ numPages: n }) => {
1802
+ setNumPages(n);
1889
1803
  }, []);
1890
- const leaflet = useLazyModule(lazyReactLeaflet);
1891
- const height = config.height ?? 400;
1892
- const center = config.center ?? [51.505, -0.09];
1893
- const zoom = config.zoom ?? 13;
1894
- if (!leaflet) {
1895
- return React12.createElement(Alert7, { severity: "info" }, "Loading map library...");
1804
+ const goToPrev = useCallback3(() => setPageNumber((p) => Math.max(1, p - 1)), []);
1805
+ const goToNext = useCallback3(() => setPageNumber((p) => Math.min(numPages, p + 1)), []);
1806
+ const zoomIn = useCallback3(() => setScale((s) => Math.min(3, s + 0.25)), []);
1807
+ const zoomOut = useCallback3(() => setScale((s) => Math.max(0.25, s - 0.25)), []);
1808
+ if (!reactPdf) {
1809
+ return React12.createElement("iframe", {
1810
+ className: className ?? "fmcp-pdf-content",
1811
+ src: dataUri,
1812
+ style: { width: "100%", height: "600px", border: "none" },
1813
+ title: title ?? "PDF Document"
1814
+ });
1896
1815
  }
1897
- const { MapContainer, TileLayer, Marker, Popup, GeoJSON } = leaflet;
1898
- const tileUrl = config.tileLayer?.url ?? DEFAULT_TILE_URL;
1899
- const attribution = config.tileLayer?.attribution ?? DEFAULT_ATTRIBUTION;
1816
+ const { Document, Page } = reactPdf;
1900
1817
  return React12.createElement(
1901
- MapRoot,
1818
+ PdfRoot,
1902
1819
  { className },
1903
- config.title && React12.createElement(
1904
- Typography6,
1905
- { variant: "subtitle1", sx: { fontWeight: 600, p: 1.5, borderBottom: 1, borderColor: "divider" } },
1906
- config.title
1907
- ),
1908
1820
  React12.createElement(
1909
- MapContainer,
1910
- {
1911
- center,
1912
- zoom,
1913
- style: { height, width: "100%" },
1914
- scrollWheelZoom: true
1915
- },
1916
- React12.createElement(TileLayer, { url: tileUrl, attribution }),
1917
- config.markers?.map(
1918
- (marker, i) => React12.createElement(
1919
- Marker,
1920
- { key: i, position: marker.position },
1921
- marker.popup && React12.createElement(Popup, null, React12.createElement(Paper, { elevation: 0, sx: { p: 1 } }, marker.popup))
1922
- )
1821
+ Toolbar,
1822
+ null,
1823
+ React12.createElement(
1824
+ IconButton,
1825
+ { size: "small", onClick: goToPrev, disabled: pageNumber <= 1, "aria-label": "Previous page" },
1826
+ "\u25C0"
1827
+ ),
1828
+ React12.createElement(
1829
+ Typography6,
1830
+ { variant: "body2", sx: { minWidth: 80, textAlign: "center" } },
1831
+ `${pageNumber} / ${numPages || "..."}`
1923
1832
  ),
1924
- config.geojson && React12.createElement(GeoJSON, { data: config.geojson })
1833
+ React12.createElement(
1834
+ IconButton,
1835
+ { size: "small", onClick: goToNext, disabled: pageNumber >= numPages, "aria-label": "Next page" },
1836
+ "\u25B6"
1837
+ ),
1838
+ React12.createElement(Box11, { sx: { flex: 1 } }),
1839
+ React12.createElement(IconButton, { size: "small", onClick: zoomOut, "aria-label": "Zoom out" }, "\u2212"),
1840
+ React12.createElement(
1841
+ Typography6,
1842
+ { variant: "body2", sx: { minWidth: 50, textAlign: "center" } },
1843
+ `${Math.round(scale * 100)}%`
1844
+ ),
1845
+ React12.createElement(IconButton, { size: "small", onClick: zoomIn, "aria-label": "Zoom in" }, "+"),
1846
+ React12.createElement(Box11, { sx: { flex: 1 } }),
1847
+ React12.createElement(TextField2, {
1848
+ size: "small",
1849
+ type: "number",
1850
+ value: pageNumber,
1851
+ onChange: (e) => {
1852
+ const n = parseInt(e.target.value, 10);
1853
+ if (n >= 1 && n <= numPages) setPageNumber(n);
1854
+ },
1855
+ sx: { width: 70 },
1856
+ slotProps: { htmlInput: { min: 1, max: numPages, "aria-label": "Go to page" } }
1857
+ })
1858
+ ),
1859
+ React12.createElement(
1860
+ PageContainer,
1861
+ null,
1862
+ React12.createElement(
1863
+ Document,
1864
+ { file: dataUri, onLoadSuccess: onDocumentLoadSuccess },
1865
+ React12.createElement(Page, { pageNumber, scale, renderTextLayer: false, renderAnnotationLayer: false })
1866
+ )
1925
1867
  )
1926
1868
  );
1927
1869
  }
1928
- lazyReactLeaflet.load().catch(() => {
1929
- });
1930
- var MapsRenderer = class {
1931
- type = "map";
1932
- priority = 60;
1933
- canHandle(content) {
1934
- return isMap(content);
1870
+ lazyReactPdf.load().catch(() => {
1871
+ });
1872
+ var PdfRenderer = class {
1873
+ type = "pdf";
1874
+ priority = 90;
1875
+ canHandle(content) {
1876
+ return content.startsWith("%PDF-") || content.trim().startsWith("JVBER") || /^data:application\/pdf[;,]/.test(content.trim());
1877
+ }
1878
+ render(content, options) {
1879
+ return React12.createElement(PdfView, {
1880
+ content,
1881
+ className: options?.className ?? "fmcp-pdf-content",
1882
+ title: options?.toolName ?? "PDF Document"
1883
+ });
1884
+ }
1885
+ };
1886
+ var pdfRenderer = new PdfRenderer();
1887
+
1888
+ // libs/ui/src/renderer/react/index.ts
1889
+ import Alert7 from "@mui/material/Alert";
1890
+ import Box12 from "@mui/material/Box";
1891
+ import React13, { useEffect as useEffect8, useState as useState7 } from "react";
1892
+
1893
+ // libs/ui/src/runtime/babel-runtime.ts
1894
+ var BABEL_STANDALONE_CDN = "https://esm.sh/@babel/standalone@7";
1895
+ var BABEL_STANDALONE_FALLBACK_CDN = "https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.10/babel.min.js";
1896
+ var babelState = { status: "idle" };
1897
+ async function loadBabel() {
1898
+ if (babelState.status === "loaded") {
1899
+ return babelState.babel;
1900
+ }
1901
+ if (babelState.status === "loading") {
1902
+ return babelState.promise;
1903
+ }
1904
+ if (babelState.status === "error") {
1905
+ throw babelState.error;
1906
+ }
1907
+ const promise = doLoadBabel();
1908
+ babelState = { status: "loading", promise };
1909
+ try {
1910
+ const babel = await promise;
1911
+ babelState = { status: "loaded", babel };
1912
+ return babel;
1913
+ } catch (error) {
1914
+ const err = error instanceof Error ? error : new Error(String(error));
1915
+ babelState = { status: "error", error: err };
1916
+ throw err;
1917
+ }
1918
+ }
1919
+ async function doLoadBabel() {
1920
+ const globalBabel = globalThis["Babel"];
1921
+ if (globalBabel && typeof globalBabel.transform === "function") {
1922
+ return globalBabel;
1923
+ }
1924
+ try {
1925
+ const module = await import(
1926
+ /* @vite-ignore */
1927
+ /* webpackIgnore: true */
1928
+ BABEL_STANDALONE_CDN
1929
+ );
1930
+ if (module && typeof module.transform === "function") {
1931
+ return module;
1932
+ }
1933
+ if (module.default && typeof module.default.transform === "function") {
1934
+ return module.default;
1935
+ }
1936
+ } catch {
1937
+ }
1938
+ return new Promise((resolve, reject) => {
1939
+ if (typeof document === "undefined") {
1940
+ reject(new Error("Babel Standalone requires a browser environment"));
1941
+ return;
1942
+ }
1943
+ const script = document.createElement("script");
1944
+ script.src = BABEL_STANDALONE_FALLBACK_CDN;
1945
+ script.crossOrigin = "anonymous";
1946
+ script.onload = () => {
1947
+ const babel = globalThis["Babel"];
1948
+ if (babel && typeof babel.transform === "function") {
1949
+ resolve(babel);
1950
+ } else {
1951
+ reject(new Error("Babel Standalone loaded but transform function not found"));
1952
+ }
1953
+ };
1954
+ script.onerror = () => {
1955
+ reject(new Error(`Failed to load Babel Standalone from ${BABEL_STANDALONE_FALLBACK_CDN}`));
1956
+ };
1957
+ document.head.appendChild(script);
1958
+ });
1959
+ }
1960
+ async function transpileJsx(source, filename = "component.jsx") {
1961
+ const babel = await loadBabel();
1962
+ const result = babel.transform(source, {
1963
+ presets: ["react"],
1964
+ filename
1965
+ });
1966
+ return result.code;
1967
+ }
1968
+
1969
+ // libs/ui/src/renderer/react/index.ts
1970
+ function isReactJsx(content) {
1971
+ if (/^#{1,6}\s+\S/m.test(content)) return false;
1972
+ return /(?:function|const|class)\s+\w+/.test(content) && /(?:return|=>)[\s(]*</.test(content);
1973
+ }
1974
+ function parseImports(source) {
1975
+ const results = [];
1976
+ const defaultRe = /^import\s+(\w+)\s+from\s+['"]([^'"]+)['"][\s;]*$/gm;
1977
+ let m;
1978
+ while ((m = defaultRe.exec(source)) !== null) {
1979
+ results.push({ line: m[0], localName: m[1], specifier: m[2], named: false });
1935
1980
  }
1936
- render(content, options) {
1937
- try {
1938
- const config = parseMapConfig(content);
1939
- return React12.createElement(MapView, {
1940
- config,
1941
- className: options?.className ?? "fmcp-map-content"
1942
- });
1943
- } catch {
1944
- return React12.createElement(Alert7, { severity: "error" }, "Invalid map/GeoJSON data");
1981
+ const namedRe = /^import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"][\s;]*$/gm;
1982
+ while ((m = namedRe.exec(source)) !== null) {
1983
+ const names = m[1].split(",").map((n) => n.trim()).filter(Boolean);
1984
+ for (const name of names) {
1985
+ results.push({ line: m[0], localName: name, specifier: m[2], named: true });
1945
1986
  }
1946
1987
  }
1947
- };
1948
- var mapsRenderer = new MapsRenderer();
1949
-
1950
- // libs/ui/src/renderer/media/index.ts
1951
- import React13 from "react";
1952
- import Box12 from "@mui/material/Box";
1953
- import Paper2 from "@mui/material/Paper";
1954
- import { styled as styled11 } from "@mui/material/styles";
1955
- var VIDEO_PATTERNS2 = [
1956
- /^https?:\/\/[^?#\s]+\.(?:mp4|webm|ogg|mov)(?:\?[^#\s]*)?$/i,
1957
- /^https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be|vimeo\.com)\//i,
1958
- /^data:video\//
1959
- ];
1960
- var AUDIO_PATTERNS2 = [
1961
- /^https?:\/\/[^?#\s]+\.(?:mp3|wav|ogg|aac|flac|m4a)(?:\?[^#\s]*)?$/i,
1962
- /^https?:\/\/(?:www\.)?soundcloud\.com\//i,
1963
- /^data:audio\//
1964
- ];
1965
- function isVideo(content) {
1966
- const trimmed = content.trim();
1967
- return VIDEO_PATTERNS2.some((p) => p.test(trimmed));
1988
+ return results;
1968
1989
  }
1969
- function isAudio(content) {
1970
- const trimmed = content.trim();
1971
- return AUDIO_PATTERNS2.some((p) => p.test(trimmed));
1990
+ function stripImports(source) {
1991
+ return source.replace(/^import\s+\w+\s+from\s+['"][^'"]+['"][\s;]*$/gm, "").replace(/^import\s+\{[^}]+\}\s+from\s+['"][^'"]+['"][\s;]*$/gm, "");
1972
1992
  }
1973
- function isMedia(content) {
1974
- return isVideo(content) || isAudio(content);
1993
+ function rewriteExportDefault(source) {
1994
+ let result = source.replace(/^export\s+default\s+(function|class)\b/gm, "var __default__ = $1");
1995
+ result = result.replace(/^export\s+default\s+(\([^)]*\)\s*=>)/gm, "var __default__ = $1");
1996
+ result = result.replace(/^export\s+default\s+(\w+)\s*;?\s*$/gm, "var __default__ = $1;");
1997
+ return result;
1975
1998
  }
1976
- var lazyReactPlayer = createLazyImport("react-player", async () => {
1977
- const mod = await runtimeImportWithFallback(
1978
- "react-player",
1979
- esmShUrl("react-player@2", { external: ["react", "react-dom"] })
1980
- );
1981
- return mod;
1982
- });
1983
- var MediaRoot = styled11(Paper2, {
1984
- name: "FrontMcpMedia",
1985
- slot: "Root"
1986
- })(({ theme }) => ({
1987
- overflow: "hidden",
1988
- borderRadius: theme.shape.borderRadius
1989
- }));
1990
- var VideoWrapper = styled11(Box12)({
1991
- position: "relative",
1992
- paddingTop: "56.25%",
1993
- // 16:9 aspect ratio
1994
- "& > *": {
1995
- position: "absolute",
1996
- top: 0,
1997
- left: 0
1999
+ function isReactSpecifier(specifier) {
2000
+ return specifier === "react" || /^https?:\/\/esm\.sh\/react(?:@[\d.]+)?$/.test(specifier);
2001
+ }
2002
+ async function resolveModules(imports) {
2003
+ const modules = {};
2004
+ const specifierMap = /* @__PURE__ */ new Map();
2005
+ for (const imp of imports) {
2006
+ const existing = specifierMap.get(imp.specifier) ?? [];
2007
+ existing.push(imp);
2008
+ specifierMap.set(imp.specifier, existing);
1998
2009
  }
1999
- });
2000
- var AudioWrapper = styled11(Box12)(({ theme }) => ({
2001
- padding: theme.spacing(1),
2002
- "& > *": {
2003
- width: "100%"
2010
+ for (const [specifier, specImports] of specifierMap) {
2011
+ let mod;
2012
+ if (isReactSpecifier(specifier)) {
2013
+ mod = React13;
2014
+ } else {
2015
+ mod = await runtimeImport(specifier);
2016
+ }
2017
+ for (const imp of specImports) {
2018
+ if (imp.named) {
2019
+ modules[imp.localName] = mod[imp.localName] ?? mod["default"]?.[imp.localName];
2020
+ } else {
2021
+ modules[imp.localName] = mod["default"] ?? mod;
2022
+ }
2023
+ }
2004
2024
  }
2005
- }));
2006
- function MediaView({ url, isAudioContent, className }) {
2007
- const reactPlayerMod = useLazyModule(lazyReactPlayer);
2008
- if (!reactPlayerMod) {
2009
- if (isAudioContent) {
2010
- return React13.createElement(
2011
- MediaRoot,
2012
- { variant: "outlined", className },
2013
- React13.createElement(
2014
- AudioWrapper,
2015
- null,
2016
- React13.createElement("audio", { controls: true, src: url, style: { width: "100%" } })
2017
- )
2018
- );
2025
+ return modules;
2026
+ }
2027
+ function ReactJsxView({ source, className }) {
2028
+ const [Component, setComponent] = useState7(null);
2029
+ const [error, setError] = useState7(null);
2030
+ const [loading, setLoading] = useState7(true);
2031
+ useEffect8(() => {
2032
+ let cancelled = false;
2033
+ async function transpileAndRender() {
2034
+ try {
2035
+ const imports = parseImports(source);
2036
+ const modules = await resolveModules(imports);
2037
+ let code = stripImports(source);
2038
+ code = rewriteExportDefault(code);
2039
+ code = await transpileJsx(code, "component.jsx");
2040
+ code += "\nreturn __default__;";
2041
+ const argNames = Object.keys(modules);
2042
+ const argValues = argNames.map((n) => modules[n]);
2043
+ const factory = new Function(...argNames, code);
2044
+ const Comp = factory(...argValues);
2045
+ if (!cancelled) {
2046
+ setComponent(() => Comp);
2047
+ setError(null);
2048
+ setLoading(false);
2049
+ }
2050
+ } catch (err) {
2051
+ if (!cancelled) {
2052
+ setError(err instanceof Error ? err.message : String(err));
2053
+ setComponent(null);
2054
+ setLoading(false);
2055
+ }
2056
+ }
2019
2057
  }
2058
+ setLoading(true);
2059
+ setError(null);
2060
+ setComponent(null);
2061
+ transpileAndRender();
2062
+ return () => {
2063
+ cancelled = true;
2064
+ };
2065
+ }, [source]);
2066
+ if (loading) {
2067
+ return React13.createElement(Box12, { className, sx: { p: 2, color: "text.secondary" } }, "Transpiling JSX...");
2068
+ }
2069
+ if (error) {
2020
2070
  return React13.createElement(
2021
- MediaRoot,
2022
- { variant: "outlined", className },
2071
+ Box12,
2072
+ { className },
2073
+ React13.createElement(Alert7, { severity: "error", sx: { mb: 1 } }, `JSX render error: ${error}`),
2023
2074
  React13.createElement(
2024
- VideoWrapper,
2025
- null,
2026
- React13.createElement("video", {
2027
- controls: true,
2028
- src: url,
2029
- style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%" }
2030
- })
2075
+ "pre",
2076
+ { style: { fontFamily: "monospace", whiteSpace: "pre-wrap", fontSize: "0.85em" } },
2077
+ source
2031
2078
  )
2032
2079
  );
2033
2080
  }
2034
- const ReactPlayer = reactPlayerMod.default;
2035
- if (isAudioContent) {
2081
+ if (!Component) {
2036
2082
  return React13.createElement(
2037
- MediaRoot,
2038
- { variant: "outlined", className },
2039
- React13.createElement(
2040
- AudioWrapper,
2041
- null,
2042
- React13.createElement(ReactPlayer, {
2043
- url,
2044
- controls: true,
2045
- width: "100%",
2046
- height: 50
2047
- })
2048
- )
2083
+ Box12,
2084
+ { className },
2085
+ React13.createElement(Alert7, { severity: "warning" }, "No component exported. Use `export default MyComponent`.")
2049
2086
  );
2050
2087
  }
2051
- return React13.createElement(
2052
- MediaRoot,
2053
- { variant: "outlined", className },
2054
- React13.createElement(
2055
- VideoWrapper,
2056
- null,
2057
- React13.createElement(ReactPlayer, {
2058
- url,
2059
- controls: true,
2060
- width: "100%",
2061
- height: "100%"
2062
- })
2063
- )
2064
- );
2088
+ return React13.createElement(Box12, { className }, React13.createElement(Component, {}));
2065
2089
  }
2066
- lazyReactPlayer.load().catch(() => {
2067
- });
2068
- var VideoRenderer = class {
2069
- type = "video";
2070
- priority = 20;
2071
- canHandle(content) {
2072
- return isVideo(content);
2073
- }
2074
- render(content, options) {
2075
- return React13.createElement(MediaView, {
2076
- url: content.trim(),
2077
- isAudioContent: false,
2078
- className: options?.className ?? "fmcp-media-content"
2079
- });
2080
- }
2081
- };
2082
- var AudioRenderer = class {
2083
- type = "audio";
2084
- priority = 20;
2090
+ var ReactJsxRenderer = class {
2091
+ type = "jsx";
2092
+ priority = 10;
2085
2093
  canHandle(content) {
2086
- return isAudio(content);
2094
+ return isReactJsx(content);
2087
2095
  }
2088
2096
  render(content, options) {
2089
- return React13.createElement(MediaView, {
2090
- url: content.trim(),
2091
- isAudioContent: true,
2092
- className: options?.className ?? "fmcp-media-content"
2097
+ return React13.createElement(ReactJsxView, {
2098
+ source: content,
2099
+ className: options?.className ?? "fmcp-jsx-content"
2093
2100
  });
2094
2101
  }
2095
2102
  };
2096
- var videoRenderer = new VideoRenderer();
2097
- var audioRenderer = new AudioRenderer();
2103
+ var reactJsxRenderer = new ReactJsxRenderer();
2098
2104
 
2099
2105
  // libs/ui/src/renderer/index.ts
2100
2106
  function registerAllRenderers() {