@arbidocs/react 0.5.120 → 0.5.122

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/PdfViewerImpl-ZVJZ4KNW.cjs +22 -0
  2. package/dist/PdfViewerImpl-ZVJZ4KNW.cjs.map +1 -0
  3. package/dist/{PdfViewerImpl.d-B28hLEuj.d.ts → PdfViewerImpl.d-CqnEzQO7.d.ts} +9 -1
  4. package/dist/ai/MessageArtifactCard.js +9 -1
  5. package/dist/ai/MessageArtifactCard.js.map +2 -2
  6. package/dist/annotations/freeze/index.d.ts +1 -1
  7. package/dist/artifacts/artifact-kind.js +1 -0
  8. package/dist/artifacts/artifact-kind.js.map +2 -2
  9. package/dist/artifacts/index.cjs +3 -2
  10. package/dist/artifacts/index.cjs.map +1 -1
  11. package/dist/artifacts/index.d.ts +3 -2
  12. package/dist/calls/livekit/index.cjs +51 -51
  13. package/dist/chat/chat-artifacts.js +0 -1
  14. package/dist/chat/chat-artifacts.js.map +2 -2
  15. package/dist/chat/open-document.js +3 -1
  16. package/dist/chat/open-document.js.map +2 -2
  17. package/dist/chat/turn-flush.js +16 -0
  18. package/dist/chat/turn-flush.js.map +7 -0
  19. package/dist/chat/useConversationActions.js +3 -0
  20. package/dist/chat/useConversationActions.js.map +2 -2
  21. package/dist/{chunk-QP3WQSAA.cjs → chunk-4B2GW3AY.cjs} +3 -3
  22. package/dist/{chunk-QP3WQSAA.cjs.map → chunk-4B2GW3AY.cjs.map} +1 -1
  23. package/dist/{chunk-YMB6XUR2.cjs → chunk-7L7QRIQ2.cjs} +11 -7
  24. package/dist/chunk-7L7QRIQ2.cjs.map +1 -0
  25. package/dist/{chunk-KDYYAZXP.cjs → chunk-DLEHM4IA.cjs} +227 -155
  26. package/dist/chunk-DLEHM4IA.cjs.map +1 -0
  27. package/dist/{chunk-5TRUFWKT.cjs → chunk-FAJLJDWS.cjs} +12 -12
  28. package/dist/{chunk-5TRUFWKT.cjs.map → chunk-FAJLJDWS.cjs.map} +1 -1
  29. package/dist/{PdfViewerImpl-QUSDLNNM.cjs → chunk-ICBT2XGF.cjs} +5 -2
  30. package/dist/chunk-ICBT2XGF.cjs.map +1 -0
  31. package/dist/{chunk-RLSBRZ6Z.cjs → chunk-IX4HOS5Z.cjs} +2 -3
  32. package/dist/chunk-IX4HOS5Z.cjs.map +1 -0
  33. package/dist/{chunk-AJRYZ3IC.cjs → chunk-K6ELFSG6.cjs} +3 -3
  34. package/dist/{chunk-AJRYZ3IC.cjs.map → chunk-K6ELFSG6.cjs.map} +1 -1
  35. package/dist/chunk-MAOGK7IC.cjs +61 -0
  36. package/dist/chunk-MAOGK7IC.cjs.map +1 -0
  37. package/dist/{chunk-C72FSQUE.cjs → chunk-WUYSVTKE.cjs} +14 -12
  38. package/dist/chunk-WUYSVTKE.cjs.map +1 -0
  39. package/dist/datasets/sources/documents.js +1 -1
  40. package/dist/datasets/sources/documents.js.map +2 -2
  41. package/dist/document-tools/core.cjs +80 -80
  42. package/dist/document-tools/images-to-pdf.js +43 -0
  43. package/dist/document-tools/images-to-pdf.js.map +7 -0
  44. package/dist/document-tools/index.cjs +184 -80
  45. package/dist/document-tools/index.cjs.map +1 -1
  46. package/dist/document-tools/index.d.ts +41 -1
  47. package/dist/document-tools/index.js +6 -1
  48. package/dist/document-tools/index.js.map +2 -2
  49. package/dist/document-tools/pdf-attachments.js +63 -0
  50. package/dist/document-tools/pdf-attachments.js.map +7 -0
  51. package/dist/entities/DocumentEntity.js +7 -2
  52. package/dist/entities/DocumentEntity.js.map +2 -2
  53. package/dist/entities/file-type.js +14 -2
  54. package/dist/entities/file-type.js.map +2 -2
  55. package/dist/entities/index.js +9 -1
  56. package/dist/entities/index.js.map +2 -2
  57. package/dist/icons/PresIcon.js +53 -0
  58. package/dist/icons/PresIcon.js.map +7 -0
  59. package/dist/icons/colors.js +2 -0
  60. package/dist/icons/colors.js.map +2 -2
  61. package/dist/icons/file-type.js +3 -0
  62. package/dist/icons/file-type.js.map +2 -2
  63. package/dist/icons/index.js +2 -0
  64. package/dist/icons/index.js.map +2 -2
  65. package/dist/index.cjs +964 -919
  66. package/dist/index.cjs.map +1 -1
  67. package/dist/{index.d-DYlUprG9.d.ts → index.d-D2puqWeN.d.ts} +17 -3
  68. package/dist/{index.d-oR5FKZpJ.d.ts → index.d-KbsNPTEN.d.ts} +7 -5
  69. package/dist/index.d.ts +46 -12
  70. package/dist/index.js +1 -0
  71. package/dist/index.js.map +2 -2
  72. package/dist/pdf-region-impl-ZG5G2OX2.cjs +98 -0
  73. package/dist/pdf-region-impl-ZG5G2OX2.cjs.map +1 -0
  74. package/dist/session/documents.js +9 -5
  75. package/dist/session/documents.js.map +2 -2
  76. package/dist/shell/index.cjs +54 -50
  77. package/dist/shell/index.d.ts +1 -1
  78. package/dist/ui/index.cjs +182 -170
  79. package/dist/ui/index.d.ts +1 -1
  80. package/dist/ui/slider.js +7 -1
  81. package/dist/ui/slider.js.map +2 -2
  82. package/dist/viewers/PdfViewer.cjs +3 -3
  83. package/dist/viewers/PdfViewer.d.ts +2 -2
  84. package/dist/viewers/PdfViewerImpl.js +4 -1
  85. package/dist/viewers/PdfViewerImpl.js.map +2 -2
  86. package/dist/viewers/index.cjs +28 -3
  87. package/dist/viewers/index.cjs.map +1 -1
  88. package/dist/viewers/index.d.ts +68 -2
  89. package/dist/viewers/index.js +14 -0
  90. package/dist/viewers/index.js.map +2 -2
  91. package/dist/viewers/pdf-region-impl.js +96 -0
  92. package/dist/viewers/pdf-region-impl.js.map +7 -0
  93. package/dist/viewers/pdf-region.js +57 -0
  94. package/dist/viewers/pdf-region.js.map +7 -0
  95. package/package.json +3 -3
  96. package/dist/PdfViewerImpl-QUSDLNNM.cjs.map +0 -1
  97. package/dist/chunk-C72FSQUE.cjs.map +0 -1
  98. package/dist/chunk-KDYYAZXP.cjs.map +0 -1
  99. package/dist/chunk-RLSBRZ6Z.cjs.map +0 -1
  100. package/dist/chunk-YMB6XUR2.cjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/viewers/index.ts"],
