@athenaintel/react 0.10.27 → 0.10.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -14156,7 +14156,7 @@ const useAssetPanelStore = create()(
14156
14156
  ...t,
14157
14157
  name: meta.name ?? t.name,
14158
14158
  type: meta.type ?? t.type,
14159
- slideNumber: meta.slideNumber ?? t.slideNumber
14159
+ slideNumber: "slideNumber" in meta ? meta.slideNumber : t.slideNumber
14160
14160
  } : t
14161
14161
  ) : s.tabs;
14162
14162
  return { isOpen: true, tabs, activeTabId: assetId };
@@ -50047,18 +50047,6 @@ function AssetToolCard({
50047
50047
  const assetId = extractAssetId$1(result);
50048
50048
  const title = extractTitle(argsText, result);
50049
50049
  const assetType = toolMetaToAssetType(toolName);
50050
- const wasCompleteAtMount = React.useRef(isComplete);
50051
- React.useEffect(() => {
50052
- if (isComplete && !isCancelled && assetId && !wasCompleteAtMount.current) {
50053
- const store = useAssetPanelStore.getState();
50054
- if (store.markAutoOpened(assetId)) {
50055
- store.openAsset(assetId, {
50056
- name: title ?? void 0,
50057
- type: assetType
50058
- });
50059
- }
50060
- }
50061
- }, [isComplete, isCancelled, assetId, title, assetType]);
50062
50050
  const success = isComplete && isResultSuccess(result);
50063
50051
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(
50064
50052
  "aui-tool-fallback-root my-3 w-full rounded-xl border border-border/60 bg-background py-2.5 shadow-sm",
@@ -50620,6 +50608,32 @@ function normalizeResult(result) {
50620
50608
  function truncate(text2, max2) {
50621
50609
  return text2.length > max2 ? `${text2.slice(0, max2)}...` : text2;
50622
50610
  }
50611
+ function isSdkToolDebugEnabled() {
50612
+ var _a2, _b;
50613
+ try {
50614
+ const hostname = (_a2 = globalThis.location) == null ? void 0 : _a2.hostname;
50615
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1") {
50616
+ return true;
50617
+ }
50618
+ return ((_b = globalThis.localStorage) == null ? void 0 : _b.getItem("athena:sdk:debug")) === "1";
50619
+ } catch {
50620
+ return false;
50621
+ }
50622
+ }
50623
+ function stringifyDebugPayload(payload) {
50624
+ return JSON.stringify(payload, (_key, value) => {
50625
+ if (typeof value === "string" && value.length > 800) {
50626
+ return `${value.slice(0, 800)}... [truncated ${value.length - 800} chars]`;
50627
+ }
50628
+ return value;
50629
+ });
50630
+ }
50631
+ function logDebugPayload(label, payload) {
50632
+ if (!isSdkToolDebugEnabled()) {
50633
+ return;
50634
+ }
50635
+ console.info(`${label} ${stringifyDebugPayload(payload)}`);
50636
+ }
50623
50637
  function formatToolName(name) {
50624
50638
  return name.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
50625
50639
  }
@@ -50930,6 +50944,20 @@ function getNumberField(source, keys2) {
50930
50944
  }
50931
50945
  return null;
50932
50946
  }
50947
+ const PRESENTATION_CODE_SLIDE_NUMBER_KEYS = [
50948
+ "slideNumber",
50949
+ "slide_number",
50950
+ "targetSlideNumber",
50951
+ "target_slide_number",
50952
+ "targetSlide",
50953
+ "target_slide"
50954
+ ];
50955
+ const PRESENTATION_CODE_SLIDE_INDEX_KEYS = [
50956
+ "slideIndex",
50957
+ "slide_index",
50958
+ "targetSlideIndex",
50959
+ "target_slide_index"
50960
+ ];
50933
50961
  function getStudioAssetId({
50934
50962
  args,
50935
50963
  result
@@ -50937,41 +50965,42 @@ function getStudioAssetId({
50937
50965
  const id = getStringField(result, ["asset_id", "assetId", "id"]) ?? getStringField(args, ["asset_id", "assetId", "id"]);
50938
50966
  return (id == null ? void 0 : id.startsWith("asset_")) ? id : null;
50939
50967
  }
50940
- function getSlideNumber(args) {
50941
- const explicit = getNumberField(args, ["slideNumber", "slide_number"]);
50968
+ function getPresentationCodeDeckId(code2) {
50969
+ const match2 = code2.match(/\bdeck_id\s*=\s*["']([^"']+)["']/);
50970
+ const deckId = match2 == null ? void 0 : match2[1];
50971
+ return (deckId == null ? void 0 : deckId.startsWith("asset_")) ? deckId : null;
50972
+ }
50973
+ function getPresentationCodeSlideNumberFromFields(source) {
50974
+ const explicit = getNumberField(source, PRESENTATION_CODE_SLIDE_NUMBER_KEYS);
50942
50975
  if (explicit != null && Number.isInteger(explicit) && explicit >= 1) {
50943
50976
  return explicit;
50944
50977
  }
50945
- const zeroBasedIndex = getNumberField(args, ["index", "slideIndex", "slide_index"]);
50978
+ const zeroBasedIndex = getNumberField(source, PRESENTATION_CODE_SLIDE_INDEX_KEYS);
50946
50979
  if (zeroBasedIndex != null && Number.isInteger(zeroBasedIndex) && zeroBasedIndex >= 0) {
50947
50980
  return zeroBasedIndex + 1;
50948
50981
  }
50949
50982
  return void 0;
50950
50983
  }
50951
- const completedStudioToolEffects = /* @__PURE__ */ new Set();
50952
- const MAX_COMPLETED_STUDIO_TOOL_EFFECTS = 500;
50953
- function markStudioToolEffect(key) {
50954
- if (completedStudioToolEffects.has(key)) {
50955
- return false;
50956
- }
50957
- if (completedStudioToolEffects.size >= MAX_COMPLETED_STUDIO_TOOL_EFFECTS) {
50958
- const oldest = completedStudioToolEffects.values().next().value;
50959
- if (oldest) completedStudioToolEffects.delete(oldest);
50984
+ function getPresentationCodeSlideNumberFromText(text2) {
50985
+ const match2 = text2 == null ? void 0 : text2.match(/\bslide\s*(?:#|number\s*)?(\d+)\b/i);
50986
+ if (!match2) {
50987
+ return void 0;
50960
50988
  }
50961
- completedStudioToolEffects.add(key);
50962
- return true;
50989
+ const slideNumber = Number.parseInt(match2[1] ?? "", 10);
50990
+ return Number.isInteger(slideNumber) && slideNumber >= 1 ? slideNumber : void 0;
50963
50991
  }
50964
- function dispatchPresentationNavigate(assetId, slideNumber) {
50965
- const targetSlide = slideNumber;
50966
- if (typeof targetSlide !== "number" || !Number.isInteger(targetSlide) || targetSlide < 1) {
50967
- return;
50968
- }
50969
- window.dispatchEvent(
50970
- new CustomEvent("pptx-studio-navigate", {
50971
- detail: { assetId, slideNumber: targetSlide }
50972
- })
50992
+ function getPresentationCodeSlideNumber({
50993
+ args,
50994
+ result
50995
+ }) {
50996
+ return getPresentationCodeSlideNumberFromFields(args) ?? getPresentationCodeSlideNumberFromFields(result) ?? getPresentationCodeSlideNumberFromText(
50997
+ getStringField(args, ["summary", "title", "description"])
50973
50998
  );
50974
50999
  }
51000
+ function getAddSlideNumber(args) {
51001
+ const index2 = getNumberField(args, ["index"]);
51002
+ return index2 != null && Number.isInteger(index2) && index2 >= 0 ? index2 + 1 : void 0;
51003
+ }
50975
51004
  function CreateAssetToolUIImpl({
50976
51005
  icon: Icon2,
50977
51006
  assetType,
@@ -50992,18 +51021,6 @@ function CreateAssetToolUIImpl({
50992
51021
  const isCancelled = (status == null ? void 0 : status.type) === "incomplete" && status.reason === "cancelled";
50993
51022
  const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? status.error : null;
50994
51023
  const openAsset = useAssetPanelStore((s) => s.openAsset);
50995
- const wasCompleteAtMount = React.useRef(isComplete);
50996
- React.useEffect(() => {
50997
- if (isComplete && !isCancelled && assetId && !wasCompleteAtMount.current) {
50998
- const store = useAssetPanelStore.getState();
50999
- if (store.markAutoOpened(assetId)) {
51000
- store.openAsset(assetId, {
51001
- name: createdName || name || void 0,
51002
- type: assetType
51003
- });
51004
- }
51005
- }
51006
- }, [isComplete, isCancelled, assetId, createdName, name, assetType]);
51007
51024
  const handleOpen = () => {
51008
51025
  if (assetId) {
51009
51026
  openAsset(assetId, {
@@ -51081,10 +51098,23 @@ const CreatePresentationToolUI = React.memo(
51081
51098
  CreatePresentationToolUIImpl
51082
51099
  );
51083
51100
  CreatePresentationToolUI.displayName = "CreatePresentationToolUI";
51101
+ function openStudioAsset({
51102
+ assetId,
51103
+ assetType,
51104
+ slideNumber,
51105
+ preserveExistingSlide
51106
+ }) {
51107
+ const store = useAssetPanelStore.getState();
51108
+ const existing = store.tabs.find((tab) => tab.id === assetId);
51109
+ const shouldKeepCurrentSlide = preserveExistingSlide && existing;
51110
+ store.openAsset(assetId, {
51111
+ type: assetType,
51112
+ ...!shouldKeepCurrentSlide && slideNumber !== void 0 ? { slideNumber } : {}
51113
+ });
51114
+ }
51084
51115
  function StudioPtcToolUI({
51085
51116
  config: config2,
51086
51117
  toolName,
51087
- toolCallId,
51088
51118
  args,
51089
51119
  result,
51090
51120
  status
@@ -51099,42 +51129,57 @@ function StudioPtcToolUI({
51099
51129
  const isComplete = (status == null ? void 0 : status.type) === "complete" || !status;
51100
51130
  const isCancelled = (status == null ? void 0 : status.type) === "incomplete" && status.reason === "cancelled";
51101
51131
  const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? String(status.error ?? status.reason ?? "Tool failed") : null;
51102
- const openAsset = useAssetPanelStore((s) => s.openAsset);
51103
51132
  const handleOpen = React.useCallback(() => {
51104
51133
  if (!assetId) return;
51105
- openAsset(assetId, {
51106
- type: config2.assetType,
51107
- slideNumber
51108
- });
51109
- if (config2.assetType === "presentation") {
51110
- dispatchPresentationNavigate(assetId, slideNumber);
51134
+ if (toolName === "AddSlide") {
51135
+ logDebugPayload("[Athena SDK AddSlide] manual open", {
51136
+ args: typedArgs,
51137
+ parsedResult: data,
51138
+ rawResult: result,
51139
+ assetId,
51140
+ slideNumber
51141
+ });
51111
51142
  }
51112
- }, [assetId, config2.assetType, openAsset, slideNumber]);
51143
+ openStudioAsset({
51144
+ assetId,
51145
+ assetType: config2.assetType,
51146
+ slideNumber,
51147
+ preserveExistingSlide: config2.preserveExistingSlide
51148
+ });
51149
+ }, [
51150
+ assetId,
51151
+ config2.assetType,
51152
+ config2.preserveExistingSlide,
51153
+ data,
51154
+ result,
51155
+ slideNumber,
51156
+ toolName,
51157
+ typedArgs
51158
+ ]);
51113
51159
  React.useEffect(() => {
51114
- if (!config2.autoOpen || !isComplete || isCancelled || !assetId) {
51160
+ if (toolName !== "AddSlide") {
51115
51161
  return;
51116
51162
  }
51117
- const effectKey = toolCallId ?? `${toolName ?? "studio-tool"}:${assetId}:${slideNumber ?? "asset"}`;
51118
- if (!markStudioToolEffect(effectKey)) {
51119
- return;
51120
- }
51121
- const store = useAssetPanelStore.getState();
51122
- store.openAsset(assetId, {
51123
- type: config2.assetType,
51124
- slideNumber
51163
+ logDebugPayload("[Athena SDK AddSlide] tool state", {
51164
+ args: typedArgs,
51165
+ parsedResult: data,
51166
+ rawResult: result,
51167
+ status,
51168
+ assetId,
51169
+ slideNumber,
51170
+ isComplete,
51171
+ isCancelled
51125
51172
  });
51126
- if (config2.assetType === "presentation") {
51127
- dispatchPresentationNavigate(assetId, slideNumber);
51128
- }
51129
51173
  }, [
51130
51174
  assetId,
51131
- config2.assetType,
51132
- config2.autoOpen,
51175
+ data,
51133
51176
  isCancelled,
51134
51177
  isComplete,
51178
+ result,
51135
51179
  slideNumber,
51136
- toolCallId,
51137
- toolName
51180
+ status,
51181
+ toolName,
51182
+ typedArgs
51138
51183
  ]);
51139
51184
  return /* @__PURE__ */ jsxRuntime.jsx(
51140
51185
  ToolCard,
@@ -51192,8 +51237,7 @@ const StudioCreateWorkbookToolUI = createStudioPtcToolUI(
51192
51237
  },
51193
51238
  doneLabel: () => "Workbook created",
51194
51239
  getSubtitle: getNameSubtitle,
51195
- openLabel: () => "Open workbook",
51196
- autoOpen: true
51240
+ openLabel: () => "Open workbook"
51197
51241
  }
51198
51242
  );
51199
51243
  const StudioAddSheetToolUI = createStudioPtcToolUI(
@@ -51211,8 +51255,7 @@ const StudioAddSheetToolUI = createStudioPtcToolUI(
51211
51255
  return name ? `Sheet added: ${name}` : "Sheet added";
51212
51256
  },
51213
51257
  getSubtitle: (args) => getStringField(args, ["name", "sheetName", "sheet_name"]) ?? void 0,
51214
- openLabel: () => "Open workbook",
51215
- autoOpen: true
51258
+ openLabel: () => "Open workbook"
51216
51259
  }
51217
51260
  );
51218
51261
  const StudioOpenWorkbookToolUI = createStudioPtcToolUI(
@@ -51223,8 +51266,7 @@ const StudioOpenWorkbookToolUI = createStudioPtcToolUI(
51223
51266
  badge: "Sheet",
51224
51267
  runningLabel: () => "Opening workbook...",
51225
51268
  doneLabel: () => "Workbook opened",
51226
- openLabel: () => "Open workbook",
51227
- autoOpen: true
51269
+ openLabel: () => "Open workbook"
51228
51270
  }
51229
51271
  );
51230
51272
  const StudioCreatePresentationToolUI = createStudioPtcToolUI(
@@ -51240,8 +51282,7 @@ const StudioCreatePresentationToolUI = createStudioPtcToolUI(
51240
51282
  doneLabel: () => "Presentation created",
51241
51283
  getSubtitle: getNameSubtitle,
51242
51284
  getSlideNumber: () => 1,
51243
- openLabel: () => "Open presentation",
51244
- autoOpen: true
51285
+ openLabel: () => "Open presentation"
51245
51286
  }
51246
51287
  );
51247
51288
  const StudioOpenPresentationToolUI = createStudioPtcToolUI(
@@ -51255,7 +51296,7 @@ const StudioOpenPresentationToolUI = createStudioPtcToolUI(
51255
51296
  getSlideNumber: () => 1,
51256
51297
  getSubtitle: (_args, _result, slideNumber) => slideNumber ? `Slide ${slideNumber}` : void 0,
51257
51298
  openLabel: () => "Open presentation",
51258
- autoOpen: true
51299
+ preserveExistingSlide: true
51259
51300
  }
51260
51301
  );
51261
51302
  const StudioAddSlideToolUI = createStudioPtcToolUI(
@@ -51267,9 +51308,8 @@ const StudioAddSlideToolUI = createStudioPtcToolUI(
51267
51308
  runningLabel: () => "Adding slide...",
51268
51309
  doneLabel: (_args, slideNumber) => slideNumber ? `Slide ${slideNumber} added` : "Slide added",
51269
51310
  getSubtitle: getSlideSubtitle,
51270
- getSlideNumber,
51271
- openLabel: (slideNumber) => slideNumber ? `Open slide ${slideNumber}` : "Open presentation",
51272
- autoOpen: true
51311
+ getSlideNumber: (args) => getAddSlideNumber(args) ?? 1,
51312
+ openLabel: (slideNumber) => slideNumber ? `Open slide ${slideNumber}` : "Open presentation"
51273
51313
  }
51274
51314
  );
51275
51315
  const StudioCreateDocumentToolUI = createStudioPtcToolUI(
@@ -51284,8 +51324,7 @@ const StudioCreateDocumentToolUI = createStudioPtcToolUI(
51284
51324
  },
51285
51325
  doneLabel: () => "Document created",
51286
51326
  getSubtitle: getNameSubtitle,
51287
- openLabel: () => "Open document",
51288
- autoOpen: true
51327
+ openLabel: () => "Open document"
51289
51328
  }
51290
51329
  );
51291
51330
  const StudioCreateParagraphToolUI = createStudioPtcToolUI(
@@ -51297,8 +51336,7 @@ const StudioCreateParagraphToolUI = createStudioPtcToolUI(
51297
51336
  runningLabel: () => "Adding paragraph...",
51298
51337
  doneLabel: () => "Paragraph added",
51299
51338
  getSubtitle: getParagraphSubtitle,
51300
- openLabel: () => "Open document",
51301
- autoOpen: true
51339
+ openLabel: () => "Open document"
51302
51340
  }
51303
51341
  );
51304
51342
  const StudioOpenDocumentToolUI = createStudioPtcToolUI(
@@ -51309,8 +51347,7 @@ const StudioOpenDocumentToolUI = createStudioPtcToolUI(
51309
51347
  badge: "Doc",
51310
51348
  runningLabel: () => "Opening document...",
51311
51349
  doneLabel: () => "Document opened",
51312
- openLabel: () => "Open document",
51313
- autoOpen: true
51350
+ openLabel: () => "Open document"
51314
51351
  }
51315
51352
  );
51316
51353
  const CreateEmailDraftToolUIImpl = ({
@@ -51818,7 +51855,7 @@ const ExecutePresentationCodeToolUIImpl = ({
51818
51855
  const typedArgs = args;
51819
51856
  const code2 = (typedArgs == null ? void 0 : typedArgs.code) ?? "";
51820
51857
  const summary = (typedArgs == null ? void 0 : typedArgs.summary) ?? null;
51821
- const deckIdFromArgs = typeof (typedArgs == null ? void 0 : typedArgs.deck_id) === "string" ? typedArgs.deck_id : null;
51858
+ const deckIdFromArgs = getStringField(typedArgs, ["deck_id", "deckId", "asset_id", "assetId"]) ?? getPresentationCodeDeckId(code2);
51822
51859
  const isRunning = (status == null ? void 0 : status.type) === "running";
51823
51860
  const isComplete = (status == null ? void 0 : status.type) === "complete";
51824
51861
  const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? status.error : null;
@@ -51826,21 +51863,55 @@ const ExecutePresentationCodeToolUIImpl = ({
51826
51863
  () => isComplete ? parsePythonResult(result) : null,
51827
51864
  [result, isComplete]
51828
51865
  );
51866
+ const resultData = React.useMemo(() => normalizeResult(result), [result]);
51829
51867
  const outputText = parsed ? getExecutionTextOutput(parsed) : null;
51830
- const assetId = (parsed == null ? void 0 : parsed.assetId) ?? null;
51868
+ const targetSlideNumber = getPresentationCodeSlideNumber({
51869
+ args: typedArgs,
51870
+ result: resultData
51871
+ });
51831
51872
  const deckId = (parsed == null ? void 0 : parsed.deckId) ?? deckIdFromArgs;
51873
+ const assetId = (parsed == null ? void 0 : parsed.assetId) ?? ((deckId == null ? void 0 : deckId.startsWith("asset_")) ? deckId : null);
51832
51874
  const hasError = parsed && (parsed.error || parsed.exception);
51833
51875
  const openAsset = useAssetPanelStore((s) => s.openAsset);
51834
- const wasCompleteAtMount = React.useRef(isComplete);
51835
51876
  React.useEffect(() => {
51836
- if (!isComplete || !assetId || wasCompleteAtMount.current) {
51877
+ if (!isComplete) {
51837
51878
  return;
51838
51879
  }
51839
- const store = useAssetPanelStore.getState();
51840
- if (store.markAutoOpened(assetId)) {
51841
- store.openAsset(assetId, { type: "presentation" });
51880
+ logDebugPayload("[Athena SDK execute_presentation_code] tool state", {
51881
+ args: typedArgs,
51882
+ parsedResult: resultData,
51883
+ status,
51884
+ assetId,
51885
+ deckId,
51886
+ targetSlideNumber,
51887
+ outputText
51888
+ });
51889
+ }, [
51890
+ assetId,
51891
+ deckId,
51892
+ isComplete,
51893
+ outputText,
51894
+ resultData,
51895
+ status,
51896
+ targetSlideNumber,
51897
+ typedArgs
51898
+ ]);
51899
+ const openPresentation = React.useCallback(() => {
51900
+ if (!assetId) {
51901
+ return;
51842
51902
  }
51843
- }, [assetId, isComplete]);
51903
+ logDebugPayload("[Athena SDK execute_presentation_code] open deck", {
51904
+ args: typedArgs,
51905
+ parsedResult: resultData,
51906
+ assetId,
51907
+ deckId,
51908
+ targetSlideNumber
51909
+ });
51910
+ openAsset(assetId, {
51911
+ type: "presentation",
51912
+ ...targetSlideNumber !== void 0 ? { slideNumber: targetSlideNumber } : {}
51913
+ });
51914
+ }, [assetId, deckId, openAsset, resultData, targetSlideNumber, typedArgs]);
51844
51915
  return /* @__PURE__ */ jsxRuntime.jsxs(
51845
51916
  ToolCard,
51846
51917
  {
@@ -51863,11 +51934,11 @@ const ExecutePresentationCodeToolUIImpl = ({
51863
51934
  "button",
51864
51935
  {
51865
51936
  type: "button",
51866
- onClick: () => openAsset(assetId, { type: "presentation" }),
51937
+ onClick: openPresentation,
51867
51938
  className: "flex shrink-0 items-center gap-1.5 rounded-md border border-border/60 px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground",
51868
51939
  children: [
51869
51940
  /* @__PURE__ */ jsxRuntime.jsx(ExternalLink, { className: "size-3" }),
51870
- "Open deck"
51941
+ targetSlideNumber ? `Open slide ${targetSlideNumber}` : "Open deck"
51871
51942
  ]
51872
51943
  }
51873
51944
  )
@@ -51926,15 +51997,6 @@ const OpenAssetToolUIImpl = ({
51926
51997
  const isCancelled = (status == null ? void 0 : status.type) === "incomplete" && status.reason === "cancelled";
51927
51998
  const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? status.error : null;
51928
51999
  const openAsset = useAssetPanelStore((s) => s.openAsset);
51929
- const wasCompleteAtMount = React.useRef(isComplete);
51930
- React.useEffect(() => {
51931
- if (isComplete && !isCancelled && assetId && !wasCompleteAtMount.current) {
51932
- const store = useAssetPanelStore.getState();
51933
- if (store.markAutoOpened(assetId)) {
51934
- store.openAsset(assetId);
51935
- }
51936
- }
51937
- }, [isComplete, isCancelled, assetId]);
51938
52000
  return /* @__PURE__ */ jsxRuntime.jsx(
51939
52001
  ToolCard,
51940
52002
  {
@@ -52673,18 +52735,6 @@ const CaptureMomentToolUIImpl = ({
52673
52735
  const isComplete = (status == null ? void 0 : status.type) === "complete" || !status;
52674
52736
  const isCancelled = (status == null ? void 0 : status.type) === "incomplete" && status.reason === "cancelled";
52675
52737
  const openAsset = useAssetPanelStore((s) => s.openAsset);
52676
- const wasCompleteAtMount = React.useRef(isComplete);
52677
- React.useEffect(() => {
52678
- if (isComplete && !isCancelled && createdAssetId && !wasCompleteAtMount.current) {
52679
- const store = useAssetPanelStore.getState();
52680
- if (store.markAutoOpened(createdAssetId)) {
52681
- store.openAsset(createdAssetId, {
52682
- name: void 0,
52683
- type: "unknown"
52684
- });
52685
- }
52686
- }
52687
- }, [isComplete, isCancelled, createdAssetId, resultType]);
52688
52738
  const handleOpen = () => {
52689
52739
  if (createdAssetId) {
52690
52740
  openAsset(createdAssetId, {
@@ -52744,6 +52794,153 @@ const CaptureMomentToolUI = React.memo(
52744
52794
  CaptureMomentToolUIImpl
52745
52795
  );
52746
52796
  CaptureMomentToolUI.displayName = "CaptureMomentToolUI";
52797
+ function normalizeContentResult(result) {
52798
+ if (Array.isArray(result)) {
52799
+ return result;
52800
+ }
52801
+ if (typeof result === "string") {
52802
+ try {
52803
+ const parsed = JSON.parse(result);
52804
+ return normalizeContentResult(parsed);
52805
+ } catch {
52806
+ return [];
52807
+ }
52808
+ }
52809
+ if (result && typeof result === "object") {
52810
+ const obj = result;
52811
+ if (Array.isArray(obj.result)) {
52812
+ return obj.result;
52813
+ }
52814
+ if (typeof obj.result === "string") {
52815
+ return normalizeContentResult(obj.result);
52816
+ }
52817
+ if (Array.isArray(obj.content)) {
52818
+ return obj.content;
52819
+ }
52820
+ }
52821
+ return [];
52822
+ }
52823
+ function getSlideScreenshotResult(result) {
52824
+ let text2 = null;
52825
+ let imageUrl = null;
52826
+ let mediaType = null;
52827
+ let s3Key = null;
52828
+ for (const item of normalizeContentResult(result)) {
52829
+ if (!item || typeof item !== "object") {
52830
+ continue;
52831
+ }
52832
+ const part = item;
52833
+ if (!text2 && part.type === "text" && typeof part.text === "string") {
52834
+ text2 = part.text;
52835
+ continue;
52836
+ }
52837
+ if (part.type !== "image") {
52838
+ continue;
52839
+ }
52840
+ const source = part.source && typeof part.source === "object" ? part.source : {};
52841
+ if (!imageUrl) {
52842
+ imageUrl = getStringField(source, ["presigned_url", "url"]) ?? getStringField(part, ["url", "image_url"]);
52843
+ }
52844
+ if (!mediaType) {
52845
+ mediaType = getStringField(source, ["media_type", "mime_type"]) ?? getStringField(part, ["media_type", "mime_type"]);
52846
+ }
52847
+ if (!s3Key) {
52848
+ s3Key = getStringField(source, ["data", "s3_key"]);
52849
+ }
52850
+ }
52851
+ return { text: text2, imageUrl, mediaType, s3Key };
52852
+ }
52853
+ const CaptureSlideScreenshotToolUIImpl = ({
52854
+ toolName,
52855
+ args,
52856
+ result,
52857
+ status
52858
+ }) => {
52859
+ const typedArgs = args;
52860
+ const assetId = getStringField(typedArgs, ["asset_id", "assetId"]);
52861
+ const slideNumber = getNumberField(typedArgs, ["slide_number", "slideNumber"]) ?? void 0;
52862
+ const { text: text2, imageUrl, mediaType } = React.useMemo(
52863
+ () => getSlideScreenshotResult(result),
52864
+ [result]
52865
+ );
52866
+ const isRunning = (status == null ? void 0 : status.type) === "running";
52867
+ const isComplete = (status == null ? void 0 : status.type) === "complete" || !status;
52868
+ const isCancelled = (status == null ? void 0 : status.type) === "incomplete" && status.reason === "cancelled";
52869
+ const errorMsg = (status == null ? void 0 : status.type) === "incomplete" ? String(status.error ?? status.reason ?? "Capture failed") : null;
52870
+ const openAsset = useAssetPanelStore((s) => s.openAsset);
52871
+ const handleOpenSlide = React.useCallback(() => {
52872
+ if (!assetId) {
52873
+ return;
52874
+ }
52875
+ openAsset(assetId, {
52876
+ type: "presentation",
52877
+ ...slideNumber ? { slideNumber } : {}
52878
+ });
52879
+ }, [assetId, openAsset, slideNumber]);
52880
+ const subtitle = [
52881
+ slideNumber ? `Slide ${slideNumber}` : null,
52882
+ assetId ? truncate(assetId, 34) : null
52883
+ ].filter((value) => Boolean(value)).join(" · ");
52884
+ return /* @__PURE__ */ jsxRuntime.jsxs(
52885
+ ToolCard,
52886
+ {
52887
+ icon: Camera,
52888
+ status: (status == null ? void 0 : status.type) ?? "complete",
52889
+ title: isRunning ? "Capturing slide screenshot..." : errorMsg ? "Slide screenshot failed" : "Slide screenshot captured",
52890
+ subtitle: subtitle || void 0,
52891
+ badge: slideNumber ? `Slide ${slideNumber}` : mediaType ?? void 0,
52892
+ toolName,
52893
+ args: typedArgs,
52894
+ result,
52895
+ error: errorMsg,
52896
+ children: [
52897
+ isComplete && !isCancelled && imageUrl && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border/40 p-3", children: [
52898
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-lg border border-border/50 bg-muted/20", children: /* @__PURE__ */ jsxRuntime.jsx(
52899
+ "img",
52900
+ {
52901
+ src: imageUrl,
52902
+ alt: slideNumber ? `Slide ${slideNumber} screenshot` : "Slide screenshot",
52903
+ className: "aspect-video w-full object-contain",
52904
+ loading: "lazy"
52905
+ }
52906
+ ) }),
52907
+ text2 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-[11px] leading-relaxed text-muted-foreground", children: truncate(text2, 160) })
52908
+ ] }),
52909
+ isComplete && !isCancelled && assetId && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2 border-t border-border/40 px-4 py-2", children: [
52910
+ /* @__PURE__ */ jsxRuntime.jsxs(
52911
+ "button",
52912
+ {
52913
+ type: "button",
52914
+ onClick: handleOpenSlide,
52915
+ className: "flex items-center gap-1.5 rounded-md border border-border/60 px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground",
52916
+ children: [
52917
+ /* @__PURE__ */ jsxRuntime.jsx(ExternalLink, { className: "size-3" }),
52918
+ slideNumber ? `Open slide ${slideNumber}` : "Open presentation"
52919
+ ]
52920
+ }
52921
+ ),
52922
+ imageUrl && /* @__PURE__ */ jsxRuntime.jsxs(
52923
+ "a",
52924
+ {
52925
+ href: imageUrl,
52926
+ target: "_blank",
52927
+ rel: "noreferrer",
52928
+ className: "flex items-center gap-1.5 rounded-md border border-border/60 px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground",
52929
+ children: [
52930
+ /* @__PURE__ */ jsxRuntime.jsx(Image, { className: "size-3" }),
52931
+ "Open image"
52932
+ ]
52933
+ }
52934
+ )
52935
+ ] })
52936
+ ]
52937
+ }
52938
+ );
52939
+ };
52940
+ const CaptureSlideScreenshotToolUI = React.memo(
52941
+ CaptureSlideScreenshotToolUIImpl
52942
+ );
52943
+ CaptureSlideScreenshotToolUI.displayName = "CaptureSlideScreenshotToolUI";
52747
52944
  const TOOL_UI_REGISTRY = {
52748
52945
  search: WebSearchToolUI,
52749
52946
  browse: BrowseToolUI,
@@ -52771,6 +52968,7 @@ const TOOL_UI_REGISTRY = {
52771
52968
  open_asset_in_workspace: OpenAssetToolUI,
52772
52969
  // Media capture
52773
52970
  capture_moment: CaptureMomentToolUI,
52971
+ capture_slide_screenshot: CaptureSlideScreenshotToolUI,
52774
52972
  // Spreadsheet toolkit
52775
52973
  update_sheet_range: UpdateSheetRangeToolUI,
52776
52974
  // Database toolkit
@@ -53880,10 +54078,37 @@ const AthenaUserMessage = ({
53880
54078
  }
53881
54079
  );
53882
54080
  const embedCache = /* @__PURE__ */ new Map();
54081
+ function resolveAssetEmbedUrl({
54082
+ embedUrl,
54083
+ appUrl
54084
+ }) {
54085
+ if (!appUrl) {
54086
+ return embedUrl;
54087
+ }
54088
+ try {
54089
+ const resolvedEmbedUrl = new URL(embedUrl);
54090
+ const resolvedAppUrl = new URL(appUrl);
54091
+ if (!resolvedEmbedUrl.pathname.startsWith("/embed/")) {
54092
+ return embedUrl;
54093
+ }
54094
+ resolvedEmbedUrl.protocol = resolvedAppUrl.protocol;
54095
+ resolvedEmbedUrl.host = resolvedAppUrl.host;
54096
+ return resolvedEmbedUrl.toString();
54097
+ } catch {
54098
+ return embedUrl;
54099
+ }
54100
+ }
53883
54101
  function useAssetEmbed(assetId, options = {
53884
54102
  backendUrl: ""
53885
54103
  }) {
53886
- const { readOnly = false, expiresInSeconds = 60 * 60 * 24 * 30, backendUrl, apiKey, token } = options;
54104
+ const {
54105
+ readOnly = false,
54106
+ expiresInSeconds = 60 * 60 * 24 * 30,
54107
+ backendUrl,
54108
+ appUrl,
54109
+ apiKey,
54110
+ token
54111
+ } = options;
53887
54112
  const [embedUrl, setEmbedUrl] = React.useState(null);
53888
54113
  const [isLoading, setIsLoading] = React.useState(false);
53889
54114
  const [error2, setError] = React.useState(null);
@@ -53900,7 +54125,7 @@ function useAssetEmbed(assetId, options = {
53900
54125
  return;
53901
54126
  }
53902
54127
  const authContext = hasToken ? "token" : apiKey ?? "anon";
53903
- const cacheKey = `${assetId}:${readOnly}:${authContext}`;
54128
+ const cacheKey = `${assetId}:${readOnly}:${authContext}:${appUrl ?? ""}`;
53904
54129
  const cached2 = embedCache.get(cacheKey);
53905
54130
  if (cached2 && cached2.expiresAt > Date.now() / 1e3) {
53906
54131
  setEmbedUrl(cached2.url);
@@ -53938,8 +54163,12 @@ function useAssetEmbed(assetId, options = {
53938
54163
  }
53939
54164
  return res.json();
53940
54165
  }).then((data) => {
53941
- embedCache.set(cacheKey, { url: data.embed_url, expiresAt: data.expires_at });
53942
- setEmbedUrl(data.embed_url);
54166
+ const resolvedEmbedUrl = resolveAssetEmbedUrl({
54167
+ embedUrl: data.embed_url,
54168
+ appUrl
54169
+ });
54170
+ embedCache.set(cacheKey, { url: resolvedEmbedUrl, expiresAt: data.expires_at });
54171
+ setEmbedUrl(resolvedEmbedUrl);
53943
54172
  setIsLoading(false);
53944
54173
  }).catch((err) => {
53945
54174
  if (err.name === "AbortError") return;
@@ -53947,7 +54176,7 @@ function useAssetEmbed(assetId, options = {
53947
54176
  setIsLoading(false);
53948
54177
  });
53949
54178
  return () => controller.abort();
53950
- }, [assetId, readOnly, expiresInSeconds, backendUrl, apiKey, hasToken]);
54179
+ }, [assetId, readOnly, expiresInSeconds, backendUrl, appUrl, apiKey, hasToken]);
53951
54180
  return { embedUrl, isLoading, error: error2 };
53952
54181
  }
53953
54182
  const ABSOLUTE_URL_PATTERN = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
@@ -53970,6 +54199,21 @@ function buildAssetIframeSrc({
53970
54199
  return `${embedUrl}${separator}slide=${targetSlide}`;
53971
54200
  }
53972
54201
  }
54202
+ function buildPresentationNavigationMessage({
54203
+ assetId,
54204
+ assetType,
54205
+ slideNumber
54206
+ }) {
54207
+ if (assetType !== "presentation" || typeof slideNumber !== "number" || !Number.isInteger(slideNumber) || slideNumber < 1) {
54208
+ return null;
54209
+ }
54210
+ return {
54211
+ type: "NAVIGATE_TO_SLIDE",
54212
+ assetId,
54213
+ deckId: assetId,
54214
+ slideNumber
54215
+ };
54216
+ }
53973
54217
  const ASSET_TYPE_CONFIG = {
53974
54218
  presentation: { icon: Presentation, label: "Presentation" },
53975
54219
  spreadsheet: { icon: FileSpreadsheet, label: "Spreadsheet" },
@@ -53979,9 +54223,11 @@ const ASSET_TYPE_CONFIG = {
53979
54223
  };
53980
54224
  const AssetIframe = React.memo(
53981
54225
  ({ tab }) => {
53982
- const { backendUrl, apiKey, token } = useAthenaConfig();
54226
+ const iframeRef = React.useRef(null);
54227
+ const { backendUrl, appUrl, apiKey, token } = useAthenaConfig();
53983
54228
  const { embedUrl, isLoading, error: error2 } = useAssetEmbed(tab.id, {
53984
54229
  backendUrl,
54230
+ appUrl,
53985
54231
  apiKey,
53986
54232
  token
53987
54233
  });
@@ -53993,6 +54239,22 @@ const AssetIframe = React.memo(
53993
54239
  }) : null,
53994
54240
  [embedUrl, tab.slideNumber, tab.type]
53995
54241
  );
54242
+ const navigationMessage = React.useMemo(
54243
+ () => buildPresentationNavigationMessage({
54244
+ assetId: tab.id,
54245
+ assetType: tab.type,
54246
+ slideNumber: tab.slideNumber
54247
+ }),
54248
+ [tab.id, tab.slideNumber, tab.type]
54249
+ );
54250
+ const postNavigationMessage = React.useCallback(() => {
54251
+ var _a2, _b;
54252
+ if (!navigationMessage) return;
54253
+ (_b = (_a2 = iframeRef.current) == null ? void 0 : _a2.contentWindow) == null ? void 0 : _b.postMessage(navigationMessage, "*");
54254
+ }, [navigationMessage]);
54255
+ React.useEffect(() => {
54256
+ postNavigationMessage();
54257
+ }, [postNavigationMessage]);
53996
54258
  if (isLoading) {
53997
54259
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
53998
54260
  /* @__PURE__ */ jsxRuntime.jsx(LoaderCircle, { className: "mx-auto size-6 animate-spin text-muted-foreground" }),
@@ -54010,16 +54272,19 @@ const AssetIframe = React.memo(
54010
54272
  return /* @__PURE__ */ jsxRuntime.jsx(
54011
54273
  "iframe",
54012
54274
  {
54275
+ ref: iframeRef,
54013
54276
  src: iframeSrc,
54014
54277
  width: "100%",
54015
54278
  height: "100%",
54016
54279
  frameBorder: "0",
54017
54280
  allow: "fullscreen",
54018
54281
  title: tab.name ?? `Asset ${tab.id}`,
54019
- className: "h-full w-full"
54282
+ className: "h-full w-full",
54283
+ onLoad: postNavigationMessage
54020
54284
  }
54021
54285
  );
54022
- }
54286
+ },
54287
+ (prev, next) => prev.tab.id === next.tab.id && prev.tab.name === next.tab.name && prev.tab.type === next.tab.type && prev.tab.slideNumber === next.tab.slideNumber
54023
54288
  );
54024
54289
  AssetIframe.displayName = "AssetIframe";
54025
54290
  const PanelContent = ({