@drewswiredin/backstage-plugin-assistants 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +113 -0
- package/dist/alpha.d.ts +64 -0
- package/dist/alpha.esm.js +35 -0
- package/dist/alpha.esm.js.map +1 -0
- package/dist/api/AssistantsApi.esm.js +55 -0
- package/dist/api/AssistantsApi.esm.js.map +1 -0
- package/dist/collapsible/AssistantDetailDialog.esm.js +209 -0
- package/dist/collapsible/AssistantDetailDialog.esm.js.map +1 -0
- package/dist/collapsible/AssistantsList.esm.js +119 -0
- package/dist/collapsible/AssistantsList.esm.js.map +1 -0
- package/dist/collapsible/CollapsiblePage.esm.js +724 -0
- package/dist/collapsible/CollapsiblePage.esm.js.map +1 -0
- package/dist/collapsible/ConversationsPanel.esm.js +198 -0
- package/dist/collapsible/ConversationsPanel.esm.js.map +1 -0
- package/dist/collapsible/FullHeightRegion.esm.js +54 -0
- package/dist/collapsible/FullHeightRegion.esm.js.map +1 -0
- package/dist/collapsible/SidePane.esm.js +89 -0
- package/dist/collapsible/SidePane.esm.js.map +1 -0
- package/dist/collapsible/surface/AssistantAvatar.esm.js +98 -0
- package/dist/collapsible/surface/AssistantAvatar.esm.js.map +1 -0
- package/dist/collapsible/surface/BackstageLogo.esm.js +24 -0
- package/dist/collapsible/surface/BackstageLogo.esm.js.map +1 -0
- package/dist/collapsible/surface/ConversationSurface.esm.js +268 -0
- package/dist/collapsible/surface/ConversationSurface.esm.js.map +1 -0
- package/dist/collapsible/surface/MarkdownText.esm.js +24 -0
- package/dist/collapsible/surface/MarkdownText.esm.js.map +1 -0
- package/dist/collapsible/surface/MermaidDiagram.esm.js +245 -0
- package/dist/collapsible/surface/MermaidDiagram.esm.js.map +1 -0
- package/dist/collapsible/surface/parts.esm.js +216 -0
- package/dist/collapsible/surface/parts.esm.js.map +1 -0
- package/dist/collapsible/surface/styles/assistant-ui-markdown.css.esm.js +5 -0
- package/dist/collapsible/surface/styles/assistant-ui-markdown.css.esm.js.map +1 -0
- package/dist/collapsible/surface/styles/assistant-ui.css.esm.js +5 -0
- package/dist/collapsible/surface/styles/assistant-ui.css.esm.js.map +1 -0
- package/dist/collapsible/unreadStore.esm.js +79 -0
- package/dist/collapsible/unreadStore.esm.js.map +1 -0
- package/dist/collapsible/useConversations.esm.js +171 -0
- package/dist/collapsible/useConversations.esm.js.map +1 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.esm.js +3 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js +29 -0
- package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js.map +1 -0
- package/dist/routes.esm.js +8 -0
- package/dist/routes.esm.js.map +1 -0
- package/package.json +107 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssistantAvatar.esm.js","sources":["../../../src/collapsible/surface/AssistantAvatar.tsx"],"sourcesContent":["import { makeStyles, useTheme } from '@material-ui/core/styles';\nimport { BackstageLogo } from './BackstageLogo';\n\n/**\n * Default avatar tint for assistants with no configured `color`. Matches the\n * official Backstage brand teal used by the app's own logo (LogoIcon/LogoFull).\n *\n * @public\n */\nexport const DEFAULT_AVATAR_COLOR = '#7df3e1';\n\nconst clamp = (n: number, min: number, max: number) =>\n Math.min(Math.max(n, min), max);\n\nfunction hexToRgb(hex: string): [number, number, number] | null {\n let h = hex.trim().replace(/^#/, '');\n if (h.length === 3) {\n h = h\n .split('')\n .map(c => c + c)\n .join('');\n }\n if (!/^[0-9a-fA-F]{6}$/.test(h)) {\n return null;\n }\n const num = parseInt(h, 16);\n return [(num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff];\n}\n\nfunction rgbToHsl(r: number, g: number, b: number): [number, number, number] {\n const [rn, gn, bn] = [r / 255, g / 255, b / 255];\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const d = max - min;\n let h = 0;\n if (d !== 0) {\n if (max === rn) {\n h = ((gn - bn) / d) % 6;\n } else if (max === gn) {\n h = (bn - rn) / d + 2;\n } else {\n h = (rn - gn) / d + 4;\n }\n h = h * 60;\n if (h < 0) {\n h += 360;\n }\n }\n const l = (max + min) / 2;\n const s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));\n return [h, s, l];\n}\n\nfunction hslToHex(h: number, s: number, l: number): string {\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = l - c / 2;\n let rgb: [number, number, number];\n if (h < 60) rgb = [c, x, 0];\n else if (h < 120) rgb = [x, c, 0];\n else if (h < 180) rgb = [0, c, x];\n else if (h < 240) rgb = [0, x, c];\n else if (h < 300) rgb = [x, 0, c];\n else rgb = [c, 0, x];\n const channel = (v: number) =>\n Math.round((v + m) * 255)\n .toString(16)\n .padStart(2, '0');\n return `#${channel(rgb[0])}${channel(rgb[1])}${channel(rgb[2])}`;\n}\n\n/**\n * Resolve an assistant's configured `color` to a shade with enough contrast for\n * the active theme's surfaces. The hue and saturation (the assistant's identity)\n * are preserved; only lightness is clamped into a legible band — bright on dark,\n * deep on light — so a single configured color works as a floating, no-background\n * icon in both modes. Non-hex values are returned unchanged, and the function is\n * idempotent (re-resolving a resolved color is a no-op).\n *\n * @public\n */\nexport function resolveAssistantColor(\n color: string | undefined,\n themeType: 'light' | 'dark',\n): string {\n const base = color ?? DEFAULT_AVATAR_COLOR;\n const rgb = hexToRgb(base);\n if (!rgb) {\n return base;\n }\n const [h, s, l] = rgbToHsl(rgb[0], rgb[1], rgb[2]);\n const adjustedL =\n themeType === 'dark' ? clamp(l, 0.6, 0.82) : clamp(l, 0.3, 0.46);\n return hslToHex(h, s, adjustedL);\n}\n\nconst useStyles = makeStyles({\n // Transparent — the tinted logo \"floats\", no disc fill.\n disc: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n },\n logo: {\n height: '100%',\n width: 'auto',\n },\n});\n\n/**\n * Avatar generator: the Backstage logo mark tinted by a hex `color` on a neutral\n * disc. `color` is the assistant's configured color; it falls back to\n * {@link DEFAULT_AVATAR_COLOR}. Reused in the assistant switcher list and the\n * collapsed rail (the chat surface tints its own message/welcome avatars with\n * the same color). A future variant can also emit a data-URI for `<img>`/favicon\n * use.\n *\n * @public\n */\nexport function AssistantAvatar({\n color,\n size = 28,\n className,\n}: {\n color?: string;\n size?: number;\n className?: string;\n}) {\n const classes = useStyles();\n const theme = useTheme();\n const resolved = resolveAssistantColor(color, theme.palette.type);\n return (\n <span\n aria-hidden\n className={className ? `${classes.disc} ${className}` : classes.disc}\n style={{ width: size, height: size, color: resolved }}\n >\n <BackstageLogo className={classes.logo} />\n </span>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
function BackstageLogo({ className }) {
|
|
4
|
+
return /* @__PURE__ */ jsx(
|
|
5
|
+
"svg",
|
|
6
|
+
{
|
|
7
|
+
className,
|
|
8
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
9
|
+
viewBox: "0 0 337.46 428.5",
|
|
10
|
+
role: "img",
|
|
11
|
+
"aria-label": "Assistant",
|
|
12
|
+
children: /* @__PURE__ */ jsx(
|
|
13
|
+
"path",
|
|
14
|
+
{
|
|
15
|
+
fill: "currentColor",
|
|
16
|
+
d: "M303,166.05a80.69,80.69,0,0,0,13.45-10.37c.79-.77,1.55-1.53,2.3-2.3a83.12,83.12,0,0,0,7.93-9.38A63.69,63.69,0,0,0,333,133.23a48.58,48.58,0,0,0,4.35-16.4c1.49-19.39-10-38.67-35.62-54.22L198.56,0,78.3,115.23,0,190.25l108.6,65.91a111.59,111.59,0,0,0,57.76,16.41c24.92,0,48.8-8.8,66.42-25.69,19.16-18.36,25.52-42.12,13.7-61.87a49.22,49.22,0,0,0-6.8-8.87A89.17,89.17,0,0,0,259,178.29h.15a85.08,85.08,0,0,0,31-5.79A80.88,80.88,0,0,0,303,166.05ZM202.45,225.86c-19.32,18.51-50.4,21.23-75.7,5.9L51.61,186.15l67.45-64.64,76.41,46.38C223,184.58,221.49,207.61,202.45,225.86Zm8.93-82.22-70.65-42.89L205.14,39,274.51,81.1c25.94,15.72,29.31,37,10.55,55A60.69,60.69,0,0,1,211.38,143.64Zm29.86,190c-19.57,18.75-46.17,29.09-74.88,29.09a123.73,123.73,0,0,1-64.1-18.2L0,282.52v24.67L108.6,373.1a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.81,66.42-25.69,12.88-12.34,20-27.13,19.68-41.49v-1.79A87.27,87.27,0,0,1,241.24,333.68Zm0-39c-19.57,18.75-46.17,29.08-74.88,29.08a123.81,123.81,0,0,1-64.1-18.19L0,243.53v24.68l108.6,65.91a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.81,66.42-25.69,12.88-12.34,20-27.13,19.68-41.5v-1.78A87.27,87.27,0,0,1,241.24,294.7Zm0-39c-19.57,18.76-46.17,29.09-74.88,29.09a123.81,123.81,0,0,1-64.1-18.19L0,204.55v24.68l108.6,65.91a111.59,111.59,0,0,0,57.76,16.41c24.92,0,48.8-8.8,66.42-25.68,12.88-12.35,20-27.13,19.68-41.5v-1.82A86.09,86.09,0,0,1,241.24,255.71Zm83.7,25.74a94.15,94.15,0,0,1-60.2,25.86h0V334a81.6,81.6,0,0,0,51.74-22.37c14-13.38,21.14-28.11,21-42.64v-2.19A94.92,94.92,0,0,1,324.94,281.45Zm-83.7,91.21c-19.57,18.76-46.17,29.09-74.88,29.09a123.73,123.73,0,0,1-64.1-18.2L0,321.5v24.68l108.6,65.9a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.8,66.42-25.69,12.88-12.34,20-27.13,19.68-41.49v-1.79A86.29,86.29,0,0,1,241.24,372.66ZM327,162.45c-.68.69-1.35,1.38-2.05,2.06a94.37,94.37,0,0,1-10.64,8.65,91.35,91.35,0,0,1-11.6,7,94.53,94.53,0,0,1-26.24,8.71,97.69,97.69,0,0,1-14.16,1.57c.5,1.61.9,3.25,1.25,4.9a53.27,53.27,0,0,1,1.14,12V217h.05a84.41,84.41,0,0,0,25.35-5.55,81,81,0,0,0,26.39-16.82c.8-.77,1.5-1.56,2.26-2.34a82.08,82.08,0,0,0,7.93-9.38A63.76,63.76,0,0,0,333,172.17a48.55,48.55,0,0,0,4.32-16.45c.09-1.23.2-2.47.19-3.7V150q-1.08,1.54-2.25,3.09A96.73,96.73,0,0,1,327,162.45Zm0,77.92c-.69.7-1.31,1.41-2,2.1a94.2,94.2,0,0,1-60.2,25.86h0l0,26.67h0a81.6,81.6,0,0,0,51.74-22.37A73.51,73.51,0,0,0,333,250.13a48.56,48.56,0,0,0,4.32-16.44c.09-1.24.2-2.47.19-3.71v-2.19c-.74,1.07-1.46,2.15-2.27,3.21A95.68,95.68,0,0,1,327,240.37Zm0-39c-.69.7-1.31,1.41-2,2.1a93.18,93.18,0,0,1-10.63,8.65,91.63,91.63,0,0,1-11.63,7,95.47,95.47,0,0,1-37.94,10.18h0V256h0a81.65,81.65,0,0,0,51.74-22.37c.8-.77,1.5-1.56,2.26-2.34a82.08,82.08,0,0,0,7.93-9.38A63.76,63.76,0,0,0,333,211.15a48.56,48.56,0,0,0,4.32-16.44c.09-1.24.2-2.48.19-3.71v-2.2c-.74,1.08-1.46,2.16-2.27,3.22A95.68,95.68,0,0,1,327,201.39Z"
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { BackstageLogo };
|
|
24
|
+
//# sourceMappingURL=BackstageLogo.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BackstageLogo.esm.js","sources":["../../../src/collapsible/surface/BackstageLogo.tsx"],"sourcesContent":["/**\n * The Backstage logo mark as an inline SVG (no host asset dependency), used as\n * the assistant's avatar. Fills with `currentColor`, so a single parent `color`\n * drives it — today one fixed color, later a config/hex-driven avatar generator\n * can swap or tint it per assistant.\n */\nexport function BackstageLogo({ className }: { className?: string }) {\n return (\n <svg\n className={className}\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 337.46 428.5\"\n role=\"img\"\n aria-label=\"Assistant\"\n >\n <path\n fill=\"currentColor\"\n d=\"M303,166.05a80.69,80.69,0,0,0,13.45-10.37c.79-.77,1.55-1.53,2.3-2.3a83.12,83.12,0,0,0,7.93-9.38A63.69,63.69,0,0,0,333,133.23a48.58,48.58,0,0,0,4.35-16.4c1.49-19.39-10-38.67-35.62-54.22L198.56,0,78.3,115.23,0,190.25l108.6,65.91a111.59,111.59,0,0,0,57.76,16.41c24.92,0,48.8-8.8,66.42-25.69,19.16-18.36,25.52-42.12,13.7-61.87a49.22,49.22,0,0,0-6.8-8.87A89.17,89.17,0,0,0,259,178.29h.15a85.08,85.08,0,0,0,31-5.79A80.88,80.88,0,0,0,303,166.05ZM202.45,225.86c-19.32,18.51-50.4,21.23-75.7,5.9L51.61,186.15l67.45-64.64,76.41,46.38C223,184.58,221.49,207.61,202.45,225.86Zm8.93-82.22-70.65-42.89L205.14,39,274.51,81.1c25.94,15.72,29.31,37,10.55,55A60.69,60.69,0,0,1,211.38,143.64Zm29.86,190c-19.57,18.75-46.17,29.09-74.88,29.09a123.73,123.73,0,0,1-64.1-18.2L0,282.52v24.67L108.6,373.1a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.81,66.42-25.69,12.88-12.34,20-27.13,19.68-41.49v-1.79A87.27,87.27,0,0,1,241.24,333.68Zm0-39c-19.57,18.75-46.17,29.08-74.88,29.08a123.81,123.81,0,0,1-64.1-18.19L0,243.53v24.68l108.6,65.91a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.81,66.42-25.69,12.88-12.34,20-27.13,19.68-41.5v-1.78A87.27,87.27,0,0,1,241.24,294.7Zm0-39c-19.57,18.76-46.17,29.09-74.88,29.09a123.81,123.81,0,0,1-64.1-18.19L0,204.55v24.68l108.6,65.91a111.59,111.59,0,0,0,57.76,16.41c24.92,0,48.8-8.8,66.42-25.68,12.88-12.35,20-27.13,19.68-41.5v-1.82A86.09,86.09,0,0,1,241.24,255.71Zm83.7,25.74a94.15,94.15,0,0,1-60.2,25.86h0V334a81.6,81.6,0,0,0,51.74-22.37c14-13.38,21.14-28.11,21-42.64v-2.19A94.92,94.92,0,0,1,324.94,281.45Zm-83.7,91.21c-19.57,18.76-46.17,29.09-74.88,29.09a123.73,123.73,0,0,1-64.1-18.2L0,321.5v24.68l108.6,65.9a111.6,111.6,0,0,0,57.76,16.42c24.92,0,48.8-8.8,66.42-25.69,12.88-12.34,20-27.13,19.68-41.49v-1.79A86.29,86.29,0,0,1,241.24,372.66ZM327,162.45c-.68.69-1.35,1.38-2.05,2.06a94.37,94.37,0,0,1-10.64,8.65,91.35,91.35,0,0,1-11.6,7,94.53,94.53,0,0,1-26.24,8.71,97.69,97.69,0,0,1-14.16,1.57c.5,1.61.9,3.25,1.25,4.9a53.27,53.27,0,0,1,1.14,12V217h.05a84.41,84.41,0,0,0,25.35-5.55,81,81,0,0,0,26.39-16.82c.8-.77,1.5-1.56,2.26-2.34a82.08,82.08,0,0,0,7.93-9.38A63.76,63.76,0,0,0,333,172.17a48.55,48.55,0,0,0,4.32-16.45c.09-1.23.2-2.47.19-3.7V150q-1.08,1.54-2.25,3.09A96.73,96.73,0,0,1,327,162.45Zm0,77.92c-.69.7-1.31,1.41-2,2.1a94.2,94.2,0,0,1-60.2,25.86h0l0,26.67h0a81.6,81.6,0,0,0,51.74-22.37A73.51,73.51,0,0,0,333,250.13a48.56,48.56,0,0,0,4.32-16.44c.09-1.24.2-2.47.19-3.71v-2.19c-.74,1.07-1.46,2.15-2.27,3.21A95.68,95.68,0,0,1,327,240.37Zm0-39c-.69.7-1.31,1.41-2,2.1a93.18,93.18,0,0,1-10.63,8.65,91.63,91.63,0,0,1-11.63,7,95.47,95.47,0,0,1-37.94,10.18h0V256h0a81.65,81.65,0,0,0,51.74-22.37c.8-.77,1.5-1.56,2.26-2.34a82.08,82.08,0,0,0,7.93-9.38A63.76,63.76,0,0,0,333,211.15a48.56,48.56,0,0,0,4.32-16.44c.09-1.24.2-2.48.19-3.71v-2.2c-.74,1.08-1.46,2.16-2.27,3.22A95.68,95.68,0,0,1,327,201.39Z\"\n />\n </svg>\n );\n}\n"],"names":[],"mappings":";;AAMO,SAAS,aAAA,CAAc,EAAE,SAAA,EAAU,EAA2B;AACnE,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA;AAAA,MACA,KAAA,EAAM,4BAAA;AAAA,MACN,OAAA,EAAQ,kBAAA;AAAA,MACR,IAAA,EAAK,KAAA;AAAA,MACL,YAAA,EAAW,WAAA;AAAA,MAEX,QAAA,kBAAA,GAAA;AAAA,QAAC,MAAA;AAAA,QAAA;AAAA,UACC,IAAA,EAAK,cAAA;AAAA,UACL,CAAA,EAAE;AAAA;AAAA;AACJ;AAAA,GACF;AAEJ;;;;"}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import { createContext, useState, useEffect, useContext } from 'react';
|
|
3
|
+
import { useApi, identityApiRef } from '@backstage/core-plugin-api';
|
|
4
|
+
import { Avatar } from '@backstage/core-components';
|
|
5
|
+
import { useTheme, makeStyles } from '@material-ui/core/styles';
|
|
6
|
+
import { Typography, Button } from '@material-ui/core';
|
|
7
|
+
import { BackstageLogo } from './BackstageLogo.esm.js';
|
|
8
|
+
import { resolveAssistantColor, DEFAULT_AVATAR_COLOR } from './AssistantAvatar.esm.js';
|
|
9
|
+
import { ThreadPrimitive } from '@assistant-ui/react';
|
|
10
|
+
import { Thread, UserMessage, ThreadWelcome, AssistantMessage, BranchPicker, AssistantActionBar } from '@assistant-ui/react-ui';
|
|
11
|
+
import { MarkdownText } from './MarkdownText.esm.js';
|
|
12
|
+
import { ToolFallback, ThinkingMessage, ReasoningPart } from './parts.esm.js';
|
|
13
|
+
|
|
14
|
+
const DEFAULT_WELCOME_SUBTITLE = "Ask me about services, APIs, teams, TechDocs, or anything in the catalog.";
|
|
15
|
+
const AvatarColorContext = createContext(void 0);
|
|
16
|
+
const useStyles = makeStyles((theme) => ({
|
|
17
|
+
threadHost: {
|
|
18
|
+
flex: 1,
|
|
19
|
+
minHeight: 0,
|
|
20
|
+
"& .aui-thread-root": {
|
|
21
|
+
// Wide conversation pane to maximize room for diagrams / visual artifacts.
|
|
22
|
+
// (The composer is narrowed independently via the footer max-width below.)
|
|
23
|
+
"--aui-thread-max-width": "90%",
|
|
24
|
+
"--aui-background": theme.palette.type === "dark" ? "0 0% 18%" : "0 0% 100%",
|
|
25
|
+
"--aui-foreground": theme.palette.type === "dark" ? "0 0% 98%" : "240 10% 3.9%",
|
|
26
|
+
"--aui-muted": theme.palette.type === "dark" ? "0 0% 24%" : "240 4.8% 95.9%",
|
|
27
|
+
"--aui-muted-foreground": theme.palette.type === "dark" ? "0 0% 70%" : "240 3.8% 46.1%",
|
|
28
|
+
"--aui-accent": theme.palette.type === "dark" ? "0 0% 24%" : "240 4.8% 95.9%",
|
|
29
|
+
"--aui-accent-foreground": theme.palette.type === "dark" ? "0 0% 98%" : "240 5.9% 10%",
|
|
30
|
+
"--aui-border": theme.palette.type === "dark" ? "0 0% 40%" : "240 5.9% 90%",
|
|
31
|
+
"--aui-input": theme.palette.type === "dark" ? "0 0% 40%" : "240 5.9% 90%",
|
|
32
|
+
"--aui-ring": theme.palette.type === "dark" ? "207 90% 68%" : "210 90% 45%",
|
|
33
|
+
"--aui-primary": theme.palette.type === "dark" ? "0 0% 98%" : "240 5.9% 10%",
|
|
34
|
+
"--aui-primary-foreground": theme.palette.type === "dark" ? "240 5.9% 10%" : "0 0% 98%"
|
|
35
|
+
},
|
|
36
|
+
// Active composer border tinted to the agent color (set as --aui-composer-focus
|
|
37
|
+
// on the host below); falls back to the theme primary if unset.
|
|
38
|
+
"& .aui-composer-root:focus-within": {
|
|
39
|
+
borderColor: "var(--aui-composer-focus)",
|
|
40
|
+
boxShadow: "0 0 0 1px var(--aui-composer-focus)"
|
|
41
|
+
},
|
|
42
|
+
"& .aui-assistant-message-content": {
|
|
43
|
+
maxWidth: "100%",
|
|
44
|
+
width: "100%"
|
|
45
|
+
},
|
|
46
|
+
// The composer is narrower than the conversation: cap the footer (which holds
|
|
47
|
+
// the composer and is centered in the viewport) below the 90% thread width.
|
|
48
|
+
// Breathing room below the composer lives here too — the footer is sticky
|
|
49
|
+
// bottom:0 and inherits the Thread's --aui-background, so padding lifts the
|
|
50
|
+
// composer off the bottom WITHOUT exposing the card's paper bg (a two-tone
|
|
51
|
+
// seam). Padding the card/threadBody instead would re-create that seam.
|
|
52
|
+
"& .aui-thread-viewport-footer": {
|
|
53
|
+
maxWidth: "48rem",
|
|
54
|
+
paddingBottom: "2.5rem"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
botAvatar: {
|
|
58
|
+
display: "inline-flex",
|
|
59
|
+
alignItems: "center",
|
|
60
|
+
justifyContent: "center",
|
|
61
|
+
// Transparent so the tinted logo floats (overrides react-ui's aui-avatar-root).
|
|
62
|
+
backgroundColor: "transparent",
|
|
63
|
+
// Bumped to ~match the user avatar (which has an encircling disc); the
|
|
64
|
+
// floating logo looked small by comparison.
|
|
65
|
+
width: 32,
|
|
66
|
+
height: 32
|
|
67
|
+
},
|
|
68
|
+
botLogo: {
|
|
69
|
+
height: "100%",
|
|
70
|
+
width: "auto"
|
|
71
|
+
},
|
|
72
|
+
userMessageWithAvatar: {
|
|
73
|
+
display: "flex",
|
|
74
|
+
width: "100%",
|
|
75
|
+
maxWidth: "var(--aui-thread-max-width)",
|
|
76
|
+
flexDirection: "column",
|
|
77
|
+
alignItems: "flex-end",
|
|
78
|
+
gap: theme.spacing(0.5),
|
|
79
|
+
paddingTop: theme.spacing(1),
|
|
80
|
+
paddingBottom: theme.spacing(1)
|
|
81
|
+
},
|
|
82
|
+
userMessageBody: {
|
|
83
|
+
display: "flex",
|
|
84
|
+
maxWidth: "80%",
|
|
85
|
+
alignItems: "flex-start",
|
|
86
|
+
justifyContent: "flex-end",
|
|
87
|
+
gap: theme.spacing(1),
|
|
88
|
+
"& .aui-user-message-content": {
|
|
89
|
+
maxWidth: "100%"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
userAvatar: {
|
|
93
|
+
width: theme.spacing(5),
|
|
94
|
+
height: theme.spacing(5),
|
|
95
|
+
marginTop: theme.spacing(0.25),
|
|
96
|
+
flexShrink: 0
|
|
97
|
+
},
|
|
98
|
+
welcomeRoot: {
|
|
99
|
+
display: "flex",
|
|
100
|
+
flexDirection: "column",
|
|
101
|
+
alignItems: "center",
|
|
102
|
+
gap: theme.spacing(2),
|
|
103
|
+
maxWidth: "28rem",
|
|
104
|
+
textAlign: "center"
|
|
105
|
+
},
|
|
106
|
+
welcomeLogo: {
|
|
107
|
+
display: "inline-flex",
|
|
108
|
+
alignItems: "center",
|
|
109
|
+
justifyContent: "center",
|
|
110
|
+
width: 56,
|
|
111
|
+
height: 56
|
|
112
|
+
},
|
|
113
|
+
welcomeLogoIcon: {
|
|
114
|
+
width: 30,
|
|
115
|
+
height: "auto"
|
|
116
|
+
},
|
|
117
|
+
welcomeGreeting: {
|
|
118
|
+
fontWeight: 500,
|
|
119
|
+
color: theme.palette.text.primary
|
|
120
|
+
},
|
|
121
|
+
welcomeSubtitle: {
|
|
122
|
+
color: theme.palette.text.secondary
|
|
123
|
+
},
|
|
124
|
+
suggestions: {
|
|
125
|
+
display: "flex",
|
|
126
|
+
flexWrap: "wrap",
|
|
127
|
+
justifyContent: "center",
|
|
128
|
+
gap: theme.spacing(1),
|
|
129
|
+
marginTop: theme.spacing(1)
|
|
130
|
+
},
|
|
131
|
+
suggestionButton: {
|
|
132
|
+
textTransform: "none",
|
|
133
|
+
borderRadius: 999
|
|
134
|
+
}
|
|
135
|
+
}));
|
|
136
|
+
function AssistantBotAvatar() {
|
|
137
|
+
const classes = useStyles();
|
|
138
|
+
const color = useContext(AvatarColorContext) ?? DEFAULT_AVATAR_COLOR;
|
|
139
|
+
return /* @__PURE__ */ jsx(
|
|
140
|
+
"span",
|
|
141
|
+
{
|
|
142
|
+
className: `aui-avatar-root ${classes.botAvatar}`,
|
|
143
|
+
style: { color, backgroundColor: "transparent" },
|
|
144
|
+
"aria-label": "Backstage assistant",
|
|
145
|
+
role: "img",
|
|
146
|
+
children: /* @__PURE__ */ jsx(BackstageLogo, { className: classes.botLogo })
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
function AssistantMessageWithAvatar() {
|
|
151
|
+
return /* @__PURE__ */ jsxs(AssistantMessage.Root, { children: [
|
|
152
|
+
/* @__PURE__ */ jsx(AssistantBotAvatar, {}),
|
|
153
|
+
/* @__PURE__ */ jsx(AssistantMessage.Content, { components: { Reasoning: ReasoningPart } }),
|
|
154
|
+
/* @__PURE__ */ jsx(BranchPicker, {}),
|
|
155
|
+
/* @__PURE__ */ jsx(AssistantActionBar, {})
|
|
156
|
+
] });
|
|
157
|
+
}
|
|
158
|
+
function useProfile() {
|
|
159
|
+
const identityApi = useApi(identityApiRef);
|
|
160
|
+
const [profile, setProfile] = useState();
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
let mounted = true;
|
|
163
|
+
identityApi.getProfileInfo().then((value) => {
|
|
164
|
+
if (mounted) {
|
|
165
|
+
setProfile(value);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return () => {
|
|
169
|
+
mounted = false;
|
|
170
|
+
};
|
|
171
|
+
}, [identityApi]);
|
|
172
|
+
return profile;
|
|
173
|
+
}
|
|
174
|
+
function UserChatAvatar() {
|
|
175
|
+
const classes = useStyles();
|
|
176
|
+
const profile = useProfile();
|
|
177
|
+
return /* @__PURE__ */ jsx(
|
|
178
|
+
Avatar,
|
|
179
|
+
{
|
|
180
|
+
classes: { avatar: classes.userAvatar },
|
|
181
|
+
displayName: profile?.displayName ?? profile?.email ?? "You",
|
|
182
|
+
picture: profile?.picture
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
function UserMessageWithAvatar() {
|
|
187
|
+
const classes = useStyles();
|
|
188
|
+
return /* @__PURE__ */ jsxs(UserMessage.Root, { className: classes.userMessageWithAvatar, children: [
|
|
189
|
+
/* @__PURE__ */ jsx(UserMessage.Attachments, {}),
|
|
190
|
+
/* @__PURE__ */ jsxs("div", { className: classes.userMessageBody, children: [
|
|
191
|
+
/* @__PURE__ */ jsx(UserMessage.Content, {}),
|
|
192
|
+
/* @__PURE__ */ jsx(UserChatAvatar, {})
|
|
193
|
+
] })
|
|
194
|
+
] });
|
|
195
|
+
}
|
|
196
|
+
function ConversationSurface(props) {
|
|
197
|
+
const { composerPlaceholder, suggestions, welcome, assistantColor, className } = props;
|
|
198
|
+
const classes = useStyles();
|
|
199
|
+
const theme = useTheme();
|
|
200
|
+
const resolvedColor = resolveAssistantColor(assistantColor, theme.palette.type);
|
|
201
|
+
function EmptyThreadWelcome() {
|
|
202
|
+
const profile = useProfile();
|
|
203
|
+
const firstName = profile?.displayName?.split(" ")[0] ?? "there";
|
|
204
|
+
const title = welcome?.title ?? `Hi ${firstName}, welcome to Backstage`;
|
|
205
|
+
const subtitle = welcome?.subtitle ?? DEFAULT_WELCOME_SUBTITLE;
|
|
206
|
+
return /* @__PURE__ */ jsx(ThreadWelcome.Root, { children: /* @__PURE__ */ jsx(ThreadWelcome.Center, { children: /* @__PURE__ */ jsxs("div", { className: classes.welcomeRoot, children: [
|
|
207
|
+
/* @__PURE__ */ jsx(
|
|
208
|
+
"span",
|
|
209
|
+
{
|
|
210
|
+
className: classes.welcomeLogo,
|
|
211
|
+
style: { color: resolvedColor },
|
|
212
|
+
"aria-hidden": "true",
|
|
213
|
+
children: /* @__PURE__ */ jsx(BackstageLogo, { className: classes.welcomeLogoIcon })
|
|
214
|
+
}
|
|
215
|
+
),
|
|
216
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h5", className: classes.welcomeGreeting, children: title }),
|
|
217
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: classes.welcomeSubtitle, children: subtitle }),
|
|
218
|
+
suggestions && suggestions.length > 0 && /* @__PURE__ */ jsx("div", { className: classes.suggestions, children: suggestions.map((suggestion) => /* @__PURE__ */ jsx(
|
|
219
|
+
ThreadPrimitive.Suggestion,
|
|
220
|
+
{
|
|
221
|
+
prompt: suggestion.prompt,
|
|
222
|
+
send: true,
|
|
223
|
+
asChild: true,
|
|
224
|
+
children: /* @__PURE__ */ jsx(
|
|
225
|
+
Button,
|
|
226
|
+
{
|
|
227
|
+
variant: "outlined",
|
|
228
|
+
size: "small",
|
|
229
|
+
className: classes.suggestionButton,
|
|
230
|
+
children: suggestion.title
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
},
|
|
234
|
+
suggestion.prompt
|
|
235
|
+
)) })
|
|
236
|
+
] }) }) });
|
|
237
|
+
}
|
|
238
|
+
return /* @__PURE__ */ jsx(AvatarColorContext.Provider, { value: resolvedColor, children: /* @__PURE__ */ jsx(
|
|
239
|
+
"div",
|
|
240
|
+
{
|
|
241
|
+
className: `${classes.threadHost} ${className ?? ""}`,
|
|
242
|
+
style: {
|
|
243
|
+
"--aui-composer-focus": resolvedColor
|
|
244
|
+
},
|
|
245
|
+
children: /* @__PURE__ */ jsx(
|
|
246
|
+
Thread,
|
|
247
|
+
{
|
|
248
|
+
strings: composerPlaceholder ? { composer: { input: { placeholder: composerPlaceholder } } } : void 0,
|
|
249
|
+
components: {
|
|
250
|
+
AssistantMessage: AssistantMessageWithAvatar,
|
|
251
|
+
ThreadWelcome: EmptyThreadWelcome,
|
|
252
|
+
UserMessage: UserMessageWithAvatar
|
|
253
|
+
},
|
|
254
|
+
assistantMessage: {
|
|
255
|
+
components: {
|
|
256
|
+
Text: MarkdownText,
|
|
257
|
+
Empty: ThinkingMessage,
|
|
258
|
+
ToolFallback
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
) });
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export { ConversationSurface };
|
|
268
|
+
//# sourceMappingURL=ConversationSurface.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConversationSurface.esm.js","sources":["../../../src/collapsible/surface/ConversationSurface.tsx"],"sourcesContent":["// The prebuilt react-ui `<Thread>` stylesheet is vendored locally under\n// `styles/` and imported once at the top of `CollapsiblePage.tsx`. It is NOT\n// imported here: react-ui's `sideEffects: false` lets bundlers tree-shake a\n// bare CSS import from inside a lazily-loaded component, which left the Thread\n// unstyled.\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n type CSSProperties,\n} from 'react';\nimport type { ProfileInfo } from '@backstage/core-plugin-api';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\nimport { Avatar as BackstageAvatar } from '@backstage/core-components';\nimport { makeStyles, useTheme } from '@material-ui/core/styles';\nimport { Button, Typography } from '@material-ui/core';\nimport { BackstageLogo } from './BackstageLogo';\nimport { DEFAULT_AVATAR_COLOR, resolveAssistantColor } from './AssistantAvatar';\nimport { ThreadPrimitive } from '@assistant-ui/react';\nimport {\n AssistantActionBar,\n AssistantMessage,\n BranchPicker,\n Thread,\n ThreadWelcome,\n UserMessage,\n} from '@assistant-ui/react-ui';\nimport { MarkdownText } from './MarkdownText';\nimport { ReasoningPart, ThinkingMessage, ToolFallback } from './parts';\n\nconst DEFAULT_WELCOME_SUBTITLE =\n 'Ask me about services, APIs, teams, TechDocs, or anything in the catalog.';\n\n// The active assistant's avatar color. ConversationSurface provides it; the\n// message components (passed to <Thread> by reference, so they can't take props)\n// read it from context. Falls back to DEFAULT_AVATAR_COLOR.\nconst AvatarColorContext = createContext<string | undefined>(undefined);\n\nconst useStyles = makeStyles(theme => ({\n threadHost: {\n flex: 1,\n minHeight: 0,\n '& .aui-thread-root': {\n // Wide conversation pane to maximize room for diagrams / visual artifacts.\n // (The composer is narrowed independently via the footer max-width below.)\n '--aui-thread-max-width': '90%',\n '--aui-background':\n theme.palette.type === 'dark' ? '0 0% 18%' : '0 0% 100%',\n '--aui-foreground':\n theme.palette.type === 'dark' ? '0 0% 98%' : '240 10% 3.9%',\n '--aui-muted':\n theme.palette.type === 'dark' ? '0 0% 24%' : '240 4.8% 95.9%',\n '--aui-muted-foreground':\n theme.palette.type === 'dark' ? '0 0% 70%' : '240 3.8% 46.1%',\n '--aui-accent':\n theme.palette.type === 'dark' ? '0 0% 24%' : '240 4.8% 95.9%',\n '--aui-accent-foreground':\n theme.palette.type === 'dark' ? '0 0% 98%' : '240 5.9% 10%',\n '--aui-border':\n theme.palette.type === 'dark' ? '0 0% 40%' : '240 5.9% 90%',\n '--aui-input':\n theme.palette.type === 'dark' ? '0 0% 40%' : '240 5.9% 90%',\n '--aui-ring':\n theme.palette.type === 'dark' ? '207 90% 68%' : '210 90% 45%',\n '--aui-primary':\n theme.palette.type === 'dark' ? '0 0% 98%' : '240 5.9% 10%',\n '--aui-primary-foreground':\n theme.palette.type === 'dark' ? '240 5.9% 10%' : '0 0% 98%',\n },\n // Active composer border tinted to the agent color (set as --aui-composer-focus\n // on the host below); falls back to the theme primary if unset.\n '& .aui-composer-root:focus-within': {\n borderColor: 'var(--aui-composer-focus)',\n boxShadow: '0 0 0 1px var(--aui-composer-focus)',\n },\n '& .aui-assistant-message-content': {\n maxWidth: '100%',\n width: '100%',\n },\n // The composer is narrower than the conversation: cap the footer (which holds\n // the composer and is centered in the viewport) below the 90% thread width.\n // Breathing room below the composer lives here too — the footer is sticky\n // bottom:0 and inherits the Thread's --aui-background, so padding lifts the\n // composer off the bottom WITHOUT exposing the card's paper bg (a two-tone\n // seam). Padding the card/threadBody instead would re-create that seam.\n '& .aui-thread-viewport-footer': {\n maxWidth: '48rem',\n paddingBottom: '2.5rem',\n },\n },\n botAvatar: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n // Transparent so the tinted logo floats (overrides react-ui's aui-avatar-root).\n backgroundColor: 'transparent',\n // Bumped to ~match the user avatar (which has an encircling disc); the\n // floating logo looked small by comparison.\n width: 32,\n height: 32,\n },\n botLogo: {\n height: '100%',\n width: 'auto',\n },\n userMessageWithAvatar: {\n display: 'flex',\n width: '100%',\n maxWidth: 'var(--aui-thread-max-width)',\n flexDirection: 'column',\n alignItems: 'flex-end',\n gap: theme.spacing(0.5),\n paddingTop: theme.spacing(1),\n paddingBottom: theme.spacing(1),\n },\n userMessageBody: {\n display: 'flex',\n maxWidth: '80%',\n alignItems: 'flex-start',\n justifyContent: 'flex-end',\n gap: theme.spacing(1),\n '& .aui-user-message-content': {\n maxWidth: '100%',\n },\n },\n userAvatar: {\n width: theme.spacing(5),\n height: theme.spacing(5),\n marginTop: theme.spacing(0.25),\n flexShrink: 0,\n },\n welcomeRoot: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n gap: theme.spacing(2),\n maxWidth: '28rem',\n textAlign: 'center',\n },\n welcomeLogo: {\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 56,\n height: 56,\n },\n welcomeLogoIcon: {\n width: 30,\n height: 'auto',\n },\n welcomeGreeting: {\n fontWeight: 500,\n color: theme.palette.text.primary,\n },\n welcomeSubtitle: {\n color: theme.palette.text.secondary,\n },\n suggestions: {\n display: 'flex',\n flexWrap: 'wrap',\n justifyContent: 'center',\n gap: theme.spacing(1),\n marginTop: theme.spacing(1),\n },\n suggestionButton: {\n textTransform: 'none',\n borderRadius: 999,\n },\n}));\n\n/** A circular MUI-styled bot avatar (no host asset dependency). */\nfunction AssistantBotAvatar() {\n const classes = useStyles();\n const color = useContext(AvatarColorContext) ?? DEFAULT_AVATAR_COLOR;\n\n return (\n <span\n className={`aui-avatar-root ${classes.botAvatar}`}\n style={{ color, backgroundColor: 'transparent' }}\n aria-label=\"Backstage assistant\"\n role=\"img\"\n >\n <BackstageLogo className={classes.botLogo} />\n </span>\n );\n}\n\nfunction AssistantMessageWithAvatar() {\n return (\n <AssistantMessage.Root>\n <AssistantBotAvatar />\n <AssistantMessage.Content components={{ Reasoning: ReasoningPart }} />\n <BranchPicker />\n <AssistantActionBar />\n </AssistantMessage.Root>\n );\n}\n\n/** Read the signed-in user's profile (display name, picture) once. */\nfunction useProfile() {\n const identityApi = useApi(identityApiRef);\n const [profile, setProfile] = useState<ProfileInfo>();\n\n useEffect(() => {\n let mounted = true;\n\n identityApi.getProfileInfo().then(value => {\n if (mounted) {\n setProfile(value);\n }\n });\n\n return () => {\n mounted = false;\n };\n }, [identityApi]);\n\n return profile;\n}\n\nfunction UserChatAvatar() {\n const classes = useStyles();\n const profile = useProfile();\n\n return (\n <BackstageAvatar\n classes={{ avatar: classes.userAvatar }}\n displayName={profile?.displayName ?? profile?.email ?? 'You'}\n picture={profile?.picture}\n />\n );\n}\n\nfunction UserMessageWithAvatar() {\n const classes = useStyles();\n\n return (\n <UserMessage.Root className={classes.userMessageWithAvatar}>\n <UserMessage.Attachments />\n <div className={classes.userMessageBody}>\n <UserMessage.Content />\n <UserChatAvatar />\n </div>\n </UserMessage.Root>\n );\n}\n\n/**\n * Props for {@link ConversationSurface}.\n *\n * @public\n */\nexport interface ConversationSurfaceProps {\n /** Placeholder text for the composer input (from the assistant's ui config). */\n composerPlaceholder?: string;\n /** Starter prompts shown as clickable chips on the empty thread. */\n suggestions?: Array<{ title: string; prompt: string }>;\n /** Overrides for the default empty-thread greeting. */\n welcome?: { title?: string; subtitle?: string };\n /** The active assistant's avatar tint (hex); defaults to Backstage teal. */\n assistantColor?: string;\n /** Host layout escape hatch (applied alongside the themed thread host). */\n className?: string;\n}\n\n/**\n * The react-ui `<Thread>` rendered with Implementation 1's custom message\n * components: a bot-avatar assistant message, an avatar'd user message,\n * Markdown (with streaming-safe Mermaid), a tool-call fallback, a thinking /\n * error empty-state, and a personalized empty-thread welcome with optional\n * starter-prompt chips.\n *\n * This is a \"bring your own runtime\" surface — it expects an\n * `AssistantRuntimeProvider` higher in the tree.\n *\n * @public\n */\nexport function ConversationSurface(props: ConversationSurfaceProps) {\n const { composerPlaceholder, suggestions, welcome, assistantColor, className } =\n props;\n const classes = useStyles();\n const theme = useTheme();\n // One mode-appropriate shade for every place the agent color appears here\n // (chat bot avatar via context, welcome logo, composer focus border).\n const resolvedColor = resolveAssistantColor(assistantColor, theme.palette.type);\n\n function EmptyThreadWelcome() {\n const profile = useProfile();\n const firstName = profile?.displayName?.split(' ')[0] ?? 'there';\n\n const title = welcome?.title ?? `Hi ${firstName}, welcome to Backstage`;\n const subtitle = welcome?.subtitle ?? DEFAULT_WELCOME_SUBTITLE;\n\n return (\n <ThreadWelcome.Root>\n <ThreadWelcome.Center>\n <div className={classes.welcomeRoot}>\n <span\n className={classes.welcomeLogo}\n style={{ color: resolvedColor }}\n aria-hidden=\"true\"\n >\n <BackstageLogo className={classes.welcomeLogoIcon} />\n </span>\n <Typography variant=\"h5\" className={classes.welcomeGreeting}>\n {title}\n </Typography>\n <Typography variant=\"body2\" className={classes.welcomeSubtitle}>\n {subtitle}\n </Typography>\n {suggestions && suggestions.length > 0 && (\n <div className={classes.suggestions}>\n {suggestions.map(suggestion => (\n <ThreadPrimitive.Suggestion\n key={suggestion.prompt}\n prompt={suggestion.prompt}\n send\n asChild\n >\n <Button\n variant=\"outlined\"\n size=\"small\"\n className={classes.suggestionButton}\n >\n {suggestion.title}\n </Button>\n </ThreadPrimitive.Suggestion>\n ))}\n </div>\n )}\n </div>\n </ThreadWelcome.Center>\n </ThreadWelcome.Root>\n );\n }\n\n return (\n <AvatarColorContext.Provider value={resolvedColor}>\n <div\n className={`${classes.threadHost} ${className ?? ''}`}\n style={\n {\n '--aui-composer-focus': resolvedColor,\n } as CSSProperties\n }\n >\n <Thread\n strings={\n composerPlaceholder\n ? { composer: { input: { placeholder: composerPlaceholder } } }\n : undefined\n }\n components={{\n AssistantMessage: AssistantMessageWithAvatar,\n ThreadWelcome: EmptyThreadWelcome,\n UserMessage: UserMessageWithAvatar,\n }}\n assistantMessage={{\n components: {\n Text: MarkdownText,\n Empty: ThinkingMessage,\n ToolFallback,\n },\n }}\n />\n </div>\n </AvatarColorContext.Provider>\n );\n}\n"],"names":["BackstageAvatar"],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import remarkGfm from 'remark-gfm';
|
|
2
|
+
import { makeMarkdownText } from '@assistant-ui/react-ui';
|
|
3
|
+
import { MermaidDiagram } from './MermaidDiagram.esm.js';
|
|
4
|
+
|
|
5
|
+
function closeStreamingMermaidFence(text) {
|
|
6
|
+
const matches = [...text.matchAll(/```mermaid[^\n]*(?:\n|$)/gi)];
|
|
7
|
+
const last = matches[matches.length - 1];
|
|
8
|
+
if (!last || last.index === void 0) {
|
|
9
|
+
return text;
|
|
10
|
+
}
|
|
11
|
+
const afterFence = text.slice(last.index + last[0].length);
|
|
12
|
+
return afterFence.includes("```") ? text : `${text}
|
|
13
|
+
\`\`\``;
|
|
14
|
+
}
|
|
15
|
+
const MarkdownText = makeMarkdownText({
|
|
16
|
+
remarkPlugins: [remarkGfm],
|
|
17
|
+
preprocess: closeStreamingMermaidFence,
|
|
18
|
+
componentsByLanguage: {
|
|
19
|
+
mermaid: { SyntaxHighlighter: MermaidDiagram }
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export { MarkdownText, closeStreamingMermaidFence };
|
|
24
|
+
//# sourceMappingURL=MarkdownText.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkdownText.esm.js","sources":["../../../src/collapsible/surface/MarkdownText.tsx"],"sourcesContent":["import remarkGfm from 'remark-gfm';\nimport { makeMarkdownText } from '@assistant-ui/react-ui';\nimport { MermaidDiagram } from './MermaidDiagram';\n\n/**\n * While an assistant message is streaming, a ```mermaid fenced block may not\n * yet have its closing ``` fence. Markdown parsers won't treat the block as a\n * code block until it's closed, so the half-streamed diagram source leaks into\n * the rendered text. This appends a temporary closing fence so the in-progress\n * block is parsed (and handed to the Mermaid renderer) immediately.\n */\nexport function closeStreamingMermaidFence(text: string) {\n const matches = [...text.matchAll(/```mermaid[^\\n]*(?:\\n|$)/gi)];\n const last = matches[matches.length - 1];\n if (!last || last.index === undefined) {\n return text;\n }\n\n const afterFence = text.slice(last.index + last[0].length);\n return afterFence.includes('```') ? text : `${text}\\n\\`\\`\\``;\n}\n\nexport const MarkdownText = makeMarkdownText({\n remarkPlugins: [remarkGfm],\n preprocess: closeStreamingMermaidFence,\n componentsByLanguage: {\n mermaid: { SyntaxHighlighter: MermaidDiagram as any },\n },\n});\n"],"names":[],"mappings":";;;;AAWO,SAAS,2BAA2B,IAAA,EAAc;AACvD,EAAA,MAAM,UAAU,CAAC,GAAG,IAAA,CAAK,QAAA,CAAS,4BAA4B,CAAC,CAAA;AAC/D,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA;AACvC,EAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,KAAA,KAAU,MAAA,EAAW;AACrC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,UAAA,GAAa,KAAK,KAAA,CAAM,IAAA,CAAK,QAAQ,IAAA,CAAK,CAAC,EAAE,MAAM,CAAA;AACzD,EAAA,OAAO,WAAW,QAAA,CAAS,KAAK,CAAA,GAAI,IAAA,GAAO,GAAG,IAAI;AAAA,MAAA,CAAA;AACpD;AAEO,MAAM,eAAe,gBAAA,CAAiB;AAAA,EAC3C,aAAA,EAAe,CAAC,SAAS,CAAA;AAAA,EACzB,UAAA,EAAY,0BAAA;AAAA,EACZ,oBAAA,EAAsB;AAAA,IACpB,OAAA,EAAS,EAAE,iBAAA,EAAmB,cAAA;AAAsB;AAExD,CAAC;;;;"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useRef, useEffect } from 'react';
|
|
3
|
+
import mermaid from 'mermaid';
|
|
4
|
+
import { CircularProgress, Typography, Tooltip, Dialog, IconButton } from '@material-ui/core';
|
|
5
|
+
import { makeStyles } from '@material-ui/core/styles';
|
|
6
|
+
import CloseIcon from '@material-ui/icons/Close';
|
|
7
|
+
|
|
8
|
+
mermaid.initialize({
|
|
9
|
+
startOnLoad: false,
|
|
10
|
+
theme: "base",
|
|
11
|
+
themeVariables: {
|
|
12
|
+
background: "#ffffff",
|
|
13
|
+
mainBkg: "#ffffff",
|
|
14
|
+
primaryColor: "#f8fafc",
|
|
15
|
+
primaryTextColor: "#111827",
|
|
16
|
+
primaryBorderColor: "#334155",
|
|
17
|
+
lineColor: "#475569",
|
|
18
|
+
secondaryColor: "#eef2ff",
|
|
19
|
+
tertiaryColor: "#f8fafc",
|
|
20
|
+
clusterBkg: "#f8fafc",
|
|
21
|
+
clusterBorder: "#cbd5e1",
|
|
22
|
+
edgeLabelBackground: "#ffffff",
|
|
23
|
+
fontFamily: "Inter, Roboto, Arial, sans-serif"
|
|
24
|
+
},
|
|
25
|
+
suppressErrorRendering: true
|
|
26
|
+
});
|
|
27
|
+
let idCounter = 0;
|
|
28
|
+
const DEBOUNCE_MS = 600;
|
|
29
|
+
const useStyles = makeStyles((theme) => ({
|
|
30
|
+
wrapper: {
|
|
31
|
+
position: "relative",
|
|
32
|
+
margin: theme.spacing(1.5, 0),
|
|
33
|
+
borderRadius: theme.shape.borderRadius,
|
|
34
|
+
overflow: "hidden",
|
|
35
|
+
minHeight: 80
|
|
36
|
+
},
|
|
37
|
+
diagram: {
|
|
38
|
+
display: "flex",
|
|
39
|
+
justifyContent: "center",
|
|
40
|
+
padding: theme.spacing(1),
|
|
41
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
42
|
+
borderRadius: theme.shape.borderRadius,
|
|
43
|
+
backgroundColor: "#ffffff",
|
|
44
|
+
color: "#111827",
|
|
45
|
+
cursor: "zoom-in",
|
|
46
|
+
overflow: "auto",
|
|
47
|
+
transition: "filter 0.3s ease, opacity 0.3s ease",
|
|
48
|
+
"&:focus-visible": {
|
|
49
|
+
outline: `2px solid ${theme.palette.primary.main}`,
|
|
50
|
+
outlineOffset: 2
|
|
51
|
+
},
|
|
52
|
+
"& svg": {
|
|
53
|
+
maxWidth: "100%",
|
|
54
|
+
height: "auto"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
diagramLoading: {
|
|
58
|
+
filter: "blur(4px)",
|
|
59
|
+
opacity: 0.4,
|
|
60
|
+
pointerEvents: "none"
|
|
61
|
+
},
|
|
62
|
+
overlay: {
|
|
63
|
+
position: "absolute",
|
|
64
|
+
inset: 0,
|
|
65
|
+
display: "flex",
|
|
66
|
+
flexDirection: "column",
|
|
67
|
+
alignItems: "center",
|
|
68
|
+
justifyContent: "center",
|
|
69
|
+
gap: theme.spacing(1),
|
|
70
|
+
zIndex: 1,
|
|
71
|
+
// frosted glass
|
|
72
|
+
backgroundColor: theme.palette.type === "dark" ? "rgba(30, 30, 30, 0.6)" : "rgba(255, 255, 255, 0.6)",
|
|
73
|
+
backdropFilter: "blur(8px)",
|
|
74
|
+
borderRadius: theme.shape.borderRadius
|
|
75
|
+
},
|
|
76
|
+
dialogPaper: {
|
|
77
|
+
backgroundColor: theme.palette.background.default
|
|
78
|
+
},
|
|
79
|
+
dialogBody: {
|
|
80
|
+
position: "relative",
|
|
81
|
+
display: "flex",
|
|
82
|
+
alignItems: "center",
|
|
83
|
+
justifyContent: "center",
|
|
84
|
+
minHeight: "100vh",
|
|
85
|
+
padding: theme.spacing(7, 3, 3),
|
|
86
|
+
overflow: "auto"
|
|
87
|
+
},
|
|
88
|
+
closeButton: {
|
|
89
|
+
position: "fixed",
|
|
90
|
+
top: theme.spacing(1.5),
|
|
91
|
+
right: theme.spacing(1.5),
|
|
92
|
+
zIndex: 1,
|
|
93
|
+
backgroundColor: theme.palette.background.paper,
|
|
94
|
+
"&:hover": {
|
|
95
|
+
backgroundColor: theme.palette.action.hover
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
fullscreenDiagram: {
|
|
99
|
+
display: "flex",
|
|
100
|
+
alignItems: "center",
|
|
101
|
+
justifyContent: "center",
|
|
102
|
+
minWidth: "100%",
|
|
103
|
+
padding: theme.spacing(2),
|
|
104
|
+
borderRadius: theme.shape.borderRadius,
|
|
105
|
+
backgroundColor: "#ffffff",
|
|
106
|
+
color: "#111827",
|
|
107
|
+
"& svg": {
|
|
108
|
+
width: "auto",
|
|
109
|
+
height: "auto",
|
|
110
|
+
maxWidth: "calc(100vw - 48px)",
|
|
111
|
+
maxHeight: "calc(100vh - 96px)"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
error: {
|
|
115
|
+
margin: theme.spacing(1.5, 0),
|
|
116
|
+
padding: theme.spacing(1.5),
|
|
117
|
+
overflow: "auto",
|
|
118
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
119
|
+
borderRadius: theme.shape.borderRadius,
|
|
120
|
+
backgroundColor: theme.palette.type === "dark" ? theme.palette.grey[900] : theme.palette.grey[100],
|
|
121
|
+
color: theme.palette.text.primary,
|
|
122
|
+
fontSize: theme.typography.caption.fontSize
|
|
123
|
+
}
|
|
124
|
+
}));
|
|
125
|
+
function MermaidDiagram({ code }) {
|
|
126
|
+
const classes = useStyles();
|
|
127
|
+
const [svg, setSvg] = useState("");
|
|
128
|
+
const [rendering, setRendering] = useState(true);
|
|
129
|
+
const [error, setError] = useState("");
|
|
130
|
+
const [showError, setShowError] = useState(false);
|
|
131
|
+
const [open, setOpen] = useState(false);
|
|
132
|
+
const timerRef = useRef();
|
|
133
|
+
const errorTimerRef = useRef();
|
|
134
|
+
const lastRenderedCode = useRef("");
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
const trimmed = code.trim();
|
|
137
|
+
const isFirst = !svg && !error;
|
|
138
|
+
const delay = isFirst ? 0 : DEBOUNCE_MS;
|
|
139
|
+
setRendering(true);
|
|
140
|
+
setError("");
|
|
141
|
+
setShowError(false);
|
|
142
|
+
clearTimeout(timerRef.current);
|
|
143
|
+
clearTimeout(errorTimerRef.current);
|
|
144
|
+
timerRef.current = setTimeout(() => {
|
|
145
|
+
if (trimmed === lastRenderedCode.current) {
|
|
146
|
+
setRendering(false);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
let active = true;
|
|
150
|
+
const id = `mermaid-${++idCounter}`;
|
|
151
|
+
mermaid.render(id, trimmed).then(({ svg: rendered }) => {
|
|
152
|
+
if (active) {
|
|
153
|
+
setSvg(rendered);
|
|
154
|
+
setError("");
|
|
155
|
+
lastRenderedCode.current = trimmed;
|
|
156
|
+
setRendering(false);
|
|
157
|
+
}
|
|
158
|
+
}).catch((err) => {
|
|
159
|
+
if (active) {
|
|
160
|
+
if (svg) {
|
|
161
|
+
setRendering(false);
|
|
162
|
+
} else {
|
|
163
|
+
setError(err.message);
|
|
164
|
+
setRendering(false);
|
|
165
|
+
errorTimerRef.current = setTimeout(() => {
|
|
166
|
+
setShowError(true);
|
|
167
|
+
}, 1500);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
return () => {
|
|
172
|
+
active = false;
|
|
173
|
+
};
|
|
174
|
+
}, delay);
|
|
175
|
+
return () => {
|
|
176
|
+
clearTimeout(timerRef.current);
|
|
177
|
+
clearTimeout(errorTimerRef.current);
|
|
178
|
+
};
|
|
179
|
+
}, [code]);
|
|
180
|
+
const handleKeyDown = (event) => {
|
|
181
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
182
|
+
event.preventDefault();
|
|
183
|
+
setOpen(true);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
if (error && !svg && showError) {
|
|
187
|
+
return /* @__PURE__ */ jsx("pre", { className: classes.error, children: /* @__PURE__ */ jsx("code", { children: code }) });
|
|
188
|
+
}
|
|
189
|
+
const isLoading = rendering && !!svg;
|
|
190
|
+
const isInitialLoading = !svg && (rendering || error && !showError);
|
|
191
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
192
|
+
/* @__PURE__ */ jsxs("div", { className: classes.wrapper, children: [
|
|
193
|
+
isLoading && /* @__PURE__ */ jsxs("div", { className: classes.overlay, children: [
|
|
194
|
+
/* @__PURE__ */ jsx(CircularProgress, { size: 24 }),
|
|
195
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "textSecondary", children: "Rendering diagram\u2026" })
|
|
196
|
+
] }),
|
|
197
|
+
isInitialLoading && /* @__PURE__ */ jsxs("div", { className: classes.overlay, children: [
|
|
198
|
+
/* @__PURE__ */ jsx(CircularProgress, { size: 24 }),
|
|
199
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "textSecondary", children: "Rendering diagram\u2026" })
|
|
200
|
+
] }),
|
|
201
|
+
svg && /* @__PURE__ */ jsx(Tooltip, { title: "Open diagram fullscreen", children: /* @__PURE__ */ jsx(
|
|
202
|
+
"div",
|
|
203
|
+
{
|
|
204
|
+
className: `${classes.diagram} ${isLoading ? classes.diagramLoading : ""}`,
|
|
205
|
+
dangerouslySetInnerHTML: { __html: svg },
|
|
206
|
+
role: "button",
|
|
207
|
+
tabIndex: 0,
|
|
208
|
+
"aria-label": "Open Mermaid diagram fullscreen",
|
|
209
|
+
onClick: () => !isLoading && setOpen(true),
|
|
210
|
+
onKeyDown: handleKeyDown
|
|
211
|
+
}
|
|
212
|
+
) })
|
|
213
|
+
] }),
|
|
214
|
+
/* @__PURE__ */ jsx(
|
|
215
|
+
Dialog,
|
|
216
|
+
{
|
|
217
|
+
fullScreen: true,
|
|
218
|
+
open,
|
|
219
|
+
onClose: () => setOpen(false),
|
|
220
|
+
PaperProps: { className: classes.dialogPaper },
|
|
221
|
+
children: /* @__PURE__ */ jsxs("div", { className: classes.dialogBody, children: [
|
|
222
|
+
/* @__PURE__ */ jsx(Tooltip, { title: "Close", children: /* @__PURE__ */ jsx(
|
|
223
|
+
IconButton,
|
|
224
|
+
{
|
|
225
|
+
className: classes.closeButton,
|
|
226
|
+
"aria-label": "Close fullscreen diagram",
|
|
227
|
+
onClick: () => setOpen(false),
|
|
228
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
229
|
+
}
|
|
230
|
+
) }),
|
|
231
|
+
/* @__PURE__ */ jsx(
|
|
232
|
+
"div",
|
|
233
|
+
{
|
|
234
|
+
className: classes.fullscreenDiagram,
|
|
235
|
+
dangerouslySetInnerHTML: { __html: svg }
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
] })
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
] });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export { MermaidDiagram };
|
|
245
|
+
//# sourceMappingURL=MermaidDiagram.esm.js.map
|