docblocks-react-ui 0.3.1 → 0.4.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.
@@ -43,6 +43,40 @@ function assertNever(value) {
43
43
  throw new Error(`Unhandled value: ${String(value)}`);
44
44
  }
45
45
  //#endregion
46
+ //#region src/shared/ui/icons/ChevronLeft.tsx
47
+ function ChevronLeft(props) {
48
+ return /* @__PURE__ */ jsx("svg", {
49
+ xmlns: "http://www.w3.org/2000/svg",
50
+ width: 24,
51
+ height: 24,
52
+ viewBox: "0 0 24 24",
53
+ fill: "none",
54
+ stroke: "currentColor",
55
+ strokeWidth: 2,
56
+ strokeLinecap: "round",
57
+ strokeLinejoin: "round",
58
+ ...props,
59
+ children: /* @__PURE__ */ jsx("path", { d: "m15 18-6-6 6-6" })
60
+ });
61
+ }
62
+ //#endregion
63
+ //#region src/shared/ui/icons/ChevronRight.tsx
64
+ function ChevronRight(props) {
65
+ return /* @__PURE__ */ jsx("svg", {
66
+ xmlns: "http://www.w3.org/2000/svg",
67
+ width: 24,
68
+ height: 24,
69
+ viewBox: "0 0 24 24",
70
+ fill: "none",
71
+ stroke: "currentColor",
72
+ strokeWidth: 2,
73
+ strokeLinecap: "round",
74
+ strokeLinejoin: "round",
75
+ ...props,
76
+ children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
77
+ });
78
+ }
79
+ //#endregion
46
80
  //#region src/shared/ui/icons/TriangleAlert.tsx
47
81
  function TriangleAlert(props) {
48
82
  return /* @__PURE__ */ jsxs("svg", {
@@ -64,6 +98,23 @@ function TriangleAlert(props) {
64
98
  });
65
99
  }
66
100
  //#endregion
101
+ //#region src/shared/ui/icons/X.tsx
102
+ function X(props) {
103
+ return /* @__PURE__ */ jsxs("svg", {
104
+ xmlns: "http://www.w3.org/2000/svg",
105
+ width: 24,
106
+ height: 24,
107
+ viewBox: "0 0 24 24",
108
+ fill: "none",
109
+ stroke: "currentColor",
110
+ strokeWidth: 2,
111
+ strokeLinecap: "round",
112
+ strokeLinejoin: "round",
113
+ ...props,
114
+ children: [/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }), /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })]
115
+ });
116
+ }
117
+ //#endregion
67
118
  //#region src/pdf/constants.ts
68
119
  /** The iframe's embeddable-url gate: only the http(s) scheme may stand the
69
120
  * viewer's iframe. The data / blob / javascript schemes (and the
@@ -78,4 +129,4 @@ function isEmbeddablePdfUrl(url) {
78
129
  }
79
130
  }
80
131
  //#endregion
81
- export { isSafeDownloadHref as a, cn as c, clamp as i, TriangleAlert as n, preventDefault as o, assertNever as r, stopPropagation as s, isEmbeddablePdfUrl as t };
132
+ export { ChevronLeft as a, isSafeDownloadHref as c, cn as d, ChevronRight as i, preventDefault as l, X as n, assertNever as o, TriangleAlert as r, clamp as s, isEmbeddablePdfUrl as t, stopPropagation as u };
@@ -767,6 +767,12 @@ interface DatabaseTableProps {
767
767
  view?: Partial<DatabaseTableView>;
768
768
  /** The view's change's hook (the persistence's stand; the patch = the changed's attrs only). */
769
769
  onViewChange?: (patch: Partial<DatabaseTableView>) => void;
770
+ /** The consumer's sizing / layout's classes over the root (the package's
771
+ * token's scope's anchor + the flex-column's stand): the width stands the
772
+ * consumer's layout (the grid's `w-full`'s parity), and a height-
773
+ * constrained root stands the grid's inner scroll (the unconstrained's
774
+ * stand is the content's height, as over the document's block). */
775
+ className?: string;
770
776
  }
771
777
  /**
772
778
  * The standalone's database table: the live grid over the bridge's table
@@ -777,9 +783,12 @@ interface DatabaseTableProps {
777
783
  * prop) + the consumer's persistence over the `onViewChange`'s patch
778
784
  * (one patch per change, as the block's `updateBlock` stand). The i18n's
779
785
  * scope stands self-contained (the package's instance — the same's stand
780
- * as the consumer-placed components).
786
+ * as the consumer-placed components). The sizing's stand is the
787
+ * consumer's: the `className` over the root (a height-constrained root
788
+ * stands the grid's inner scroll; the unconstrained's stand is the
789
+ * content's height).
781
790
  */
782
- declare function DatabaseTable({ bridge, tableId, readOnly, view, onViewChange }: DatabaseTableProps): import("react").JSX.Element;
791
+ declare function DatabaseTable({ bridge, tableId, readOnly, view, onViewChange, className }: DatabaseTableProps): import("react").JSX.Element;
783
792
  //#endregion
784
793
  //#region src/database/standalone/use-table-list.d.ts
