@embedpdf/viewer-chrome 3.0.0-next.10 → 3.0.0-next.12
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/dist/chrome.cjs +1 -0
- package/dist/chrome.cjs.map +1 -1
- package/dist/chrome.js +1 -0
- package/dist/chrome.js.map +1 -1
- package/dist/commands.cjs +59 -3
- package/dist/commands.cjs.map +1 -1
- package/dist/commands.js +59 -3
- package/dist/commands.js.map +1 -1
- package/dist/{es-B7b8KjR3.cjs → es-BRYSk4rY.cjs} +19 -2
- package/dist/es-BRYSk4rY.cjs.map +1 -0
- package/dist/{es-aQlZ3hcL.js → es-DT5ZoB99.js} +19 -2
- package/dist/es-DT5ZoB99.js.map +1 -0
- package/dist/index.cjs +373 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -18
- package/dist/index.d.ts +30 -18
- package/dist/index.js +370 -26
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/es-B7b8KjR3.cjs.map +0 -1
- package/dist/es-aQlZ3hcL.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -32,7 +32,9 @@ let _embedpdf_react_page_edit = require("@embedpdf/react/page-edit");
|
|
|
32
32
|
let _embedpdf_react_interaction = require("@embedpdf/react/interaction");
|
|
33
33
|
let _embedpdf_react_selection = require("@embedpdf/react/selection");
|
|
34
34
|
let _embedpdf_react_annotation = require("@embedpdf/react/annotation");
|
|
35
|
+
let _embedpdf_react_stamp = require("@embedpdf/react/stamp");
|
|
35
36
|
let _embedpdf_react_redaction = require("@embedpdf/react/redaction");
|
|
37
|
+
let _embedpdf_react_actions = require("@embedpdf/react/actions");
|
|
36
38
|
let _embedpdf_react_form = require("@embedpdf/react/form");
|
|
37
39
|
let _embedpdf_react_link = require("@embedpdf/react/link");
|
|
38
40
|
let _embedpdf_react_search = require("@embedpdf/react/search");
|
|
@@ -43,6 +45,7 @@ let _embedpdf_react_toolbar = require("@embedpdf/react/toolbar");
|
|
|
43
45
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
44
46
|
let _embedpdf_react_scrollbar = require("@embedpdf/react/scrollbar");
|
|
45
47
|
let _embedpdf_react_annotation_menu = require("@embedpdf/react/annotation-menu");
|
|
48
|
+
let _embedpdf_default_stamps_library = require("@embedpdf/default-stamps/library");
|
|
46
49
|
let _embedpdf_react_metadata = require("@embedpdf/react/metadata");
|
|
47
50
|
//#region src/config/stage.ts
|
|
48
51
|
/**
|
|
@@ -57,24 +60,23 @@ const ThumbsStageToken = (0, _embedpdf_react_runtime.createCapabilityToken)("sta
|
|
|
57
60
|
//#endregion
|
|
58
61
|
//#region src/config/demo-tools.plugin.ts
|
|
59
62
|
/**
|
|
60
|
-
* Example-only: register the
|
|
61
|
-
* interaction tools. v3 doesn't yet ship signature
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
63
|
+
* Example-only: register the still-unimplemented authoring tools as INERT
|
|
64
|
+
* interaction tools. v3 doesn't yet ship a signature plugin, but this is a
|
|
65
|
+
* CHROME demo — its job is selection state and layout, not the tool's effect.
|
|
66
|
+
* Registering them as real (behaviourless) tools lets every tool button in
|
|
67
|
+
* every mode band go through the SAME path (`InteractionToken.activateTool`),
|
|
68
|
+
* so `active` is uniform and honest: the button really is the active tool; it
|
|
69
|
+
* just has no handler behind it.
|
|
67
70
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
+
* Everything else in the Insert band is REAL now: stamps come from the stamp
|
|
72
|
+
* plugin's libraries (armed by the stamps sidebar), and image/attachment are
|
|
73
|
+
* the annotation plugin's click-then-pick tools. The annotate + shapes tools
|
|
74
|
+
* were never here either — nor the form palette (the form plugin's
|
|
75
|
+
* draw-to-place).
|
|
71
76
|
*/
|
|
72
77
|
const INERT_TOOLS = [{
|
|
73
78
|
id: "signature",
|
|
74
79
|
cursor: "copy"
|
|
75
|
-
}, {
|
|
76
|
-
id: "image",
|
|
77
|
-
cursor: "copy"
|
|
78
80
|
}];
|
|
79
81
|
const demoToolsPlugin = () => (0, _embedpdf_react_runtime.definePlugin)({
|
|
80
82
|
id: "demo-tools",
|
|
@@ -169,6 +171,7 @@ const en = {
|
|
|
169
171
|
delete: "Delete",
|
|
170
172
|
group: "Group",
|
|
171
173
|
ungroup: "Ungroup",
|
|
174
|
+
stampFromSelection: "Make stamp",
|
|
172
175
|
strip: "Annotation actions"
|
|
173
176
|
},
|
|
174
177
|
selection: {
|
|
@@ -293,7 +296,23 @@ const en = {
|
|
|
293
296
|
redactApplying: "Applying…",
|
|
294
297
|
redactLabelTitle: "Redaction label",
|
|
295
298
|
redactLabelPlaceholder: "e.g. REDACTED",
|
|
296
|
-
redactLabelRepeat: "Repeat label across region"
|
|
299
|
+
redactLabelRepeat: "Repeat label across region",
|
|
300
|
+
stampsTitle: "Stamps",
|
|
301
|
+
stampsAll: "All stamps",
|
|
302
|
+
stampsLoading: "Loading stamps…",
|
|
303
|
+
stampsEmpty: "No stamps yet. Import a PDF to turn each of its pages into a stamp.",
|
|
304
|
+
stampsRemoveStamp: "Remove stamp",
|
|
305
|
+
stampsArmedHint: "Click a page to place the stamp.",
|
|
306
|
+
stampsImport: "Import PDF…",
|
|
307
|
+
stampsImporting: "Importing…",
|
|
308
|
+
stampsImportHint: "Import a PDF as a stamp library — each page becomes one stamp.",
|
|
309
|
+
stampsRemoveLibrary: "Remove library",
|
|
310
|
+
stampsExportLibrary: "Export library as PDF",
|
|
311
|
+
stampsCustomLibrary: "My stamps",
|
|
312
|
+
stampsCustomLabel: "Custom stamp",
|
|
313
|
+
stampsError: "The built-in stamps could not be loaded.",
|
|
314
|
+
stampsArmError: "That stamp could not be armed.",
|
|
315
|
+
stampsImportError: "That PDF could not be imported as a stamp library."
|
|
297
316
|
}
|
|
298
317
|
}
|
|
299
318
|
};
|
|
@@ -310,7 +329,8 @@ const en = {
|
|
|
310
329
|
*/
|
|
311
330
|
const ViewerConfigContext = (0, react.createContext)({
|
|
312
331
|
chrome: require_chrome.defaultChrome,
|
|
313
|
-
icons: {}
|
|
332
|
+
icons: {},
|
|
333
|
+
stamps: {}
|
|
314
334
|
});
|
|
315
335
|
const ViewerConfigProvider = ViewerConfigContext.Provider;
|
|
316
336
|
function useChromeSchema() {
|
|
@@ -325,6 +345,9 @@ function useStripSchema(id) {
|
|
|
325
345
|
function useCustomIcons() {
|
|
326
346
|
return (0, react.useContext)(ViewerConfigContext).icons;
|
|
327
347
|
}
|
|
348
|
+
function useStampsConfig() {
|
|
349
|
+
return (0, react.useContext)(ViewerConfigContext).stamps;
|
|
350
|
+
}
|
|
328
351
|
//#endregion
|
|
329
352
|
//#region src/handle.ts
|
|
330
353
|
function createViewerHandle(kernel) {
|
|
@@ -3480,6 +3503,310 @@ function SearchPanel() {
|
|
|
3480
3503
|
});
|
|
3481
3504
|
}
|
|
3482
3505
|
//#endregion
|
|
3506
|
+
//#region src/config/default-stamps.ts
|
|
3507
|
+
/**
|
|
3508
|
+
* The viewer's BUILT-IN stamp library: `@embedpdf/default-stamps`, the
|
|
3509
|
+
* standard rubber stamps as one Acrobat-compatible PDF per locale.
|
|
3510
|
+
*
|
|
3511
|
+
* Delivery: the library ships INSIDE whatever ships this viewer. The package
|
|
3512
|
+
* exposes each locale as a lazy ES module (`@embedpdf/default-stamps/library`),
|
|
3513
|
+
* so it travels through the module graph like any other code — every bundler
|
|
3514
|
+
* splits it into a chunk served from the app's own origin, the CDN snippet
|
|
3515
|
+
* carries it as a sibling chunk in its folder, and nothing is fetched from a
|
|
3516
|
+
* third party. `stamps.defaultLibrary` overrides that:
|
|
3517
|
+
* - `false` → no built-in library, no request (air-gapped);
|
|
3518
|
+
* - a URL template → exactly that (`{locale}` slot), for self-hosted copies.
|
|
3519
|
+
*
|
|
3520
|
+
* Loading is LAZY (first open of the stamps panel, never at boot) and
|
|
3521
|
+
* locale-aware: the chrome locale leads, the browser languages break ties
|
|
3522
|
+
* for the locales the chrome does not translate itself, and a locale change
|
|
3523
|
+
* while a default library is loaded swaps it for the new one. The file names
|
|
3524
|
+
* itself (`/Title`, `Id: embedpdf-standard`), so no overrides are passed.
|
|
3525
|
+
*/
|
|
3526
|
+
/** The built-in library's PieceInfo id — excluded from persistence (it is
|
|
3527
|
+
* loaded again on every first open, in the locale of that moment). */
|
|
3528
|
+
const DEFAULT_LIBRARY_ID = "embedpdf-standard";
|
|
3529
|
+
/** Which shipped locale to show for the chrome's locale + the browser's. */
|
|
3530
|
+
function resolveStampsLocale(chromeLocale) {
|
|
3531
|
+
return (0, _embedpdf_react_i18n.negotiateLocale)(_embedpdf_default_stamps_library.LOCALES, [chromeLocale, ...typeof navigator !== "undefined" ? navigator.languages ?? [] : []]) ?? "en";
|
|
3532
|
+
}
|
|
3533
|
+
const fetchBytes = async (url) => {
|
|
3534
|
+
const response = await fetch(url);
|
|
3535
|
+
if (!response.ok) throw new Error(`${url}: HTTP ${response.status}`);
|
|
3536
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
3537
|
+
};
|
|
3538
|
+
/** The library bytes for `locale`: your URL when you gave one, otherwise the
|
|
3539
|
+
* copy that shipped with the viewer. Never anything else. */
|
|
3540
|
+
const defaultLibraryBytes = (locale, source) => source ? fetchBytes(source.replace("{locale}", locale)) : (0, _embedpdf_default_stamps_library.loadDefaultLibrary)(locale);
|
|
3541
|
+
/** Per workspace: the locale the default library was last brought to, and
|
|
3542
|
+
* the in-flight work — the panel mounts and unmounts with the sidebar, so
|
|
3543
|
+
* this cannot live in component state. */
|
|
3544
|
+
const loads = /* @__PURE__ */ new WeakMap();
|
|
3545
|
+
/**
|
|
3546
|
+
* Bring the built-in library to `locale`. Idempotent per workspace and
|
|
3547
|
+
* locale; a locale change swaps the loaded default (remove, then import);
|
|
3548
|
+
* a default the user removed in this session stays removed — the locale is
|
|
3549
|
+
* recorded, nothing is re-added. An embedder that seeded a library under
|
|
3550
|
+
* the same id is left alone.
|
|
3551
|
+
*/
|
|
3552
|
+
function ensureDefaultLibrary(stamp, locale, source) {
|
|
3553
|
+
if (source === false) return Promise.resolve();
|
|
3554
|
+
const current = loads.get(stamp);
|
|
3555
|
+
if (current?.locale === locale) return current.promise;
|
|
3556
|
+
const run = async () => {
|
|
3557
|
+
await current?.promise.catch(() => void 0);
|
|
3558
|
+
const loaded = stamp.library(DEFAULT_LIBRARY_ID);
|
|
3559
|
+
if (loaded?.locale === locale) return;
|
|
3560
|
+
if (current && !loaded) return;
|
|
3561
|
+
if (loaded) await stamp.removeLibrary(DEFAULT_LIBRARY_ID);
|
|
3562
|
+
await stamp.importLibraryPdf(await defaultLibraryBytes(locale, source));
|
|
3563
|
+
};
|
|
3564
|
+
const promise = run();
|
|
3565
|
+
loads.set(stamp, {
|
|
3566
|
+
locale,
|
|
3567
|
+
promise
|
|
3568
|
+
});
|
|
3569
|
+
return promise;
|
|
3570
|
+
}
|
|
3571
|
+
//#endregion
|
|
3572
|
+
//#region src/ui/stamps-panel.tsx
|
|
3573
|
+
/**
|
|
3574
|
+
* The stamps sidebar (right panel) — v2's rubber-stamp sidebar on v3 parts.
|
|
3575
|
+
* The stamp plugin owns the libraries, the asset bytes and the cached
|
|
3576
|
+
* previews; this panel is the PICKER over them:
|
|
3577
|
+
*
|
|
3578
|
+
* a library <select> ("All stamps" + one per library, shown once there
|
|
3579
|
+
* are two) → a two-column grid of thumbnails → click a stamp → `armAsset`
|
|
3580
|
+
* arms the annotation plugin's stamp tool → hovering a page ghosts the
|
|
3581
|
+
* exact placement → each click on a page places one.
|
|
3582
|
+
*
|
|
3583
|
+
* Deliberately NOT a file dialog: picking arbitrary image bytes is the Image
|
|
3584
|
+
* tool's job (a click-then-pick tool, `insert:add-image`). The stamp button
|
|
3585
|
+
* opens a library; a library is a set of reusable, named assets — the two
|
|
3586
|
+
* gestures are different, so they are different controls.
|
|
3587
|
+
*
|
|
3588
|
+
* The panel stays open while you place, so a stamp can be dropped on several
|
|
3589
|
+
* pages and swapped without a round trip through the toolbar. A command can
|
|
3590
|
+
* open it ON a library (`annotation:stamp-from-selection` → "My stamps")
|
|
3591
|
+
* through the surface's open props.
|
|
3592
|
+
*/
|
|
3593
|
+
const ALL = "all";
|
|
3594
|
+
/** The user's libraries live in the browser (IndexedDB): restored on first
|
|
3595
|
+
* open, written on every change. The built-in set is never stored — it is
|
|
3596
|
+
* fetched again next time, in the locale of that moment. */
|
|
3597
|
+
const store = typeof indexedDB === "undefined" ? null : (0, _embedpdf_react_stamp.indexedDbByteStore)("embedpdf-stamps", { storeName: "libraries" });
|
|
3598
|
+
/** Restore runs ONCE per workspace — the panel mounts and unmounts with the
|
|
3599
|
+
* sidebar, so the guard cannot live in component state. */
|
|
3600
|
+
const restored = /* @__PURE__ */ new WeakMap();
|
|
3601
|
+
const restoreOnce = (stamp) => {
|
|
3602
|
+
let pending = restored.get(stamp);
|
|
3603
|
+
if (!pending) {
|
|
3604
|
+
pending = store ? (0, _embedpdf_react_stamp.restoreStampLibraries)(stamp, store) : Promise.resolve();
|
|
3605
|
+
restored.set(stamp, pending);
|
|
3606
|
+
}
|
|
3607
|
+
return pending;
|
|
3608
|
+
};
|
|
3609
|
+
function StampsPanel() {
|
|
3610
|
+
const t = (0, _embedpdf_react_i18n.useT)();
|
|
3611
|
+
const stamp = (0, _embedpdf_react_stamp.useStamp)();
|
|
3612
|
+
const libraries = (0, _embedpdf_react_stamp.useStampLibraries)();
|
|
3613
|
+
const assets = (0, _embedpdf_react_stamp.useStampAssets)();
|
|
3614
|
+
const { armAsset } = (0, _embedpdf_react_stamp.useArmStampAsset)();
|
|
3615
|
+
const { activeToolId } = (0, _embedpdf_react_interaction.useTool)();
|
|
3616
|
+
const { locale } = (0, _embedpdf_react_i18n.useLocale)();
|
|
3617
|
+
const { defaultLibrary } = useStampsConfig();
|
|
3618
|
+
const surface = (0, _embedpdf_react_shell.useSurface)("stamps");
|
|
3619
|
+
const fileRef = (0, react.useRef)(null);
|
|
3620
|
+
const [armedId, setArmedId] = (0, react.useState)(null);
|
|
3621
|
+
const [busy, setBusy] = (0, react.useState)(null);
|
|
3622
|
+
const [error, setError] = (0, react.useState)(null);
|
|
3623
|
+
const [picked, setPicked] = (0, react.useState)(ALL);
|
|
3624
|
+
const openedOn = surface.props?.libraryId;
|
|
3625
|
+
(0, react.useEffect)(() => {
|
|
3626
|
+
if (typeof openedOn === "string") setPicked(openedOn);
|
|
3627
|
+
}, [openedOn]);
|
|
3628
|
+
(0, react.useEffect)(() => {
|
|
3629
|
+
let live = true;
|
|
3630
|
+
setBusy("loading");
|
|
3631
|
+
restoreOnce(stamp).then(() => ensureDefaultLibrary(stamp, resolveStampsLocale(locale), defaultLibrary)).catch((err) => {
|
|
3632
|
+
console.error("[embedpdf] default stamps failed:", err);
|
|
3633
|
+
if (live) setError(t("demo.stampsError"));
|
|
3634
|
+
}).finally(() => live && setBusy(null));
|
|
3635
|
+
return () => {
|
|
3636
|
+
live = false;
|
|
3637
|
+
};
|
|
3638
|
+
}, [stamp, locale]);
|
|
3639
|
+
(0, react.useEffect)(() => store ? (0, _embedpdf_react_stamp.persistStampLibraries)(stamp, store, { except: [DEFAULT_LIBRARY_ID] }) : void 0, [stamp]);
|
|
3640
|
+
(0, react.useEffect)(() => {
|
|
3641
|
+
if (activeToolId !== "stamp") setArmedId(null);
|
|
3642
|
+
}, [activeToolId]);
|
|
3643
|
+
const arm = (asset) => {
|
|
3644
|
+
setError(null);
|
|
3645
|
+
setArmedId(asset.id);
|
|
3646
|
+
armAsset(asset.id).catch((err) => {
|
|
3647
|
+
console.error("[embedpdf] arm stamp failed:", err);
|
|
3648
|
+
setArmedId(null);
|
|
3649
|
+
setError(t("demo.stampsArmError"));
|
|
3650
|
+
});
|
|
3651
|
+
};
|
|
3652
|
+
const importPdf = (file) => {
|
|
3653
|
+
setError(null);
|
|
3654
|
+
setBusy("importing");
|
|
3655
|
+
stamp.importLibraryPdf(file, { name: file.name.replace(/\.pdf$/i, "") }).then((id) => setPicked(id)).catch((err) => {
|
|
3656
|
+
console.error("[embedpdf] stamp library import failed:", err);
|
|
3657
|
+
setError(t("demo.stampsImportError"));
|
|
3658
|
+
}).finally(() => setBusy(null));
|
|
3659
|
+
};
|
|
3660
|
+
/** The library as the PDF it is — title, registry, artwork — for Acrobat
|
|
3661
|
+
* or another viewer. */
|
|
3662
|
+
const exportPdf = (libraryId, name) => {
|
|
3663
|
+
const bytes = stamp.exportLibrary(libraryId);
|
|
3664
|
+
if (!bytes) return;
|
|
3665
|
+
const blob = new Blob([bytes], { type: "application/pdf" });
|
|
3666
|
+
const url = URL.createObjectURL(blob);
|
|
3667
|
+
const a = document.createElement("a");
|
|
3668
|
+
a.href = url;
|
|
3669
|
+
a.download = `${name || "stamps"}.pdf`;
|
|
3670
|
+
a.click();
|
|
3671
|
+
URL.revokeObjectURL(url);
|
|
3672
|
+
};
|
|
3673
|
+
const selectedId = libraries.length === 1 ? libraries[0].id : libraries.some((library) => library.id === picked) ? picked : ALL;
|
|
3674
|
+
const selected = selectedId === ALL ? null : libraries.find((l) => l.id === selectedId) ?? null;
|
|
3675
|
+
const shown = selectedId === ALL ? assets : assets.filter((a) => a.libraryId === selectedId);
|
|
3676
|
+
const isDefault = (libraryId) => libraryId === DEFAULT_LIBRARY_ID;
|
|
3677
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3678
|
+
className: "flex min-h-0 flex-1 flex-col",
|
|
3679
|
+
children: [
|
|
3680
|
+
libraries.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3681
|
+
className: "border-border-subtle flex items-center gap-1 border-b p-2",
|
|
3682
|
+
children: [libraries.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
3683
|
+
value: selectedId,
|
|
3684
|
+
onChange: (event) => setPicked(event.target.value),
|
|
3685
|
+
"aria-label": t("demo.stampsTitle"),
|
|
3686
|
+
className: "border-border bg-surface text-fg min-w-0 flex-1 rounded border px-2 py-1.5 text-sm",
|
|
3687
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3688
|
+
value: ALL,
|
|
3689
|
+
children: t("demo.stampsAll")
|
|
3690
|
+
}), libraries.map((library) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
3691
|
+
value: library.id,
|
|
3692
|
+
children: library.name
|
|
3693
|
+
}, library.id))]
|
|
3694
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3695
|
+
className: "text-fg-muted min-w-0 flex-1 truncate px-1 text-xs font-semibold uppercase tracking-wide",
|
|
3696
|
+
children: libraries[0].name
|
|
3697
|
+
}), selected && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3698
|
+
type: "button",
|
|
3699
|
+
onClick: () => exportPdf(selected.id, selected.name),
|
|
3700
|
+
title: t("demo.stampsExportLibrary"),
|
|
3701
|
+
className: "text-fg-muted hover:text-fg grid h-7 w-7 shrink-0 place-items-center rounded",
|
|
3702
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
3703
|
+
name: "download",
|
|
3704
|
+
size: 14
|
|
3705
|
+
})
|
|
3706
|
+
}), !isDefault(selected.id) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3707
|
+
type: "button",
|
|
3708
|
+
onClick: () => void stamp.removeLibrary(selected.id),
|
|
3709
|
+
title: t("demo.stampsRemoveLibrary"),
|
|
3710
|
+
className: "text-fg-muted hover:text-fg grid h-7 w-7 shrink-0 place-items-center rounded",
|
|
3711
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
3712
|
+
name: "trash",
|
|
3713
|
+
size: 14
|
|
3714
|
+
})
|
|
3715
|
+
})] })]
|
|
3716
|
+
}),
|
|
3717
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3718
|
+
className: "min-h-0 flex-1 overflow-y-auto p-3",
|
|
3719
|
+
children: busy === "loading" && shown.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3720
|
+
className: "flex flex-col items-center gap-3 px-4 py-10 text-center",
|
|
3721
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "border-border-subtle border-t-accent h-6 w-6 animate-spin rounded-full border-2" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3722
|
+
className: "text-fg-muted text-sm",
|
|
3723
|
+
children: t("demo.stampsLoading")
|
|
3724
|
+
})]
|
|
3725
|
+
}) : shown.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3726
|
+
className: "flex flex-col items-center gap-3 px-4 py-10 text-center",
|
|
3727
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
3728
|
+
name: "rubberStamp",
|
|
3729
|
+
size: 32,
|
|
3730
|
+
className: "text-fg-muted"
|
|
3731
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3732
|
+
className: "text-fg-muted text-sm",
|
|
3733
|
+
children: t("demo.stampsEmpty")
|
|
3734
|
+
})]
|
|
3735
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
3736
|
+
className: "grid grid-cols-2 gap-3",
|
|
3737
|
+
children: shown.map((asset) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
3738
|
+
className: "group relative",
|
|
3739
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StampAssetCell, {
|
|
3740
|
+
asset,
|
|
3741
|
+
armed: armedId === asset.id,
|
|
3742
|
+
onArm: () => arm(asset)
|
|
3743
|
+
}), !isDefault(asset.libraryId) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3744
|
+
type: "button",
|
|
3745
|
+
onClick: () => void stamp.removeAsset(asset.id),
|
|
3746
|
+
title: t("demo.stampsRemoveStamp"),
|
|
3747
|
+
className: "bg-surface border-border text-fg-muted hover:text-fg absolute right-1 top-1 grid h-5 w-5 place-items-center rounded-full border opacity-0 shadow-sm focus:opacity-100 group-hover:opacity-100",
|
|
3748
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
3749
|
+
name: "x",
|
|
3750
|
+
size: 12
|
|
3751
|
+
})
|
|
3752
|
+
})]
|
|
3753
|
+
}, asset.id))
|
|
3754
|
+
})
|
|
3755
|
+
}),
|
|
3756
|
+
(error || armedId) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3757
|
+
className: `border-border-subtle shrink-0 border-t px-3 py-2 text-xs ${error ? "text-red-600" : "text-fg-muted"}`,
|
|
3758
|
+
children: error ?? t("demo.stampsArmedHint")
|
|
3759
|
+
}),
|
|
3760
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3761
|
+
className: "border-border-subtle shrink-0 border-t p-3",
|
|
3762
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3763
|
+
type: "button",
|
|
3764
|
+
disabled: busy != null,
|
|
3765
|
+
onClick: () => fileRef.current?.click(),
|
|
3766
|
+
title: t("demo.stampsImportHint"),
|
|
3767
|
+
className: "border-border text-fg hover:bg-hover flex w-full items-center justify-center gap-2 rounded-md border px-3 py-1.5 text-sm disabled:opacity-40",
|
|
3768
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, {
|
|
3769
|
+
name: "rubberStampPlus",
|
|
3770
|
+
size: 16
|
|
3771
|
+
}), busy === "importing" ? t("demo.stampsImporting") : t("demo.stampsImport")]
|
|
3772
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
3773
|
+
ref: fileRef,
|
|
3774
|
+
type: "file",
|
|
3775
|
+
accept: "application/pdf",
|
|
3776
|
+
className: "hidden",
|
|
3777
|
+
onChange: (e) => {
|
|
3778
|
+
const file = e.currentTarget.files?.[0];
|
|
3779
|
+
e.currentTarget.value = "";
|
|
3780
|
+
if (file) importPdf(file);
|
|
3781
|
+
}
|
|
3782
|
+
})]
|
|
3783
|
+
})
|
|
3784
|
+
]
|
|
3785
|
+
});
|
|
3786
|
+
}
|
|
3787
|
+
/** One grid cell: the plugin's cached preview through the object-URL hook
|
|
3788
|
+
* (the DOM resource's lifetime belongs to the framework layer, not the
|
|
3789
|
+
* store). Image only, as v2 — the artwork IS the label; the label is the
|
|
3790
|
+
* tooltip for the curious. */
|
|
3791
|
+
function StampAssetCell({ asset, armed, onArm }) {
|
|
3792
|
+
const url = (0, _embedpdf_react_stamp.useStampAssetPreviewUrl)(asset.id);
|
|
3793
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3794
|
+
type: "button",
|
|
3795
|
+
onClick: onArm,
|
|
3796
|
+
title: asset.label,
|
|
3797
|
+
"aria-pressed": armed,
|
|
3798
|
+
className: `flex aspect-square w-full items-center justify-center rounded-md border p-2 ${armed ? "border-accent bg-accent-light ring-accent ring-2" : "border-border-subtle hover:border-border hover:bg-hover"}`,
|
|
3799
|
+
children: url ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
3800
|
+
src: url,
|
|
3801
|
+
alt: asset.label,
|
|
3802
|
+
className: "max-h-full max-w-full object-contain"
|
|
3803
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3804
|
+
className: "text-fg truncate text-sm",
|
|
3805
|
+
children: asset.label
|
|
3806
|
+
})
|
|
3807
|
+
});
|
|
3808
|
+
}
|
|
3809
|
+
//#endregion
|
|
3483
3810
|
//#region src/ui/panels.tsx
|
|
3484
3811
|
/**
|
|
3485
3812
|
* The static chrome around the toolbars: the left/right sidebars (shell
|
|
@@ -3573,10 +3900,11 @@ function RightSidebar() {
|
|
|
3573
3900
|
const comment = (0, _embedpdf_react_shell.useSurface)("comment");
|
|
3574
3901
|
const style = (0, _embedpdf_react_shell.useSurface)("annotation-style");
|
|
3575
3902
|
const redaction = (0, _embedpdf_react_shell.useSurface)("redaction");
|
|
3576
|
-
const
|
|
3903
|
+
const stamps = (0, _embedpdf_react_shell.useSurface)("stamps");
|
|
3904
|
+
const active = search.isOpen ? "search" : comment.isOpen ? "comment" : style.isOpen ? "style" : redaction.isOpen ? "redaction" : stamps.isOpen ? "stamps" : null;
|
|
3577
3905
|
if (!active) return null;
|
|
3578
|
-
const titleKey = active === "search" ? "demo.searchTitle" : active === "comment" ? "demo.commentsTitle" : active === "redaction" ? "demo.redactionTitle" : "demo.styleTitle";
|
|
3579
|
-
const close = active === "search" ? search.close : active === "comment" ? comment.close : active === "redaction" ? redaction.close : style.close;
|
|
3906
|
+
const titleKey = active === "search" ? "demo.searchTitle" : active === "comment" ? "demo.commentsTitle" : active === "redaction" ? "demo.redactionTitle" : active === "stamps" ? "demo.stampsTitle" : "demo.styleTitle";
|
|
3907
|
+
const close = active === "search" ? search.close : active === "comment" ? comment.close : active === "redaction" ? redaction.close : active === "stamps" ? stamps.close : style.close;
|
|
3580
3908
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
|
|
3581
3909
|
className: "border-border-subtle bg-surface flex w-72 shrink-0 flex-col border-l",
|
|
3582
3910
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3593,7 +3921,7 @@ function RightSidebar() {
|
|
|
3593
3921
|
size: 16
|
|
3594
3922
|
})
|
|
3595
3923
|
})]
|
|
3596
|
-
}), active === "style" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnnotationStylePanel, {}) : active === "search" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchPanel, {}, documentId ?? "none") : active === "redaction" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RedactionPanel, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommentsPanel, {}, documentId ?? "none")]
|
|
3924
|
+
}), active === "style" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnnotationStylePanel, {}) : active === "search" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchPanel, {}, documentId ?? "none") : active === "redaction" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RedactionPanel, {}) : active === "stamps" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StampsPanel, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CommentsPanel, {}, documentId ?? "none")]
|
|
3597
3925
|
});
|
|
3598
3926
|
}
|
|
3599
3927
|
function PageControls() {
|
|
@@ -3913,7 +4241,7 @@ function DocumentArea({ children }) {
|
|
|
3913
4241
|
function Shell() {
|
|
3914
4242
|
(0, _embedpdf_react_commands.useCommandShortcuts)();
|
|
3915
4243
|
(0, _embedpdf_react_annotation.useFilePickerProvider)();
|
|
3916
|
-
(0,
|
|
4244
|
+
(0, _embedpdf_react_actions.useActionsUiAdapter)();
|
|
3917
4245
|
const schema = useChromeSchema();
|
|
3918
4246
|
const frame = schema.frame ?? {};
|
|
3919
4247
|
const toolbarEdge = frame.toolbar ?? "top";
|
|
@@ -3993,7 +4321,7 @@ function Shell() {
|
|
|
3993
4321
|
* changing these props on a mounted viewer does not rebuild the workspace.
|
|
3994
4322
|
*/
|
|
3995
4323
|
/** Lazy built-in packs — a strings override for one of these wraps its loader. */
|
|
3996
|
-
const BUILTIN_LOADERS = { es: () => Promise.resolve().then(() => require("./es-
|
|
4324
|
+
const BUILTIN_LOADERS = { es: () => Promise.resolve().then(() => require("./es-BRYSk4rY.cjs")).then((m) => m.es) };
|
|
3997
4325
|
const isDict = (v) => typeof v === "object" && v !== null;
|
|
3998
4326
|
/**
|
|
3999
4327
|
* Deep-merge `over` into `base` (immutably), expanding dotted keys —
|
|
@@ -4037,7 +4365,7 @@ function Booting() {
|
|
|
4037
4365
|
})
|
|
4038
4366
|
});
|
|
4039
4367
|
}
|
|
4040
|
-
function FullViewer({ engine, initialDocuments, fallback, commands, icons, strings, locale = "auto", disabledCategories, chrome, theme, themeTarget, onViewer }) {
|
|
4368
|
+
function FullViewer({ engine, initialDocuments, fallback, commands, icons, strings, locale = "auto", disabledCategories, chrome, stamps, theme, themeTarget, onViewer }) {
|
|
4041
4369
|
const [resolved] = (0, react.useState)(() => {
|
|
4042
4370
|
const byId = new Map(require_commands.defaultCommands.map((c) => [c.id, c]));
|
|
4043
4371
|
for (const c of commands ?? []) byId.set(c.id, c);
|
|
@@ -4068,6 +4396,7 @@ function FullViewer({ engine, initialDocuments, fallback, commands, icons, strin
|
|
|
4068
4396
|
commands: resolvedCommands,
|
|
4069
4397
|
chrome: resolvedChrome,
|
|
4070
4398
|
icons: icons ?? {},
|
|
4399
|
+
stamps: stamps ?? {},
|
|
4071
4400
|
i18n: {
|
|
4072
4401
|
locales,
|
|
4073
4402
|
loaders,
|
|
@@ -4116,8 +4445,23 @@ function FullViewer({ engine, initialDocuments, fallback, commands, icons, strin
|
|
|
4116
4445
|
start: "none",
|
|
4117
4446
|
end: "open-arrow"
|
|
4118
4447
|
} }
|
|
4448
|
+
}, {
|
|
4449
|
+
id: "image",
|
|
4450
|
+
extends: "stamp",
|
|
4451
|
+
source: {
|
|
4452
|
+
kind: "prompt",
|
|
4453
|
+
accept: "image/png,image/jpeg"
|
|
4454
|
+
}
|
|
4119
4455
|
}] }),
|
|
4120
|
-
(0,
|
|
4456
|
+
(0, _embedpdf_react_stamp.stampPlugin)(),
|
|
4457
|
+
(0, _embedpdf_react_actions.actionsPlugin)({ javascript: {
|
|
4458
|
+
enabled: true,
|
|
4459
|
+
identity: {
|
|
4460
|
+
name: "John Doe",
|
|
4461
|
+
corporation: "Acme Inc"
|
|
4462
|
+
}
|
|
4463
|
+
} }),
|
|
4464
|
+
(0, _embedpdf_react_form.formPlugin)(),
|
|
4121
4465
|
(0, _embedpdf_react_link.linkPlugin)(),
|
|
4122
4466
|
(0, _embedpdf_react_redaction.redactionPlugin)(),
|
|
4123
4467
|
(0, _embedpdf_react_search.searchPlugin)(),
|
|
@@ -4224,6 +4568,12 @@ Object.defineProperty(exports, "StageToken", {
|
|
|
4224
4568
|
return _embedpdf_react_stage.StageToken;
|
|
4225
4569
|
}
|
|
4226
4570
|
});
|
|
4571
|
+
Object.defineProperty(exports, "StampToken", {
|
|
4572
|
+
enumerable: true,
|
|
4573
|
+
get: function() {
|
|
4574
|
+
return _embedpdf_react_stamp.StampToken;
|
|
4575
|
+
}
|
|
4576
|
+
});
|
|
4227
4577
|
Object.defineProperty(exports, "addItem", {
|
|
4228
4578
|
enumerable: true,
|
|
4229
4579
|
get: function() {
|