@deepseek-ai/dsh-client-ui-sidebar-documentpreview 0.1.7-alpha.1 → 0.1.7-rc.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.
package/lib/client.js CHANGED
@@ -130,6 +130,16 @@ window.__ModuleLoader__.load({
130
130
  name: trimmed.slice(cut)
131
131
  };
132
132
  }
133
+ /**
134
+ * Address a decoded absolute file path through the authenticated file route.
135
+ * @param base - HTTP(S) application base, including its deployment prefix.
136
+ * @param path - Native file path; URL escapes in authored Markdown must already be decoded.
137
+ * @returns File URL, or undefined for unsupported transports and non-absolute paths.
138
+ */
139
+ function fileMediaUrl(base, path) {
140
+ if (!/^https?:/u.test(base) || !isAbsoluteWorkspacePath(path) || /^[/\\]{2}/u.test(path) || /[\u0000-\u001f\u007f]/u.test(path)) return void 0;
141
+ return new URL(`api/file?path=${encodeURIComponent(path)}`, base).href;
142
+ }
133
143
  //#endregion
134
144
  //#region lib/types/client/failure-line.js
135
145
  /** Render a byte count the way a person reads one. */
@@ -1502,7 +1512,6 @@ window.__ModuleLoader__.load({
1502
1512
  * @returns an HTTP(S) file URL, or undefined for unsupported or malformed destinations.
1503
1513
  */
1504
1514
  function markdownImageUrl(base, documentPath, destination) {
1505
- if (!base.startsWith("http:") && !base.startsWith("https:")) return void 0;
1506
1515
  const suffix = destination.search(/[?#]/u);
1507
1516
  let path;
1508
1517
  try {
@@ -1510,13 +1519,13 @@ window.__ModuleLoader__.load({
1510
1519
  } catch (_error) {
1511
1520
  return;
1512
1521
  }
1513
- if (path.length === 0 || path.includes("\0") || /^[/\\]{2}/u.test(path)) return void 0;
1522
+ if (path.length === 0) return void 0;
1514
1523
  if (!/^[a-z]:[/\\]/iu.test(path) && /^[a-z][a-z\d+.-]*:/iu.test(path)) return void 0;
1515
1524
  if (!isAbsoluteWorkspacePath(path)) {
1516
1525
  if (documentPath === void 0) return void 0;
1517
1526
  path = pathPartsOf(documentPath).directory + path;
1518
1527
  }
1519
- return new URL(`api/file?path=${encodeURIComponent(path)}`, base).href;
1528
+ return fileMediaUrl(base, path);
1520
1529
  }
1521
1530
  //#endregion
1522
1531
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-sidebar-documentpreview/src/client/markdown/MarkdownBody.module.css.mjs
@@ -1544,16 +1553,27 @@ window.__ModuleLoader__.load({
1544
1553
  const copyLabel = t("code.copy");
1545
1554
  const copiedLabel = t("code.copied");
1546
1555
  const footnotes = t("footnotes");
1556
+ const codeLabel = t("codeBlock.title");
1557
+ const wrapLabel = t("codeBlock.wrap");
1558
+ const unwrapLabel = t("codeBlock.unwrap");
1547
1559
  const labels = (0, react.useMemo)(() => ({
1548
1560
  code: {
1549
1561
  copyLabel,
1550
- copiedLabel
1562
+ copiedLabel,
1563
+ toolbarLabels: {
1564
+ codeLabel,
1565
+ wrapLabel,
1566
+ unwrapLabel
1567
+ }
1551
1568
  },
1552
1569
  footnotes
1553
1570
  }), [
1554
1571
  copyLabel,
1555
1572
  copiedLabel,
1556
- footnotes
1573
+ footnotes,
1574
+ codeLabel,
1575
+ wrapLabel,
1576
+ unwrapLabel
1557
1577
  ]);
1558
1578
  if (content.kind !== "text") return null;
1559
1579
  return (0, react_jsx_runtime.jsx)("div", {
@@ -4502,7 +4522,12 @@ document.open();document.write(html);document.close();
4502
4522
  frame.current?.setAttribute("data-document-zoom-mode", props.preference.kind);
4503
4523
  frame.current?.style.setProperty("--document-zoom", String(zoom));
4504
4524
  controls.current?.showZoom(zoom);
4505
- }, [props.preference.kind, zoom]);
4525
+ props.onRenderZoom?.(zoom);
4526
+ }, [
4527
+ props.onRenderZoom,
4528
+ props.preference.kind,
4529
+ zoom
4530
+ ]);
4506
4531
  (0, react.useEffect)(() => {
4507
4532
  const node = scrollport.current;
4508
4533
  const clearPendingPinch = () => {
@@ -4990,7 +5015,7 @@ document.open();document.write(html);document.close();
4990
5015
  }
4991
5016
  //#endregion
4992
5017
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-sidebar-documentpreview/src/client/code/CodeBody.module.css.mjs
4993
- const css$2 = ".Java6a_renderer{white-space:normal;flex-direction:column;flex:auto;width:100%;min-width:0;height:100%;min-height:0;display:flex;overflow:hidden}.Java6a_renderer .Java6a_code{--dsl-code-block-border-radius:0px;--dsl-code-block-line-white-space:pre;--dsl-code-block-background:transparent;flex-direction:column;flex:auto;min-width:0;height:100%;min-height:0;margin:0;display:flex;position:static}.Java6a_renderer .Java6a_code>[data-code-block-content]{flex:auto;min-width:0;min-height:0;display:block;position:relative;overflow:auto}.Java6a_renderer .Java6a_code>[data-code-block-content]::-webkit-scrollbar-track{margin:2px}.Java6a_renderer .Java6a_code pre{box-sizing:border-box;white-space:pre;word-break:normal;overflow-wrap:normal;min-width:100%;overflow:visible}.Java6a_renderer[data-wrap=true] .Java6a_code{--dsl-code-block-line-white-space:pre-wrap}.Java6a_renderer[data-wrap=true] .Java6a_code pre{white-space:pre-wrap;overflow-wrap:anywhere}";
5018
+ const css$2 = ".Java6a_renderer{white-space:normal;flex-direction:column;flex:auto;width:100%;min-width:0;height:100%;min-height:0;display:flex;overflow:hidden}.Java6a_renderer .Java6a_code{--dsl-code-block-border-radius:0px;--dsl-code-block-line-white-space:pre;--dsl-code-block-background:transparent;flex-direction:column;flex:auto;min-width:0;height:100%;min-height:0;margin:0;display:flex;position:static}.Java6a_renderer .Java6a_code>[data-code-block-content]{flex:auto;min-width:0;min-height:0;display:block;position:relative;overflow:auto}.Java6a_renderer .Java6a_code>[data-code-block-content]::-webkit-scrollbar-track{margin:2px}.Java6a_renderer .Java6a_code pre{box-sizing:border-box;white-space:pre;word-break:normal;overflow-wrap:normal;min-width:100%;padding:16px;overflow:visible}.Java6a_renderer[data-wrap=true] .Java6a_code{--dsl-code-block-line-white-space:pre-wrap}.Java6a_renderer[data-wrap=true] .Java6a_code pre{white-space:pre-wrap;overflow-wrap:anywhere}";
4994
5019
  const tagId$2 = "@deepseek-ai/dsh-client-ui-sidebar-documentpreview/CodeBody.module.css";
4995
5020
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
4996
5021
  const tag = document.createElement("style");
@@ -5021,9 +5046,15 @@ document.open();document.write(html);document.close();
5021
5046
  code: content.text,
5022
5047
  lang: language,
5023
5048
  streaming: !content.eof,
5049
+ wrap,
5024
5050
  lineNumbers: true,
5025
5051
  copyLabel: t("copy"),
5026
- copiedLabel: t("copied")
5052
+ copiedLabel: t("copied"),
5053
+ toolbarLabels: {
5054
+ codeLabel: t("codeBlock.title"),
5055
+ wrapLabel: t("codeBlock.wrap"),
5056
+ unwrapLabel: t("codeBlock.unwrap")
5057
+ }
5027
5058
  })
5028
5059
  });
5029
5060
  }
package/lib/client.pdf.js CHANGED
@@ -959,7 +959,7 @@ window.__ModuleLoader__.load({
959
959
  * @param pageNumber - 1-based selected page.
960
960
  * @param canvas - canvas owned by this render only.
961
961
  * @param signal - render lifetime.
962
- * @param pixelRatio - display pixel ratio.
962
+ * @param pixelRatio - device pixel ratio multiplied by the settled document zoom.
963
963
  * @param renderText - optional selectable text layer sharing this page and viewport.
964
964
  * @returns the page's CSS dimensions after rendering completes.
965
965
  */
@@ -32412,6 +32412,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
32412
32412
  const [load, setLoad] = (0, react.useState)();
32413
32413
  const [attempt, setAttempt] = (0, react.useState)(0);
32414
32414
  const [pageWidth, setPageWidth] = (0, react.useState)();
32415
+ const [renderZoom, setRenderZoom] = (0, react.useState)(1);
32415
32416
  const { retainTab, actions, t, ZoomViewport, zoomSurfaceClass } = props;
32416
32417
  const preference = view.zoom ?? FIT_WIDTH;
32417
32418
  const labels = {
@@ -32502,6 +32503,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
32502
32503
  onVisible: pageVisible,
32503
32504
  signal: tab.signal,
32504
32505
  onWidth: index === 0 ? setPageWidth : void 0,
32506
+ zoom: renderZoom,
32505
32507
  zoomSurfaceClass,
32506
32508
  t
32507
32509
  }, index))
@@ -32513,66 +32515,99 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
32513
32515
  signal: tab.signal,
32514
32516
  scrollportRef: props.scrollportRef,
32515
32517
  onPreference: setPreference,
32518
+ onRenderZoom: setRenderZoom,
32516
32519
  children: pages
32517
32520
  });
32518
32521
  }
