@athenaintel/react 0.10.37 → 0.10.39

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
@@ -144,28 +144,44 @@ Set `linkClicks={{ interceptAthenaCitations: false }}` to keep Athena citation l
144
144
 
145
145
  ## Standalone Asset Embeds
146
146
 
147
- Use `AthenaAssetEmbed` to render a native Athena asset without chat, thread history, tabs, or the SDK split-pane layout. Full mode supports documents, spreadsheets, and presentations.
147
+ Use `AthenaAssetEmbed` to render a native Athena asset without chat, thread history, tabs, or the SDK split-pane layout. It defaults to the editable, full native asset experience (`readOnly={false}` and `displayMode="full"`). The embed token and the current user must still have edit permission; otherwise Athena renders the asset read-only.
148
148
 
149
149
  ```tsx
150
- import { AthenaAssetEmbed, AthenaProvider } from '@athenaintel/react';
150
+ import { useState } from 'react';
151
+ import {
152
+ AthenaAssetEmbed,
153
+ AthenaProvider,
154
+ type AthenaAssetCitationReference,
155
+ } from '@athenaintel/react';
151
156
 
152
157
  function AssetView() {
158
+ const [linkedReference, setLinkedReference] =
159
+ useState<AthenaAssetCitationReference | null>(null);
160
+
153
161
  return (
154
162
  <AthenaProvider config={{ token: accessToken }}>
155
163
  <div style={{ height: 800 }}>
156
164
  <AthenaAssetEmbed
157
165
  assetId="asset_123"
158
166
  displayMode="full"
159
- readOnly
167
+ readOnly={false}
160
168
  onCitationClick={({ reference, sourceAssetId }) => {
161
169
  console.log('Citation clicked', { reference, sourceAssetId });
162
170
  }}
163
171
  onCitationAction={({ action, reference }) => {
164
172
  if (action === 'open-in-spaces') {
165
- openAssetInMyApp(reference);
173
+ setLinkedReference(reference);
166
174
  }
167
175
  }}
168
176
  />
177
+ {linkedReference && (
178
+ <AthenaAssetEmbed
179
+ assetId={linkedReference.id}
180
+ reference={linkedReference}
181
+ displayMode="full"
182
+ readOnly={false}
183
+ />
184
+ )}
169
185
  </div>
170
186
  </AthenaProvider>
171
187
  );
@@ -176,6 +192,43 @@ When `onCitationClick` is provided, citation navigation is delegated to the host
176
192
 
177
193
  `onCitationAction` handles actions initiated from a citation popover. It currently receives `action: 'open-in-spaces'` when the user clicks the (+) button. Providing the handler delegates that action to the host by default. Set `citationActionBehavior="browser"` to observe the event while preserving Athena's default navigation; requesting host mode without a handler safely falls back to browser behavior.
178
194
 
195
+ Pass the callback's complete `reference` to the linked `AthenaAssetEmbed`, rather than storing only
196
+ `reference.id`. The embed uses the reference anchor to navigate PDFs to the cited page. Updating the
197
+ reference also navigates when the host opens another citation in the same asset.
198
+
199
+ If the host starts with an Athena citation URL/string instead of an
200
+ `onCitationAction` event, parse it and pass the returned `reference` directly to the embed:
201
+
202
+ ```tsx
203
+ import {
204
+ AthenaAssetEmbed,
205
+ parseAthenaCitationLink,
206
+ } from '@athenaintel/react';
207
+
208
+ const citation = parseAthenaCitationLink({
209
+ href: citationString,
210
+ appUrl: 'https://app.athenaintel.com',
211
+ });
212
+
213
+ return citation ? (
214
+ <AthenaAssetEmbed
215
+ assetId={citation.assetId}
216
+ reference={citation.reference}
217
+ displayMode="full"
218
+ readOnly={false}
219
+ />
220
+ ) : null;
221
+ ```
222
+
223
+ For example, this citation opens the PDF directly at page 34:
224
+
225
+ ```text
226
+ https://app.athenaintel.com/dashboard/spaces/?asset_ids=asset_pdf&anchor=page&page=34&excerpt=Term%20debt
227
+ ```
228
+
229
+ The important query parameters are `asset_ids`, `anchor=page`, and `page`. Page ranges and
230
+ positioned citations are also preserved by `parseAthenaCitationLink`.
231
+
179
232
  ## Theming
180
233
 
181
234
  ```tsx
