@cherrystudio/file-preview 0.1.3 → 0.2.1

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.
@@ -1,11 +1,13 @@
1
1
  import "./documentAnchor-J7GGLU15.js";
2
2
  import { r as readPreviewDocument, t as PreviewError } from "./source-Rx3etU3y.js";
3
- import { F as cn, I as LoaderCircle, N as EmptyState, i as FilePreviewLayout, o as usePreviewHost, r as FilePreviewToolbarButton, s as usePreviewLogger, t as useTranslation, z as cva } from "./useTranslation-DMhY277o.js";
4
- import { n as normalizeSelectionText, r as CircleAlert, t as createPreviewSelection } from "./selection-CQPD5a6C.js";
5
- import { n as ZoomIn, t as ZoomOut } from "./zoom-out-DLOQGt1e.js";
6
- import { t as FilePreviewTooLarge } from "./FilePreviewTooLarge-DkHa6toZ.js";
3
+ import { _ as cva, a as usePreviewHost, d as EmptyState, m as LoaderCircle, o as usePreviewLogger, p as cn, r as FilePreviewLayout, t as useTranslation } from "./useTranslation-BJY4EKsz.js";
4
+ import { t as CircleAlert } from "./circle-alert-sByhBDN4.js";
5
+ import { n as ZoomIn, t as ZoomOut } from "./zoom-out-B2XJ64Kx.js";
6
+ import { t as FilePreviewToolbarButton } from "./FilePreviewToolbarButton-ptqwCTnz.js";
7
+ import { t as FilePreviewTooLarge } from "./FilePreviewTooLarge-DC7vPyMI.js";
8
+ import { n as normalizeSelectionText, t as createPreviewSelection } from "./selection-B0INvh6t.js";
7
9
  import * as React$1 from "react";