32519
- function PdfPage({ document, page, requested: initiallyRequested, onVisible, signal, onWidth, zoomSurfaceClass, t }) {
32522
+ function PdfPage({ document, page, requested: initiallyRequested, onVisible, signal, onWidth, zoom, zoomSurfaceClass, t }) {
32520
32523
  const host = (0, react.useRef)(null);
32521
32524
  const canvas = (0, react.useRef)(null);
32522
32525
  const text = (0, react.useRef)(null);
32523
32526
  const [width, setWidth] = (0, react.useState)();
32524
32527
  const [requested, setRequested] = (0, react.useState)(initiallyRequested);
32528
+ const [visible, setVisible] = (0, react.useState)(initiallyRequested);
32529
+ const renderedZoom = (0, react.useRef)();
32530
+ const pendingRender = (0, react.useRef)(Promise.resolve());
32531
+ const textTask = (0, react.useRef)();
32525
32532
  const [state, setState] = (0, react.useState)("loading");
32526
32533
  const [failure, setFailure] = (0, react.useState)();
32527
32534
  const [attempt, setAttempt] = (0, react.useState)(0);
32528
32535
  (0, react.useEffect)(() => {
32529
- const node = host.current;
32536
+ const node = state === "ready" ? canvas.current : host.current;
32530
32537
  if (typeof IntersectionObserver === "undefined") {
32531
32538
  setRequested(true);
32539
+ setVisible(true);
32532
32540
  return;
32533
32541
  }
32534
32542
  let disposed = false;
32535
32543
  const observer = new IntersectionObserver((entries) => {
32536
- if (disposed || !entries.some((entry) => entry.isIntersecting)) return;
32544
+ if (disposed) return;
32545
+ const intersects = entries.some((entry) => entry.isIntersecting);
32546
+ setVisible(intersects);
32547
+ if (!intersects) return;
32537
32548
  setRequested(true);
32538
32549
  onVisible(page);
32539
- observer.disconnect();
32540
32550
  }, { rootMargin: "100% 0px" });
32541
32551
  observer.observe(node);
32542
32552
  return () => {
32543
32553
  disposed = true;
32544
32554
  observer.disconnect();
32545
32555
  };
32546
- }, [page, onVisible]);
32556
+ }, [
32557
+ page,
32558
+ onVisible,
32559
+ state
32560
+ ]);
32561
+ (0, react.useEffect)(() => () => {
32562
+ textTask.current?.cancel();
32563
+ }, []);
32547
32564
  (0, react.useEffect)(() => {
32548
32565
  if (!requested) return;
32566
+ setFailure(void 0);
32567
+ if (renderedZoom.current !== void 0 && (!visible || renderedZoom.current === zoom)) return;
32549
32568
  const node = canvas.current;
32550
32569
  const lifetime = new AbortController();
32551
32570
  const renderSignal = AbortSignal.any([lifetime.signal, signal]);
32552
- setState("loading");
32553
- setFailure(void 0);
32554
- const createText = pdfTextRenderer(text.current);
32555
- let textTask;
32556
- renderPdfPage(document, page, node, renderSignal, window.devicePixelRatio, (pdfPage, viewport) => {
32557
- textTask = createText(pdfPage, viewport);
32558
- return textTask;
32559
- }).then((size) => {
32560
- if (!renderSignal.aborted) {
32571
+ let newText;
32572
+ pendingRender.current = pendingRender.current.then(async () => {
32573
+ renderSignal.throwIfAborted();
32574
+ const buffer = node.ownerDocument.createElement("canvas");
32575
+ try {
32576
+ const size = await renderPdfPage(document, page, buffer, renderSignal, window.devicePixelRatio * zoom, textTask.current === void 0 ? (pdfPage, viewport) => {
32577
+ newText = pdfTextRenderer(text.current)(pdfPage, viewport);
32578
+ return newText;
32579
+ } : void 0);
32580
+ renderSignal.throwIfAborted();
32581
+ const context = node.getContext("2d");
32582
+ if (context === null) throw new Error("PDF canvas has no 2D context");
32583
+ node.width = buffer.width;
32584
+ node.height = buffer.height;
32585
+ node.style.setProperty("--pdf-page-width", `${size.width}px`);
32586
+ node.style.setProperty("--pdf-page-height", `${size.height}px`);
32587
+ context.drawImage(buffer, 0, 0);
32588
+ textTask.current ??= newText;
32589
+ newText = void 0;
32590
+ renderedZoom.current = zoom;
32561
32591
  setWidth(size.width);
32562
32592
  onWidth?.(size.width);
32563
32593
  setState("ready");
32594
+ } finally {
32595
+ buffer.width = buffer.height = 0;
32596
+ newText?.cancel();
32564
32597
  }
32565
- }, (error) => {
32598
+ }).catch((error) => {
32566
32599
  if (!renderSignal.aborted) setFailure({ error });
32567
32600
  });
32568
32601
  return () => {
32569
32602
  lifetime.abort();
32570
- textTask?.cancel();
32603
+ newText?.cancel();
32571
32604
  };
32572
32605
  }, [
32573
32606
  document,
32574
32607
  page,
32575
32608
  requested,
32609
+ visible,
32610
+ zoom,
32576
32611
  signal,
32577
32612
  attempt,
32578
32613
  onWidth
@@ -32602,7 +32637,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
32602
32637
  className: `${PdfBody_module_css_default.surface} ${zoomSurfaceClass}`,
32603
32638
  "data-document-zoom-surface": true,
32604
32639
  style: { "--document-zoom-width": `${width ?? 0}px` },
32605
- hidden: state !== "ready" || failure !== void 0,
32640
+ hidden: state !== "ready",
32606
32641
  children: [(0, react_jsx_runtime.jsx)("canvas", {
32607
32642
  ref: canvas,
32608
32643
  className: PdfBody_module_css_default.canvas,
@@ -3,6 +3,7 @@ import { type ReactNode } from 'react';
3
3
  import type { LoadedExcelBodyProps } from './LazyExcelBody.tsx';
4
4
  /**
5
5
  * Display stored spreadsheet values and formats without editing or recalculation.
6
+ * Keep the mounted workbook sized to its preview pane.
6
7
  * @param props - Complete workbook bytes, limits, and locale.
7
8
  * @returns An isolated spreadsheet surface with cancellable loading.
8
9
  */
@@ -4,9 +4,11 @@ export declare class XlsxPreviewArchive {
4
4
  /** Detected workbook content that the preview does not display. */
5
5
  readonly unsupportedFeatures: Set<"charts" | "images" | "shapes" | "conditionalFormatting">;
6
6
  private readonly files;
7
+ private readonly parts;
7
8
  /**
8
9
  * Read an archive without modifying the borrowed source buffer.
9
10
  * @param bytes - Complete XLSX source bytes.
11
+ * @throws When ZIP entries have ASCII case-equivalent names.
10
12
  */
11
13
  constructor(bytes: Uint8Array<ArrayBuffer>);
12
14
  /**
@@ -30,7 +30,7 @@ export type RenderPdfText = (page: PDFPageProxy, viewport: PageViewport) => {
30
30
  * @param pageNumber - 1-based selected page.
31
31
  * @param canvas - canvas owned by this render only.
32
32
  * @param signal - render lifetime.
33
- * @param pixelRatio - display pixel ratio.
33
+ * @param pixelRatio - device pixel ratio multiplied by the settled document zoom.
34
34
  * @param renderText - optional selectable text layer sharing this page and viewport.
35
35
  * @returns the page's CSS dimensions after rendering completes.
36
36
  */
@@ -12,6 +12,8 @@ export interface ZoomViewportProps {
12
12
  readonly signal: AbortSignal;
13
13
  readonly scrollportRef: RefCallback<HTMLElement>;
14
14
  readonly onPreference: (preference: ZoomPreference) => void;
15
+ /** Resolved scale after a gesture settles or fit-width measurement changes. */
16
+ readonly onRenderZoom?: (zoom: number) => void;
15
17
  readonly children: ReactNode;
16
18
  }
17
19
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-sidebar-documentpreview",
3
3
  "description": "Extensible Sidebar previews for Office documents, spreadsheets, Markdown, code, images, PDF, HTML, and plain text",
4
- "version": "0.1.7-alpha.1",
4
+ "version": "0.1.7-rc.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "license": "MIT",
44
44
  "peerDependencies": {
45
- "@deepseek-ai/cordis": "^4.0.3"
45
+ "@deepseek-ai/cordis": "~4.0.4"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@fortune-sheet/core": "1.0.4",
@@ -61,26 +61,26 @@
61
61
  "react": "^18.2.0",
62
62
  "react-dom": "^18.2.0",
63
63
  "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
64
- "@deepseek-ai/cordis": "^4.0.3",
65
- "@deepseek-ai/dsh-api-remotes": "^0.1.7-alpha.1",
66
- "@deepseek-ai/dsh-api-workspace-files": "^0.1.7-alpha.1",
67
- "@deepseek-ai/dsh-client-connection": "^0.1.7-alpha.1",
68
- "@deepseek-ai/dsh-api-gateway": "^0.1.7-alpha.1",
69
- "@deepseek-ai/dsh-client-locale": "^0.1.7-alpha.1",
70
- "@deepseek-ai/dsh-client-resources": "^0.1.7-alpha.1",
71
- "@deepseek-ai/dsh-client-store": "^0.1.7-alpha.1",
72
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.7-alpha.1",
73
- "@deepseek-ai/dsh-client-ui-dockkit": "^0.1.7-alpha.1",
74
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-alpha.1",
75
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-alpha.1",
76
- "@deepseek-ai/dsh-client-ui-session": "^0.1.7-alpha.1",
77
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.7-alpha.1",
78
- "@deepseek-ai/dsh-client-ui-sidebar-right": "^0.1.7-alpha.1",
79
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-alpha.1",
80
- "@deepseek-ai/dsh-office-to-pdf": "^0.1.7-alpha.1",
81
- "@deepseek-ai/dsh-host-webserver": "^0.1.7-alpha.1",
82
- "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
83
- "@deepseek-ai/dsh-util-workspace-path": "^0.1.7-alpha.1"
64
+ "@deepseek-ai/cordis": "~4.0.4",
65
+ "@deepseek-ai/dsh-api-gateway": "0.1.7-rc.1",
66
+ "@deepseek-ai/dsh-api-remotes": "0.1.7-rc.1",
67
+ "@deepseek-ai/dsh-api-workspace-files": "0.1.7-rc.1",
68
+ "@deepseek-ai/dsh-client-connection": "0.1.7-rc.1",
69
+ "@deepseek-ai/dsh-client-locale": "0.1.7-rc.1",
70
+ "@deepseek-ai/dsh-client-resources": "0.1.7-rc.1",
71
+ "@deepseek-ai/dsh-client-ui-dockkit": "0.1.7-rc.1",
72
+ "@deepseek-ai/dsh-client-test-runtime": "0.1.7-rc.1",
73
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.1",
74
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.1",
75
+ "@deepseek-ai/dsh-client-ui-session": "0.1.7-rc.1",
76
+ "@deepseek-ai/dsh-client-store": "0.1.7-rc.1",
77
+ "@deepseek-ai/dsh-client-ui-sidebar-right": "0.1.7-rc.1",
78
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
79
+ "@deepseek-ai/dsh-office-to-pdf": "0.1.7-rc.1",
80
+ "@deepseek-ai/dsh-host-webserver": "0.1.7-rc.1",
81
+ "@deepseek-ai/dsh-session": "0.1.7-rc.1",
82
+ "@deepseek-ai/dsh-util-workspace-path": "0.1.7-rc.1",
83
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1"
84
84
  },
85
85
  "files": [
86
86
  "lib/index.js",
@@ -89,7 +89,7 @@
89
89
  "lib/types/**/*.d.ts"
90
90
  ],
91
91
  "dependencies": {
92
- "@deepseek-ai/schemastery": "^3.18.3"
92
+ "@deepseek-ai/schemastery": "~3.18.4"
93
93
  },
94
94
  "scripts": {
95
95
  "bundle": "tsdown",