4
- "sourcesContent": ["/**\n * `@arbidocs/react/viewers` \u2014 native document rendering.\n *\n * A SEPARATE ENTRY POINT on purpose. These viewers depend on heavyweight\n * rendering libraries (`react-pdf` + `pdfjs-dist`, `xlsx`, `docx-preview`) that\n * are declared as OPTIONAL peer dependencies, so an app that never renders a\n * PDF or a spreadsheet neither installs nor bundles them. Importing from the\n * root `@arbidocs/react` entry gets you none of this weight.\n *\n * Pair these with `DocumentReview` (root entry) \u2014 that renders parsed chunks\n * with AI citation highlighting; these render the original file as it looks.\n */\n\nexport { PdfViewer, pdfViewerChunk, type PdfViewerProps, type PdfViewerAssets } from './PdfViewer'\nexport { installPdfjsPolyfills } from './pdfjs-polyfills'\nexport { DocxViewer, type DocxViewerProps } from './DocxViewer'\nexport { XlsxViewer, type XlsxViewerProps } from './XlsxViewer'\nexport { ImageViewer, type ImageViewerProps } from './ImageViewer'\nexport { AudioViewer, type AudioViewerProps } from './AudioViewer'\nexport { VideoViewer, type VideoViewerProps } from './VideoViewer'\n\nexport {\n MIN_ZOOM,\n MAX_ZOOM,\n clampZoom,\n cycleZoom,\n doubleTapZoom,\n zoomLabel,\n pinchZoom,\n anchoredScroll,\n renderDpr,\n usePinchZoom,\n} from './viewer-zoom'\nexport { PAGE_SELECTOR, anchorDelta, useScrollAnchor } from './page-scroll'\nexport { usePageIntersectionObserver } from './usePageIntersectionObserver'\nexport {\n LAZY_LOAD_BUFFER_BEFORE,\n LAZY_LOAD_BUFFER_AFTER,\n INITIAL_LAZY_LOAD_BUFFER,\n} from './pdf-viewer-constants'\n\n// Parsed-text viewer helpers: chunks grouped by page (footnotes inlined) and selected passages\n// collapsed into contiguous runs.\nexport {\n useProcessedChunks,\n type RawChunk,\n type ProcessedChunk,\n type ProcessedPage,\n type UseProcessedChunksResult,\n} from './useProcessedChunks'\nexport {\n computeSelectionRuns,\n type RunChunk,\n type RunPage,\n type ChunkRunInfo,\n} from './selection-runs'\n"],
5
- "mappings": "AAaA,SAAS,WAAW,sBAAiE;AACrF,SAAS,6BAA6B;AACtC,SAAS,kBAAwC;AACjD,SAAS,kBAAwC;AACjD,SAAS,mBAA0C;AACnD,SAAS,mBAA0C;AACnD,SAAS,mBAA0C;AAEnD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,eAAe,aAAa,uBAAuB;AAC5D,SAAS,mCAAmC;AAC5C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP;AAAA,EACE;AAAA,OAKK;AACP;AAAA,EACE;AAAA,OAIK;",
4
+ "sourcesContent": ["/**\n * `@arbidocs/react/viewers` \u2014 native document rendering.\n *\n * A SEPARATE ENTRY POINT on purpose. These viewers depend on heavyweight\n * rendering libraries (`react-pdf` + `pdfjs-dist`, `xlsx`, `docx-preview`) that\n * are declared as OPTIONAL peer dependencies, so an app that never renders a\n * PDF or a spreadsheet neither installs nor bundles them. Importing from the\n * root `@arbidocs/react` entry gets you none of this weight.\n *\n * Pair these with `DocumentReview` (root entry) \u2014 that renders parsed chunks\n * with AI citation highlighting; these render the original file as it looks.\n */\n\nexport { PdfViewer, pdfViewerChunk, type PdfViewerProps, type PdfViewerAssets } from './PdfViewer'\nexport { installPdfjsPolyfills } from './pdfjs-polyfills'\nexport { DocxViewer, type DocxViewerProps } from './DocxViewer'\nexport { XlsxViewer, type XlsxViewerProps } from './XlsxViewer'\nexport { ImageViewer, type ImageViewerProps } from './ImageViewer'\nexport { AudioViewer, type AudioViewerProps } from './AudioViewer'\nexport { VideoViewer, type VideoViewerProps } from './VideoViewer'\n\nexport {\n MIN_ZOOM,\n MAX_ZOOM,\n clampZoom,\n cycleZoom,\n doubleTapZoom,\n zoomLabel,\n pinchZoom,\n anchoredScroll,\n renderDpr,\n usePinchZoom,\n} from './viewer-zoom'\nexport { PAGE_SELECTOR, anchorDelta, useScrollAnchor } from './page-scroll'\nexport { usePageIntersectionObserver } from './usePageIntersectionObserver'\nexport {\n LAZY_LOAD_BUFFER_BEFORE,\n LAZY_LOAD_BUFFER_AFTER,\n INITIAL_LAZY_LOAD_BUFFER,\n} from './pdf-viewer-constants'\n\n// Parsed-text viewer helpers: chunks grouped by page (footnotes inlined) and selected passages\n// collapsed into contiguous runs.\nexport {\n useProcessedChunks,\n type RawChunk,\n type ProcessedChunk,\n type ProcessedPage,\n type UseProcessedChunksResult,\n} from './useProcessedChunks'\nexport {\n computeSelectionRuns,\n type RunChunk,\n type RunPage,\n type ChunkRunInfo,\n} from './selection-runs'\nexport {\n renderPdfRegion,\n releasePdfRegionSource,\n closePdfRegionImage,\n regionGeometry,\n regionMasks,\n PDF_REGION_MAX_PIXELS,\n type PdfRegionImage,\n type PdfRegionRect,\n type PdfRegionRequest,\n} from './pdf-region'\n"],
5
+ "mappings": "AAaA,SAAS,WAAW,sBAAiE;AACrF,SAAS,6BAA6B;AACtC,SAAS,kBAAwC;AACjD,SAAS,kBAAwC;AACjD,SAAS,mBAA0C;AACnD,SAAS,mBAA0C;AACnD,SAAS,mBAA0C;AAEnD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,eAAe,aAAa,uBAAuB;AAC5D,SAAS,mCAAmC;AAC5C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP;AAAA,EACE;AAAA,OAKK;AACP;AAAA,EACE;AAAA,OAIK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,96 @@
1
+ import { pdfjs } from "react-pdf";
2
+ import { DEFAULT_ASSETS, ensurePdfWorker } from "./PdfViewerImpl.js";
3
+ import {
4
+ regionGeometry,
5
+ regionMasks,
6
+ PDF_REGION_MAX_PIXELS
7
+ } from "./pdf-region.js";
8
+ const RENDER_SLOTS = 2;
9
+ const documents = /* @__PURE__ */ new WeakMap();
10
+ function documentFor(request) {
11
+ const cached = documents.get(request.file);
12
+ if (cached) return cached;
13
+ const assets = { ...DEFAULT_ASSETS, ...request.assets };
14
+ ensurePdfWorker(assets.workerSrc);
15
+ const loading = request.file.arrayBuffer().then(
16
+ (buffer) => pdfjs.getDocument({
17
+ data: new Uint8Array(buffer),
18
+ cMapUrl: assets.cMapUrl,
19
+ cMapPacked: true,
20
+ standardFontDataUrl: assets.standardFontDataUrl,
21
+ wasmUrl: assets.wasmUrl
22
+ }).promise
23
+ );
24
+ loading.catch(() => documents.delete(request.file));
25
+ documents.set(request.file, loading);
26
+ return loading;
27
+ }
28
+ function releaseSource(file) {
29
+ const loading = documents.get(file);
30
+ if (!loading) return;
31
+ documents.delete(file);
32
+ void loading.then((doc) => doc.loadingTask.destroy()).catch(() => void 0);
33
+ }
34
+ let running = 0;
35
+ const waiting = [];
36
+ async function slot() {
37
+ if (running >= RENDER_SLOTS) await new Promise((resolve) => waiting.push(resolve));
38
+ running++;
39
+ return () => {
40
+ running--;
41
+ waiting.shift()?.();
42
+ };
43
+ }
44
+ const aborted = () => new DOMException("The clip render was superseded", "AbortError");
45
+ async function renderRegion(request) {
46
+ const { signal } = request;
47
+ const release = await slot();
48
+ try {
49
+ if (signal?.aborted) throw aborted();
50
+ const doc = await documentFor(request);
51
+ const page = await doc.getPage(request.page);
52
+ const base = page.getViewport({ scale: 1 });
53
+ const geometry = regionGeometry(
54
+ base,
55
+ request.rect,
56
+ request.width,
57
+ request.maxPixels ?? PDF_REGION_MAX_PIXELS
58
+ );
59
+ if (!geometry.width) throw new RangeError("The clip is empty");
60
+ const viewport = page.getViewport({
61
+ scale: geometry.scale,
62
+ offsetX: geometry.offsetX,
63
+ offsetY: geometry.offsetY
64
+ });
65
+ const canvas = document.createElement("canvas");
66
+ canvas.width = geometry.width;
67
+ canvas.height = geometry.height;
68
+ const task = page.render({ canvas, viewport });
69
+ const cancel = () => task.cancel();
70
+ signal?.addEventListener("abort", cancel);
71
+ try {
72
+ await task.promise;
73
+ } finally {
74
+ signal?.removeEventListener("abort", cancel);
75
+ }
76
+ if (signal?.aborted) throw aborted();
77
+ const masks = regionMasks(request.rect, request.masks ?? [], geometry);
78
+ if (masks.length) {
79
+ const context = canvas.getContext("2d");
80
+ if (!context) throw new Error("Could not draw the redactions");
81
+ context.fillStyle = "#000";
82
+ for (const m of masks) context.fillRect(m.x, m.y, m.width, m.height);
83
+ }
84
+ if (typeof createImageBitmap !== "function") return canvas;
85
+ const bitmap = await createImageBitmap(canvas);
86
+ canvas.width = canvas.height = 0;
87
+ return bitmap;
88
+ } finally {
89
+ release();
90
+ }
91
+ }
92
+ export {
93
+ releaseSource,
94
+ renderRegion
95
+ };
96
+ //# sourceMappingURL=pdf-region-impl.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/viewers/pdf-region-impl.ts"],
4
+ "sourcesContent": ["/**\n * The pdf.js side of `renderPdfRegion`: one loaded document per file, shared by every clip from it;\n * a page region drawn into a canvas of exactly the output size and handed back as a bitmap (no\n * image encoding). At most RENDER_SLOTS regions render at once, and a request whose signal aborts\n * is dropped before it starts or cancelled while it renders, so resizing or scrolling a clip never\n * queues up stale work.\n */\n\nimport { pdfjs } from 'react-pdf'\nimport { DEFAULT_ASSETS, ensurePdfWorker } from './PdfViewerImpl'\nimport {\n regionGeometry,\n regionMasks,\n PDF_REGION_MAX_PIXELS,\n type PdfRegionImage,\n type PdfRegionRequest,\n} from './pdf-region'\n\ntype PdfDocument = Awaited<ReturnType<typeof pdfjs.getDocument>['promise']>\n\n/** Regions rendering at once: enough to fill a slide quickly, few enough to keep the page smooth. */\nconst RENDER_SLOTS = 2\n\nconst documents = new WeakMap<Blob, Promise<PdfDocument>>()\n\nfunction documentFor(request: PdfRegionRequest): Promise<PdfDocument> {\n const cached = documents.get(request.file)\n if (cached) return cached\n const assets = { ...DEFAULT_ASSETS, ...request.assets }\n ensurePdfWorker(assets.workerSrc)\n const loading = request.file.arrayBuffer().then(\n (buffer) =>\n pdfjs.getDocument({\n data: new Uint8Array(buffer),\n cMapUrl: assets.cMapUrl,\n cMapPacked: true,\n standardFontDataUrl: assets.standardFontDataUrl,\n wasmUrl: assets.wasmUrl,\n }).promise\n )\n // A failed load is not kept: the next render tries again.\n loading.catch(() => documents.delete(request.file))\n documents.set(request.file, loading)\n return loading\n}\n\nexport function releaseSource(file: Blob): void {\n const loading = documents.get(file)\n if (!loading) return\n documents.delete(file)\n void loading.then((doc) => doc.loadingTask.destroy()).catch(() => undefined)\n}\n\nlet running = 0\nconst waiting: (() => void)[] = []\n\n/** Wait for a render slot; the returned function gives it back. */\nasync function slot(): Promise<() => void> {\n if (running >= RENDER_SLOTS) await new Promise<void>((resolve) => waiting.push(resolve))\n running++\n return () => {\n running--\n waiting.shift()?.()\n }\n}\n\nconst aborted = () => new DOMException('The clip render was superseded', 'AbortError')\n\nexport async function renderRegion(request: PdfRegionRequest): Promise<PdfRegionImage> {\n const { signal } = request\n const release = await slot()\n try {\n if (signal?.aborted) throw aborted()\n const doc = await documentFor(request)\n const page = await doc.getPage(request.page)\n const base = page.getViewport({ scale: 1 })\n const geometry = regionGeometry(\n base,\n request.rect,\n request.width,\n request.maxPixels ?? PDF_REGION_MAX_PIXELS\n )\n if (!geometry.width) throw new RangeError('The clip is empty')\n const viewport = page.getViewport({\n scale: geometry.scale,\n offsetX: geometry.offsetX,\n offsetY: geometry.offsetY,\n })\n const canvas = document.createElement('canvas')\n canvas.width = geometry.width\n canvas.height = geometry.height\n const task = page.render({ canvas, viewport })\n const cancel = () => task.cancel()\n signal?.addEventListener('abort', cancel)\n try {\n await task.promise\n } finally {\n signal?.removeEventListener('abort', cancel)\n }\n if (signal?.aborted) throw aborted()\n const masks = regionMasks(request.rect, request.masks ?? [], geometry)\n if (masks.length) {\n const context = canvas.getContext('2d')\n if (!context) throw new Error('Could not draw the redactions')\n context.fillStyle = '#000'\n for (const m of masks) context.fillRect(m.x, m.y, m.width, m.height)\n }\n // A bitmap draws fast in any window; without the API, the canvas itself is drawable.\n if (typeof createImageBitmap !== 'function') return canvas\n const bitmap = await createImageBitmap(canvas)\n canvas.width = canvas.height = 0\n return bitmap\n } finally {\n release()\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,57 @@
1
+ import { lazyChunk } from "../lazy-chunk.js";
2
+ function closePdfRegionImage(image) {
3
+ if (!image) return;
4
+ if ("close" in image) image.close();
5
+ else image.width = image.height = 0;
6
+ }
7
+ const PDF_REGION_MAX_PIXELS = 16e6;
8
+ function regionGeometry(page, rect, width, maxPixels = PDF_REGION_MAX_PIXELS) {
9
+ const regionWidth = rect.w * page.width;
10
+ const regionHeight = rect.h * page.height;
11
+ if (!(regionWidth > 0) || !(regionHeight > 0) || !(width > 0)) {
12
+ return { scale: 0, width: 0, height: 0, offsetX: 0, offsetY: 0 };
13
+ }
14
+ let scale = width / regionWidth;
15
+ const pixels = regionWidth * scale * regionHeight * scale;
16
+ if (pixels > maxPixels) scale *= Math.sqrt(maxPixels / pixels);
17
+ return {
18
+ scale,
19
+ width: Math.max(1, Math.round(regionWidth * scale)),
20
+ height: Math.max(1, Math.round(regionHeight * scale)),
21
+ offsetX: -rect.x * page.width * scale,
22
+ offsetY: -rect.y * page.height * scale
23
+ };
24
+ }
25
+ function regionMasks(rect, masks, out) {
26
+ const result = [];
27
+ for (const m of masks) {
28
+ const x1 = Math.max(m.x, rect.x);
29
+ const y1 = Math.max(m.y, rect.y);
30
+ const x2 = Math.min(m.x + m.w, rect.x + rect.w);
31
+ const y2 = Math.min(m.y + m.h, rect.y + rect.h);
32
+ if (x2 <= x1 || y2 <= y1) continue;
33
+ const left = Math.floor((x1 - rect.x) / rect.w * out.width);
34
+ const top = Math.floor((y1 - rect.y) / rect.h * out.height);
35
+ const right = Math.ceil((x2 - rect.x) / rect.w * out.width);
36
+ const bottom = Math.ceil((y2 - rect.y) / rect.h * out.height);
37
+ result.push({ x: left, y: top, width: right - left, height: bottom - top });
38
+ }
39
+ return result;
40
+ }
41
+ const regionChunk = lazyChunk(() => import("./pdf-region-impl.js"));
42
+ async function renderPdfRegion(request) {
43
+ const { renderRegion } = await regionChunk.load();
44
+ return renderRegion(request);
45
+ }
46
+ function releasePdfRegionSource(file) {
47
+ regionChunk.loaded()?.releaseSource(file);
48
+ }
49
+ export {
50
+ PDF_REGION_MAX_PIXELS,
51
+ closePdfRegionImage,
52
+ regionGeometry,
53
+ regionMasks,
54
+ releasePdfRegionSource,
55
+ renderPdfRegion
56
+ };
57
+ //# sourceMappingURL=pdf-region.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/viewers/pdf-region.ts"],
4
+ "sourcesContent": ["/**\n * One rectangle of a PDF page drawn at a chosen pixel width: a presentation clip. It is rendered\n * again from the PDF (vector text, embedded images at their own resolution) for the size it is\n * shown at, never scaled up from a screenshot, so it stays sharp on a large screen.\n *\n * The geometry is here (pure, tested); the pdf.js work (a few at a time, cancellable) is in\n * `pdf-region-impl`, in the PDF engine's chunk, loaded the first time a region renders.\n */\n\nimport { lazyChunk } from '../lazy-chunk'\nimport type { PdfViewerAssets } from './PdfViewerImpl'\n\n/** Fractions of the displayed (rotated) page, origin top-left. */\nexport interface PdfRegionRect {\n x: number\n y: number\n w: number\n h: number\n}\n\nexport interface PdfRegionRequest {\n file: Blob\n /** 1-based page number. */\n page: number\n rect: PdfRegionRect\n /** Width of the output in device pixels. */\n width: number\n /** Rectangles of the page (same fractions) painted solid black over the output: redactions. */\n masks?: readonly PdfRegionRect[]\n assets?: PdfViewerAssets\n /** Cap on output pixels (canvas limits, memory). Default 16 megapixels. */\n maxPixels?: number\n /** Aborting drops the render (before it starts) or cancels it (while it runs). */\n signal?: AbortSignal\n}\n\n/**\n * A rendered region: a bitmap (or, without `createImageBitmap`, a canvas), ready to draw into a\n * canvas in any window. Its `width` \u00D7 `height` are the output pixels. A bitmap holds memory until\n * closed: `closePdfRegionImage` when it is replaced.\n */\nexport type PdfRegionImage = ImageBitmap | HTMLCanvasElement\n\nexport function closePdfRegionImage(image: PdfRegionImage | null | undefined): void {\n if (!image) return\n if ('close' in image) image.close()\n else image.width = image.height = 0\n}\n\nexport const PDF_REGION_MAX_PIXELS = 16_000_000\n\n/** The scale and output size for a region of a `pageWidth \u00D7 pageHeight` (points) page. */\nexport function regionGeometry(\n page: { width: number; height: number },\n rect: PdfRegionRect,\n width: number,\n maxPixels = PDF_REGION_MAX_PIXELS\n): { scale: number; width: number; height: number; offsetX: number; offsetY: number } {\n const regionWidth = rect.w * page.width\n const regionHeight = rect.h * page.height\n if (!(regionWidth > 0) || !(regionHeight > 0) || !(width > 0)) {\n return { scale: 0, width: 0, height: 0, offsetX: 0, offsetY: 0 }\n }\n let scale = width / regionWidth\n const pixels = regionWidth * scale * regionHeight * scale\n if (pixels > maxPixels) scale *= Math.sqrt(maxPixels / pixels)\n return {\n scale,\n width: Math.max(1, Math.round(regionWidth * scale)),\n height: Math.max(1, Math.round(regionHeight * scale)),\n offsetX: -rect.x * page.width * scale,\n offsetY: -rect.y * page.height * scale,\n }\n}\n\n/** The masks that fall in the region, in output pixels. */\nexport function regionMasks(\n rect: PdfRegionRect,\n masks: readonly PdfRegionRect[],\n out: { width: number; height: number }\n): { x: number; y: number; width: number; height: number }[] {\n const result: { x: number; y: number; width: number; height: number }[] = []\n for (const m of masks) {\n const x1 = Math.max(m.x, rect.x)\n const y1 = Math.max(m.y, rect.y)\n const x2 = Math.min(m.x + m.w, rect.x + rect.w)\n const y2 = Math.min(m.y + m.h, rect.y + rect.h)\n if (x2 <= x1 || y2 <= y1) continue\n // Rounded outwards: a mask never leaves a sliver of what it covers.\n const left = Math.floor(((x1 - rect.x) / rect.w) * out.width)\n const top = Math.floor(((y1 - rect.y) / rect.h) * out.height)\n const right = Math.ceil(((x2 - rect.x) / rect.w) * out.width)\n const bottom = Math.ceil(((y2 - rect.y) / rect.h) * out.height)\n result.push({ x: left, y: top, width: right - left, height: bottom - top })\n }\n return result\n}\n\nconst regionChunk = lazyChunk(() => import('./pdf-region-impl'))\n\n/** The region, drawn. */\nexport async function renderPdfRegion(request: PdfRegionRequest): Promise<PdfRegionImage> {\n const { renderRegion } = await regionChunk.load()\n return renderRegion(request)\n}\n\n/** Free the PDF engine's copy of a file no clip shows any more. */\nexport function releasePdfRegionSource(file: Blob): void {\n regionChunk.loaded()?.releaseSource(file)\n}\n"],
5
+ "mappings": "AASA,SAAS,iBAAiB;AAkCnB,SAAS,oBAAoB,OAAgD;AAClF,MAAI,CAAC,MAAO;AACZ,MAAI,WAAW,MAAO,OAAM,MAAM;AAAA,MAC7B,OAAM,QAAQ,MAAM,SAAS;AACpC;AAEO,MAAM,wBAAwB;AAG9B,SAAS,eACd,MACA,MACA,OACA,YAAY,uBACwE;AACpF,QAAM,cAAc,KAAK,IAAI,KAAK;AAClC,QAAM,eAAe,KAAK,IAAI,KAAK;AACnC,MAAI,EAAE,cAAc,MAAM,EAAE,eAAe,MAAM,EAAE,QAAQ,IAAI;AAC7D,WAAO,EAAE,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,EAAE;AAAA,EACjE;AACA,MAAI,QAAQ,QAAQ;AACpB,QAAM,SAAS,cAAc,QAAQ,eAAe;AACpD,MAAI,SAAS,UAAW,UAAS,KAAK,KAAK,YAAY,MAAM;AAC7D,SAAO;AAAA,IACL;AAAA,IACA,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,cAAc,KAAK,CAAC;AAAA,IAClD,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,eAAe,KAAK,CAAC;AAAA,IACpD,SAAS,CAAC,KAAK,IAAI,KAAK,QAAQ;AAAA,IAChC,SAAS,CAAC,KAAK,IAAI,KAAK,SAAS;AAAA,EACnC;AACF;AAGO,SAAS,YACd,MACA,OACA,KAC2D;AAC3D,QAAM,SAAoE,CAAC;AAC3E,aAAW,KAAK,OAAO;AACrB,UAAM,KAAK,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC;AAC/B,UAAM,KAAK,KAAK,IAAI,EAAE,GAAG,KAAK,CAAC;AAC/B,UAAM,KAAK,KAAK,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,IAAI,KAAK,CAAC;AAC9C,UAAM,KAAK,KAAK,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,IAAI,KAAK,CAAC;AAC9C,QAAI,MAAM,MAAM,MAAM,GAAI;AAE1B,UAAM,OAAO,KAAK,OAAQ,KAAK,KAAK,KAAK,KAAK,IAAK,IAAI,KAAK;AAC5D,UAAM,MAAM,KAAK,OAAQ,KAAK,KAAK,KAAK,KAAK,IAAK,IAAI,MAAM;AAC5D,UAAM,QAAQ,KAAK,MAAO,KAAK,KAAK,KAAK,KAAK,IAAK,IAAI,KAAK;AAC5D,UAAM,SAAS,KAAK,MAAO,KAAK,KAAK,KAAK,KAAK,IAAK,IAAI,MAAM;AAC9D,WAAO,KAAK,EAAE,GAAG,MAAM,GAAG,KAAK,OAAO,QAAQ,MAAM,QAAQ,SAAS,IAAI,CAAC;AAAA,EAC5E;AACA,SAAO;AACT;AAEA,MAAM,cAAc,UAAU,MAAM,OAAO,mBAAmB,CAAC;AAG/D,eAAsB,gBAAgB,SAAoD;AACxF,QAAM,EAAE,aAAa,IAAI,MAAM,YAAY,KAAK;AAChD,SAAO,aAAa,OAAO;AAC7B;AAGO,SAAS,uBAAuB,MAAkB;AACvD,cAAY,OAAO,GAAG,cAAc,IAAI;AAC1C;",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arbidocs/react",
3
- "version": "0.5.120",
3
+ "version": "0.5.122",
4
4
  "description": "React hooks and provider for the ARBI SDK",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -117,8 +117,8 @@
117
117
  "build-storybook": "storybook build"
118
118
  },