8
- import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
10
+ import React, { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
9
11
  import { jsx, jsxs } from "react/jsx-runtime";
10
12
  import { flushSync } from "react-dom";
11
13
  typeof window !== "undefined" && window.document && window.document.createElement;
@@ -971,7 +973,7 @@ function SpreadsheetFilePreviewToolbar({ canZoomIn, canZoomOut, onZoomIn, onZoom
971
973
  var XLSX_PREVIEW_MAX_SIZE_BYTES = 20971520;
972
974
  function useXlsxWorkbook(document, fileName) {
973
975
  const logger = usePreviewLogger("useXlsxWorkbook");
974
- const { failDocument, resources } = usePreviewHost();
976
+ const { failDocument, resources, isCurrentDocument } = usePreviewHost();
975
977
  const { baseUrl, createWorker } = resources ?? {};
976
978
  const [state, setState] = useState({ status: "idle" });
977
979
  const workerRef = useRef(null);
@@ -981,6 +983,7 @@ function useXlsxWorkbook(document, fileName) {
981
983
  let cancelled = false;
982
984
  const controller = new AbortController();
983
985
  const requestId = ++requestIdRef.current;
986
+ const isCurrent = () => !cancelled && requestId === requestIdRef.current && isCurrentDocument?.() !== false;
984
987
  if (document.size > 20971520) {
985
988
  failDocument?.(new PreviewError("too_large", "Workbook exceeds the preview size limit"));
986
989
  setState({
@@ -989,15 +992,18 @@ function useXlsxWorkbook(document, fileName) {
989
992
  });
990
993
  return;
991
994
  }
992
- setState({ status: "loading" });
995
+ setState((current) => ({
996
+ status: "loading",
997
+ model: "model" in current ? current.model : void 0
998
+ }));
993
999
  (async () => {
994
1000
  let bytes;
995
1001
  try {
996
1002
  const raw = await readPreviewDocument(document, XLSX_PREVIEW_MAX_SIZE_BYTES, controller.signal);
997
- if (cancelled || requestId !== requestIdRef.current) return;
1003
+ if (!isCurrent()) return;
998
1004
  bytes = raw.slice().buffer;
999
1005
  } catch (error) {
1000
- if (cancelled || requestId !== requestIdRef.current) return;
1006
+ if (!isCurrent()) return;
1001
1007
  const normalized = error instanceof Error ? error : new Error(String(error));
1002
1008
  failDocument?.(normalized);
1003
1009
  setState({
@@ -1010,7 +1016,7 @@ function useXlsxWorkbook(document, fileName) {
1010
1016
  try {
1011
1017
  worker = createXlsxWorker(baseUrl, createWorker);
1012
1018
  } catch (error) {
1013
- if (cancelled || requestId !== requestIdRef.current) return;
1019
+ if (!isCurrent()) return;
1014
1020
  const normalized = error instanceof Error ? error : new Error(String(error));
1015
1021
  failDocument?.(normalized);
1016
1022
  setState({
@@ -1019,7 +1025,7 @@ function useXlsxWorkbook(document, fileName) {
1019
1025
  });
1020
1026
  return;
1021
1027
  }
1022
- if (cancelled || requestId !== requestIdRef.current) {
1028
+ if (!isCurrent()) {
1023
1029
  worker.terminate();
1024
1030
  return;
1025
1031
  }
@@ -1028,6 +1034,7 @@ function useXlsxWorkbook(document, fileName) {
1028
1034
  if (cancelled || event.data.id !== requestIdRef.current) return;
1029
1035
  worker.terminate();
1030
1036
  if (workerRef.current === worker) workerRef.current = null;
1037
+ if (!isCurrent()) return;
1031
1038
  if (event.data.ok) {
1032
1039
  for (const warning of event.data.model.warnings) {
1033
1040
  if (loggedWarningsRef.current.has(warning)) continue;
@@ -1050,6 +1057,7 @@ function useXlsxWorkbook(document, fileName) {
1050
1057
  if (cancelled || requestId !== requestIdRef.current) return;
1051
1058
  worker.terminate();
1052
1059
  if (workerRef.current === worker) workerRef.current = null;
1060
+ if (!isCurrent()) return;
1053
1061
  failDocument?.(event.error instanceof Error ? event.error : new Error(event.message));
1054
1062
  setState({
1055
1063
  status: "error",
@@ -1066,7 +1074,7 @@ function useXlsxWorkbook(document, fileName) {
1066
1074
  } catch (error) {
1067
1075
  worker.terminate();
1068
1076
  if (workerRef.current === worker) workerRef.current = null;
1069
- if (cancelled || requestId !== requestIdRef.current) return;
1077
+ if (!isCurrent()) return;
1070
1078
  const normalized = error instanceof Error ? error : new Error(String(error));
1071
1079
  failDocument?.(normalized);
1072
1080
  setState({
@@ -1087,7 +1095,8 @@ function useXlsxWorkbook(document, fileName) {
1087
1095
  logger,
1088
1096
  failDocument,
1089
1097
  baseUrl,
1090
- createWorker
1098
+ createWorker,
1099
+ isCurrentDocument
1091
1100
  ]);
1092
1101
  return state;
1093
1102
  }
@@ -2172,7 +2181,7 @@ var CellView = memo(function CellView({ cell, style, covered = false, isFirstRow
2172
2181
  borderLeft: covered ? void 0 : isFirstCol ? css.borderLeft ?? DEFAULT_GRID_LINE : css.borderLeft
2173
2182
  };
2174
2183
  return /* @__PURE__ */ jsx("div", {
2175
- className: "absolute px-1 text-foreground text-sm",
2184
+ className: "absolute px-1 text-sm text-foreground",
2176
2185
  style: finalCss,
2177
2186
  children: cell && /* @__PURE__ */ jsx("span", {
2178
2187
  className: cn(cell.formulaState === "unevaluated" && "text-muted-foreground italic"),
@@ -2206,7 +2215,7 @@ var SelectedCellOverlay = ({ cell, style, rect }) => {
2206
2215
  };
2207
2216
  return /* @__PURE__ */ jsx("div", {
2208
2217
  "data-testid": "xlsx-grid-selected-overlay",
2209
- className: "z-10 px-1 text-foreground text-sm shadow-md outline outline-primary",
2218
+ className: "z-10 px-1 text-sm text-foreground shadow-md outline outline-primary",
2210
2219
  style: finalCss,
2211
2220
  children: cell && /* @__PURE__ */ jsx("span", {
2212
2221
  className: cn(cell.formulaState === "unevaluated" && "text-muted-foreground italic"),
@@ -2219,7 +2228,7 @@ var SelectedCellOverlay = ({ cell, style, rect }) => {
2219
2228
  var UnsupportedChartPlaceholder = ({ chart }) => {
2220
2229
  const { t } = useTranslation();
2221
2230
  return /* @__PURE__ */ jsxs("div", {
2222
- className: "flex h-full w-full flex-col items-center justify-center gap-1 rounded-md border border-border border-dashed text-center text-muted-foreground text-xs",
2231
+ className: "text-muted-foreground flex h-full w-full flex-col items-center justify-center gap-1 rounded-md border border-dashed border-border text-center text-xs",
2223
2232
  children: [/* @__PURE__ */ jsx("span", { children: t("xlsx_preview.chart_unsupported") }), chart.rawTypeName && /* @__PURE__ */ jsx("span", { children: chart.rawTypeName })]
2224
2233
  });
2225
2234
  };
@@ -2292,6 +2301,20 @@ var XlsxGrid = ({ sheet, styles, imageUrls, zoom, onSelectCell, pickerActive, re
2292
2301
  estimateSize: (index) => colLayout.sizes[index] * zoom,
2293
2302
  overscan: OVERSCAN
2294
2303
  });
2304
+ useLayoutEffect(() => {
2305
+ rowVirtualizer.measure();
2306
+ }, [
2307
+ rowVirtualizer,
2308
+ rowLayout,
2309
+ zoom
2310
+ ]);
2311
+ useLayoutEffect(() => {
2312
+ colVirtualizer.measure();
2313
+ }, [
2314
+ colVirtualizer,
2315
+ colLayout,
2316
+ zoom
2317
+ ]);
2295
2318
  const virtualRows = rowVirtualizer.getVirtualItems();
2296
2319
  const virtualCols = colVirtualizer.getVirtualItems();
2297
2320
  const readViewport = useCallback((el) => ({
@@ -2406,6 +2429,12 @@ var XlsxGrid = ({ sheet, styles, imageUrls, zoom, onSelectCell, pickerActive, re
2406
2429
  commitSelection(null);
2407
2430
  }, [applySelection, commitSelection]);
2408
2431
  const pickerWasActiveRef = useRef(false);
2432
+ const previousSheetRef = useRef(sheet);
2433
+ useEffect(() => {
2434
+ if (previousSheetRef.current === sheet) return;
2435
+ previousSheetRef.current = sheet;
2436
+ clearSelection();
2437
+ }, [sheet, clearSelection]);
2409
2438
  useEffect(() => {
2410
2439
  const isActive = Boolean(pickerActive);
2411
2440
  if (pickerWasActiveRef.current === isActive) return;
@@ -2776,7 +2805,7 @@ var XlsxGrid = ({ sheet, styles, imageUrls, zoom, onSelectCell, pickerActive, re
2776
2805
  className: "absolute",
2777
2806
  style: zoomTransform,
2778
2807
  children: virtualCols.map((vc) => /* @__PURE__ */ jsx("div", {
2779
- className: "absolute flex items-center justify-center border-border border-r text-muted-foreground text-xs",
2808
+ className: "text-muted-foreground absolute flex items-center justify-center border-r border-border text-xs",
2780
2809
  style: {
2781
2810
  left: axisOffset(colLayout, vc.index),
2782
2811
  width: colLayout.sizes[vc.index],
@@ -2798,7 +2827,7 @@ var XlsxGrid = ({ sheet, styles, imageUrls, zoom, onSelectCell, pickerActive, re
2798
2827
  className: "absolute",
2799
2828
  style: zoomTransform,
2800
2829
  children: virtualRows.map((vr) => /* @__PURE__ */ jsx("div", {
2801
- className: "absolute flex items-center justify-center border-border border-b text-muted-foreground text-xs",
2830
+ className: "text-muted-foreground absolute flex items-center justify-center border-b border-border text-xs",
2802
2831
  style: {
2803
2832
  top: axisOffset(rowLayout, vr.index),
2804
2833
  height: rowLayout.sizes[vr.index],
@@ -3037,23 +3066,21 @@ function SpreadsheetFilePreview({ sourceId, fileName, document: previewDocument,
3037
3066
  const [selectedCell, setSelectedCell] = useState(null);
3038
3067
  const [chartRenderer, setChartRenderer] = useState(null);
3039
3068
  const [imageUrls, setImageUrls] = useState({});
3040
- const model = state.status === "ready" ? state.model : null;
3069
+ const model = "model" in state ? state.model : null;
3041
3070
  const sheets = useMemo(() => model ? visibleSheets(model) : [], [model]);
3042
3071
  const activeSheet = sheets.find((sheet) => sheet.name === activeSheetName) ?? sheets[0] ?? null;
3043
3072
  useEffect(() => {
3044
3073
  setSelectedCell(null);
3045
- if (sheets.length === 0) {
3046
- setActiveSheetName(null);
3047
- return;
3048
- }
3074
+ if (sheets.length === 0) return;
3049
3075
  if (!sheets.some((sheet) => sheet.name === activeSheetName)) setActiveSheetName(sheets[0].name);
3050
3076
  }, [sheets, activeSheetName]);
3051
3077
  const handleSelectCell = useCallback((info) => setSelectedCell(info && activeSheet ? {
3052
3078
  ...info,
3053
- sheet: activeSheet
3054
- } : null), [activeSheet]);
3079
+ sheet: activeSheet,
3080
+ document: previewDocument
3081
+ } : null), [activeSheet, previewDocument]);
3055
3082
  const selectionReference = useMemo(() => {
3056
- if (!selectedCell || !activeSheet || selectedCell.sheet !== activeSheet) return null;
3083
+ if (state.status !== "ready" || !selectedCell || !activeSheet || selectedCell.sheet !== activeSheet || selectedCell.document !== previewDocument) return null;
3057
3084
  return createPreviewSelection({
3058
3085
  sourceId,
3059
3086
  anchor: {
@@ -3068,7 +3095,8 @@ function SpreadsheetFilePreview({ sourceId, fileName, document: previewDocument,
3068
3095
  selectedCell,
3069
3096
  activeSheet,
3070
3097
  sourceId,
3071
- previewDocument.revision
3098
+ previewDocument,
3099
+ state.status
3072
3100
  ]);
3073
3101
  const captureArmedRef = useRef(false);
3074
3102
  useEffect(() => {
@@ -3127,9 +3155,9 @@ function SpreadsheetFilePreview({ sourceId, fileName, document: previewDocument,
3127
3155
  });
3128
3156
  }, []);
3129
3157
  let content;
3130
- if (state.status === "loading" || state.status === "idle") content = /* @__PURE__ */ jsxs("div", {
3158
+ if (state.status === "loading" && !model || state.status === "idle") content = /* @__PURE__ */ jsxs("div", {
3131
3159
  role: "status",
3132
- className: "flex h-full w-full items-center justify-center gap-2 bg-background text-muted-foreground text-sm",
3160
+ className: "text-muted-foreground flex h-full w-full items-center justify-center gap-2 bg-background text-sm",
3133
3161
  children: [/* @__PURE__ */ jsx(LoaderCircle, {
3134
3162
  className: "size-4 animate-spin",
3135
3163
  "aria-hidden": true
@@ -3157,13 +3185,14 @@ function SpreadsheetFilePreview({ sourceId, fileName, document: previewDocument,
3157
3185
  className: "relative flex h-full w-full flex-col overflow-hidden bg-background",
3158
3186
  children: [/* @__PURE__ */ jsx("div", {
3159
3187
  className: "min-h-0 flex-1",
3188
+ inert: state.status !== "ready",
3160
3189
  children: /* @__PURE__ */ jsx(XlsxGrid_default, {
3161
3190
  sheet: activeSheet,
3162
3191
  styles: model.styles,
3163
3192
  imageUrls,
3164
3193
  zoom,
3165
3194
  onSelectCell: handleSelectCell,
3166
- pickerActive: onSelection !== void 0,
3195
+ pickerActive: state.status === "ready" && onSelection !== void 0,
3167
3196
  renderChart
3168
3197
  }, activeSheet.name)
3169
3198
  }), /* @__PURE__ */ jsxs("div", {
@@ -3187,9 +3216,9 @@ function SpreadsheetFilePreview({ sourceId, fileName, document: previewDocument,
3187
3216
  })
3188
3217
  }),
3189
3218
  /* @__PURE__ */ jsxs("div", {
3190
- className: "flex min-w-0 flex-1 items-center justify-end gap-2 text-muted-foreground text-xs",
3219
+ className: "text-muted-foreground flex min-w-0 flex-1 items-center justify-end gap-2 text-xs",
3191
3220
  children: [statusBarText ? /* @__PURE__ */ jsx("span", {
3192
- className: "selectable cursor-text select-text truncate",
3221
+ className: "selectable cursor-text truncate select-text",
3193
3222
  "data-testid": "xlsx-preview-status-bar",
3194
3223
  children: statusBarText
3195
3224
  }) : null, cellOnActiveSheet?.cell?.formulaState === "unevaluated" ? /* @__PURE__ */ jsx("span", {
@@ -1,10 +1,12 @@
1
1
  import { i as __toESM } from "./rolldown-runtime-B-lAHAz2.js";
2
2
  import { r as readPreviewDocument, t as PreviewError } from "./source-Rx3etU3y.js";
3
- import { I as LoaderCircle, N as EmptyState, c as FilePreviewToolbar, i as FilePreviewLayout, o as usePreviewHost, r as FilePreviewToolbarButton, t as useTranslation } from "./useTranslation-DMhY277o.js";
4
- import { n as ChevronRight, r as ChevronLeft, t as RotateCcw } from "./rotate-ccw-YWx7RzY9.js";
5
- import { r as CircleAlert, t as createPreviewSelection } from "./selection-CQPD5a6C.js";
6
- import { n as ZoomIn, t as ZoomOut } from "./zoom-out-DLOQGt1e.js";
7
- import { t as FilePreviewTooLarge } from "./FilePreviewTooLarge-DkHa6toZ.js";
3
+ import { a as usePreviewHost, d as EmptyState, m as LoaderCircle, r as FilePreviewLayout, s as FilePreviewToolbar, t as useTranslation } from "./useTranslation-BJY4EKsz.js";
4
+ import { n as ChevronRight, r as ChevronLeft, t as RotateCcw } from "./rotate-ccw-Bx-jjhVm.js";
5
+ import { t as CircleAlert } from "./circle-alert-sByhBDN4.js";
6
+ import { n as ZoomIn, t as ZoomOut } from "./zoom-out-B2XJ64Kx.js";
7
+ import { t as FilePreviewToolbarButton } from "./FilePreviewToolbarButton-ptqwCTnz.js";
8
+ import { t as FilePreviewTooLarge } from "./FilePreviewTooLarge-DC7vPyMI.js";
9
+ import { t as createPreviewSelection } from "./selection-B0INvh6t.js";
8
10
  import { t as attachTouchPinch } from "./touchPinch-C4fl-9fg.js";
9
11
  import { t as require_jszip_min } from "./jszip.min-uwqlc4KL.js";
10
12
  import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
@@ -3834,7 +3836,7 @@ function sanitizeHyperlinks(body) {
3834
3836
  });
3835
3837
  }
3836
3838
  function WordFilePreview({ sourceId, fileName, document: previewDocument, onSelection }) {
3837
- const { options, failDocument } = usePreviewHost();
3839
+ const { options, failDocument, isCurrentDocument } = usePreviewHost();
3838
3840
  const { t } = useTranslation();
3839
3841
  const docxClassName = `docx-preview-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
3840
3842
  const containerRef = useRef(null);
@@ -3851,7 +3853,10 @@ function WordFilePreview({ sourceId, fileName, document: previewDocument, onSele
3851
3853
  const [zoom, setZoom] = useState(DOCX_PREVIEW_DEFAULT_ZOOM);
3852
3854
  const [fitZoom, setFitZoom] = useState(DOCX_PREVIEW_DEFAULT_ZOOM);
3853
3855
  const [manualZoom, setManualZoom] = useState(false);
3854
- const [normalizeSymbolBullets] = useState(() => options?.docx?.normalizeSymbolBullets === true);
3856
+ const normalizeSymbolBulletsRef = useRef(options?.docx?.normalizeSymbolBullets === true);
3857
+ useLayoutEffect(() => {
3858
+ normalizeSymbolBulletsRef.current = options?.docx?.normalizeSymbolBullets === true;
3859
+ }, [options?.docx?.normalizeSymbolBullets]);
3855
3860
  const fitWidth = options?.docx?.initialZoom === "fit-width";
3856
3861
  const minZoom = fitWidth ? Math.min(DOCX_PREVIEW_MIN_ZOOM, fitZoom) : DOCX_PREVIEW_MIN_ZOOM;
3857
3862
  useLayoutEffect(() => {
@@ -3921,16 +3926,10 @@ function WordFilePreview({ sourceId, fileName, document: previewDocument, onSele
3921
3926
  if (!bodyContainer || !styleContainer) return;
3922
3927
  const controller = new AbortController();
3923
3928
  const token = ++renderTokenRef.current;
3924
- const isCurrent = () => renderTokenRef.current === token;
3929
+ const isCurrent = () => renderTokenRef.current === token && isCurrentDocument?.() !== false;
3930
+ const normalizeSymbolBullets = normalizeSymbolBulletsRef.current;
3925
3931
  setError(null);
3926
3932
  setLoading(true);
3927
- setCurrentPage(0);
3928
- setPageCount(0);
3929
- setZoom(DOCX_PREVIEW_DEFAULT_ZOOM);
3930
- setFitZoom(DOCX_PREVIEW_DEFAULT_ZOOM);
3931
- setManualZoom(false);
3932
- fitScrollTopRef.current = null;
3933
- pinchAnchorRef.current = null;
3934
3933
  const stagingHost = document.createElement("div");
3935
3934
  const stagingBody = document.createElement("div");
3936
3935
  const stagingStyle = document.createElement("div");
@@ -3965,11 +3964,17 @@ function WordFilePreview({ sourceId, fileName, document: previewDocument, onSele
3965
3964
  page.classList.add("docx-preview-page", "docx-preview");
3966
3965
  });
3967
3966
  sanitizeHyperlinks(stagingBody);
3967
+ const scrollTop = containerRef.current?.scrollTop ?? 0;
3968
+ const scrollLeft = containerRef.current?.scrollLeft ?? 0;
3968
3969
  bodyContainer.replaceChildren(...stagingBody.childNodes);
3969
3970
  styleContainer.replaceChildren(...stagingStyle.childNodes);
3971
+ if (containerRef.current) {
3972
+ containerRef.current.scrollTop = scrollTop;
3973
+ containerRef.current.scrollLeft = scrollLeft;
3974
+ }
3970
3975
  const nextPageCount = Math.max(pages.length, 1);
3971
3976
  setPageCount(nextPageCount);
3972
- setCurrentPage(nextPageCount > 0 ? 1 : 0);
3977
+ setCurrentPage((page) => clamp(page, 1, nextPageCount));
3973
3978
  focusContainer();
3974
3979
  } catch (loadError) {
3975
3980
  if (!isCurrent()) return;
@@ -3984,8 +3989,6 @@ function WordFilePreview({ sourceId, fileName, document: previewDocument, onSele
3984
3989
  return () => {
3985
3990
  controller.abort();
3986
3991
  renderTokenRef.current += 1;
3987
- bodyContainer.innerHTML = "";
3988
- styleContainer.innerHTML = "";
3989
3992
  stagingHost.remove();
3990
3993
  };
3991
3994
  }, [
@@ -3994,12 +3997,12 @@ function WordFilePreview({ sourceId, fileName, document: previewDocument, onSele
3994
3997
  previewDocument,
3995
3998
  failDocument,
3996
3999
  docxClassName,
3997
- normalizeSymbolBullets
4000
+ isCurrentDocument
3998
4001
  ]);
3999
4002
  useEffect(() => {
4000
4003
  const scrollRoot = containerRef.current;
4001
4004
  const bodyContainer = bodyRef.current;
4002
- if (!scrollRoot || !bodyContainer || pageCount <= 0) return;
4005
+ if (!scrollRoot || !bodyContainer || pageCount <= 0 || loading) return;
4003
4006
  const pages = Array.from(bodyContainer.querySelectorAll(".docx-preview-page"));
4004
4007
  if (pages.length === 0) return;
4005
4008
  const visiblePages = /* @__PURE__ */ new Set();
@@ -4022,7 +4025,7 @@ function WordFilePreview({ sourceId, fileName, document: previewDocument, onSele
4022
4025
  });
4023
4026
  pages.forEach((page) => observer.observe(page));
4024
4027
  return () => observer.disconnect();
4025
- }, [pageCount]);
4028
+ }, [pageCount, loading]);
4026
4029
  const handlePick = useCallback((event) => {
4027
4030
  if (!onSelection || !(event.target instanceof Element)) return;
4028
4031
  if (event.target.closest("a[href]")) event.preventDefault();
@@ -4105,12 +4108,12 @@ function WordFilePreview({ sourceId, fileName, document: previewDocument, onSele
4105
4108
  "data-picker": onSelection ? "true" : void 0,
4106
4109
  onClick: handlePick,
4107
4110
  style: contentStyle,
4108
- className: "mx-auto w-fit min-w-0 [&[data-picker=true]_p[data-docx-part=body]:not([data-docx-picked=true]):hover]:bg-primary/10 [&[data-picker=true]_p[data-docx-part=body]]:cursor-pointer [&_.docx-preview-wrapper]:mx-auto [&_.docx-preview]:box-border [&_.docx-preview]:max-w-full [&_p[data-docx-picked=true]]:bg-primary/15 [&_p[data-docx-picked=true]]:outline [&_p[data-docx-picked=true]]:outline-1 [&_p[data-docx-picked=true]]:outline-primary/60 [&_section]:overflow-hidden [&_section]:rounded-sm [&_section]:shadow-md"
4111
+ className: "mx-auto w-fit min-w-0 [&_.docx-preview]:box-border [&_.docx-preview]:max-w-full [&_.docx-preview-wrapper]:mx-auto [&_p[data-docx-picked=true]]:bg-primary/15 [&_p[data-docx-picked=true]]:outline [&_p[data-docx-picked=true]]:outline-1 [&_p[data-docx-picked=true]]:outline-primary/60 [&_section]:overflow-hidden [&_section]:rounded-sm [&_section]:shadow-md [&[data-picker=true]_p[data-docx-part=body]]:cursor-pointer [&[data-picker=true]_p[data-docx-part=body]:not([data-docx-picked=true]):hover]:bg-primary/10"
4109
4112
  })]
4110
4113
  }),
4111
4114
  loading ? /* @__PURE__ */ jsxs("div", {
4112
4115
  role: "status",
4113
- className: "absolute inset-0 flex items-center justify-center gap-2 bg-background text-sm text-muted-foreground",
4116
+ className: "text-muted-foreground absolute inset-0 flex items-center justify-center gap-2 bg-background text-sm",
4114
4117
  children: [/* @__PURE__ */ jsx(LoaderCircle, {
4115
4118
  className: "size-4 animate-spin",
4116
4119
  "aria-hidden": true
@@ -0,0 +1,31 @@
1
+ import { g as createLucideIcon } from "./useTranslation-BJY4EKsz.js";
2
+ /**
3
+ * @license lucide-react v0.545.0 - ISC
4
+ *
5
+ * This source code is licensed under the ISC license.
6
+ * See the LICENSE file in the root directory of this source tree.
7
+ */
8
+ var CircleAlert = createLucideIcon("circle-alert", [
9
+ ["circle", {
10
+ cx: "12",
11
+ cy: "12",
12
+ r: "10",
13
+ key: "1mglay"
14
+ }],
15
+ ["line", {
16
+ x1: "12",
17
+ x2: "12",
18
+ y1: "8",
19
+ y2: "12",
20
+ key: "1pkeuh"
21
+ }],
22
+ ["line", {
23
+ x1: "12",
24
+ x2: "12.01",
25
+ y1: "16",
26
+ y2: "16",
27
+ key: "4dfq90"
28
+ }]
29
+ ]);
30
+ //#endregion
31
+ export { CircleAlert as t };
@@ -23,7 +23,7 @@ var imageFilePreviewPlugin = {
23
23
  "image/vnd.microsoft.icon",
24
24
  "image/svg+xml"
25
25
  ],
26
- load: () => import("./ImageFilePreview-Be0jdBJ-.js")
26
+ load: () => import("./ImageFilePreview-C_P1iIEi.js")
27
27
  };
28
28
  //#endregion
29
29
  export { imageFilePreviewPlugin as t };
package/dist/react.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { n as assertPreviewRange, t as PreviewError } from "./source-Rx3etU3y.js";
2
- import { F as cn, I as LoaderCircle, L as FileWarning, N as EmptyState, R as createLucideIcon, a as PreviewHostContext, c as FilePreviewToolbar, f as DialogPortalContainerProvider, i as FilePreviewLayout, l as FilePreviewToolbarPortalHost, n as I18nContext, o as usePreviewHost, p as PortalContainerProvider, r as FilePreviewToolbarButton, t as useTranslation, u as FilePreviewToolbarPortalProvider } from "./useTranslation-DMhY277o.js";
3
- import { t as imageFilePreviewPlugin } from "./imageFilePreviewPlugin-CYLM2bwR.js";
4
- import { Component, Suspense, createContext, createElement, lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { a as usePreviewHost, c as FilePreviewToolbarPortalHost, d as EmptyState, g as createLucideIcon, h as FileWarning, i as PreviewHostContext, l as FilePreviewToolbarPortalProvider, m as LoaderCircle, n as I18nContext, p as cn, r as FilePreviewLayout, s as FilePreviewToolbar, t as useTranslation } from "./useTranslation-BJY4EKsz.js";
3
+ import { n as DialogPortalContainerProvider, r as PortalContainerProvider, t as FilePreviewToolbarButton } from "./FilePreviewToolbarButton-ptqwCTnz.js";
4
+ import { t as imageFilePreviewPlugin } from "./imageFilePreviewPlugin-D6hV1Hm1.js";
5
+ import { Component, Suspense, createContext, createElement, lazy, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  /**
7
8
  * @license lucide-react v0.545.0 - ISC
@@ -31,28 +32,38 @@ var plugins = [
31
32
  id: "pdf",
32
33
  extensions: ["pdf"],
33
34
  mediaTypes: ["application/pdf"],
34
- load: () => import("./PdfFilePreview-D3Ie0_mm.js"),
35
+ load: () => import("./PdfFilePreview-CbrMcPzo.js"),
35
36
  supportsSelectionReference: true
36
37
  },
37
38
  {
38
39
  id: "powerpoint",
39
40
  extensions: ["pptx"],
40
41
  mediaTypes: ["application/vnd.openxmlformats-officedocument.presentationml.presentation"],
41
- load: () => import("./PowerPointFilePreview-CyQHTp4B.js"),
42
+ load: () => import("./PowerPointFilePreview-9E6kUy_A.js"),
42
43
  supportsSelectionReference: true
43
44
  },
45
+ {
46
+ id: "rtf",
47
+ extensions: ["rtf"],
48
+ mediaTypes: [
49
+ "application/rtf",
50
+ "text/rtf",
51
+ "application/x-rtf"
52
+ ],
53
+ load: () => import("./RtfFilePreview-Bgl6lmWy.js")
54
+ },
44
55
  {
45
56
  id: "spreadsheet",
46
57
  extensions: ["xlsx"],
47
58
  mediaTypes: ["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
48
- load: () => import("./SpreadsheetFilePreview-DJdoyeis.js"),
59
+ load: () => import("./SpreadsheetFilePreview-PWFG3UY3.js"),
49
60
  supportsSelectionReference: true
50
61
  },
51
62
  {
52
63
  id: "word",
53
64
  extensions: ["docx"],
54
65
  mediaTypes: ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
55
- load: () => import("./WordFilePreview-Tvm-vK91.js"),
66
+ load: () => import("./WordFilePreview-hfALJk2N.js"),
56
67
  supportsSelectionReference: true
57
68
  }
58
69
  ];
@@ -2997,6 +3008,8 @@ function PreviewSession({ source, header, className, style, refreshKey = 0, reso
2997
3008
  const [session, setSession] = useState(null);
2998
3009
  const [failure, setFailure] = useState(null);
2999
3010
  const closeRef = useRef(void 0);
3011
+ const plugin = useMemo(() => resolvePreviewPlugin(source.name, source.mediaType), [source.name, source.mediaType]);
3012
+ const Plugin = useMemo(() => plugin ? lazy(plugin.load) : null, [plugin]);
3000
3013
  const hasFailedRef = useRef(false);
3001
3014
  const callbacks = useRef({
3002
3015
  onSelection,
@@ -3010,7 +3023,7 @@ function PreviewSession({ source, header, className, style, refreshKey = 0, reso
3010
3023
  onError,
3011
3024
  onRequestOpen
3012
3025
  };
3013
- const failDocument = useCallback((error) => {
3026
+ const reportFailure = useCallback((error) => {
3014
3027
  if (hasFailedRef.current) return;
3015
3028
  hasFailedRef.current = true;
3016
3029
  closeRef.current?.();
@@ -3024,7 +3037,24 @@ function PreviewSession({ source, header, className, style, refreshKey = 0, reso
3024
3037
  });
3025
3038
  callbacks.current.onError?.(normalized);
3026
3039
  }, []);
3027
- const reportSelection = useCallback((selection) => callbacks.current.onSelection?.(selection), []);
3040
+ const requestIdRef = useRef(0);
3041
+ useLayoutEffect(() => {
3042
+ requestIdRef.current += 1;
3043
+ return () => {
3044
+ requestIdRef.current += 1;
3045
+ };
3046
+ }, [
3047
+ source,
3048
+ refreshKey,
3049
+ plugin
3050
+ ]);
3051
+ const isCurrentSession = useCallback(() => session?.requestId === requestIdRef.current, [session]);
3052
+ const failDocument = useCallback((error) => {
3053
+ if (isCurrentSession()) reportFailure(error);
3054
+ }, [isCurrentSession, reportFailure]);
3055
+ const reportSelection = useCallback((selection) => {
3056
+ if (isCurrentSession()) callbacks.current.onSelection?.(selection);
3057
+ }, [isCurrentSession]);
3028
3058
  const reportDiagnostic = useCallback((diagnostic) => callbacks.current.onDiagnostic?.(diagnostic), []);
3029
3059
  const requestOpen = useCallback((reason) => callbacks.current.onRequestOpen?.(reason), []);
3030
3060
  const hasOpenAction = onRequestOpen !== void 0;
@@ -3034,7 +3064,8 @@ function PreviewSession({ source, header, className, style, refreshKey = 0, reso
3034
3064
  options,
3035
3065
  onDiagnostic: reportDiagnostic,
3036
3066
  onRequestOpen: hasOpenAction ? requestOpen : void 0,
3037
- failDocument
3067
+ failDocument,
3068
+ isCurrentDocument: isCurrentSession
3038
3069
  }), [
3039
3070
  root,
3040
3071
  resources,
@@ -3042,15 +3073,14 @@ function PreviewSession({ source, header, className, style, refreshKey = 0, reso
3042
3073
  reportDiagnostic,
3043
3074
  requestOpen,
3044
3075
  hasOpenAction,
3045
- failDocument
3076
+ failDocument,
3077
+ isCurrentSession
3046
3078
  ]);
3047
- const plugin = useMemo(() => resolvePreviewPlugin(source.name, source.mediaType), [source.name, source.mediaType]);
3048
- const Plugin = useMemo(() => plugin ? lazy(plugin.load) : null, [plugin]);
3049
3079
  useEffect(() => {
3050
- setSession(null);
3051
3080
  setFailure(null);
3052
3081
  hasFailedRef.current = false;
3053
3082
  if (!plugin) return;
3083
+ const requestId = requestIdRef.current;
3054
3084
  const controller = new AbortController();
3055
3085
  let opened = null;
3056
3086
  let closed = false;
@@ -3075,8 +3105,9 @@ function PreviewSession({ source, header, className, style, refreshKey = 0, reso
3075
3105
  assertPreviewRange(opened.size, 0, opened.size);
3076
3106
  setSession({
3077
3107
  source,
3078
- refreshKey,
3079
- document: opened
3108
+ requestId,
3109
+ document: opened,
3110
+ plugin
3080
3111
  });
3081
3112
  } catch (error) {
3082
3113
  close();
@@ -3085,7 +3116,7 @@ function PreviewSession({ source, header, className, style, refreshKey = 0, reso
3085
3116
  source,
3086
3117
  refreshKey
3087
3118
  });
3088
- failDocument(error);
3119
+ reportFailure(error);
3089
3120
  }
3090
3121
  })();
3091
3122
  return () => {
@@ -3097,9 +3128,9 @@ function PreviewSession({ source, header, className, style, refreshKey = 0, reso
3097
3128
  source,
3098
3129
  refreshKey,
3099
3130
  plugin,
3100
- failDocument
3131
+ reportFailure
3101
3132
  ]);
3102
- const ready = session?.source === source && session.refreshKey === refreshKey ? session : null;
3133
+ const ready = session?.source.id === source.id && session.plugin === plugin ? session : null;
3103
3134
  const failed = failure?.source === source && failure.refreshKey === refreshKey;
3104
3135
  const content = !Plugin ? /* @__PURE__ */ jsx(PreviewState, { kind: "unsupported" }) : failed ? /* @__PURE__ */ jsx(PreviewState, { kind: "error" }) : !ready ? /* @__PURE__ */ jsx(PreviewState, { kind: "loading" }) : /* @__PURE__ */ jsx(m, {
3105
3136
  resetKeys: [ready.document],
@@ -3108,14 +3139,14 @@ function PreviewSession({ source, header, className, style, refreshKey = 0, reso
3108
3139
  children: /* @__PURE__ */ jsx(Suspense, {
3109
3140
  fallback: /* @__PURE__ */ jsx(PreviewState, { kind: "loading" }),
3110
3141
  children: /* @__PURE__ */ jsx(Plugin, {
3111
- sourceId: source.id,
3112
- fileName: source.name,
3113
- mediaType: source.mediaType,
3142
+ sourceId: ready.source.id,
3143
+ fileName: ready.source.name,
3144
+ mediaType: ready.source.mediaType,
3114
3145
  document: ready.document,
3115
3146
  onSelection: onSelection ? reportSelection : void 0
3116
- }, `${source.id}:${refreshKey}:${ready.document.revision}`)
3147
+ })
3117
3148
  })
3118
- }, `${source.id}:${refreshKey}:${ready.document.revision}`);
3149
+ }, `${source.id}:${plugin?.id}`);
3119
3150
  return /* @__PURE__ */ jsx("div", {
3120
3151
  ref: setRoot,
3121
3152
  "data-file-preview-root": "",
@@ -1,4 +1,4 @@
1
- import { R as createLucideIcon } from "./useTranslation-DMhY277o.js";
1
+ import { g as createLucideIcon } from "./useTranslation-BJY4EKsz.js";
2
2
  /**
3
3
  * @license lucide-react v0.545.0 - ISC
4
4
  *
@@ -1,34 +1,4 @@
1
1
  import { n as SELECTION_EXCERPT_MAX_LENGTH } from "./documentAnchor-J7GGLU15.js";
2
- import { R as createLucideIcon } from "./useTranslation-DMhY277o.js";
3
- /**
4
- * @license lucide-react v0.545.0 - ISC
5
- *
6
- * This source code is licensed under the ISC license.
7
- * See the LICENSE file in the root directory of this source tree.
8
- */
9
- var CircleAlert = createLucideIcon("circle-alert", [
10
- ["circle", {
11
- cx: "12",
12
- cy: "12",
13
- r: "10",
14
- key: "1mglay"
15
- }],
16
- ["line", {
17
- x1: "12",
18
- x2: "12",
19
- y1: "8",
20
- y2: "12",
21
- key: "1pkeuh"
22
- }],
23
- ["line", {
24
- x1: "12",
25
- x2: "12.01",
26
- y1: "16",
27
- y2: "16",
28
- key: "4dfq90"
29
- }]
30
- ]);
31
- //#endregion
32
2
  //#region src/selection.ts
33
3
  var SELECTION_WHITESPACE = /[\t\n\v\f\r \u0085\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff\u001c-\u001f]+/g;
34
4
  function normalizeSelectionText(text) {
@@ -44,4 +14,4 @@ function createPreviewSelection(input) {
44
14
  } : null;
45
15
  }
46
16
  //#endregion
47
- export { normalizeSelectionText as n, CircleAlert as r, createPreviewSelection as t };
17
+ export { normalizeSelectionText as n, createPreviewSelection as t };