785
794
  /**
@@ -1227,15 +1236,18 @@ declare function EditorProvider(props: EditorProviderProps): import("react").JSX
1227
1236
  //#endregion
1228
1237
  //#region src/editor-provider/constants.d.ts
1229
1238
  /**
1230
- * The safe embed sandbox (the default when no `embedSandbox` policy is
1231
- * supplied): the embedded content stands an opaque origin (no
1232
- * `allow-same-origin`), so it cannot read the parent's DOM, cookies, or state;
1233
- * scripts, forms, and popups still run, so interactive embeds keep working.
1234
- * Grant `allow-same-origin` — and `allow-scripts` together with it — only for
1235
- * trusted origins via the `embedSandbox` policy: that combination lets the
1236
- * frame lift its own sandbox.
1239
+ * The embed sandbox (the default when no `embedSandbox` policy is supplied):
1240
+ * the embedded content runs with its own origin's grants (`allow-same-origin`
1241
+ * — the real-app requirement: a dashboard's own storage / cookies stand,
1242
+ * so the app boots like in a plain tab) + scripts, forms, and popups.
1243
+ * The frame's document stands a separate document either way: a
1244
+ * cross-origin embed cannot reach the parent's DOM or state. The one
1245
+ * posture consequence: a same-origin URL (the page's own origin) reaches
1246
+ * the parent over `window.parent` — the document author's own choice to
1247
+ * embed it. The `embedSandbox` policy stands the restriction (the
1248
+ * opaque-origin stand — no `allow-same-origin` — for the untrusted embeds).
1237
1249
  */
1238
- declare const DEFAULT_EMBED_SANDBOX = "allow-scripts allow-forms allow-popups";
1250
+ declare const DEFAULT_EMBED_SANDBOX = "allow-scripts allow-same-origin allow-forms allow-popups";
1239
1251
  //#endregion
1240
1252
  //#region src/editor-provider/context.d.ts
1241
1253
  /** Read the per-instance editor context; throws outside an EditorProvider. */
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as BlockRendererMap, r as BlockRendererProps, t as BlockRenderer } from "./index-YJcgf8Hh.mjs";
2
- import { $ as ProtocolLimits, A as filterIsComplete, B as DatabaseBridge, C as DatabaseTableView, Ct as BlobSubtype, D as DatabaseTableRow, E as DatabaseTablePhase, F as BridgeTableDescription, G as IsoInstant, H as FilterConditionInput, I as BridgeValueInput, J as OperationStatus, K as MutationContext, L as CellMap, M as BatchOp, N as BridgeColumn, O as UseDatabaseTable, P as BridgeColumnAnnotation, Q as ProtocolDescriptor, R as CellValueParse, S as DatabaseTableProps, St as BlobStore, T as useDatabaseTable, Tt as createMemoryBlobStore, U as FilterInput, V as DomainEvent, W as Id, X as PageSnapshot, Y as PageQuery, Z as ProtocolCapability, _ as PresencePeer, _t as isBridgeError, a as EditorContextValue, at as SortInput, b as useTableList, bt as BlobMeta, c as EmbedSandboxPolicy, ct as SubscriptionStatus, d as PdfPreview, dt as TableEvent, et as QueryOperator, f as PdfPreviewProps, ft as TableRef, g as PresenceIdentity, gt as BridgeErrorCode, h as PresenceCrypto, ht as BridgeError, i as AccessLevel, it as RowWire, j as FilterBuilder, k as emptyFilter, l as MentionEntry, lt as TableDescription, m as SyncEmbedResolver, mt as BRIDGE_ERROR_CODES, n as DEFAULT_EMBED_SANDBOX, nt as Revision, o as EditorMode, ot as SubscriptionHandle, p as PresenceConfig, pt as TableSnapshot, q as MutationReceipt, r as EditorProvider, rt as RowPage, s as EditorProviderProps, st as SubscriptionObserver, t as useEditorContext, tt as QuerySemantics, u as MentionProvider, ut as TableEnvelopeBase, v as PresenceSharing, vt as isBridgeErrorCode, w as UseTableList, wt as blobSubtypeOf, x as DatabaseTable, xt as BlobRef, y as PresenceSharingMode, yt as toBridgeError, z as Column } from "./index-DG0lJy_t.mjs";
2
+ import { $ as ProtocolLimits, A as filterIsComplete, B as DatabaseBridge, C as DatabaseTableView, Ct as BlobSubtype, D as DatabaseTableRow, E as DatabaseTablePhase, F as BridgeTableDescription, G as IsoInstant, H as FilterConditionInput, I as BridgeValueInput, J as OperationStatus, K as MutationContext, L as CellMap, M as BatchOp, N as BridgeColumn, O as UseDatabaseTable, P as BridgeColumnAnnotation, Q as ProtocolDescriptor, R as CellValueParse, S as DatabaseTableProps, St as BlobStore, T as useDatabaseTable, Tt as createMemoryBlobStore, U as FilterInput, V as DomainEvent, W as Id, X as PageSnapshot, Y as PageQuery, Z as ProtocolCapability, _ as PresencePeer, _t as isBridgeError, a as EditorContextValue, at as SortInput, b as useTableList, bt as BlobMeta, c as EmbedSandboxPolicy, ct as SubscriptionStatus, d as PdfPreview, dt as TableEvent, et as QueryOperator, f as PdfPreviewProps, ft as TableRef, g as PresenceIdentity, gt as BridgeErrorCode, h as PresenceCrypto, ht as BridgeError, i as AccessLevel, it as RowWire, j as FilterBuilder, k as emptyFilter, l as MentionEntry, lt as TableDescription, m as SyncEmbedResolver, mt as BRIDGE_ERROR_CODES, n as DEFAULT_EMBED_SANDBOX, nt as Revision, o as EditorMode, ot as SubscriptionHandle, p as PresenceConfig, pt as TableSnapshot, q as MutationReceipt, r as EditorProvider, rt as RowPage, s as EditorProviderProps, st as SubscriptionObserver, t as useEditorContext, tt as QuerySemantics, u as MentionProvider, ut as TableEnvelopeBase, v as PresenceSharing, vt as isBridgeErrorCode, w as UseTableList, wt as blobSubtypeOf, x as DatabaseTable, xt as BlobRef, y as PresenceSharingMode, yt as toBridgeError, z as Column } from "./index-BQRTt8x2.mjs";
3
3
  import * as React from "react";