119
119
  "dependencies": {
120
- "@arbidocs/client": "0.5.120",
121
- "@arbidocs/sdk": "0.5.120",
120
+ "@arbidocs/client": "0.5.122",
121
+ "@arbidocs/sdk": "0.5.122",
122
122
  "@didit-protocol/sdk-web": "0.4.0",
123
123
  "@livekit/components-react": "2.9.24",
124
124
  "@radix-ui/react-avatar": "^1.2.0",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/viewers/PdfViewerImpl.tsx"],"names":["installPdfjsPolyfills","pdfjs","useState","INITIAL_LAZY_LOAD_BUFFER","useMemo","useEffect","useRef","useCallback","LAZY_LOAD_BUFFER_BEFORE","LAZY_LOAD_BUFFER_AFTER","handle","fitPageWidth","contentBoxWidth","currentPage","usePageIntersectionObserver","useScrollAnchor","usePinchZoom","jsx","Document","jsxs","Fragment","Page","ratio","renderDpr"],"mappings":"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","file":"PdfViewerImpl-QUSDLNNM.cjs","sourcesContent":["/**\n * PdfViewer — paged PDF rendering with bidirectional page synchronisation.\n *\n * Uses IntersectionObserver to track the visible page and to drive two separate\n * lazy-load windows: a wide canvas buffer (so a page image is ready before the\n * user scrolls to it) and a narrow text-layer window (text spans are expensive\n * DOM, so they only exist for the active reading area).\n *\n * DECOUPLED FOR REUSE: page changes are reported through `onPageChange` rather\n * than written to an app store, and the pdf.js asset URLs are props rather than\n * hardcoded paths, because a consuming app decides where it serves the worker,\n * cmaps and standard fonts from.\n *\n * `react-pdf` and `pdfjs-dist` are OPTIONAL peer dependencies — install them\n * only if you render PDFs. The consuming app must also serve the pdf.js worker\n * (see `assets.workerSrc`).\n */\n\nimport { useState, useEffect, useRef, useCallback, useMemo, type ReactNode } from 'react'\nimport { Document, Page, pdfjs } from 'react-pdf'\nimport 'react-pdf/dist/Page/AnnotationLayer.css'\nimport 'react-pdf/dist/Page/TextLayer.css'\nimport { installPdfjsPolyfills } from './pdfjs-polyfills'\nimport { usePageIntersectionObserver } from './usePageIntersectionObserver'\nimport { useScrollAnchor } from './page-scroll'\nimport { contentBoxWidth, fitPageWidth, renderDpr, usePinchZoom } from './viewer-zoom'\nimport {\n LAZY_LOAD_BUFFER_BEFORE,\n LAZY_LOAD_BUFFER_AFTER,\n INITIAL_LAZY_LOAD_BUFFER,\n} from './pdf-viewer-constants'\n\n/** Where the app serves the pdf.js runtime assets from. */\nexport interface PdfViewerAssets {\n /** pdf.js worker bundle. Default `/pdf.worker.min.mjs`. */\n workerSrc?: string\n /** Directory of pdf.js character maps. Default `/cmaps/`. */\n cMapUrl?: string\n /** Directory of pdf.js standard font data. Default `/standard_fonts/`. */\n standardFontDataUrl?: string\n /**\n * Directory of the pdf.js wasm decoders (openjpeg for JPEG 2000, jbig2,\n * qcms). Scanned PDFs routinely embed JPX/JBIG2-compressed page images;\n * without these modules pdf.js fails image decoding with only a console\n * warning and renders blank white pages. Default `/wasm/`.\n */\n wasmUrl?: string\n}\n\nconst DEFAULT_ASSETS: Required<PdfViewerAssets> = {\n workerSrc: '/pdf.worker.min.mjs',\n cMapUrl: '/cmaps/',\n standardFontDataUrl: '/standard_fonts/',\n wasmUrl: '/wasm/',\n}\n\n/**\n * Ready pdf.js before any Document mounts: the built-ins it needs on this thread (older browsers\n * lack them and every page render would throw), and the worker, re-set only if it changed.\n */\nfunction ensurePdfWorker(workerSrc: string) {\n installPdfjsPolyfills()\n if (pdfjs.GlobalWorkerOptions.workerSrc !== workerSrc) {\n pdfjs.GlobalWorkerOptions.workerSrc = workerSrc\n }\n}\n\n// Page rendering constants\n// Height / width used for a page until its real size is known (the first page is read as soon as\n// the document opens, every other page as it renders). A placeholder that differs from the real\n// page shifts everything below it when the page renders.\nconst DEFAULT_PAGE_ASPECT_RATIO = 1.414 // A4 (√2)\n/** The page wrapper's own border (border, top + bottom), included in its fixed height. */\nconst PAGE_BORDER_PX = 2\n/** Wait for the zoom to settle before re-rasterising every visible page. */\nconst RERENDER_DEBOUNCE_MS = 250\n/** Above this zoom a page canvas is large enough that the render window shrinks. */\nconst ZOOMED_BUFFER_THRESHOLD = 1.5\n\n/** Same set contents: lets a state setter bail out instead of re-rendering on every scroll tick. */\nfunction sameSet(a: ReadonlySet<number>, b: ReadonlySet<number>): boolean {\n if (a.size !== b.size) return false\n for (const v of a) if (!b.has(v)) return false\n return true\n}\n\n// Text layer is enabled for pages within 1 page of the visible set — enough for smooth\n// scrolling while keeping DOM node count low on large documents.\nconst TEXT_LAYER_BUFFER = 1\n\ninterface PdfPageSize {\n getViewport: (options: { scale: number }) => { width: number; height: number }\n}\nconst ratioOf = (page: PdfPageSize): number => {\n const { width, height } = page.getViewport({ scale: 1 })\n return width > 0 && height > 0 ? height / width : DEFAULT_PAGE_ASPECT_RATIO\n}\n\nexport interface PdfViewerProps {\n file: string | Blob\n currentPage?: number\n onLoadSuccess?: (numPages: number) => void\n /** Incremented to trigger scroll to currentPage */\n scrollTrigger?: number\n /** Zoom as a multiplier of the fit-width size (1 = fit width). Default 1. */\n zoom?: number\n /** Called by pinch and double-tap; the host owns the value (its buttons change it too). */\n onZoomChange?: (zoom: number) => void\n /**\n * Called as the user scrolls, with the page occupying most of the viewport.\n * This is the reusable form of the monolith's `setViewingDocumentPage`.\n */\n onPageChange?: (pageNumber: number) => void\n /**\n * Force pdf.js to rasterise fonts onto the canvas instead of using @font-face.\n * Required when rendering into a different document (e.g. a popped-out window),\n * where @font-face rules from the opener do not apply.\n */\n disableFontFace?: boolean\n /** Where the app serves the pdf.js worker, cmaps and standard fonts from. */\n assets?: PdfViewerAssets\n /**\n * Content drawn over every page, inside its border box (e.g. the annotation layer). It fills the\n * page area and moves and scales with it. `pageWidthPt` is the page's width in PDF points once\n * known, so overlays can size strokes and text to match what a later export draws.\n */\n renderPageOverlay?: (page: { pageNumber: number; pageWidthPt: number | undefined }) => ReactNode\n}\n\nexport function PdfViewer({\n file,\n currentPage,\n onLoadSuccess,\n scrollTrigger,\n zoom = 1,\n onZoomChange,\n onPageChange,\n disableFontFace = false,\n assets,\n renderPageOverlay,\n}: PdfViewerProps) {\n const workerSrc = assets?.workerSrc ?? DEFAULT_ASSETS.workerSrc\n const cMapUrl = assets?.cMapUrl ?? DEFAULT_ASSETS.cMapUrl\n const standardFontDataUrl = assets?.standardFontDataUrl ?? DEFAULT_ASSETS.standardFontDataUrl\n const wasmUrl = assets?.wasmUrl ?? DEFAULT_ASSETS.wasmUrl\n\n // Ensure the pdf.js worker is configured before any Document mounts\n ensurePdfWorker(workerSrc)\n\n const [numPages, setNumPages] = useState<number>(0)\n const [containerWidth, setContainerWidth] = useState<number>(0)\n // Real height/width of the document's pages: the first page's, then each page as it loads.\n const [defaultRatio, setDefaultRatio] = useState<number>(DEFAULT_PAGE_ASPECT_RATIO)\n const [pageRatios, setPageRatios] = useState<ReadonlyMap<number, number>>(new Map())\n // Each page's width in PDF points, for overlays that must size strokes and text like an export.\n const [pageWidthsPt, setPageWidthsPt] = useState<ReadonlyMap<number, number>>(new Map())\n const [loadError, setLoadError] = useState<Error | null>(null)\n const [documentKey, setDocumentKey] = useState<string>(() => `pdf-${Date.now()}`)\n\n // Initialize with pages around the current page (or first 3 pages if no current page).\n // Start with a small initial buffer so the first paint is cheap; expand to the full\n // buffer lazily via requestIdleCallback after the first render settles.\n const initialPage = currentPage || 1\n const [renderedPages, setRenderedPages] = useState<Set<number>>(\n new Set([\n Math.max(1, initialPage - INITIAL_LAZY_LOAD_BUFFER.before),\n initialPage,\n initialPage + INITIAL_LAZY_LOAD_BUFFER.after,\n ])\n )\n\n // Pages currently in/near the viewport — used to gate the text layer.\n // Keeping this separate from renderedPages lets the canvas still render on buffer pages\n // while text DOM nodes are only created for the user's active reading area.\n const [visiblePages, setVisiblePages] = useState<Set<number>>(new Set([initialPage]))\n\n // A late PDF.js completion belongs only to the file that started it.\n const loadLifetime = useMemo(() => ({ file, active: true }), [file])\n useEffect(() => {\n loadLifetime.active = true\n return () => {\n loadLifetime.active = false\n }\n }, [loadLifetime])\n\n // Reset document key when file changes to force remount\n useEffect(() => {\n setDocumentKey(`pdf-${Date.now()}`)\n setNumPages(0)\n setLoadError(null)\n }, [file])\n\n const containerRef = useRef<HTMLDivElement>(null)\n const pageRefs = useRef<Map<number, HTMLDivElement>>(new Map())\n\n // Handle document load success\n const onDocumentLoadSuccess = useCallback(\n (pdf: { numPages: number; getPage?: (n: number) => Promise<PdfPageSize> }) => {\n if (!loadLifetime.active) return\n const n = pdf.numPages\n void pdf\n .getPage?.(1)\n .then((page) => {\n if (loadLifetime.active) setDefaultRatio(ratioOf(page))\n })\n .catch(() => {})\n setNumPages(n)\n setLoadError(null) // Clear any previous errors\n\n try {\n onLoadSuccess?.(n)\n } catch (error) {\n console.error('Error in onLoadSuccess callback:', error)\n }\n },\n [onLoadSuccess, loadLifetime]\n )\n\n // Expand only while this file is mounted. Cancellation also guards callbacks\n // that were already queued when the idle/timer handle was cancelled.\n useEffect(() => {\n if (numPages === 0) return\n let cancelled = false\n const expand = () => {\n if (cancelled || !loadLifetime.active) return\n setRenderedPages((prev) => {\n if (prev.size === 0) return prev\n const newSet = new Set(prev)\n const anchor = currentPage || 1\n for (\n let i = Math.max(1, anchor - LAZY_LOAD_BUFFER_BEFORE);\n i <= Math.min(numPages, anchor + LAZY_LOAD_BUFFER_AFTER);\n i++\n ) {\n newSet.add(i)\n }\n return newSet\n })\n }\n if (typeof requestIdleCallback !== 'undefined') {\n const handle = requestIdleCallback(expand, { timeout: 500 })\n return () => {\n cancelled = true\n if (typeof cancelIdleCallback !== 'undefined') cancelIdleCallback(handle)\n }\n }\n const handle = setTimeout(expand, 500)\n return () => {\n cancelled = true\n clearTimeout(handle)\n }\n }, [numPages, currentPage, loadLifetime])\n\n // Handle document load error\n const onDocumentLoadError = useCallback((error: Error) => {\n console.error('PDF loading error:', error)\n setLoadError(error)\n setNumPages(0)\n }, [])\n\n // react-pdf calls this when the PDF is encrypted (reason === 1) or the\n // supplied password was wrong (reason === 2). Default behaviour is a\n // window.prompt() that, when cancelled, never resolves the password\n // callback — pdf.js then leaves the document load Promise pending forever\n // and the viewer is stuck on \"Loading PDF...\".\n //\n // Always call updatePassword exactly once. On cancel we surface a\n // synthetic load error so onLoadError → setLoadError fires the standard\n // error UI instead of the indefinite spinner.\n const onPassword = useCallback((updatePassword: (password: string) => void, reason: number) => {\n const message =\n reason === 2\n ? 'Incorrect password. Enter the password to unlock this PDF:'\n : 'This PDF is password-protected. Enter the password to view it:'\n const password = window.prompt(message)\n if (password === null) {\n // Cancelled. Synthesise a load error so the error UI takes over.\n setLoadError(new Error('Password required to open this PDF'))\n setNumPages(0)\n return\n }\n updatePassword(password)\n }, [])\n\n // Fit width: the scroller's content box (inside its padding AND its vertical scrollbar) less the\n // page's own border. `offsetWidth` would count the scrollbar and overflow sideways by its width.\n useEffect(() => {\n const container = containerRef.current\n if (!container) return\n const setFromContentWidth = (contentWidth: number) =>\n setContainerWidth(fitPageWidth(contentWidth, PAGE_BORDER_PX))\n\n setFromContentWidth(contentBoxWidth(container))\n\n // Re-measure on any change of the content box: a panel resize, or the vertical scrollbar\n // appearing once the pages are taller than the scroller.\n const resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[entries.length - 1]\n const box = entry?.contentBoxSize?.[0]\n setFromContentWidth(box ? box.inlineSize : (entry?.contentRect.width ?? 0))\n })\n resizeObserver.observe(container)\n\n return () => {\n resizeObserver.disconnect()\n }\n }, [])\n\n // Track whether initial scroll has completed to prevent feedback loop\n const scrollCompleteRef = useRef(false)\n\n // Scroll to page ONLY when PDF loads - not when page number updates from scrolling\n useEffect(() => {\n if (numPages === 0) return\n\n // If we already scrolled, don't scroll again\n if (scrollCompleteRef.current) return\n\n // Ensure the target page and surrounding pages are rendered\n if (currentPage) {\n setRenderedPages((prev) => {\n const newSet = new Set(prev)\n for (\n let i = Math.max(1, currentPage - LAZY_LOAD_BUFFER_BEFORE);\n i <= Math.min(numPages, currentPage + LAZY_LOAD_BUFFER_AFTER);\n i++\n ) {\n newSet.add(i)\n }\n return newSet\n })\n\n // Wait for DOM update, then scroll\n requestAnimationFrame(() => {\n const pageElement = pageRefs.current.get(currentPage)\n if (pageElement) {\n pageElement.scrollIntoView({ behavior: 'auto', block: 'start' })\n }\n // Wait for scroll to complete before allowing page number updates\n setTimeout(() => {\n scrollCompleteRef.current = true\n }, 100)\n })\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [numPages]) // Note: currentPage is intentionally NOT a dependency to prevent feedback loop\n\n // The page the last explicit request (overlay arrows, typed number) asked for. Read when the\n // trigger fires; scrolling itself changes `currentPage` and must NOT re-run the scroll below,\n // or every page the user scrolls past would pull the view back to that page's top.\n const currentPageRef = useRef(currentPage)\n currentPageRef.current = currentPage\n\n // Scroll to page when scrollTrigger changes (user entered a page number)\n useEffect(() => {\n const currentPage = currentPageRef.current\n if (!scrollTrigger || numPages === 0 || !currentPage) return\n\n // Temporarily disable observer updates during manual scroll\n scrollCompleteRef.current = false\n\n // Ensure the target page and surrounding pages are rendered\n setRenderedPages((prev) => {\n const newSet = new Set(prev)\n for (\n let i = Math.max(1, currentPage - LAZY_LOAD_BUFFER_BEFORE);\n i <= Math.min(numPages, currentPage + LAZY_LOAD_BUFFER_AFTER);\n i++\n ) {\n newSet.add(i)\n }\n return newSet\n })\n\n // Double-rAF: first frame lets React commit the setRenderedPages update so\n // pages before the target are in the DOM at their correct heights; second\n // frame scrolls after layout has settled. Instant scroll avoids a race\n // where smooth-scroll animation is still running when the observer\n // re-enables and reports the wrong page.\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n const pageElement = pageRefs.current.get(currentPage)\n if (pageElement) {\n pageElement.scrollIntoView({ behavior: 'instant', block: 'start' })\n }\n scrollCompleteRef.current = true\n })\n })\n }, [scrollTrigger, numPages])\n\n // Handle lazy loading based on visible pages\n const handleVisibilityUpdate = useCallback(\n (visibilityMap: Map<number, number>) => {\n const visiblePageNums = Array.from(visibilityMap.entries())\n .filter(([, ratio]) => ratio > 0)\n .map(([pageNum]) => pageNum)\n\n if (visiblePageNums.length > 0) {\n const minVisible = Math.min(...visiblePageNums)\n const maxVisible = Math.max(...visiblePageNums)\n\n // Update canvas render buffer (wider window for smooth pre-loading). Zoomed pages are\n // big canvases: keep fewer of them alive so far pages release their memory.\n const zoomed = zoom > ZOOMED_BUFFER_THRESHOLD\n const before = zoomed ? 1 : LAZY_LOAD_BUFFER_BEFORE\n const after = zoomed ? 2 : LAZY_LOAD_BUFFER_AFTER\n const pagesToRender = new Set<number>()\n for (\n let i = Math.max(1, minVisible - before);\n i <= Math.min(numPages, maxVisible + after);\n i++\n ) {\n pagesToRender.add(i)\n }\n setRenderedPages((prev) => (sameSet(prev, pagesToRender) ? prev : pagesToRender))\n\n // Update text-layer set (narrow window — text layer is expensive DOM)\n const nearViewport = new Set<number>()\n for (\n let i = Math.max(1, minVisible - TEXT_LAYER_BUFFER);\n i <= Math.min(numPages, maxVisible + TEXT_LAYER_BUFFER);\n i++\n ) {\n nearViewport.add(i)\n }\n setVisiblePages((prev) => (sameSet(prev, nearViewport) ? prev : nearViewport))\n }\n },\n [numPages, zoom]\n )\n\n // Memoize options to prevent unnecessary Document reloads.\n // When the options object changes reference, react-pdf reloads the document,\n // which fails if the source Blob's ArrayBuffer was already transferred to the worker.\n const documentOptions = useMemo(\n () => ({\n standardFontDataUrl,\n cMapUrl,\n cMapPacked: true,\n wasmUrl,\n ...(disableFontFace && { disableFontFace: true }),\n }),\n [standardFontDataUrl, cMapUrl, wasmUrl, disableFontFace]\n )\n\n // Observer callback is optional for consumers that don't track the page.\n const handlePageChange = useCallback(\n (pageNumber: number) => onPageChange?.(pageNumber),\n [onPageChange]\n )\n\n // Use IntersectionObserver to track visible pages and update page number\n usePageIntersectionObserver({\n containerRef,\n scrollCompleteRef,\n isEnabled: numPages > 0,\n getPageElements: () => Array.from(pageRefs.current.values()),\n getPageNumber: (element) => parseInt(element.getAttribute('data-page-number') ?? '0'),\n onPageChange: handlePageChange,\n onVisibilityUpdate: handleVisibilityUpdate,\n observeKey: `${numPages}:${containerWidth > 0}`,\n })\n\n // Pages above the reading position change height as they load their real size.\n useScrollAnchor(containerRef, `${numPages}:${containerWidth > 0}`)\n\n // Pinch / double-tap, and the focal point kept still for every zoom change.\n usePinchZoom({ containerRef, zoom, onZoomChange })\n\n // Layout follows `zoom` at once (a CSS scale of the already rendered canvas, so the gesture is\n // smooth); the canvases are re-rendered at the new resolution once the zoom has settled. Without\n // this the bitmap is only magnified and stays blurry.\n const [renderZoom, setRenderZoom] = useState(zoom)\n useEffect(() => {\n if (zoom === renderZoom) return\n const timer = setTimeout(() => setRenderZoom(zoom), RERENDER_DEBOUNCE_MS)\n return () => clearTimeout(timer)\n }, [zoom, renderZoom])\n\n return (\n <div\n ref={containerRef}\n className=\"w-full h-full overflow-auto bg-muted flex flex-col items-start p-4 pb-20\"\n // Two-finger gestures belong to the viewer (real zoom), not to the browser (bitmap zoom).\n // The scrollbar's gutter is always reserved, so the fit width does not change (and every page\n // re-rasterise) when the scrollbar appears, and cannot flip back and forth at the threshold.\n style={{ touchAction: 'pan-x pan-y', scrollbarGutter: 'stable' }}\n role=\"region\"\n aria-label=\"PDF document viewer\"\n data-testid=\"pdf-viewer\"\n >\n <Document\n // Wider than the scroller when zoomed in: the scroller pans horizontally over it.\n className=\"flex w-max min-w-full flex-col\"\n key={documentKey}\n // react-pdf 11 suspends while loading and sends failures to an error boundary. This viewer\n // owns its loading and error UI (and the password flow), so keep the callback behaviour.\n suspense={false}\n file={file}\n onLoadSuccess={onDocumentLoadSuccess}\n onLoadError={onDocumentLoadError}\n onPassword={onPassword}\n options={documentOptions}\n loading={\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-sm text-muted-foreground\">Loading PDF...</div>\n </div>\n }\n error={\n <div className=\"flex flex-col items-center justify-center h-full gap-4 p-8\">\n <div className=\"text-destructive text-center\">\n {loadError?.message ===\n \"Cannot read properties of null (reading 'sendWithPromise')\" ? (\n <>\n <p className=\"font-semibold mb-2\">PDF viewer initialization failed</p>\n <p className=\"text-sm text-muted-foreground\">Please refresh the page to retry</p>\n </>\n ) : (\n <>\n <p className=\"font-semibold mb-2\">Failed to load PDF</p>\n {loadError && (\n <p className=\"text-sm text-muted-foreground\">{loadError.message}</p>\n )}\n </>\n )}\n </div>\n </div>\n }\n >\n {containerWidth > 0 &&\n Array.from(new Array(numPages), (_, index) => {\n const pageNumber = index + 1\n const shouldRender = renderedPages.has(pageNumber)\n const ratio = pageRatios.get(pageNumber) ?? defaultRatio\n const pageWidth = containerWidth * zoom\n const pageHeight = pageWidth * ratio\n // Resolution the canvas is (re-)rendered at, and how far it is CSS-scaled meanwhile.\n const renderWidth = containerWidth * renderZoom\n const scale = zoom / renderZoom\n // Only enable the text layer for pages in/near the viewport.\n // Pages in the wider render buffer get a canvas (so the image is ready when\n // the user scrolls to them) but no text spans until they're nearby — this\n // avoids committing thousands of DOM nodes synchronously on large documents.\n const shouldRenderTextLayer = visiblePages.has(pageNumber)\n\n return (\n <div\n key={`page_${pageNumber}`}\n ref={(el) => {\n if (el) {\n pageRefs.current.set(pageNumber, el)\n } else {\n pageRefs.current.delete(pageNumber)\n }\n }}\n data-page-number={pageNumber}\n // Fixed height in every state: a flex item would otherwise shrink (it is a\n // scroller itself via blocks.css) and a placeholder/real mismatch would shift the\n // pages below.\n className=\"relative mx-auto mb-6 shrink-0 shadow-xl bg-white border border-border\"\n style={{ width: pageWidth + PAGE_BORDER_PX, height: pageHeight + PAGE_BORDER_PX }}\n role=\"article\"\n aria-label={`Page ${pageNumber} of ${numPages}`}\n >\n {shouldRender ? (\n <div\n data-testid={`pdf-page-scaler-${pageNumber}`}\n style={\n scale === 1\n ? undefined\n : { transform: `scale(${scale})`, transformOrigin: '0 0' }\n }\n >\n <Page\n pageNumber={pageNumber}\n width={renderWidth}\n renderTextLayer={shouldRenderTextLayer}\n renderAnnotationLayer={false}\n onLoadSuccess={(page: { originalWidth: number; originalHeight: number }) => {\n setPageWidthsPt((prev) =>\n prev.get(pageNumber) === page.originalWidth\n ? prev\n : new Map(prev).set(pageNumber, page.originalWidth)\n )\n const ratio = page.originalHeight / page.originalWidth\n setPageRatios((prev) =>\n Math.abs((prev.get(pageNumber) ?? defaultRatio) - ratio) < 0.001\n ? prev\n : new Map(prev).set(pageNumber, ratio)\n )\n }}\n // DPR capped at 2x and lowered further when the page would exceed the canvas\n // pixel budget (zoomed pages are big; iOS Safari fails above ~16.7M pixels).\n devicePixelRatio={renderDpr(\n renderWidth,\n renderWidth * ratio,\n window.devicePixelRatio || 1\n )}\n loading={\n <div\n className=\"flex items-center justify-center bg-muted\"\n style={{ width: renderWidth, height: renderWidth * ratio }}\n >\n <div className=\"text-sm text-muted-foreground\">\n Loading page {pageNumber}...\n </div>\n </div>\n }\n />\n </div>\n ) : (\n <div\n className=\"flex items-center justify-center bg-muted\"\n style={{ width: pageWidth, height: pageHeight }}\n >\n <div className=\"text-sm text-muted-foreground\">Page {pageNumber}</div>\n </div>\n )}\n {renderPageOverlay?.({ pageNumber, pageWidthPt: pageWidthsPt.get(pageNumber) })}\n </div>\n )\n })}\n </Document>\n </div>\n )\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/search/session.ts","../src/notifications/Toaster.tsx","../src/notifications/NotificationBell.tsx","../src/host/ArbiHost.tsx","../src/host/assistant-copy.ts","../src/chat/mentions.ts","../src/chat/open-document.ts","../src/chat/thread-document.ts"],"names":["createStore","storage","jsx","Sonner","CheckCircle2","XCircle","AlertTriangle","Info","useState","useNotify","selectUnread","DEFAULT_NOTIFY_TONE","jsxs","DropdownMenu","DropdownMenuTrigger","Bell","DropdownMenuContent","CheckCheck","cn","BellOff","DropdownMenuItem","X","ScrollArea","formatRelativeTime","useContext","ArbiHostContext","useMemo","mergeHostConfig","OWN_SESSION_SLOT","useHostConfigRecord","useRef","useEffect","useSyncExternalStore","parseVersionRef"],"mappings":"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","file":"chunk-C72FSQUE.cjs","sourcesContent":["/**\n * A search's state outside any component: the query last searched, its passages, and whether it\n * is running. A page that shows the shared session (`sharedSearchSession`) keeps its results when\n * it is left and opened again, another surface can hand it a query to run on arrival\n * ({@link requestSearch}), and {@link persistSearchSession} keeps the last query across reloads.\n */\nimport { createStore, type StoreApi } from 'zustand/vanilla'\nimport type { SearchChunk } from '@arbidocs/sdk/browser'\n\nexport type SearchStatus = 'idle' | 'running' | 'done' | 'error'\n\nexport interface SearchSessionState {\n /** The workspace the query and results belong to. */\n workspaceId: string | null\n /** The query last searched, or handed over to run. */\n query: string\n /** The last search's passages, best first. */\n results: SearchChunk[]\n /** A search has finished with results to show (cleared by {@link clearSearchSession}). */\n searched: boolean\n status: SearchStatus\n error: string | null\n /** A query handed over by {@link requestSearch}, run once by the page that shows the session. */\n pending: string | null\n /** Counts searches, so the reply to an older one never overwrites a newer one. */\n run: number\n}\n\nexport type SearchSession = StoreApi<SearchSessionState>\n\nconst EMPTY: Omit<SearchSessionState, 'workspaceId' | 'run'> = {\n query: '',\n results: [],\n searched: false,\n status: 'idle',\n error: null,\n pending: null,\n}\n\nexport function createSearchSession(): SearchSession {\n return createStore<SearchSessionState>(() => ({ ...EMPTY, workspaceId: null, run: 0 }))\n}\n\n/** The session a host's search page shows. */\nexport const sharedSearchSession: SearchSession = createSearchSession()\n\n/** Back to nothing searched (in `workspaceId` when given); a running search's reply is dropped. */\nexport function clearSearchSession(\n workspaceId?: string | null,\n session: SearchSession = sharedSearchSession\n): void {\n session.setState((s) => ({\n ...EMPTY,\n workspaceId: workspaceId === undefined ? s.workspaceId : workspaceId,\n run: s.run + 1,\n }))\n}\n\n/**\n * Hand a query to the search page, which runs it when it shows the session (the user already\n * asked once; the page must not wait for a second Enter).\n */\nexport function requestSearch(\n query: string,\n workspaceId: string | null,\n session: SearchSession = sharedSearchSession\n): void {\n clearSearchSession(workspaceId, session)\n session.setState({ query, pending: query })\n}\n\n/** The handed-over query, once: the next call returns null. */\nexport function takePendingSearch(session: SearchSession = sharedSearchSession): string | null {\n const { pending } = session.getState()\n if (pending !== null) session.setState({ pending: null })\n return pending\n}\n\ninterface SavedSearch {\n query?: string\n workspaceId?: string | null\n}\n\n/**\n * Keep the last query (and its workspace) in `storage` under `key`, so the search box shows it\n * after a reload. Results are not kept. Returns the unsubscribe.\n */\nexport function persistSearchSession(\n key: string,\n storage: Pick<Storage, 'getItem' | 'setItem'> | undefined = globalThis.localStorage,\n session: SearchSession = sharedSearchSession\n): () => void {\n if (!storage) return () => {}\n try {\n const saved = JSON.parse(storage.getItem(key) ?? 'null') as SavedSearch | null\n if (saved && typeof saved.query === 'string' && !session.getState().query)\n session.setState({ query: saved.query, workspaceId: saved.workspaceId ?? null })\n } catch {\n // Unreadable: start empty.\n }\n let last = ''\n return session.subscribe(({ query, workspaceId }) => {\n const next = JSON.stringify({ query, workspaceId })\n if (next === last) return\n last = next\n try {\n storage.setItem(key, next)\n } catch {\n // Storage full or blocked: the query is simply not kept.\n }\n })\n}\n","/**\n * Toaster — the one shared toast surface, built on sonner (the mature engine:\n * stacking, swipe-to-dismiss, a11y, timing). Mount once near the app root; any\n * code then calls `notify.*(...)` (or the re-exported `toast`) from anywhere.\n *\n * Firm-agnostic: every sonner `ToasterProps` passes through, so a consumer sets\n * its own `position` / `theme` / `toastOptions`. The defaults give a neutral\n * shadcn-token card that both apps can override.\n */\nimport { Toaster as Sonner, type ToasterProps } from 'sonner'\n\nexport type { ToasterProps }\n\nconst DEFAULT_TOAST_OPTIONS: ToasterProps['toastOptions'] = {\n classNames: {\n toast:\n 'group toast group-[.toaster]:bg-card group-[.toaster]:text-foreground group-[.toaster]:border group-[.toaster]:border-border group-[.toaster]:shadow-lg group-[.toaster]:rounded-lg',\n description: 'group-[.toast]:text-muted-foreground',\n actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',\n cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',\n },\n}\n\nexport function Toaster({ toastOptions, ...props }: ToasterProps) {\n return (\n <Sonner\n className=\"toaster group\"\n toastOptions={toastOptions ?? DEFAULT_TOAST_OPTIONS}\n {...props}\n />\n )\n}\n","/**\n * NotificationBell — the topbar notification centre: a bell with an unread badge\n * that opens a dropdown of recorded entries (newest first) with an unread dot,\n * relative time and optional deep-link. \"Mark all read\" clears the badge. Backed\n * by the shared {@link useNotify} store (the same one the <Toaster/> renders).\n *\n * Firm-agnostic: navigation is a callback (`onNavigate`), styling tracks semantic\n * tokens, and per-variant glyph colours are overridable via `toneClassNames`.\n */\nimport { useState, type ReactElement } from 'react'\nimport {\n Bell,\n BellOff,\n CheckCheck,\n CheckCircle2,\n AlertTriangle,\n XCircle,\n Info,\n X,\n} from 'lucide-react'\nimport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n} from '../ui/dropdown-menu'\nimport { ScrollArea } from '../ui/scroll-area'\nimport { cn } from '../lib/utils'\nimport { formatRelativeTime } from '../lib/format-relative-time'\nimport {\n useNotify,\n selectUnread,\n DEFAULT_NOTIFY_TONE,\n type NotifyVariant,\n type AppNotification,\n} from './notify-store'\n\nconst GLYPH: Record<NotifyVariant, typeof Info> = {\n success: CheckCircle2,\n error: XCircle,\n warning: AlertTriangle,\n info: Info,\n}\n\n/** Entries the panel lists, newest first. */\nconst BELL_LIMIT = 100\n\n/** What a custom bell trigger needs to render itself. */\nexport interface NotificationBellTriggerState {\n /** Unread count (0 when none). */\n unread: number\n /** Accessible name, including the count: \"Notifications, 2 unread\". */\n ariaLabel: string\n testId: string\n}\n\nexport interface NotificationBellProps {\n /**\n * Replaces the default icon button with the host's own trigger (for example a labelled sidebar\n * row). It must forward its ref and rest props: it is mounted as the dropdown trigger.\n */\n renderTrigger?: (state: NotificationBellTriggerState) => ReactElement\n /** Called with an entry's `href` when it's clicked (e.g. router navigate). */\n onNavigate?: (href: string) => void\n /** Open this entry in the host notification centre instead of following its link. */\n onSelectNotification?: (notification: AppNotification) => void\n /** Called after an entry is locally marked read — the hook for a backend\n * bridge to propagate read state upstream (filter by the ids it owns). */\n onMarkRead?: (id: string) => void\n /** Called after \"Mark all read\" — same upstream-propagation hook. */\n onMarkAllRead?: () => void\n /** Show the store's toast mute switch in the header — the bell is where notification\n * behaviour is controlled. */\n showMuteToggle?: boolean\n /** Override per-variant glyph colours (e.g. brand emerald for success). */\n toneClassNames?: Partial<Record<NotifyVariant, string>>\n emptyText?: string\n /** Footer link to the app's full notification surface. Omit to hide. */\n onViewAll?: () => void\n viewAllLabel?: string\n testId?: string\n /** Where the panel opens relative to the bell (a sidebar-footer bell opens upward). */\n side?: 'top' | 'right' | 'bottom' | 'left'\n align?: 'start' | 'center' | 'end'\n}\n\nexport function NotificationBell({\n renderTrigger,\n onNavigate,\n onSelectNotification,\n onMarkRead,\n onMarkAllRead,\n showMuteToggle = false,\n toneClassNames,\n emptyText = \"You're all caught up.\",\n onViewAll,\n viewAllLabel = 'View all',\n testId = 'notifications',\n side = 'bottom',\n align = 'end',\n}: NotificationBellProps) {\n const [open, setOpen] = useState(false)\n const notifications = useNotify((s) => s.notifications)\n const unread = useNotify(selectUnread)\n const markAllRead = useNotify((s) => s.markAllRead)\n const markRead = useNotify((s) => s.markRead)\n const muted = useNotify((s) => s.muted)\n const setMuted = useNotify((s) => s.setMuted)\n const tone = (v: NotifyVariant) => toneClassNames?.[v] ?? DEFAULT_NOTIFY_TONE[v]\n\n // Ambient entries (routine session activity — \"Opening workspace…\") are kept\n // for the log but never surfaced in the bell panel: they never badge and are\n // always filtered out here. The panel is the recent history; a host's\n // notification page (`onViewAll`) holds the rest.\n const visible = notifications.filter((n) => !n.ambient).slice(0, BELL_LIMIT)\n\n return (\n <DropdownMenu open={open} onOpenChange={setOpen}>\n <DropdownMenuTrigger asChild>\n {renderTrigger ? (\n renderTrigger({\n unread,\n ariaLabel: `Notifications${unread ? `, ${unread} unread` : ''}`,\n testId,\n })\n ) : (\n <button\n className=\"relative flex size-9 pointer-coarse:size-11 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n data-testid={testId}\n aria-label={`Notifications${unread ? `, ${unread} unread` : ''}`}\n title=\"Notifications\"\n >\n <Bell className=\"size-5\" />\n {unread > 0 && (\n <span\n className=\"absolute -right-0.5 -top-0.5 flex min-w-4 items-center justify-center rounded-full bg-primary px-1 text-xs font-semibold leading-4 text-primary-foreground\"\n data-testid={`${testId}-badge`}\n >\n {unread > 9 ? '9+' : unread}\n </span>\n )}\n </button>\n )}\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align={align}\n side={side}\n sideOffset={8}\n collisionPadding={12}\n className=\"w-60 rounded-lg border-border/60 p-0 shadow-lg pointer-coarse:w-88\"\n data-testid={`${testId}-panel`}\n data-topbar-popover=\"\"\n >\n <div className=\"flex items-center justify-between gap-3 border-b border-border px-3.5 py-2.5 pointer-coarse:py-0\">\n <p className=\"text-sm font-semibold text-foreground\">Notifications</p>\n <div className=\"flex items-center gap-3 pointer-coarse:gap-1\">\n {unread > 0 && (\n <button\n onClick={() => {\n markAllRead()\n onMarkAllRead?.()\n }}\n className=\"flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:opacity-80 pointer-coarse:min-h-11 pointer-coarse:px-2\"\n data-testid={`${testId}-mark-all`}\n >\n <CheckCheck className=\"size-3.5\" /> Mark all read\n </button>\n )}\n {showMuteToggle && (\n <button\n onClick={() => setMuted(!muted)}\n aria-pressed={muted}\n title={muted ? 'Unmute notifications' : 'Mute notifications'}\n aria-label={muted ? 'Unmute notifications' : 'Mute notifications'}\n className={cn(\n 'flex size-6 shrink-0 items-center justify-center rounded-md transition-colors hover:bg-accent pointer-coarse:size-11',\n muted ? 'text-foreground' : 'text-muted-foreground hover:text-foreground'\n )}\n data-testid={`${testId}-mute`}\n >\n <BellOff className=\"size-3.5\" />\n </button>\n )}\n {/* Explicit close — the dropdown otherwise only dismisses via\n tap-outside/Escape, which isn't discoverable on touch and (at\n mobile width, see blocks.css) the panel covers most\n of the screen so there's nothing to tap outside of. Radix\n closes a DropdownMenu on any item select by default, so a\n plain item with no other effect doubles as a close button. */}\n <DropdownMenuItem\n className=\"flex size-6 shrink-0 items-center justify-center rounded-md p-0 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus:bg-accent pointer-coarse:size-11\"\n aria-label=\"Close notifications\"\n data-testid={`${testId}-close`}\n >\n <X className=\"size-3.5\" />\n </DropdownMenuItem>\n </div>\n </div>\n {muted && (\n <p\n className=\"border-b border-border/60 bg-amber-500/10 px-3.5 py-1.5 text-xs text-muted-foreground\"\n data-testid={`${testId}-muted-note`}\n >\n Toasts are muted — everything still lands here.\n </p>\n )}\n\n {visible.length === 0 ? (\n <div className=\"px-3.5 py-10 text-center text-sm text-muted-foreground\">{emptyText}</div>\n ) : (\n <ScrollArea className=\"max-h-[380px]\">\n <ul data-testid={`${testId}-list`}>\n {visible.map((n) => {\n const Glyph = GLYPH[n.variant]\n return (\n <li key={n.id}>\n <button\n onClick={() => {\n markRead(n.id)\n if (!n.read) onMarkRead?.(n.id)\n setOpen(false)\n if (onSelectNotification) {\n onSelectNotification(n)\n return\n }\n // The specific action wins over the generic deep link.\n if (n.onSelect) n.onSelect()\n else if (n.href) onNavigate?.(n.href)\n }}\n className={cn(\n 'flex w-full items-start gap-3 border-b border-border/60 px-3.5 py-3 text-left transition-colors hover:bg-accent',\n !n.read && 'bg-accent/40'\n )}\n data-testid={`${testId}-item`}\n data-notification-id={n.id}\n >\n <Glyph className={cn('mt-0.5 size-4 shrink-0', tone(n.variant))} />\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium leading-snug text-foreground\">\n {n.title}\n </p>\n {n.description && (\n <p className=\"mt-0.5 line-clamp-2 text-xs leading-relaxed text-muted-foreground\">\n {n.description}\n </p>\n )}\n <p className=\"mt-1 text-xs text-muted-foreground/80\">\n {formatRelativeTime(new Date(n.ts).toISOString())}\n </p>\n </div>\n {(n.onSelect || n.href) && (\n <span className=\"shrink-0 self-center rounded-md bg-primary/10 px-2 py-1 text-xs font-medium text-primary\">\n {n.actionLabel ?? 'Open'}\n </span>\n )}\n {!n.read && (\n <span className=\"mt-1.5 size-2 shrink-0 rounded-full bg-primary\" />\n )}\n </button>\n </li>\n )\n })}\n </ul>\n </ScrollArea>\n )}\n {onViewAll && (\n // A real menu item, not a bare button: selecting it must CLOSE the\n // dropdown on its way to the full surface.\n <DropdownMenuItem\n onClick={onViewAll}\n className=\"justify-center rounded-none border-t border-border px-3.5 py-2.5 text-xs font-medium text-primary focus:text-primary pointer-coarse:min-h-11\"\n data-testid={`${testId}-view-all`}\n >\n {viewAllLabel}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n","/**\n * `<ArbiHost config>`: the one place a host app hands its configuration to every hook and kit.\n *\n * <ArbiHost config={{ arbi, session, documents, calendar, mail, account, … }}>\n * <App />\n * </ArbiHost>\n *\n * Every field is optional and has a default (see `ArbiHostConfig`); a kit reads its domain from\n * the config (`useArbiHost().documents`, `useHost().billing` in blocks). A nested `<ArbiHost>`\n * overrides only the fields it gives and inherits the rest (`environment` field by field), for a\n * subtree that opens projects in its own tabs, say. There is no other host provider: a new domain\n * is a new optional field, never a new provider.\n *\n * SDK-free (this module, ./context, ./merge and the entity vocabulary are all it reaches), so a\n * marketing site that only sets the blocks environment does not bundle the SDK.\n */\n\nimport { useContext, useMemo, useState, type ComponentType, type ReactNode } from 'react'\nimport { ArbiHostContext } from './context'\nimport { mergeHostConfig, OWN_SESSION_SLOT } from './merge'\nimport { configureEntityVocabulary, type EntityVocabulary } from '../entities/vocabulary'\n\n/** A sign-in layer mounted inside the host (`defaultHost` in `@arbidocs/blocks/connect`). */\nexport interface ArbiHostConnection {\n Provider: ComponentType<{ children: ReactNode }>\n}\n\nexport interface ArbiHostProps<Config extends object> {\n config: Config\n children?: ReactNode\n}\n\n/**\n * Provide the host configuration. Typed by the package it is imported from: `@arbidocs/react`'s\n * takes the react domains, `@arbidocs/blocks`' the blocks domains as well.\n */\nexport function ArbiHost<Config extends object>({\n config,\n children,\n}: ArbiHostProps<Config>): React.ReactElement {\n const parent = useContext(ArbiHostContext)\n // A top-level host without a session store gets its own, made on first use (useArbiSessionStore).\n const [ownSession] = useState(() => ({ current: null as unknown }))\n const value = useMemo(() => {\n const merged = mergeHostConfig(parent, config)\n return merged[OWN_SESSION_SLOT] ? merged : { ...merged, [OWN_SESSION_SLOT]: ownSession }\n }, [parent, config, ownSession])\n // The site's nouns are in place before any kit below renders a label.\n const { vocabulary } = config as { vocabulary?: EntityVocabulary }\n if (vocabulary) configureEntityVocabulary(vocabulary)\n const Connection = (config as { connection?: ArbiHostConnection }).connection?.Provider\n return (\n <ArbiHostContext.Provider value={value}>\n {Connection ? <Connection>{children}</Connection> : children}\n </ArbiHostContext.Provider>\n )\n}\n","/**\n * What the kits call the assistant in their own words, from the host's name for it\n * (`ArbiHostConfig.assistantName`, also what people @mention). One label source: every kit phrase\n * that names the assistant is here, written once around the name, so a custom site's name shows\n * everywhere and ARBI's own text is unchanged (\"ARBI\" when the host names it nothing else).\n *\n * const copy = useAssistantCopy()\n * <Button>{copy.ask}</Button> // \"Ask ARBI\", or \"Ask Acme Assistant\"\n *\n * SDK-free: exported from `@arbidocs/react/shell` too, for presentational parts.\n */\nimport { useMemo } from 'react'\n// The record, not `useArbiHost` (`./config` names the SDK's types): this module stays SDK-free.\nimport { useHostConfigRecord } from './context'\n\n/** The assistant's name when the host names it nothing else. */\nexport const DEFAULT_ASSISTANT_NAME = 'ARBI'\n\n/** Every kit phrase that names the assistant. */\nexport interface AssistantCopy {\n /** The name itself. */\n name: string\n /** \"Ask ARBI\": a button or tab that starts a question. */\n ask: string\n /** \"Ask ARBI anything\": an empty list's call to ask. */\n askAnything: string\n /** \"Ask ARBI anything…\": the chat composer's placeholder. */\n askAnythingPlaceholder: string\n /** \"Ask ARBI...\": a compact composer's placeholder. */\n askPlaceholder: string\n /** \"Ask ARBI about your documents\": the empty chat's heading. */\n askAboutDocuments: string\n /** The files bulk bar's chat tooltip. */\n askAboutSelection: string\n /** \"ARBI is asking\": the heading over a question the assistant asks. */\n isAsking: string\n /** The composer's placeholder while the assistant waits for an answer. */\n answerPlaceholder: string\n /** The empty threads list, with the composer below it. */\n emptyThreadsComposer: string\n /** The empty threads list, with a New Task button. */\n emptyThreadsNewTask: string\n /** The drafting chip's hint while the thread drafts in a document. */\n draftingHint: string\n /** The drafting chip's hint before the document is attached. */\n draftingAttachHint: string\n /** The chat's facts tab note. */\n factsInContext: (facts: string) => string\n /** \"Ask one question — ARBI answers it for each of …\": the AI review's ask step. */\n reviewEach: (count: number, scope: string) => string\n /** The facts bar's note on the chat's facts budget. */\n factsBudgetHint: string\n /** An AI review cell left without an answer. */\n noAnswerHint: string\n /** The new-thread dialog's button that hands the thread to the assistant. */\n startWith: string\n /** The mail action bar's \"Reply with ARBI\". */\n replyWith: string\n /** The Word editor's comment button that @mentions the assistant. */\n include: string\n /** A reply to an email asked for outside a workspace. */\n replyNeedsWorkspace: string\n /** After an upload. */\n uploadedIndexing: string\n /** Before an upload completes. */\n afterUpload: string\n /** The built-in Deep research workflow's description and details. */\n deepResearch: { description: string; details: string }\n /** The built-in AI annotation operation's details. */\n annotate: { details: string; placeholder: string }\n}\n\nconst plural = (n: number) => (n === 1 ? '' : 's')\n\n/** The phrases for one name. `assistantCopy()` is ARBI's own text. */\nexport function assistantCopy(name: string = DEFAULT_ASSISTANT_NAME): AssistantCopy {\n return {\n name,\n ask: `Ask ${name}`,\n askAnything: `Ask ${name} anything`,\n askAnythingPlaceholder: `Ask ${name} anything…`,\n askPlaceholder: `Ask ${name}...`,\n askAboutDocuments: `Ask ${name} about your documents`,\n askAboutSelection: `Ask ${name} about the selected document(s) in a new thread`,\n isAsking: `${name} is asking`,\n answerPlaceholder: `Type your answer to ${name} — or pick an option above`,\n emptyThreadsComposer: `Type below to ask ${name}, bring in people, or both.`,\n emptyThreadsNewTask: `Use New Task to ask ${name}, bring in people, or both.`,\n draftingHint: `This thread works in the document: ${name} reads what it needs and makes its edits as tracked changes you accept or reject.`,\n draftingAttachHint: `Attach the open document to this thread: its questions then edit the document as tracked changes. Until then, ${name} only answers.`,\n factsInContext: (facts) =>\n `${facts} added to this chat are always in ${name}'s context for this chat.`,\n reviewEach: (count, scope) =>\n `Ask one question — ${name} answers it for each of the ${count} ${scope} document${plural(count)}.`,\n factsBudgetHint: `Facts added to this chat are always in ${name}'s context for this chat, so they share one budget. This estimates the tokens they add (≈4 characters per token), capped at ~2,000.`,\n noAnswerHint: `${name} finished this document without an answer for this field. The document may not say, or the request failed. Run it again to retry.`,\n startWith: `Start with ${name}`,\n replyWith: `Reply with ${name}`,\n include: `Include ${name}`,\n replyNeedsWorkspace: `Open a workspace to chat with ${name} about this email`,\n uploadedIndexing: `Files uploaded successfully. ${name} is now indexing them in the background.`,\n afterUpload: `After upload completes, ${name} will index files in the background.`,\n deepResearch: {\n description: `Send ${name} through your files and the web, and get an answer with sources`,\n details: `${name} searches your files and the web, then answers with the sources it used. You’ll see the research unfold in the chat.`,\n },\n annotate: {\n details: `Ask one question — ${name} answers it for every selected file and fills a new column with the answers.`,\n placeholder: `What should ${name} read out? e.g. What currency is the penalty clause denominated in?`,\n },\n }\n}\n\n/** The host's name for its assistant (`ArbiHostConfig.assistantName`), else \"ARBI\". */\nexport function useAssistantName(): string {\n const name = useHostConfigRecord().assistantName\n return (typeof name === 'string' && name.trim()) || DEFAULT_ASSISTANT_NAME\n}\n\n/** The kit phrases for the host's assistant name. */\nexport function useAssistantCopy(): AssistantCopy {\n const name = useAssistantName()\n return useMemo(() => assistantCopy(name), [name])\n}\n","/**\n * @mentions in chat text: pure helpers shared by the composer (autocomplete, extraction) and\n * the message renderer (chips). No React, no SDK.\n *\n * A mention lives in two places: the readable `@Label` text in the message content, and a\n * structured entry on the message (`{ type, user_ext_id, label }`) that the server reads to share\n * the conversation and notify. The composer only ever inserts `@<label>` for a known candidate,\n * so the structured list can be recovered from the text at send time ({@link extractMentions});\n * the renderer turns each stored mention back into a chip ({@link linkMentions}).\n */\n\n/** Who can be mentioned: a workspace member, or the assistant. */\nexport type MentionKind = 'user' | 'assistant'\n\n/** One entry of the mention autocomplete. */\nexport interface MentionCandidate {\n /** User external id (`usr-…`) for a person; any stable key (e.g. `assistant`) for the assistant. */\n id: string\n kind: MentionKind\n /** Inserted as `@<label>`. For people this must be the server's display name (given + family). */\n label: string\n /** Secondary line in the menu (email, role, …). */\n description?: string\n}\n\n/** The structured mention a message carries (the API's `MessageMention`). */\nexport interface MessageMentionRef {\n type: MentionKind\n user_ext_id?: string | null\n label?: string | null\n}\n\n/** Longest text after `@` that still counts as a query in progress. */\nconst MAX_QUERY_LENGTH = 40\n\n/**\n * The `@query` the caret is in, if any: the `@` must start the text or follow whitespace, and the\n * query may contain spaces (names do) but no newline. Returns the index of the `@` and the query.\n */\nexport function findMentionQuery(\n text: string,\n caret: number\n): { start: number; query: string } | null {\n const before = text.slice(0, caret)\n const at = before.lastIndexOf('@')\n if (at < 0) return null\n if (at > 0 && !/\\s/.test(before[at - 1])) return null\n const query = before.slice(at + 1)\n if (query.length > MAX_QUERY_LENGTH || query.includes('\\n') || query.startsWith(' ')) return null\n return { start: at, query }\n}\n\n/** Candidates matching a query: label prefix (or word-prefix) matches, prefix matches first. */\nexport function filterMentionCandidates(\n candidates: ReadonlyArray<MentionCandidate>,\n query: string\n): MentionCandidate[] {\n const q = query.trim().toLowerCase()\n if (!q) return candidates.slice()\n const scored: Array<{ c: MentionCandidate; score: number }> = []\n for (const c of candidates) {\n const label = c.label.toLowerCase()\n if (label.startsWith(q)) scored.push({ c, score: 0 })\n else if (label.split(/\\s+/).some((word) => word.startsWith(q))) scored.push({ c, score: 1 })\n else if (c.description?.toLowerCase().startsWith(q)) scored.push({ c, score: 2 })\n }\n return scored.sort((a, b) => a.score - b.score).map((s) => s.c)\n}\n\n/** Replace the `@query` at `start…caret` with `@<label> `; returns the new text and caret. */\nexport function insertMention(\n text: string,\n start: number,\n caret: number,\n label: string\n): { text: string; caret: number } {\n const inserted = `@${label} `\n return {\n text: text.slice(0, start) + inserted + text.slice(caret),\n caret: start + inserted.length,\n }\n}\n\n/** Every `[from, to)` range where `@<label>` occurs as a whole token. */\nfunction tokenRanges(text: string, label: string): Array<[number, number]> {\n const needle = `@${label}`\n const ranges: Array<[number, number]> = []\n let from = 0\n for (;;) {\n const i = text.indexOf(needle, from)\n if (i < 0) break\n const end = i + needle.length\n const startsToken = i === 0 || /\\s|\\(|\\[/.test(text[i - 1])\n const endsToken = end === text.length || !/[\\p{L}\\p{N}_]/u.test(text[end])\n if (startsToken && endsToken) ranges.push([i, end])\n from = end\n }\n return ranges\n}\n\n/**\n * The candidates mentioned in `text`, in order of first appearance. Longer labels win overlaps,\n * so `@Bob Smith` is Bob Smith, not a separate `@Bob`.\n */\nexport function extractMentions(\n text: string,\n candidates: ReadonlyArray<MentionCandidate>\n): MentionCandidate[] {\n const taken: Array<[number, number]> = []\n const found: Array<{ c: MentionCandidate; at: number }> = []\n const byLength = [...candidates].sort((a, b) => b.label.length - a.label.length)\n for (const c of byLength) {\n if (!c.label) continue\n for (const [from, to] of tokenRanges(text, c.label)) {\n if (taken.some(([a, b]) => from < b && to > a)) continue\n taken.push([from, to])\n if (!found.some((f) => f.c.id === c.id)) found.push({ c, at: from })\n }\n }\n return found.sort((a, b) => a.at - b.at).map((f) => f.c)\n}\n\n/** The API payload for mentioned candidates. */\nexport function toMessageMentions(mentioned: ReadonlyArray<MentionCandidate>): MessageMentionRef[] {\n return mentioned.map((c) =>\n c.kind === 'assistant' ? { type: 'assistant' } : { type: 'user', user_ext_id: c.id }\n )\n}\n\n/** Fragment prefix of the links {@link linkMentions} writes. */\nexport const MENTION_HREF_PREFIX = '#mention-'\n\n/** Escape the characters that would end a markdown link text early. */\nfunction escapeLinkText(label: string): string {\n return label.replace(/[[\\]\\\\]/g, (ch) => `\\\\${ch}`)\n}\n\n/**\n * Turn each stored mention's `@<label>` in `content` into a markdown link\n * `[@<label>](#mention-<id>)`, so a markdown renderer can draw it as a chip (see\n * {@link mentionFromHref}). Text that no longer matches (a renamed user) stays plain text.\n * `assistantLabels` lists other names the assistant may have been mentioned by.\n */\nexport function linkMentions(\n content: string,\n mentions: ReadonlyArray<MessageMentionRef> | null | undefined,\n assistantLabels: ReadonlyArray<string> = []\n): string {\n if (!mentions || mentions.length === 0 || !content) return content\n const targets: Array<{ label: string; id: string }> = []\n for (const m of mentions) {\n const id = m.type === 'assistant' ? 'assistant' : m.user_ext_id\n if (!id) continue\n const labels = m.type === 'assistant' ? [m.label ?? '', ...assistantLabels] : [m.label ?? '']\n for (const label of labels) if (label) targets.push({ label, id })\n }\n targets.sort((a, b) => b.label.length - a.label.length)\n const replacements: Array<{ from: number; to: number; text: string }> = []\n for (const { label, id } of targets) {\n for (const [from, to] of tokenRanges(content, label)) {\n if (replacements.some((r) => from < r.to && to > r.from)) continue\n replacements.push({\n from,\n to,\n text: `[@${escapeLinkText(label)}](${MENTION_HREF_PREFIX}${id})`,\n })\n }\n }\n replacements.sort((a, b) => b.from - a.from)\n let out = content\n for (const r of replacements) out = out.slice(0, r.from) + r.text + out.slice(r.to)\n return out\n}\n\n/** The mention a `#mention-…` href points at, or null for any other href. */\nexport function mentionFromHref(\n href: string | null | undefined\n): { kind: MentionKind; id: string } | null {\n if (!href?.startsWith(MENTION_HREF_PREFIX)) return null\n const id = href.slice(MENTION_HREF_PREFIX.length)\n if (!id) return null\n return { kind: id === 'assistant' ? 'assistant' : 'user', id }\n}\n\n/** A recipient alone (by any of its labels) is not a message. */\nexport function hasMessageBody(\n value: string,\n recipientLabel: string | ReadonlyArray<string> = 'ARBI'\n): boolean {\n const text = value.trim().toLowerCase()\n const labels = typeof recipientLabel === 'string' ? [recipientLabel] : recipientLabel\n return !!text && !labels.some((label) => text === `@${label}`.toLowerCase())\n}\n","import { useEffect, useRef, useSyncExternalStore } from 'react'\nimport { parseVersionRef } from '../artifacts/versions'\n\n/**\n * Drafting mode: the documents open beside the chat, and which one a chat turn drafts in.\n *\n * A viewer registers the document it shows (`useRegisterOpenDocument`). Being open binds nothing:\n * a turn names a document (`active_artifact_ext_id`, read with `takeDocumentForTurn` as the\n * question is sent) only when its thread is bound to it (`./thread-document`: started from the\n * document, or attached in that thread), when the document was made in that thread, or when the\n * open document is ambient (an email draft open in the composer). The server then binds the live\n * document to the turn: a Word document is read on demand and edited as tracked changes in the\n * open editor, and what the assistant saves becomes a child version of it. The request carries a\n * reference, never the content.\n *\n * The named document is always the subject, also while one of its earlier versions is on screen\n * read-only: a version still names its document. Sending a turn then returns the viewer to the live\n * document (`showLive`), where the assistant's edits appear as they are made. (Pinning a specific\n * version as a source is a separate feature: it does not go through here.)\n *\n * A module-level registry (not React context), like `useChatSurface`, so a viewer and a chat agree\n * wherever they sit in the tree; the most recently opened document is on top and closing it hands\n * back to the one before. It is not the session state's viewing document (`useArbiSessionState`):\n * it holds what mounted viewers can draft in (saved documents only, with the callback that shows\n * the live one again), and a turn reads it outside React. A viewer on the session's viewing\n * document passes that as `shownId` (it may be a version reference, resolved here the same way as\n * `selectViewingDocumentId` does).\n */\n\n/** What a viewer knows about the document it shows. */\nexport interface SavedDocumentInput {\n /** The document (`doc-…`), never a version reference. */\n documentId: string | null | undefined\n /** An unsaved draft (client-minted id, no backend record). */\n isDraft: boolean\n /** Still streaming: nothing is persisted yet. */\n isStreaming: boolean\n /** An image, audio or video artifact: there is no text to draft in. */\n isBlobArtifact: boolean\n /** The loaded document record. */\n documentData:\n | { external_id?: string | null; wp_type?: string | null; file_name?: string | null }\n | null\n | undefined\n}\n\n/** The fields that decide where a document opens. */\nexport interface WordDocumentKind {\n file_name?: string | null\n wp_type?: string | null\n}\n\n/**\n * A document edited in the Word editor: a `.docx`, or a document the assistant wrote (an\n * artifact). The one rule for where a document opens (`openWordEditor` or `openDocument`) and\n * which documents a chat drafts in.\n */\nexport function opensInWord(doc: WordDocumentKind | null | undefined): boolean {\n return !!doc && (doc.wp_type === 'artifact' || !!doc.file_name?.toLowerCase().endsWith('.docx'))\n}\n\n/**\n * The saved, content-backed document a chat can draft in, or null. Only a saved artifact or Word\n * document counts: drafts, an artifact still streaming and blob artifacts do not.\n */\nexport function savedDocumentIdFor(input: SavedDocumentInput): string | null {\n const { documentId, isDraft, isStreaming, isBlobArtifact, documentData } = input\n if (!documentId || isDraft || isStreaming || isBlobArtifact) return null\n if (!documentData || !opensInWord(documentData)) return null\n // A stale record of the previously viewed document is not this one.\n if (documentData.external_id !== documentId) return null\n return documentId\n}\n\nexport interface OpenDocumentRegistration {\n /** The saved document the viewer shows (`doc-…`); null or empty registers nothing. */\n documentId: string | null | undefined\n /** What is on screen: the document itself, or one of its earlier versions (`doc-…@v2`). */\n shownId?: string | null\n /** Show the live document again. Called when a turn starts while an earlier version is shown. */\n showLive?: () => void\n /** The workspace the document belongs to; a chat in another workspace does not draft in it. */\n workspaceId?: string | null\n /** The thread the document was made in: that thread drafts in it without being bound to it. */\n conversationId?: string | null\n /**\n * Drafted in by whichever chat is beside it, bound or not (an email draft open in the composer:\n * the composer is a drafting surface of its own).\n */\n ambient?: boolean\n}\n\ninterface Entry {\n documentId: string\n current: () => OpenDocumentRegistration\n}\n\nlet entries: Entry[] = []\nconst listeners = new Set<() => void>()\n\nfunction subscribe(listener: () => void): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\nfunction emit() {\n listeners.forEach((l) => l())\n}\n\nconst top = (): Entry | undefined => entries[entries.length - 1]\n\n/**\n * Registers the document a viewer shows as the open document while the caller is mounted and\n * `documentId` is set. Pass `savedDocumentIdFor(...)`, or the id of a document known to be saved.\n */\nexport function useRegisterOpenDocument(registration: OpenDocumentRegistration): void {\n const latest = useRef(registration)\n const { documentId, workspaceId, conversationId, ambient } = registration\n useEffect(() => {\n latest.current = registration\n })\n // What decides which chat drafts in it changed (its record loaded): readers re-render.\n useEffect(() => {\n if (documentId) emit()\n }, [documentId, workspaceId, conversationId, ambient])\n useEffect(() => {\n if (!documentId) return\n const entry: Entry = { documentId, current: () => latest.current }\n entries = [...entries, entry]\n emit()\n return () => {\n entries = entries.filter((e) => e !== entry)\n emit()\n }\n }, [documentId])\n}\n\n/** The open document's id (the most recently opened), or null. Re-renders when it changes. */\nexport function useOpenDocumentId(options?: { workspaceId?: string | null }): string | null {\n const workspaceId = options?.workspaceId ?? null\n return useSyncExternalStore(\n subscribe,\n () => openDocumentId({ workspaceId }),\n () => null\n )\n}\n\n/** The open document's id (the most recently opened); with `workspaceId`, only one of it. */\nexport function openDocumentId(options?: { workspaceId?: string | null }): string | null {\n const entry = top()\n if (!entry || !inWorkspace(entry, options?.workspaceId)) return null\n return entry.documentId\n}\n\nfunction inWorkspace(entry: Entry, workspaceId: string | null | undefined): boolean {\n const own = entry.current().workspaceId\n return !workspaceId || !own || own === workspaceId\n}\n\n/** What a turn is about to be sent in: its conversation and the document its thread is bound to. */\nexport interface TurnDocumentInput {\n /** The thread's bound document (`threadDocumentId` / the session's `newChatDocumentId`). */\n boundDocumentId: string | null | undefined\n /** The conversation (null: a new chat). */\n conversationId: string | null | undefined\n /** The chat's workspace: an open document of another workspace is never picked up. */\n workspaceId?: string | null\n}\n\n/** The document a turn drafts in, and the open viewer showing it (if one is). */\nfunction pickTurnDocument({\n boundDocumentId,\n conversationId,\n workspaceId,\n}: TurnDocumentInput): { documentId: string; entry: Entry | undefined } | null {\n // A bound thread names its document on every turn, open or not.\n if (boundDocumentId) {\n const entry = [...entries].reverse().find((e) => e.documentId === boundDocumentId)\n return { documentId: boundDocumentId, entry }\n }\n // Unbound: only a document made in this thread, or an ambient one (an email draft).\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i]\n if (!inWorkspace(entry, workspaceId)) continue\n const { ambient, conversationId: madeIn } = entry.current()\n if (ambient || (conversationId && madeIn === conversationId))\n return { documentId: entry.documentId, entry }\n }\n return null\n}\n\n/** The document a turn sent now would draft in, or null. Re-renders as documents open and close. */\nexport function useTurnDocumentId(input: TurnDocumentInput): string | null {\n const { boundDocumentId = null, conversationId = null, workspaceId = null } = input\n return useSyncExternalStore(\n subscribe,\n () => pickTurnDocument({ boundDocumentId, conversationId, workspaceId })?.documentId ?? null,\n () => null\n )\n}\n\n/**\n * The document an assistant turn about to be sent drafts in, or null. Call it once per turn as the\n * question is sent: when an earlier version of the document is on screen, the viewer returns to the\n * live document, where the assistant's edits appear.\n */\nexport function takeDocumentForTurn(input: TurnDocumentInput): string | null {\n const picked = pickTurnDocument(input)\n if (!picked) return null\n const { documentId, entry } = picked\n if (entry) {\n const { shownId, showLive } = entry.current()\n if (shownId && shownId !== documentId && parseVersionRef(shownId).docId === documentId)\n showLive?.()\n }\n return documentId\n}\n","/**\n * A thread's document: the one document a conversation drafts in, part of the conversation.\n *\n * A thread is bound to a document only when it was started from it (\"Chat about this document\" in\n * the viewer) or the person attached it in that thread. Only that thread names it on its turns\n * (`active_artifact_ext_id`), on every turn and after a reload. A document merely open or docked\n * beside the chat binds nothing: a new chat (the ask bar, the new-chat button, the thread\n * launcher, home) starts unbound, so a question there never becomes an edit of whatever is open.\n *\n * The binding is the server's: the conversation's `bound_document_ext_id`, set by the first turn\n * (`bound_document_ext_id` on the turn that starts it) or by a PATCH, and read under each reader's\n * access (`bound_document_unavailable`: bound to a document this reader cannot open). It follows\n * the person to every device, and a shared thread's members all see it. The query cache holds it\n * (`./thread-binding`); a new chat's binding is the session's `newChatDocumentId` until its first\n * turn creates the conversation.\n *\n * This module is the light half (no query client, no SDK), so the shell entry can carry it.\n */\n\n/** What a conversation row says about its binding. */\nexport interface ThreadBindingRow {\n external_id: string\n bound_document_ext_id?: string | null\n bound_document_unavailable?: boolean | null\n updated_at?: string | null\n}\n\n/** A conversation's binding as this reader sees it. */\nexport interface ThreadBinding {\n /** The bound document, or null (unbound, or bound to one this reader cannot open). */\n documentId: string | null\n /** Bound to a document this reader cannot open (another member's private document, or deleted). */\n unavailable: boolean\n}\n\nexport const UNBOUND: ThreadBinding = { documentId: null, unavailable: false }\n\n/** A row's binding. */\nexport function bindingOfRow(row: ThreadBindingRow | null | undefined): ThreadBinding {\n if (!row) return UNBOUND\n const documentId = row.bound_document_ext_id ?? null\n return { documentId, unavailable: !documentId && row.bound_document_unavailable === true }\n}\n\n/** The most recently active conversation bound to a document, among `conversations`, or null. */\nexport function latestThreadOfDocument(\n conversations: readonly ThreadBindingRow[] | null | undefined,\n documentId: string\n): string | null {\n let latest: ThreadBindingRow | null = null\n for (const row of conversations ?? []) {\n if (row.bound_document_ext_id !== documentId) continue\n if (!latest || (row.updated_at ?? '') > (latest.updated_at ?? '')) latest = row\n }\n return latest?.external_id ?? null\n}\n\n// --------------------------------------------------------------------------------------------- //\n// Bindings this browser kept before the server held them (`localStorage`), moved to the server\n// once (`migrateLegacyThreadDocuments` in `./thread-binding`) and then forgotten.\n// --------------------------------------------------------------------------------------------- //\n\nexport const LEGACY_THREAD_DOCUMENTS_KEY = 'arbi-thread-documents'\n\nfunction storage(): Storage | undefined {\n try {\n return typeof localStorage === 'undefined' ? undefined : localStorage\n } catch {\n return undefined\n }\n}\n\n/** The bindings this browser kept locally (conversation id → document id); empty when none. */\nexport function readLegacyThreadDocuments(): Record<string, string> {\n const out: Record<string, string> = {}\n try {\n const parsed = JSON.parse(storage()?.getItem(LEGACY_THREAD_DOCUMENTS_KEY) ?? 'null') as unknown\n if (parsed && typeof parsed === 'object')\n for (const [conversationId, documentId] of Object.entries(parsed))\n if (typeof documentId === 'string') out[conversationId] = documentId\n } catch {\n // Damaged or blocked: nothing to move.\n }\n return out\n}\n\n/** Forget locally kept bindings (moved to the server); the key goes with the last of them. */\nexport function forgetLegacyThreadDocuments(conversationIds: readonly string[]): void {\n if (conversationIds.length === 0) return\n const rest = readLegacyThreadDocuments()\n conversationIds.forEach((id) => delete rest[id])\n try {\n if (Object.keys(rest).length === 0) storage()?.removeItem(LEGACY_THREAD_DOCUMENTS_KEY)\n else storage()?.setItem(LEGACY_THREAD_DOCUMENTS_KEY, JSON.stringify(rest))\n } catch {\n // Blocked: nothing more to do.\n }\n}\n"]}