@athenaintel/react 0.9.7 → 0.9.9

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/README.md CHANGED
@@ -60,14 +60,49 @@ import { themes } from '@athenaintel/react';
60
60
 
61
61
  Preset themes: `light`, `dark`, `midnight`, `warm`, `purple`, `green`.
62
62
 
63
+ For solid custom surfaces like `primary`, `userBubble`, and `assistantBubble`, the SDK now auto-derives a readable black/white foreground when the paired foreground token is omitted. Explicit foreground tokens are still preferred when you need exact brand colors.
64
+
63
65
  ## Key Components
64
66
 
65
67
  - **`<AthenaProvider>`** — Runtime, auth, theming, and configuration
66
68
  - **`<AthenaChat>`** — Full chat UI with composer, messages, and tool rendering
69
+ - **`<AssetRenderer>`** — Render a single Athena asset anywhere with just an `assetId`
67
70
  - **`<AthenaLayout>`** — Split-pane layout with asset panel
68
71
  - **`<ThreadList>`** — Conversation history sidebar
69
72
  - **`Toolkits`** — Constants for all available backend toolkits
70
73
 
74
+ ## Assets
75
+
76
+ Use `AssetRenderer` when you want to embed a single asset outside the built-in side panel. It only needs `assetId`; the SDK resolves the correct viewer from that asset automatically.
77
+
78
+ ```tsx
79
+ import { AssetRenderer } from '@athenaintel/react';
80
+
81
+ function PreviewPane() {
82
+ return <AssetRenderer assetId="asset_123" title="Quarterly report" className="min-h-[32rem]" />;
83
+ }
84
+ ```
85
+
86
+ ## Composer Hooks
87
+
88
+ Use `useAppendToComposer()` when you want to prefill a draft without sending it.
89
+
90
+ Use `useSendMessage()` for workflow buttons, sidebar shortcuts, and other UI that lives outside `<AthenaChat>` but still needs to submit a prompt.
91
+
92
+ ```tsx
93
+ import { useSendMessage } from '@athenaintel/react';
94
+
95
+ function WorkflowButton() {
96
+ const sendMessage = useSendMessage();
97
+
98
+ return (
99
+ <button onClick={() => void sendMessage('Run the quarterly workflow')}>
100
+ Run workflow
101
+ </button>
102
+ );
103
+ }
104
+ ```
105
+
71
106
  ## License
72
107
 
73
108
  Proprietary. For licensed enterprise customers only.
package/dist/index.cjs CHANGED
@@ -11934,7 +11934,7 @@ const oppositeSideMap = {
11934
11934
  bottom: "top",
11935
11935
  top: "bottom"
11936
11936
  };