@@ -1,10 +1,12 @@
1
1
  import { type FC, type IframeHTMLAttributes } from "react";
2
- import { type AthenaAssetCitationActionEvent, type AthenaAssetCitationBehavior, type AthenaAssetCitationClickEvent } from "./asset-embed-messages";
2
+ import { type AthenaAssetCitationActionEvent, type AthenaAssetCitationBehavior, type AthenaAssetCitationClickEvent, type AthenaAssetCitationReference } from "./asset-embed-messages";
3
3
  export interface AthenaAssetEmbedProps extends Omit<IframeHTMLAttributes<HTMLIFrameElement>, "src" | "onError" | "onLoad"> {
4
4
  assetId: string;
5
+ /** Optional citation target to open inside the embedded asset, such as a PDF page. */
6
+ reference?: AthenaAssetCitationReference;
5
7
  /** Defaults to the native full asset experience. */
6
8
  displayMode?: "minimal" | "full";
7
- /** Defaults to true. */
9
+ /** Defaults to false so authorized users can edit the embedded asset. */
8
10
  readOnly?: boolean;
9
11
  expiresInSeconds?: number;
10
12
  citationBehavior?: AthenaAssetCitationBehavior;
@@ -1,9 +1,12 @@
1
+ import type { AthenaAssetCitationReference } from './asset-embed-messages';
1
2
  export interface UseAssetEmbedOptions {
2
3
  readOnly?: boolean;
3
4
  expiresInSeconds?: number;
4
- /** Native Olympus surface rendered by the iframe. Defaults to `minimal`. */
5
+ /** Native Olympus surface rendered by the iframe. Defaults to `full`. */
5
6
  displayMode?: 'minimal' | 'full';
6
7
  }
8
+ export declare const DEFAULT_ASSET_EMBED_READ_ONLY = false;
9
+ export declare const DEFAULT_ASSET_EMBED_DISPLAY_MODE: 'full';
7
10
  export interface UseAssetEmbedResult {
8
11
  embedUrl: string | null;
9
12
  isLoading: boolean;
@@ -26,7 +29,7 @@ export declare function resolveAssetEmbedUrl({ embedUrl, appUrl, backendUrl, cur
26
29
  backendUrl?: string | null;
27
30
  currentOrigin?: string | null;
28
31
  }): string;
29
- export declare function buildSdkAssetEmbedUrl(embedUrl: string): string;
32
+ export declare function buildSdkAssetEmbedUrl(embedUrl: string, reference?: AthenaAssetCitationReference): string;
30
33
  export declare function getAssetEmbedAuthContext({ token, apiKey }: {
31
34
  token?: string | null;
32
35
  apiKey?: string;
@@ -1,3 +1,4 @@
1
+ import type { AthenaAssetCitationReference } from '../assets/asset-embed-messages';
1
2
  import { type AssetType } from '../assets/asset-panel-store';
2
3
  import type { AthenaCitationLinkHandler, AthenaLinkClickHandler } from '../provider/citation-links';
3
4
  export interface ParsedAthenaCitationLink {
@@ -5,6 +6,8 @@ export interface ParsedAthenaCitationLink {
5
6
  assetId: string;
6
7
  assetIds: string[];
7
8
  assetType: AssetType | null;
9
+ /** Complete target that can be passed directly to AthenaAssetEmbed.reference. */
10
+ reference: AthenaAssetCitationReference;
8
11
  }
9
12
  export declare const parseAthenaCitationLink: ({ href, appUrl, }: {
10
13
  href: string;
package/dist/index.cjs CHANGED
@@ -47521,6 +47521,51 @@ const ASSET_TYPE_ALIASES = {
47521
47521
  word_document: "document"
47522
47522
  };
47523
47523
  const isAthenaSpacesPath = (url) => url.pathname.replace(/\/+$/, "") === "/dashboard/spaces";
47524
+ const getFiniteNumber = (url, key) => {
47525
+ const value = url.searchParams.get(key);
47526
+ if (value === null || value.trim() === "") return null;
47527
+ const parsed = Number(value);
47528
+ return Number.isFinite(parsed) ? parsed : null;
47529
+ };
47530
+ const parseCitationReference = ({
47531
+ url,
47532
+ assetId
47533
+ }) => {
47534
+ const reference2 = { id: assetId };
47535
+ const anchorType = url.searchParams.get("anchor");
47536
+ const page = getFiniteNumber(url, "page");
47537
+ switch (anchorType) {
47538
+ case "page":
47539
+ if (page !== null) reference2.anchor = { type: anchorType, page };
47540
+ break;
47541
+ case "page_range": {
47542
+ const startPage = getFiniteNumber(url, "start_page");
47543
+ const endPage = getFiniteNumber(url, "end_page");
47544
+ if (startPage !== null && endPage !== null) {
47545
+ reference2.anchor = { type: anchorType, startPage, endPage };
47546
+ }
47547
+ break;
47548
+ }
47549
+ case "page_rect": {
47550
+ const x = getFiniteNumber(url, "x");
47551
+ const y = getFiniteNumber(url, "y");
47552
+ const width = getFiniteNumber(url, "width");
47553
+ const height = getFiniteNumber(url, "height");
47554
+ if (page !== null && x !== null && y !== null && width !== null && height !== null) {
47555
+ reference2.anchor = { type: anchorType, page, x, y, width, height };
47556
+ }
47557
+ break;
47558
+ }
47559
+ case "page_text": {
47560
+ const text2 = url.searchParams.get("text");
47561
+ if (page !== null && text2) reference2.anchor = { type: anchorType, page, text: text2 };
47562
+ break;
47563
+ }
47564
+ }
47565
+ const excerpt = url.searchParams.get("excerpt");
47566
+ if (excerpt) reference2.meta = { excerpt };
47567
+ return reference2;
47568
+ };
47524
47569
  const normalizeAssetType = (value) => {
47525
47570
  if (!value) {
47526
47571
  return null;
@@ -47609,7 +47654,8 @@ const parseAthenaCitationLink = ({
47609
47654
  url,
47610
47655
  assetId,
47611
47656
  assetIds,
47612
- assetType: normalizeAssetType(url.searchParams.get("asset_type") ?? url.searchParams.get("type"))
47657
+ assetType: normalizeAssetType(url.searchParams.get("asset_type") ?? url.searchParams.get("type")),
47658
+ reference: parseCitationReference({ url, assetId })
47613
47659
  };
47614
47660
  };
47615
47661
  const isAthenaCitationUrl = ({
@@ -56278,6 +56324,8 @@ function isAthenaAssetCitationClickEvent(value) {
56278
56324
  function isAthenaAssetCitationActionEvent(value) {
56279
56325
  return typeof value === "object" && value !== null && "type" in value && value.type === ATHENA_ASSET_CITATION_ACTION_MESSAGE && "version" in value && value.version === 1 && "action" in value && value.action === "open-in-spaces" && "reference" in value && typeof value.reference === "object" && value.reference !== null && "id" in value.reference && typeof value.reference.id === "string";
56280
56326
  }
56327
+ const DEFAULT_ASSET_EMBED_READ_ONLY = false;
56328
+ const DEFAULT_ASSET_EMBED_DISPLAY_MODE = "full";
56281
56329
  function buildAssetEmbedRequestBody({
56282
56330
  assetId,
56283
56331
  readOnly,
@@ -56390,10 +56438,92 @@ function resolveAssetEmbedUrl({
56390
56438
  return embedUrl;
56391
56439
  }
56392
56440
  }
56393
- function buildSdkAssetEmbedUrl(embedUrl) {
56441
+ function setFiniteNumberParam({ params, key, value }) {
56442
+ if (typeof value === "number" && Number.isFinite(value)) {
56443
+ params.set(key, String(value));
56444
+ }
56445
+ }
56446
+ function appendCitationReferenceParams({
56447
+ params,
56448
+ reference: reference2
56449
+ }) {
56450
+ var _a2;
56451
+ const anchor = reference2 == null ? void 0 : reference2.anchor;
56452
+ if (!anchor || typeof anchor !== "object" || !("type" in anchor) || typeof anchor.type !== "string") {
56453
+ return;
56454
+ }
56455
+ switch (anchor.type) {
56456
+ case "page":
56457
+ params.set("anchor", anchor.type);
56458
+ setFiniteNumberParam({
56459
+ params,
56460
+ key: "page",
56461
+ value: "page" in anchor ? anchor.page : void 0
56462
+ });
56463
+ break;
56464
+ case "page_range":
56465
+ params.set("anchor", anchor.type);
56466
+ setFiniteNumberParam({
56467
+ params,
56468
+ key: "start_page",
56469
+ value: "startPage" in anchor ? anchor.startPage : void 0
56470
+ });
56471
+ setFiniteNumberParam({
56472
+ params,
56473
+ key: "end_page",
56474
+ value: "endPage" in anchor ? anchor.endPage : void 0
56475
+ });
56476
+ break;
56477
+ case "page_rect":
56478
+ params.set("anchor", anchor.type);
56479
+ setFiniteNumberParam({
56480
+ params,
56481
+ key: "page",
56482
+ value: "page" in anchor ? anchor.page : void 0
56483
+ });
56484
+ setFiniteNumberParam({
56485
+ params,
56486
+ key: "x",
56487
+ value: "x" in anchor ? anchor.x : void 0
56488
+ });
56489
+ setFiniteNumberParam({
56490
+ params,
56491
+ key: "y",
56492
+ value: "y" in anchor ? anchor.y : void 0
56493
+ });
56494
+ setFiniteNumberParam({
56495
+ params,
56496
+ key: "width",
56497
+ value: "width" in anchor ? anchor.width : void 0
56498
+ });
56499
+ setFiniteNumberParam({
56500
+ params,
56501
+ key: "height",
56502
+ value: "height" in anchor ? anchor.height : void 0
56503
+ });
56504
+ break;
56505
+ case "page_text":
56506
+ params.set("anchor", anchor.type);
56507
+ setFiniteNumberParam({
56508
+ params,
56509
+ key: "page",
56510
+ value: "page" in anchor ? anchor.page : void 0
56511
+ });
56512
+ if ("text" in anchor && typeof anchor.text === "string") {
56513
+ params.set("text", anchor.text);
56514
+ }
56515
+ break;
56516
+ }
56517
+ const excerpt = (_a2 = reference2.meta) == null ? void 0 : _a2.excerpt;
56518
+ if (typeof excerpt === "string" && excerpt) {
56519
+ params.set("excerpt", excerpt);
56520
+ }
56521
+ }
56522
+ function buildSdkAssetEmbedUrl(embedUrl, reference2) {
56394
56523
  try {
56395
56524
  const url = new URL(embedUrl);
56396
56525
  url.searchParams.set("athena_sdk_bridge", "1");
56526
+ appendCitationReferenceParams({ params: url.searchParams, reference: reference2 });
56397
56527
  return url.toString();
56398
56528
  } catch {
56399
56529
  return embedUrl;
@@ -56429,9 +56559,9 @@ function useAssetEmbed(assetId, options = {
56429
56559
  backendUrl: ""
56430
56560
  }) {
56431
56561
  const {
56432
- readOnly = false,
56562
+ readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,
56433
56563
  expiresInSeconds = 60 * 60 * 24 * 30,
56434
- displayMode = "minimal",
56564
+ displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,
56435
56565
  backendUrl,
56436
56566
  appUrl,
56437
56567
  apiKey,
@@ -56523,8 +56653,9 @@ const DEFAULT_STYLE = {
56523
56653
  const AUTH_SYNC_INTERVAL_MS = 6e4;
56524
56654
  const AthenaAssetEmbed = ({
56525
56655
  assetId,
56526
- displayMode = "full",
56527
- readOnly = true,
56656
+ reference: reference2,
56657
+ displayMode = DEFAULT_ASSET_EMBED_DISPLAY_MODE,
56658
+ readOnly = DEFAULT_ASSET_EMBED_READ_ONLY,
56528
56659
  expiresInSeconds,
56529
56660
  citationBehavior,
56530
56661
  onCitationClick,
@@ -56560,7 +56691,10 @@ const AthenaAssetEmbed = ({
56560
56691
  return null;
56561
56692
  }
56562
56693
  }, [embedUrl]);
56563
- const iframeSrc = React.useMemo(() => embedUrl ? buildSdkAssetEmbedUrl(embedUrl) : null, [embedUrl]);
56694
+ const iframeSrc = React.useMemo(
56695
+ () => embedUrl ? buildSdkAssetEmbedUrl(embedUrl, reference2) : null,
56696
+ [embedUrl, reference2]
56697
+ );
56564
56698
  const effectiveCitationBehavior = resolveAthenaAssetCitationBehavior({
56565
56699
  citationBehavior,
56566
56700
  hasCitationClickHandler: !!onCitationClick