4
4
  import { ComponentType, ReactNode } from "react";
5
5
  import { Block, BlockId, BlockType, Doc, DocMeta, PresenceState, Selection, SelectionView, SyncEmbedRef, TextRun } from "docblocks-core";
@@ -294,7 +294,7 @@ declare function AlertDescription({ className, ...props }: React.ComponentProps<
294
294
  //#endregion
295
295
  //#region src/shared/ui/Badge.d.ts
296
296
  declare const badgeVariants: (props?: ({
297
- variant?: "default" | "custom" | "secondary" | "destructive" | "outline" | null | undefined;
297
+ variant?: "default" | "destructive" | "custom" | "secondary" | "outline" | null | undefined;
298
298
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
299
299
  declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<"span"> & VariantProps<typeof badgeVariants> & {
300
300
  asChild?: boolean;
@@ -305,7 +305,7 @@ declare function BareSelect({ className, children, ...props }: React.ComponentPr
305
305
  //#endregion
306
306
  //#region src/shared/ui/Button.d.ts
307
307
  declare const buttonVariants: (props?: ({
308
- variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "ghost" | null | undefined;
308
+ variant?: "link" | "default" | "destructive" | "secondary" | "outline" | "ghost" | null | undefined;
309
309
  size?: "default" | "icon" | "sm" | "lg" | "xl" | null | undefined;
310
310
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
311
311
  declare function Button({ className, variant, size, asChild, type, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as PopoverHeader, A as SelectTrigger, At as isBridgeErrorCode, B as DropdownMenuRadioGroup, Bt as useMeta, C as SelectContent, Ct as BASELINE_CAPABILITIES, D as SelectScrollDownButton, Dt as BRIDGE_ERROR_CODES, E as SelectLabel, Et as validateProtocolDescriptor, F as DropdownMenuContent, Ft as columnAnnotation, G as DropdownMenuSubContent, Gt as useText, H as DropdownMenuSeparator, Ht as usePresenceSharing, I as DropdownMenuGroup, It as ScrollArea, J as Separator, Jt as blobSubtypeOf, K as DropdownMenuSubTrigger, L as DropdownMenuItem, Lt as Input, M as Label, Mt as conditionMatches, N as DropdownMenu, Nt as parseCellValue, O as SelectScrollUpButton, Ot as BridgeError, P as DropdownMenuCheckboxItem, Pt as TEXT_TYPES, Q as PopoverDescription, R as DropdownMenuLabel, Rt as useBlock, S as Select, St as AlertTitle, T as SelectItem, Tt as isDatabaseBridge, U as DropdownMenuShortcut, Ut as useSelection, V as DropdownMenuRadioItem, Vt as usePresence, W as DropdownMenuSub, Wt as useSelectionView, X as PopoverAnchor, Y as Popover, Yt as createMemoryBlobStore, Z as PopoverContent, _ as FilterBuilder, _t as BareSelect, a as SHAPE_FONT_STACKS, at as CardAction, b as Skeleton, bt as Alert, ct as CardFooter, d as useEditorContext, dt as Pen, et as PopoverTitle, f as DEFAULT_EMBED_SANDBOX, ft as Eye, g as newMutationContext, gt as buttonVariants, h as cellValueEqual, ht as Button, i as SHAPE_FONT_OPTIONS, it as Card, j as SelectValue, jt as toBridgeError, k as SelectSeparator, kt as isBridgeError, l as markToggleAction, lt as CardHeader, m as useDatabaseTable, mt as BookOpen, nt as DocblocksI18nProvider, o as FORMAT_PALETTE, ot as CardContent, p as GridBody, q as DropdownMenuTrigger, qt as renderRuns, rt as Checkbox, st as CardDescription, t as Editor, tt as PopoverTrigger, u as EditorProvider, ut as CardTitle, v as emptyFilter, vt as Badge, w as SelectGroup, wt as PROTOCOL_VERSION, x as SegmentSelector, xt as AlertDescription, y as filterIsComplete, yt as badgeVariants, z as DropdownMenuPortal, zt as useDoc } from "./Editor-BvKqUGXH.mjs";
2
- import { c as cn, o as preventDefault } from "./constants-D4FTL0ph.mjs";
1
+ import { $ as PopoverHeader, A as SelectTrigger, At as isBridgeErrorCode, B as DropdownMenuRadioGroup, Bt as useMeta, C as SelectContent, Ct as BASELINE_CAPABILITIES, D as SelectScrollDownButton, Dt as BRIDGE_ERROR_CODES, E as SelectLabel, Et as validateProtocolDescriptor, F as DropdownMenuContent, Ft as columnAnnotation, G as DropdownMenuSubContent, Gt as useText, H as DropdownMenuSeparator, Ht as usePresenceSharing, I as DropdownMenuGroup, It as ScrollArea, J as Separator, Jt as blobSubtypeOf, K as DropdownMenuSubTrigger, L as DropdownMenuItem, Lt as Input, M as Label, Mt as conditionMatches, N as DropdownMenu, Nt as parseCellValue, O as SelectScrollUpButton, Ot as BridgeError, P as DropdownMenuCheckboxItem, Pt as TEXT_TYPES, Q as PopoverDescription, R as DropdownMenuLabel, Rt as useBlock, S as Select, St as AlertTitle, T as SelectItem, Tt as isDatabaseBridge, U as DropdownMenuShortcut, Ut as useSelection, V as DropdownMenuRadioItem, Vt as usePresence, W as DropdownMenuSub, Wt as useSelectionView, X as PopoverAnchor, Y as Popover, Yt as createMemoryBlobStore, Z as PopoverContent, _ as FilterBuilder, _t as BareSelect, a as SHAPE_FONT_STACKS, at as CardAction, b as Skeleton, bt as Alert, ct as CardFooter, d as useEditorContext, dt as Pen, et as PopoverTitle, f as DEFAULT_EMBED_SANDBOX, ft as Eye, g as newMutationContext, gt as buttonVariants, h as cellValueEqual, ht as Button, i as SHAPE_FONT_OPTIONS, it as Card, j as SelectValue, jt as toBridgeError, k as SelectSeparator, kt as isBridgeError, l as markToggleAction, lt as CardHeader, m as useDatabaseTable, mt as BookOpen, nt as DocblocksI18nProvider, o as FORMAT_PALETTE, ot as CardContent, p as GridBody, q as DropdownMenuTrigger, qt as renderRuns, rt as Checkbox, st as CardDescription, t as Editor, tt as PopoverTrigger, u as EditorProvider, ut as CardTitle, v as emptyFilter, vt as Badge, w as SelectGroup, wt as PROTOCOL_VERSION, x as SegmentSelector, xt as AlertDescription, y as filterIsComplete, yt as badgeVariants, z as DropdownMenuPortal, zt as useDoc } from "./Editor-CUToaW9z.mjs";
2
+ import { d as cn, l as preventDefault } from "./constants-BaAj3jDA.mjs";
3
3
  import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { useTranslation } from "react-i18next";
@@ -392,9 +392,12 @@ const EMPTY_VIEW = {};
392
392
  * prop) + the consumer's persistence over the `onViewChange`'s patch
393
393
  * (one patch per change, as the block's `updateBlock` stand). The i18n's
394
394
  * scope stands self-contained (the package's instance — the same's stand
395
- * as the consumer-placed components).
395
+ * as the consumer-placed components). The sizing's stand is the
396
+ * consumer's: the `className` over the root (a height-constrained root
397
+ * stands the grid's inner scroll; the unconstrained's stand is the
398
+ * content's height).
396
399
  */
397
- function DatabaseTable({ bridge, tableId, readOnly = false, view, onViewChange }) {
400
+ function DatabaseTable({ bridge, tableId, readOnly = false, view, onViewChange, className }) {
398
401
  const [viewState, setViewState] = useState(view ?? EMPTY_VIEW);
399
402
  const viewRef = useRef(viewState);
400
403
  viewRef.current = viewState;
@@ -409,6 +412,7 @@ function DatabaseTable({ bridge, tableId, readOnly = false, view, onViewChange }
409
412
  return /* @__PURE__ */ jsx(DocblocksI18nProvider, { children: /* @__PURE__ */ jsx("div", {
410
413
  "data-slot": "docblocks/editor",
411
414
  "data-readonly": readOnly ? "true" : void 0,
415
+ className: cn("flex flex-col", className),
412
416
  children: /* @__PURE__ */ jsx(GridBody, {
413
417
  bridge,
414
418
  tableId,
@@ -1,5 +1,5 @@
1
- import { Kt as INLINE_CODE_CLASS, St as AlertTitle, b as Skeleton, bt as Alert, c as textOf, d as useEditorContext, ft as Eye, n as childrenSlot, pt as CodeXml, r as CodeHeader, s as TextBlock, x as SegmentSelector, xt as AlertDescription } from "../Editor-BvKqUGXH.mjs";
2
- import { n as TriangleAlert } from "../constants-D4FTL0ph.mjs";
1
+ import { Kt as INLINE_CODE_CLASS, St as AlertTitle, b as Skeleton, bt as Alert, c as textOf, d as useEditorContext, ft as Eye, n as childrenSlot, pt as CodeXml, r as CodeHeader, s as TextBlock, x as SegmentSelector, xt as AlertDescription } from "../Editor-CUToaW9z.mjs";
2
+ import { r as TriangleAlert } from "../constants-BaAj3jDA.mjs";
3
3
  import { t as LoaderCircle } from "../LoaderCircle-AGVFE4IP.mjs";
4
4
  import { useEffect, useRef, useState } from "react";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -1,4 +1,4 @@
1
- import { f as PdfPreviewProps } from "../index-DG0lJy_t.mjs";
1
+ import { f as PdfPreviewProps } from "../index-BQRTt8x2.mjs";
2
2
  //#region src/pdf/PdfFirstPage.d.ts
3
3
  declare function PdfFirstPage({ id, url, name }: PdfPreviewProps): import("react").JSX.Element;
4
4
  //#endregion
@@ -1,9 +1,54 @@
1
- import { a as isSafeDownloadHref, c as cn, n as TriangleAlert, t as isEmbeddablePdfUrl } from "../constants-D4FTL0ph.mjs";
1
+ import { a as ChevronLeft, c as isSafeDownloadHref, d as cn, i as ChevronRight, n as X, r as TriangleAlert, t as isEmbeddablePdfUrl } from "../constants-BaAj3jDA.mjs";
2
2
  import { t as LoaderCircle } from "../LoaderCircle-AGVFE4IP.mjs";
3
- import { useEffect, useRef, useState } from "react";
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { useTranslation } from "react-i18next";
6
6
  import { GlobalWorkerOptions, RenderingCancelledException, getDocument } from "pdfjs-dist";
7
+ import { createPortal } from "react-dom";
8
+ //#region src/shared/ui/icons/Maximize.tsx
9
+ function Maximize(props) {
10
+ return /* @__PURE__ */ jsxs("svg", {
11
+ xmlns: "http://www.w3.org/2000/svg",
12
+ width: 24,
13
+ height: 24,
14
+ viewBox: "0 0 24 24",
15
+ fill: "none",
16
+ stroke: "currentColor",
17
+ strokeWidth: 2,
18
+ strokeLinecap: "round",
19
+ strokeLinejoin: "round",
20
+ ...props,
21
+ children: [
22
+ /* @__PURE__ */ jsx("path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }),
23
+ /* @__PURE__ */ jsx("path", { d: "M21 8V5a2 2 0 0 0-2-2h-3" }),
24
+ /* @__PURE__ */ jsx("path", { d: "M3 16v3a2 2 0 0 0 2 2h3" }),
25
+ /* @__PURE__ */ jsx("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
26
+ ]
27
+ });
28
+ }
29
+ //#endregion
30
+ //#region src/shared/ui/icons/Minimize.tsx
31
+ function Minimize(props) {
32
+ return /* @__PURE__ */ jsxs("svg", {
33
+ xmlns: "http://www.w3.org/2000/svg",
34
+ width: 24,
35
+ height: 24,
36
+ viewBox: "0 0 24 24",
37
+ fill: "none",
38
+ stroke: "currentColor",
39
+ strokeWidth: 2,
40
+ strokeLinecap: "round",
41
+ strokeLinejoin: "round",
42
+ ...props,
43
+ children: [
44
+ /* @__PURE__ */ jsx("path", { d: "M8 3v3a2 2 0 0 1-2 2H3" }),
45
+ /* @__PURE__ */ jsx("path", { d: "M21 8h-3a2 2 0 0 1-2-2V3" }),
46
+ /* @__PURE__ */ jsx("path", { d: "M3 16h3a2 2 0 0 1 2 2v3" }),
47
+ /* @__PURE__ */ jsx("path", { d: "M16 21v-3a2 2 0 0 1 2-2h3" })
48
+ ]
49
+ });
50
+ }
51
+ //#endregion
7
52
  //#region src/pdf/pdf-worker.ts
8
53
  /** The pdfjs-dist worker wiring (the side-effect): the worker file is
9
54
  * copied next to this entry at build time (dist/pdf/), so the relative
@@ -28,26 +73,74 @@ if (typeof window !== "undefined") fetch(WORKER_URL, { method: "HEAD" }).catch((
28
73
  });
29
74
  //#endregion
30
75
  //#region src/pdf/PdfFirstPage.tsx
31
- /** The first-page preview of one `pdf`-subtype image block: fetch the
32
- * resolved blob, render page 1 to a canvas via pdfjs-dist. The previous
33
- * phase stands until the (re-)render settles (no flicker while the url
34
- * changes). A failure stands the error note over the native iframe
35
- * fallback (the document stays viewable). The in-flight fetch is aborted
36
- * on unmount / url change and the render task + document are released,
37
- * so a torn-down preview leaves no network request or pdf.js worker
38
- * behind. */
76
+ /** The PDF preview of one `pdf`-subtype image block: fetch the resolved
77
+ * blob, render the pages to a canvas via pdfjs-dist. The inline view stands
78
+ * the first page over the page row; the controls (the previous/next page
79
+ * buttons + the page indicator + the fullscreen toggle) stand a bar under
80
+ * the canvas. The fullscreen mode opens a portal overlay over the viewport
81
+ * (the page scaled to fill the viewport, the same page navigation, the
82
+ * Escape key / the close button / the outside click exit). A failure stands
83
+ * the error note over the native iframe fallback (the document stays
84
+ * viewable). The in-flight fetch is aborted on unmount / url change and the
85
+ * render task + document are released, so a torn-down preview leaves no
86
+ * network request or pdf.js worker behind. */
87
+ /** Render one page to a canvas (the natural page size, the backing store
88
+ * scaled by the device pixel ratio for a crisp retina display). Returns the
89
+ * render task (the caller awaits its promise + cancels it on teardown).
90
+ * Does NOT await the promise itself, so the caller can cancel the in-flight
91
+ * render before it completes. */
92
+ function renderPageToCanvas(pdf, pageNumber, canvas) {
93
+ return pdf.getPage(pageNumber).then((page) => {
94
+ const ratio = window.devicePixelRatio || 1;
95
+ const viewport = page.getViewport({ scale: ratio });
96
+ canvas.width = Math.max(1, Math.floor(viewport.width));
97
+ canvas.height = Math.max(1, Math.floor(viewport.height));
98
+ canvas.style.width = `${Math.max(1, Math.floor(viewport.width / ratio))}px`;
99
+ canvas.style.height = `${Math.max(1, Math.floor(viewport.height / ratio))}px`;
100
+ return page.render({
101
+ canvas,
102
+ viewport
103
+ });
104
+ });
105
+ }
106
+ /** Render one page to a canvas, scaled to fill the container (the
107
+ * object-fit: contain behavior — the page's aspect ratio is preserved, the
108
+ * page fills the container's width or height). Returns the render task. */
109
+ function renderPageToCanvasScaled(pdf, pageNumber, canvas, containerWidth, containerHeight) {
110
+ return pdf.getPage(pageNumber).then((page) => {
111
+ const pageViewport = page.getViewport({ scale: 1 });
112
+ const scale = Math.min(containerWidth / pageViewport.width, containerHeight / pageViewport.height);
113
+ const ratio = window.devicePixelRatio || 1;
114
+ const viewport = page.getViewport({ scale: scale * ratio });
115
+ canvas.width = Math.max(1, Math.floor(viewport.width));
116
+ canvas.height = Math.max(1, Math.floor(viewport.height));
117
+ canvas.style.width = `${Math.max(1, Math.floor(viewport.width / ratio))}px`;
118
+ canvas.style.height = `${Math.max(1, Math.floor(viewport.height / ratio))}px`;
119
+ return page.render({
120
+ canvas,
121
+ viewport
122
+ });
123
+ });
124
+ }
39
125
  function PdfFirstPage({ id, url, name }) {
40
126
  const { t } = useTranslation("blocks");
41
127
  const canvasRef = useRef(null);
128
+ const fullscreenCanvasRef = useRef(null);
129
+ const fullscreenContainerRef = useRef(null);
42
130
  const loadingTaskRef = useRef(null);
43
- const renderTaskRef = useRef(null);
131
+ const pdfDocRef = useRef(null);
44
132
  const [phase, setPhase] = useState("loading");
45
133
  const [error, setError] = useState(null);
134
+ const [currentPage, setCurrentPage] = useState(1);
135
+ const [totalPages, setTotalPages] = useState(0);
136
+ const [isFullscreen, setIsFullscreen] = useState(false);
46
137
  useEffect(() => {
47
138
  const controller = new AbortController();
48
139
  let cancelled = false;
49
140
  setPhase("loading");
50
141
  setError(null);
142
+ setCurrentPage(1);
143
+ setTotalPages(0);
51
144
  const run = async () => {
52
145
  try {
53
146
  const response = await fetch(url, { signal: controller.signal });
@@ -66,28 +159,9 @@ function PdfFirstPage({ id, url, name }) {
66
159
  task.destroy().catch(() => void 0);
67
160
  return;
68
161
  }
69
- const page = await pdf.getPage(1);
70
- if (cancelled) {
71
- loadingTaskRef.current = null;
72
- task.destroy().catch(() => void 0);
73
- return;
74
- }
75
- const canvas = canvasRef.current;
76
- if (canvas === null) return;
77
- const ratio = window.devicePixelRatio || 1;
78
- const viewport = page.getViewport({ scale: ratio });
79
- canvas.width = Math.max(1, Math.floor(viewport.width));
80
- canvas.height = Math.max(1, Math.floor(viewport.height));
81
- canvas.style.width = `${Math.max(1, Math.floor(viewport.width / ratio))}px`;
82
- canvas.style.height = `${Math.max(1, Math.floor(viewport.height / ratio))}px`;
83
- const renderTask = page.render({
84
- canvas,
85
- viewport
86
- });
87
- renderTaskRef.current = renderTask;
88
- await renderTask.promise;
89
- if (renderTaskRef.current === renderTask) renderTaskRef.current = null;
90
- if (!cancelled) setPhase("ready");
162
+ pdfDocRef.current = pdf;
163
+ setTotalPages(pdf.numPages);
164
+ setPhase("ready");
91
165
  } catch (cause) {
92
166
  if (cancelled || cause instanceof RenderingCancelledException || cause instanceof DOMException && cause.name === "AbortError") return;
93
167
  const message = cause instanceof Error ? cause.message : String(cause);
@@ -99,14 +173,85 @@ function PdfFirstPage({ id, url, name }) {
99
173
  return () => {
100
174
  cancelled = true;
101
175
  controller.abort();
102
- const renderTask = renderTaskRef.current;
103
- renderTaskRef.current = null;
104
- if (renderTask !== null) renderTask.cancel();
176
+ pdfDocRef.current = null;
105
177
  const task = loadingTaskRef.current;
106
178
  loadingTaskRef.current = null;
107
179
  if (task !== null) task.destroy().catch(() => void 0);
108
180
  };
109
181
  }, [url]);
182
+ useEffect(() => {
183
+ if (phase !== "ready") return;
184
+ const pdf = pdfDocRef.current;
185
+ const canvas = canvasRef.current;
186
+ if (pdf === null || canvas === null) return;
187
+ let cancelled = false;
188
+ let renderTask = null;
189
+ const run = async () => {
190
+ try {
191
+ renderTask = await renderPageToCanvas(pdf, currentPage, canvas);
192
+ await renderTask.promise;
193
+ } catch (cause) {
194
+ if (cancelled || cause instanceof RenderingCancelledException || cause instanceof DOMException && cause.name === "AbortError") return;
195
+ }
196
+ };
197
+ run();
198
+ return () => {
199
+ cancelled = true;
200
+ if (renderTask !== null) renderTask.cancel();
201
+ };
202
+ }, [
203
+ url,
204
+ currentPage,
205
+ phase
206
+ ]);
207
+ useEffect(() => {
208
+ if (!isFullscreen || phase !== "ready") return;
209
+ const pdf = pdfDocRef.current;
210
+ const canvas = fullscreenCanvasRef.current;
211
+ const container = fullscreenContainerRef.current;
212
+ if (pdf === null || canvas === null || container === null) return;
213
+ let cancelled = false;
214
+ let renderTask = null;
215
+ const render = () => {
216
+ const width = container.clientWidth;
217
+ const height = container.clientHeight;
218
+ if (width === 0 || height === 0) return;
219
+ (async () => {
220
+ try {
221
+ renderTask = await renderPageToCanvasScaled(pdf, currentPage, canvas, width, height);
222
+ await renderTask.promise;
223
+ } catch (cause) {
224
+ if (cancelled || cause instanceof RenderingCancelledException || cause instanceof DOMException && cause.name === "AbortError") return;
225
+ }
226
+ })();
227
+ };
228
+ render();
229
+ const observer = new ResizeObserver(() => render());
230
+ observer.observe(container);
231
+ return () => {
232
+ cancelled = true;
233
+ observer.disconnect();
234
+ if (renderTask !== null) renderTask.cancel();
235
+ };
236
+ }, [
237
+ isFullscreen,
238
+ currentPage,
239
+ phase
240
+ ]);
241
+ useEffect(() => {
242
+ if (!isFullscreen) return;
243
+ const onKeyDown = (event) => {
244
+ if (event.key === "Escape") setIsFullscreen(false);
245
+ };
246
+ window.addEventListener("keydown", onKeyDown);
247
+ return () => window.removeEventListener("keydown", onKeyDown);
248
+ }, [isFullscreen]);
249
+ const prevPage = useCallback(() => {
250
+ setCurrentPage((page) => Math.max(1, page - 1));
251
+ }, []);
252
+ const nextPage = useCallback(() => {
253
+ setCurrentPage((page) => Math.min(totalPages, page + 1));
254
+ }, [totalPages]);
110
255
  if (phase === "error") return /* @__PURE__ */ jsxs("div", {
111
256
  "data-slot": "docblocks/image-pdf-preview",
112
257
  "data-block-id": id,
@@ -139,26 +284,148 @@ function PdfFirstPage({ id, url, name }) {
139
284
  children: name ?? "document"
140
285
  })]
141
286
  });
287
+ const inlineControls = /* @__PURE__ */ jsxs("div", {
288
+ "data-slot": "docblocks/image-pdf-controls",
289
+ className: "\n flex items-center justify-center gap-1 rounded-(--docblocks-radius)\n border-(length:--docblocks-table-border-width) border-(--docblocks-table-border-color) bg-(--muted) px-2 py-1\n ",
290
+ children: [
291
+ /* @__PURE__ */ jsx("button", {
292
+ type: "button",
293
+ "data-slot": "docblocks/image-pdf-prev",
294
+ onClick: prevPage,
295
+ disabled: currentPage <= 1,
296
+ "aria-label": t("code.pdfPrevPage"),
297
+ className: "\n flex size-6 items-center justify-center rounded-sm text-(--muted-foreground) transition-colors\n hover:bg-(--accent) hover:text-(--accent-foreground)\n disabled:pointer-events-none disabled:opacity-40\n ",
298
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
299
+ }),
300
+ /* @__PURE__ */ jsxs("span", {
301
+ "data-slot": "docblocks/image-pdf-page-indicator",
302
+ className: "px-1 text-xs text-(--muted-foreground) tabular-nums",
303
+ children: [
304
+ currentPage,
305
+ " / ",
306
+ totalPages
307
+ ]
308
+ }),
309
+ /* @__PURE__ */ jsx("button", {
310
+ type: "button",
311
+ "data-slot": "docblocks/image-pdf-next",
312
+ onClick: nextPage,
313
+ disabled: currentPage >= totalPages,
314
+ "aria-label": t("code.pdfNextPage"),
315
+ className: "\n flex size-6 items-center justify-center rounded-sm text-(--muted-foreground) transition-colors\n hover:bg-(--accent) hover:text-(--accent-foreground)\n disabled:pointer-events-none disabled:opacity-40\n ",
316
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
317
+ }),
318
+ /* @__PURE__ */ jsx("button", {
319
+ type: "button",
320
+ "data-slot": "docblocks/image-pdf-fullscreen",
321
+ onClick: () => setIsFullscreen(true),
322
+ "aria-label": t("code.pdfFullscreen"),
323
+ className: "\n ml-1 flex size-6 items-center justify-center rounded-sm text-(--muted-foreground) transition-colors\n hover:bg-(--accent) hover:text-(--accent-foreground)\n ",
324
+ children: /* @__PURE__ */ jsx(Maximize, { className: "size-4" })
325
+ })
326
+ ]
327
+ });
328
+ const fullscreenControls = /* @__PURE__ */ jsxs("div", {
329
+ "data-slot": "docblocks/image-pdf-controls",
330
+ className: "\n flex items-center justify-center gap-1 rounded-(--radius) border border-(--border) bg-(--muted) px-2 py-1\n ",
331
+ children: [
332
+ /* @__PURE__ */ jsx("button", {
333
+ type: "button",
334
+ "data-slot": "docblocks/image-pdf-prev",
335
+ onClick: prevPage,
336
+ disabled: currentPage <= 1,
337
+ "aria-label": t("code.pdfPrevPage"),
338
+ className: "\n flex size-6 items-center justify-center rounded-sm text-(--muted-foreground) transition-colors\n hover:bg-(--accent) hover:text-(--accent-foreground)\n disabled:pointer-events-none disabled:opacity-40\n ",
339
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
340
+ }),
341
+ /* @__PURE__ */ jsxs("span", {
342
+ "data-slot": "docblocks/image-pdf-page-indicator",
343
+ className: "px-1 text-xs text-(--muted-foreground) tabular-nums",
344
+ children: [
345
+ currentPage,
346
+ " / ",
347
+ totalPages
348
+ ]
349
+ }),
350
+ /* @__PURE__ */ jsx("button", {
351
+ type: "button",
352
+ "data-slot": "docblocks/image-pdf-next",
353
+ onClick: nextPage,
354
+ disabled: currentPage >= totalPages,
355
+ "aria-label": t("code.pdfNextPage"),
356
+ className: "\n flex size-6 items-center justify-center rounded-sm text-(--muted-foreground) transition-colors\n hover:bg-(--accent) hover:text-(--accent-foreground)\n disabled:pointer-events-none disabled:opacity-40\n ",
357
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
358
+ }),
359
+ /* @__PURE__ */ jsx("button", {
360
+ type: "button",
361
+ "data-slot": "docblocks/image-pdf-minimize",
362
+ onClick: () => setIsFullscreen(false),
363
+ "aria-label": t("code.pdfCloseFullscreen"),
364
+ className: "\n ml-1 flex size-6 items-center justify-center rounded-sm text-(--muted-foreground) transition-colors\n hover:bg-(--accent) hover:text-(--accent-foreground)\n ",
365
+ children: /* @__PURE__ */ jsx(Minimize, { className: "size-4" })
366
+ })
367
+ ]
368
+ });
142
369
  return /* @__PURE__ */ jsxs("div", {
143
370
  "data-slot": "docblocks/image-pdf-preview",
144
371
  "data-block-id": id,
145
372
  "data-block-type": "image",
146
373
  "data-phase": phase,
147
- className: "relative mx-0 my-(--docblocks-block-spacing) flex justify-center",
148
- children: [/* @__PURE__ */ jsx("canvas", {
149
- ref: canvasRef,
150
- "data-slot": "docblocks/image-pdf-canvas",
151
- className: cn(`
152
- block max-w-full rounded-(--docblocks-image-radius) border-(length:--docblocks-table-border-width)
153
- border-(--docblocks-table-border-color) bg-(--background)
154
- `, phase === "ready" ? "h-auto" : "hidden")
155
- }), phase === "loading" && /* @__PURE__ */ jsxs("div", {
156
- className: "flex h-48 w-full items-center justify-center gap-2 text-sm text-(--muted-foreground)",
157
- children: [/* @__PURE__ */ jsx(LoaderCircle, { className: "size-4 animate-spin" }), /* @__PURE__ */ jsx("span", {
158
- "data-slot": "docblocks/image-pdf-loading",
159
- children: t("code.pdfLoading")
160
- })]
161
- })]
374
+ className: "relative mx-0 my-(--docblocks-block-spacing) flex flex-col items-center gap-2",
375
+ children: [
376
+ /* @__PURE__ */ jsxs("div", {
377
+ className: "relative flex w-full justify-center",
378
+ children: [/* @__PURE__ */ jsx("canvas", {
379
+ ref: canvasRef,
380
+ "data-slot": "docblocks/image-pdf-canvas",
381
+ className: cn(`
382
+ block max-w-full rounded-(--docblocks-image-radius) border-(length:--docblocks-table-border-width)
383
+ border-(--docblocks-table-border-color) bg-(--background)
384
+ `, phase === "ready" ? "h-auto" : "hidden")
385
+ }), phase === "loading" && /* @__PURE__ */ jsxs("div", {
386
+ className: "flex h-48 w-full items-center justify-center gap-2 text-sm text-(--muted-foreground)",
387
+ children: [/* @__PURE__ */ jsx(LoaderCircle, { className: "size-4 animate-spin" }), /* @__PURE__ */ jsx("span", {
388
+ "data-slot": "docblocks/image-pdf-loading",
389
+ children: t("code.pdfLoading")
390
+ })]
391
+ })]
392
+ }),
393
+ phase === "ready" && inlineControls,
394
+ isFullscreen && createPortal(/* @__PURE__ */ jsxs("div", {
395
+ "data-slot": "docblocks/image-pdf-fullscreen-overlay",
396
+ className: "fixed inset-0 z-50 flex flex-col items-center justify-center gap-3 bg-(--background)",
397
+ role: "dialog",
398
+ "aria-modal": "true",
399
+ "aria-label": t("code.pdfFullscreen"),
400
+ onClick: (event) => {
401
+ const target = event.target;
402
+ if (target === null) return;
403
+ if (target.closest("[data-slot=\"docblocks/image-pdf-fullscreen-canvas\"]") !== null) return;
404
+ if (target.closest("[data-slot=\"docblocks/image-pdf-controls\"]") !== null) return;
405
+ setIsFullscreen(false);
406
+ },
407
+ children: [
408
+ /* @__PURE__ */ jsx("button", {
409
+ type: "button",
410
+ "data-slot": "docblocks/image-pdf-fullscreen-close",
411
+ onClick: () => setIsFullscreen(false),
412
+ "aria-label": t("code.pdfCloseFullscreen"),
413
+ className: "\n absolute top-4 right-4 flex size-8 items-center justify-center rounded-md text-(--foreground)\n transition-colors\n hover:bg-(--accent)\n ",
414
+ children: /* @__PURE__ */ jsx(X, { className: "size-5" })
415
+ }),
416
+ /* @__PURE__ */ jsx("div", {
417
+ ref: fullscreenContainerRef,
418
+ className: "flex h-[calc(100vh-6rem)] w-[calc(100vw-4rem)] items-center justify-center overflow-hidden",
419
+ children: /* @__PURE__ */ jsx("canvas", {
420
+ ref: fullscreenCanvasRef,
421
+ "data-slot": "docblocks/image-pdf-fullscreen-canvas",
422
+ className: "max-h-full max-w-full border border-(--border) bg-(--background) shadow-lg"
423
+ })
424
+ }),
425
+ fullscreenControls
426
+ ]
427
+ }), document.body)
428
+ ]
162
429
  });
163
430
  }
164
431
  //#endregion