11937
- function clamp(start, value, end) {
11937
+ function clamp$1(start, value, end) {
11938
11938
  return max(start, min(value, end));
11939
11939
  }
11940
11940
  function evaluate(value, param) {
@@ -12291,7 +12291,7 @@ const arrow$4 = (options) => ({
12291
12291
  const min$1 = minPadding;
12292
12292
  const max2 = clientSize - arrowDimensions[length] - maxPadding;
12293
12293
  const center = clientSize / 2 - arrowDimensions[length] / 2 + centerToReference;
12294
- const offset2 = clamp(min$1, center, max2);
12294
+ const offset2 = clamp$1(min$1, center, max2);
12295
12295
  const shouldAddOffset = !middlewareData.arrow && getAlignment(placement) != null && center !== offset2 && rects.reference[length] / 2 - (center < min$1 ? minPadding : maxPadding) - arrowDimensions[length] / 2 < 0;
12296
12296
  const alignmentOffset = shouldAddOffset ? center < min$1 ? center - min$1 : center - max2 : 0;
12297
12297
  return {
@@ -12590,14 +12590,14 @@ const shift$3 = function(options) {
12590
12590
  const maxSide = mainAxis === "y" ? "bottom" : "right";
12591
12591
  const min2 = mainAxisCoord + overflow[minSide];
12592
12592
  const max2 = mainAxisCoord - overflow[maxSide];
12593
- mainAxisCoord = clamp(min2, mainAxisCoord, max2);
12593
+ mainAxisCoord = clamp$1(min2, mainAxisCoord, max2);
12594
12594
  }
12595
12595
  if (checkCrossAxis) {
12596
12596
  const minSide = crossAxis === "y" ? "top" : "left";
12597
12597
  const maxSide = crossAxis === "y" ? "bottom" : "right";
12598
12598
  const min2 = crossAxisCoord + overflow[minSide];
12599
12599
  const max2 = crossAxisCoord - overflow[maxSide];
12600
- crossAxisCoord = clamp(min2, crossAxisCoord, max2);
12600
+ crossAxisCoord = clamp$1(min2, crossAxisCoord, max2);
12601
12601
  }
12602
12602
  const limitedCoords = limiter.fn({
12603
12603
  ...state,
@@ -16491,8 +16491,16 @@ const DEFAULT_ATHENA_ENVIRONMENT = "production";
16491
16491
  const DEFAULT_API_URL = ATHENA_ENVIRONMENT_URLS.production.apiUrl;
16492
16492
  const DEFAULT_BACKEND_URL = ATHENA_ENVIRONMENT_URLS.production.backendUrl;
16493
16493
  const DEFAULT_APP_URL = ATHENA_ENVIRONMENT_URLS.production.appUrl;
16494
- const SPACES_PATHNAME = "/dashboard/spaces/";
16494
+ const MARATHON_APP_PORT = "8082";
16495
+ const SPACES_PATHNAME = "dashboard/spaces/";
16495
16496
  const normalizeBaseUrl = (url) => url.replace(/\/+$/, "");
16497
+ const createUrlWithFallback = (url, fallbackUrl) => {
16498
+ try {
16499
+ return new URL(`${normalizeBaseUrl(url)}/`);
16500
+ } catch {
16501
+ return new URL(`${normalizeBaseUrl(fallbackUrl)}/`);
16502
+ }
16503
+ };
16496
16504
  const getHostname = (value) => {
16497
16505
  if (!value) return null;
16498
16506
  try {
@@ -16514,7 +16522,7 @@ const deriveWorkspaceAppUrl = ({
16514
16522
  if (!((_a2 = match2 == null ? void 0 : match2.groups) == null ? void 0 : _a2.prefix) || !match2.groups.domain) {
16515
16523
  return null;
16516
16524
  }
16517
- return `${parsedUrl.protocol}//${match2.groups.prefix}--8082.${match2.groups.domain}`;
16525
+ return `${parsedUrl.protocol}//${match2.groups.prefix}--${MARATHON_APP_PORT}.${match2.groups.domain}`;
16518
16526
  } catch {
16519
16527
  return null;
16520
16528
  }
@@ -16563,7 +16571,7 @@ function createAthenaSpacesUrl({
16563
16571
  assetIds,
16564
16572
  sessionId
16565
16573
  }) {
16566
- const url = new URL(SPACES_PATHNAME, `${normalizeBaseUrl(appUrl)}/`);
16574
+ const url = new URL(SPACES_PATHNAME, createUrlWithFallback(appUrl, DEFAULT_APP_URL));
16567
16575
  if (assetIds) {
16568
16576
  const normalizedAssetIds = (Array.isArray(assetIds) ? assetIds : [assetIds]).map((assetId) => assetId.trim()).filter((assetId) => assetId.length > 0);
16569
16577
  if (normalizedAssetIds.length > 0) {
@@ -20625,7 +20633,9 @@ const toolMessageSchema = objectType({
20625
20633
  type: literalType("tool"),
20626
20634
  content: toolMessageContentSchema,
20627
20635
  tool_call_id: stringType(),
20628
- name: stringType().nullable(),
20636
+ // Some backend/tool result flows omit the tool name entirely.
20637
+ // The converter already treats toolName as optional, so accept that shape.
20638
+ name: nullableToOptionalString,
20629
20639
  artifact: anyType().optional(),
20630
20640
  status: enumType(["success", "error"]),
20631
20641
  additional_kwargs: recordType(stringType(), unknownType()).optional()
@@ -20792,7 +20802,9 @@ async function listThreads(backendUrl, auth, opts = {}) {
20792
20802
  body: JSON.stringify({
20793
20803
  limit: opts.limit ?? 50,
20794
20804
  offset: opts.offset ?? 0,
20795
- exclude_triggered: opts.exclude_triggered ?? true
20805
+ // Default to the full recent-conversations set unless a caller
20806
+ // explicitly asks to hide triggered/background sessions.
20807
+ exclude_triggered: opts.exclude_triggered ?? false
20796
20808
  })
20797
20809
  });
20798
20810
  if (!res.ok) {
@@ -24466,6 +24478,163 @@ const ThreadListRefreshContext = React.createContext(null);
24466
24478
  function useRefreshThreadList() {
24467
24479
  return React.useContext(ThreadListRefreshContext);
24468
24480
  }
24481
+ const WHITE = "#ffffff";
24482
+ const BLACK = "#000000";
24483
+ const CONTRAST_PAIRS = {
24484
+ primary: "primaryForeground",
24485
+ secondary: "secondaryForeground",
24486
+ accent: "accentForeground",
24487
+ card: "cardForeground",
24488
+ popover: "popoverForeground",
24489
+ userBubble: "userBubbleForeground",
24490
+ assistantBubble: "assistantForeground"
24491
+ };
24492
+ const clamp = (value, min2 = 0, max2 = 1) => Math.min(max2, Math.max(min2, value));
24493
+ const parseNumber = (raw) => {
24494
+ const value = Number.parseFloat(raw);
24495
+ return Number.isFinite(value) ? value : null;
24496
+ };
24497
+ const parsePercentLike = (raw, max2 = 1) => {
24498
+ const value = parseNumber(raw);
24499
+ if (value == null) return null;
24500
+ return raw.trim().endsWith("%") ? value / 100 * max2 : value;
24501
+ };
24502
+ const parseHexColor = (input) => {
24503
+ const hex = input.slice(1).trim();
24504
+ if (![3, 4, 6, 8].includes(hex.length)) return null;
24505
+ const expanded = hex.length <= 4 ? hex.split("").map((char) => `${char}${char}`).join("") : hex;
24506
+ const channels = expanded.match(/.{2}/g);
24507
+ if (!channels) return null;
24508
+ const [r2, g, b, alpha2 = 255] = channels.map(
24509
+ (channel) => Number.parseInt(channel, 16)
24510
+ );
24511
+ return {
24512
+ r: clamp(r2 / 255),
24513
+ g: clamp(g / 255),
24514
+ b: clamp(b / 255),
24515
+ alpha: clamp(alpha2 / 255)
24516
+ };
24517
+ };
24518
+ const splitColorArgs = (input) => input.replace(/^[^(]+\(/, "").replace(/\)$/, "").replace(/\//g, " ").replace(/,/g, " ").trim().split(/\s+/).filter(Boolean);
24519
+ const parseRgbColor = (input) => {
24520
+ const parts = splitColorArgs(input);
24521
+ if (parts.length < 3) return null;
24522
+ const r2 = parsePercentLike(parts[0], 255);
24523
+ const g = parsePercentLike(parts[1], 255);
24524
+ const b = parsePercentLike(parts[2], 255);
24525
+ const alpha2 = parts[3] ? parsePercentLike(parts[3], 1) : 1;
24526
+ if ([r2, g, b, alpha2].some((value) => value == null)) return null;
24527
+ return {
24528
+ r: clamp(r2 / 255),
24529
+ g: clamp(g / 255),
24530
+ b: clamp(b / 255),
24531
+ alpha: clamp(alpha2)
24532
+ };
24533
+ };
24534
+ const hueToRgb = (p, q, t) => {
24535
+ let value = t;
24536
+ if (value < 0) value += 1;
24537
+ if (value > 1) value -= 1;
24538
+ if (value < 1 / 6) return p + (q - p) * 6 * value;
24539
+ if (value < 1 / 2) return q;
24540
+ if (value < 2 / 3) return p + (q - p) * (2 / 3 - value) * 6;
24541
+ return p;
24542
+ };
24543
+ const parseHslColor = (input) => {
24544
+ const parts = splitColorArgs(input);
24545
+ if (parts.length < 3) return null;
24546
+ const hue = parseNumber(parts[0]);
24547
+ const saturation = parsePercentLike(parts[1], 1);
24548
+ const lightness = parsePercentLike(parts[2], 1);
24549
+ const alpha2 = parts[3] ? parsePercentLike(parts[3], 1) : 1;
24550
+ if ([hue, saturation, lightness, alpha2].some((value) => value == null)) {
24551
+ return null;
24552
+ }
24553
+ const h2 = (hue % 360 + 360) % 360 / 360;
24554
+ const s = clamp(saturation);
24555
+ const l = clamp(lightness);
24556
+ if (s === 0) {
24557
+ return { r: l, g: l, b: l, alpha: clamp(alpha2) };
24558
+ }
24559
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
24560
+ const p = 2 * l - q;
24561
+ return {
24562
+ r: clamp(hueToRgb(p, q, h2 + 1 / 3)),
24563
+ g: clamp(hueToRgb(p, q, h2)),
24564
+ b: clamp(hueToRgb(p, q, h2 - 1 / 3)),
24565
+ alpha: clamp(alpha2)
24566
+ };
24567
+ };
24568
+ const parseOklchColor = (input) => {
24569
+ const parts = splitColorArgs(input);
24570
+ if (parts.length < 3) return null;
24571
+ const lightness = parsePercentLike(parts[0], 1);
24572
+ const chroma = parseNumber(parts[1]);
24573
+ const hue = parseNumber(parts[2]);
24574
+ const alpha2 = parts[3] ? parsePercentLike(parts[3], 1) : 1;
24575
+ if ([lightness, chroma, hue, alpha2].some((value) => value == null)) {
24576
+ return null;
24577
+ }
24578
+ const l = clamp(lightness);
24579
+ const c = Math.max(0, chroma);
24580
+ const h2 = hue * Math.PI / 180;
24581
+ const a = c * Math.cos(h2);
24582
+ const b = c * Math.sin(h2);
24583
+ const lPrime = l + 0.3963377774 * a + 0.2158037573 * b;
24584
+ const mPrime = l - 0.1055613458 * a - 0.0638541728 * b;
24585
+ const sPrime = l - 0.0894841775 * a - 1.291485548 * b;
24586
+ const lLinear = lPrime ** 3;
24587
+ const mLinear = mPrime ** 3;
24588
+ const sLinear = sPrime ** 3;
24589
+ return {
24590
+ r: clamp(
24591
+ 4.0767416621 * lLinear - 3.3077115913 * mLinear + 0.2309699292 * sLinear
24592
+ ),
24593
+ g: clamp(
24594
+ -1.2684380046 * lLinear + 2.6097574011 * mLinear - 0.3413193965 * sLinear
24595
+ ),
24596
+ b: clamp(
24597
+ -0.0041960863 * lLinear - 0.7034186147 * mLinear + 1.707614701 * sLinear
24598
+ ),
24599
+ alpha: clamp(alpha2)
24600
+ };
24601
+ };
24602
+ const parseColor = (input) => {
24603
+ const value = input.trim().toLowerCase();
24604
+ if (value.startsWith("#")) return parseHexColor(value);
24605
+ if (value.startsWith("rgb")) return parseRgbColor(value);
24606
+ if (value.startsWith("hsl")) return parseHslColor(value);
24607
+ if (value.startsWith("oklch")) return parseOklchColor(value);
24608
+ return null;
24609
+ };
24610
+ const srgbToLinear = (value) => value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
24611
+ const relativeLuminance = (color) => 0.2126 * srgbToLinear(color.r) + 0.7152 * srgbToLinear(color.g) + 0.0722 * srgbToLinear(color.b);
24612
+ const contrastRatio = (luminanceA, luminanceB) => {
24613
+ const lighter = Math.max(luminanceA, luminanceB);
24614
+ const darker = Math.min(luminanceA, luminanceB);
24615
+ return (lighter + 0.05) / (darker + 0.05);
24616
+ };
24617
+ const getReadableForeground = (background) => {
24618
+ const parsed = parseColor(background);
24619
+ if (!parsed || parsed.alpha < 1) return null;
24620
+ const backgroundLuminance = relativeLuminance(parsed);
24621
+ const whiteContrast = contrastRatio(backgroundLuminance, 1);
24622
+ const blackContrast = contrastRatio(backgroundLuminance, 0);
24623
+ return whiteContrast > blackContrast ? WHITE : BLACK;
24624
+ };
24625
+ const withAutoContrastTheme = (theme) => {
24626
+ const resolvedTheme = { ...theme };
24627
+ for (const backgroundKey of Object.keys(CONTRAST_PAIRS)) {
24628
+ const foregroundKey = CONTRAST_PAIRS[backgroundKey];
24629
+ const backgroundValue = theme[backgroundKey];
24630
+ if (theme[foregroundKey] || !backgroundValue) continue;
24631
+ const derivedForeground = getReadableForeground(backgroundValue);
24632
+ if (derivedForeground) {
24633
+ resolvedTheme[foregroundKey] = derivedForeground;
24634
+ }
24635
+ }
24636
+ return resolvedTheme;
24637
+ };
24469
24638
  const THEME_TO_CSS = {
24470
24639
  primary: "--primary",
24471
24640
  primaryForeground: "--primary-foreground",
@@ -24502,7 +24671,8 @@ const THEME_TO_CSS = {
24502
24671
  };
24503
24672
  function themeToStyleVars(theme) {
24504
24673
  const vars = {};
24505
- for (const [key, value] of Object.entries(theme)) {
24674
+ const resolvedTheme = withAutoContrastTheme(theme);
24675
+ for (const [key, value] of Object.entries(resolvedTheme)) {
24506
24676
  if (value != null && THEME_TO_CSS[key]) {
24507
24677
  vars[THEME_TO_CSS[key]] = value;
24508
24678
  }
@@ -24823,9 +24993,9 @@ function AthenaProvider({
24823
24993
  const configuredAppUrl = (config2 == null ? void 0 : config2.appUrl) ?? appUrl;
24824
24994
  const bridge = useParentBridge();
24825
24995
  const effectiveToken = configuredToken !== void 0 ? configuredToken : bridge.token;
24826
- const effectiveApiUrl = configuredApiUrl ?? bridge.apiUrl ?? environmentUrls.apiUrl ?? DEFAULT_API_URL;
24827
- const effectiveBackendUrl = configuredBackendUrl ?? bridge.backendUrl ?? environmentUrls.backendUrl ?? DEFAULT_BACKEND_URL;
24828
- const effectiveAppUrl = configuredAppUrl ?? bridge.appUrl ?? deriveAthenaAppUrl({ apiUrl: effectiveApiUrl, backendUrl: effectiveBackendUrl }) ?? environmentUrls.appUrl ?? DEFAULT_APP_URL;
24996
+ const effectiveApiUrl = configuredApiUrl ?? bridge.apiUrl ?? environmentUrls.apiUrl;
24997
+ const effectiveBackendUrl = configuredBackendUrl ?? bridge.backendUrl ?? environmentUrls.backendUrl;
24998
+ const effectiveAppUrl = configuredAppUrl ?? bridge.appUrl ?? deriveAthenaAppUrl({ apiUrl: effectiveApiUrl, backendUrl: effectiveBackendUrl }) ?? environmentUrls.appUrl;
24829
24999
  if (!bridge.ready) {
24830
25000
  return null;
24831
25001
  }
@@ -60786,7 +60956,7 @@ const TiptapComposer = ({ tools = [] }) => {
60786
60956
  composerRuntime.send();
60787
60957
  }
60788
60958
  editor2.commands.clearContent();
60789
- }, [aui, composerRuntime, clearAttachments, clearQuote]);
60959
+ }, [aui, composerRuntime, clearAttachments, clearQuote, appUrl]);
60790
60960
  const handleSubmitRef = React.useRef(handleSubmit);
60791
60961
  handleSubmitRef.current = handleSubmit;
60792
60962
  const editor = useEditor({
@@ -63958,6 +64128,23 @@ const ComposerDropZone = ({
63958
64128
  }
63959
64129
  );
63960
64130
  };
64131
+ function useSendMessage() {
64132
+ const aui = useAui();
64133
+ return React.useCallback(
64134
+ async (text2, options) => {
64135
+ if (!text2.trim()) return;
64136
+ const composer = aui.composer();
64137
+ const currentText = composer.getState().text;
64138
+ const shouldReplace = (options == null ? void 0 : options.replace) ?? true;
64139
+ composer.setText(
64140
+ shouldReplace || !currentText ? text2 : `${currentText}
64141
+ ${text2}`
64142
+ );
64143
+ await composer.send();
64144
+ },
64145
+ [aui]
64146
+ );
64147
+ }
63961
64148
  const EMPTY_MENTION_TOOLS = [];
63962
64149
  function QuotePostMessageBridge() {
63963
64150
  useQuoteFromPostMessage();
@@ -63989,14 +64176,11 @@ const SuggestionCard = ({
63989
64176
  suggestion,
63990
64177
  index: index2
63991
64178
  }) => {
63992
- const aui = useAui();
63993
64179
  const Icon2 = suggestion.icon;
64180
+ const sendMessage = useSendMessage();
63994
64181
  const handleClick2 = React.useCallback(() => {
63995
- aui.thread().append({
63996
- role: "user",
63997
- content: [{ type: "text", text: suggestion.prompt }]
63998
- });
63999
- }, [aui, suggestion.prompt]);
64182
+ void sendMessage(suggestion.prompt);
64183
+ }, [sendMessage, suggestion.prompt]);
64000
64184
  return /* @__PURE__ */ jsxRuntime.jsxs(
64001
64185
  "button",
64002
64186
  {
@@ -64143,7 +64327,7 @@ const ComposerSendWithQuote = () => {
64143
64327
  editor == null ? void 0 : editor.clear();
64144
64328
  clearQuote();
64145
64329
  clearAttachments();
64146
- }, [aui, quote, attachments, isUploading, clearQuote, clearAttachments, editorRef]);
64330
+ }, [aui, quote, attachments, isUploading, clearQuote, clearAttachments, editorRef, appUrl]);
64147
64331
  if (hasExtras) {
64148
64332
  return /* @__PURE__ */ jsxRuntime.jsx(
64149
64333
  TooltipIconButton,
@@ -64337,6 +64521,75 @@ function useAssetEmbed(assetId, options = {
64337
64521
  }, [assetId, readOnly, expiresInSeconds, backendUrl, apiKey, token]);
64338
64522
  return { embedUrl, isLoading, error: error2 };
64339
64523
  }
64524
+ const AssetRenderer = ({
64525
+ assetId,
64526
+ title,
64527
+ className,
64528
+ loadingFallback,
64529
+ emptyFallback = null,
64530
+ errorFallback,
64531
+ readOnly,
64532
+ expiresInSeconds
64533
+ }) => {
64534
+ const { backendUrl, apiKey, token } = useAthenaConfig();
64535
+ const { embedUrl, isLoading, error: error2 } = useAssetEmbed(assetId, {
64536
+ backendUrl,
64537
+ apiKey,
64538
+ token,
64539
+ readOnly,
64540
+ expiresInSeconds
64541
+ });
64542
+ if (!assetId) {
64543
+ return emptyFallback ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("h-full w-full", className), children: emptyFallback }) : null;
64544
+ }
64545
+ if (isLoading) {
64546
+ return /* @__PURE__ */ jsxRuntime.jsx(
64547
+ "div",
64548
+ {
64549
+ className: cn(
64550
+ "flex h-full w-full items-center justify-center",
64551
+ className
64552
+ ),
64553
+ children: loadingFallback ?? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
64554
+ /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { className: "mx-auto size-6 animate-spin text-muted-foreground" }),
64555
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: "Loading..." })
64556
+ ] })
64557
+ }
64558
+ );
64559
+ }
64560
+ if (error2) {
64561
+ const resolvedErrorFallback = typeof errorFallback === "function" ? errorFallback(error2) : errorFallback;
64562
+ return /* @__PURE__ */ jsxRuntime.jsx(
64563
+ "div",
64564
+ {
64565
+ className: cn(
64566
+ "flex h-full w-full items-center justify-center p-4",
64567
+ className
64568
+ ),
64569
+ children: resolvedErrorFallback ?? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-sm text-center", children: [
64570
+ /* @__PURE__ */ jsxRuntime.jsx(CircleAlert, { className: "mx-auto size-5 text-red-400" }),
64571
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1.5 text-xs font-medium text-foreground", children: "Failed to load" }),
64572
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 line-clamp-2 break-words text-[10px] text-muted-foreground", children: error2 })
64573
+ ] })
64574
+ }
64575
+ );
64576
+ }
64577
+ if (!embedUrl) {
64578
+ return emptyFallback ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("h-full w-full", className), children: emptyFallback }) : null;
64579
+ }
64580
+ return /* @__PURE__ */ jsxRuntime.jsx(
64581
+ "iframe",
64582
+ {
64583
+ src: embedUrl,
64584
+ width: "100%",
64585
+ height: "100%",
64586
+ frameBorder: "0",
64587
+ allow: "fullscreen",
64588
+ title: title ?? `Asset ${assetId}`,
64589
+ className: cn("h-full w-full", className)
64590
+ }
64591
+ );
64592
+ };
64340
64593
  const ASSET_TYPE_CONFIG = {
64341
64594
  presentation: { icon: Presentation, label: "Presentation" },
64342
64595
  spreadsheet: { icon: FileSpreadsheet, label: "Spreadsheet" },
@@ -64346,38 +64599,7 @@ const ASSET_TYPE_CONFIG = {
64346
64599
  };
64347
64600
  const AssetIframe = React.memo(
64348
64601
  ({ tabId, tabName }) => {
64349
- const { backendUrl, apiKey, token } = useAthenaConfig();
64350
- const { embedUrl, isLoading, error: error2 } = useAssetEmbed(tabId, {
64351
- backendUrl,
64352
- apiKey,
64353
- token
64354
- });
64355
- if (isLoading) {
64356
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
64357
- /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { className: "mx-auto size-6 animate-spin text-muted-foreground" }),
64358
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: "Loading..." })
64359
- ] }) });
64360
- }
64361
- if (error2) {
64362
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-sm text-center", children: [
64363
- /* @__PURE__ */ jsxRuntime.jsx(CircleAlert, { className: "mx-auto size-5 text-red-400" }),
64364
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1.5 text-xs font-medium text-foreground", children: "Failed to load" }),
64365
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 line-clamp-2 break-words text-[10px] text-muted-foreground", children: error2 })
64366
- ] }) });
64367
- }
64368
- if (!embedUrl) return null;
64369
- return /* @__PURE__ */ jsxRuntime.jsx(
64370
- "iframe",
64371
- {
64372
- src: embedUrl,
64373
- width: "100%",
64374
- height: "100%",
64375
- frameBorder: "0",
64376
- allow: "fullscreen",
64377
- title: tabName ?? `Asset ${tabId}`,
64378
- className: "h-full w-full"
64379
- }
64380
- );
64602
+ return /* @__PURE__ */ jsxRuntime.jsx(AssetRenderer, { assetId: tabId, title: tabName });
64381
64603
  }
64382
64604
  );
64383
64605
  AssetIframe.displayName = "AssetIframe";
@@ -64718,6 +64940,7 @@ const Toolkits = {
64718
64940
  };
64719
64941
  exports.AppendDocumentToolUI = AppendDocumentToolUI;
64720
64942
  exports.AssetPanel = AssetPanel;
64943
+ exports.AssetRenderer = AssetRenderer;
64721
64944
  exports.AthenaChat = AthenaChat;
64722
64945
  exports.AthenaLayout = AthenaLayout;
64723
64946
  exports.AthenaProvider = AthenaProvider;
@@ -64785,4 +65008,5 @@ exports.useParentAuth = useParentAuth;
64785
65008
  exports.useParentBridge = useParentBridge;
64786
65009
  exports.useQuote = useQuote;
64787
65010
  exports.useRefreshThreadList = useRefreshThreadList;
65011
+ exports.useSendMessage = useSendMessage;
64788
65012
  //# sourceMappingURL=index.cjs.map