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