@embedpdf/plugin-stage 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/contract-BU33R4oL.cjs +13 -0
- package/dist/contract-BU33R4oL.cjs.map +1 -0
- package/dist/contract-CPSX8ANN.js +8 -0
- package/dist/contract-CPSX8ANN.js.map +1 -0
- package/dist/contract.cjs +10 -0
- package/dist/contract.d.cts +3 -0
- package/dist/contract.d.ts +3 -0
- package/dist/contract.js +2 -0
- package/dist/destination.cjs +147 -0
- package/dist/destination.cjs.map +1 -0
- package/dist/destination.d.cts +19 -0
- package/dist/destination.d.ts +19 -0
- package/dist/destination.js +146 -0
- package/dist/destination.js.map +1 -0
- package/dist/index.cjs +117 -158
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -657
- package/dist/index.d.ts +6 -657
- package/dist/index.js +117 -158
- package/dist/index.js.map +1 -1
- package/dist/types-CdIFKQnj.d.ts +656 -0
- package/dist/types-qdO_v-V-.d.cts +656 -0
- package/package.json +15 -5
package/dist/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { destinationToReveal } from "./destination.js";
|
|
2
|
+
import { n as StageToken, t as ZoomMode } from "./contract-CPSX8ANN.js";
|
|
3
|
+
import { definePlugin } from "@embedpdf/core";
|
|
4
|
+
import { ActionsToken } from "@embedpdf/plugin-actions/contract";
|
|
5
|
+
import { ActionsToken as ActionsToken$1 } from "@embedpdf/plugin-actions/contract/host";
|
|
2
6
|
import * as S from "@embedpdf/core-stage";
|
|
3
|
-
import { ZoomMode
|
|
4
|
-
import { addRotations, applyPoint, applyRect, displaySize, intersectRects,
|
|
7
|
+
import { ZoomMode as ZoomMode$1 } from "@embedpdf/core-stage";
|
|
8
|
+
import { addRotations, applyPoint, applyRect, displaySize, intersectRects, pageTransform, rotateScaleMatrix, snapToDevice } from "@embedpdf/core-geometry";
|
|
5
9
|
//#region src/motion.ts
|
|
6
10
|
/**
|
|
7
11
|
* The camera's MOTION vocabulary — every formula, pure.
|
|
@@ -831,7 +835,16 @@ function createStageCapability(ctx, config = {}) {
|
|
|
831
835
|
if (isFitAll()) goToTarget(ctx.getState().cursor, { behavior: "instant" });
|
|
832
836
|
else applyAnchor(anchor, align);
|
|
833
837
|
};
|
|
838
|
+
/** Tag what drives the NEXT camera/cursor change — dispatched on flips
|
|
839
|
+
* only, read by the page-state feed (see StageState.motionCause). */
|
|
840
|
+
const markCause = (cause) => {
|
|
841
|
+
if (ctx.getState().motionCause !== cause) ctx.dispatch({
|
|
842
|
+
type: "MOTION_CAUSE",
|
|
843
|
+
cause
|
|
844
|
+
});
|
|
845
|
+
};
|
|
834
846
|
const goToTarget = (pageIndex, opts) => {
|
|
847
|
+
markCause("programmatic");
|
|
835
848
|
cancelAnim();
|
|
836
849
|
const doc = ctx.document();
|
|
837
850
|
if (!doc || doc.pageCount === 0) return;
|
|
@@ -1209,11 +1222,13 @@ function createStageCapability(ctx, config = {}) {
|
|
|
1209
1222
|
});
|
|
1210
1223
|
},
|
|
1211
1224
|
setCamera: (c) => {
|
|
1225
|
+
markCause("user");
|
|
1212
1226
|
cancelAnim();
|
|
1213
1227
|
setCam(c);
|
|
1214
1228
|
syncCursorFromCamera();
|
|
1215
1229
|
},
|
|
1216
1230
|
panBy: (dx, dy) => {
|
|
1231
|
+
markCause("user");
|
|
1217
1232
|
cancelAnim();
|
|
1218
1233
|
if (gestureDepth > 0 && gestureElastic) {
|
|
1219
1234
|
gestureRaw = S.panByScreen(gestureRaw ?? unrubberize(cam()), dx, dy);
|
|
@@ -1222,6 +1237,7 @@ function createStageCapability(ctx, config = {}) {
|
|
|
1222
1237
|
syncCursorFromCamera();
|
|
1223
1238
|
},
|
|
1224
1239
|
scrollTo: (opts) => {
|
|
1240
|
+
markCause("user");
|
|
1225
1241
|
cancelAnim();
|
|
1226
1242
|
if (!buildScene().itemCount) return;
|
|
1227
1243
|
const next = S.cameraFromScroll(cam(), stayBounds(), vp(), pad(), opts);
|
|
@@ -1324,6 +1340,7 @@ function createStageCapability(ctx, config = {}) {
|
|
|
1324
1340
|
},
|
|
1325
1341
|
goToPage: (pageIndex, opts) => goToTarget(pageIndex, opts),
|
|
1326
1342
|
reveal: (pageIndex, opts) => {
|
|
1343
|
+
markCause("programmatic");
|
|
1327
1344
|
const doc = ctx.document();
|
|
1328
1345
|
if (!doc || doc.pageCount === 0) return;
|
|
1329
1346
|
const target = Math.max(0, Math.min(pageIndex, doc.pageCount - 1));
|
|
@@ -1465,22 +1482,51 @@ function createStageCapability(ctx, config = {}) {
|
|
|
1465
1482
|
//#endregion
|
|
1466
1483
|
//#region src/effects.ts
|
|
1467
1484
|
/**
|
|
1468
|
-
*
|
|
1469
|
-
* (rotate/move/delete bump `DocumentMeta.revision`), the page geometry under
|
|
1470
|
-
* this lens just changed, so re-fit — re-resolve the active zoom intent and
|
|
1471
|
-
* re-place against the new footprint.
|
|
1485
|
+
* Stage lens effects.
|
|
1472
1486
|
*
|
|
1473
|
-
*
|
|
1474
|
-
*
|
|
1475
|
-
*
|
|
1476
|
-
* only
|
|
1487
|
+
* 1. Re-fit on page-registry mutations: when rotate/move/delete bump
|
|
1488
|
+
* `DocumentMeta.revision`, the page geometry under this lens changed, so
|
|
1489
|
+
* re-resolve the active zoom intent and re-place against the new
|
|
1490
|
+
* footprint. Steady-state only — it reacts to revision CHANGES, never the
|
|
1491
|
+
* initial value, so it can't race the capability's level-triggered
|
|
1492
|
+
* initial placement.
|
|
1477
1493
|
*
|
|
1478
|
-
*
|
|
1479
|
-
*
|
|
1480
|
-
*
|
|
1494
|
+
* 2. The page-state feed (main lens only, actions plugin present): stage is
|
|
1495
|
+
* AUTHORITATIVE for what page the viewer is on; the action engine's
|
|
1496
|
+
* lifecycle coordinator owns WHEN page-lifecycle triggers fire. This feed
|
|
1497
|
+
* pushes `{ currentPon, visiblePons, placed, cause }` snapshots — pons,
|
|
1498
|
+
* not indexes (reorder-safe) — on placement and on every change; the
|
|
1499
|
+
* coordinator buffers them behind the document-open barrier and diffs
|
|
1500
|
+
* against its last-emitted state, so this side stays a dumb reporter.
|
|
1481
1501
|
*/
|
|
1482
|
-
function registerStageEffects(ctx, token) {
|
|
1502
|
+
function registerStageEffects(ctx, token, feedActions = false) {
|
|
1483
1503
|
ctx.watch(() => ctx.document()?.revision ?? 0, () => ctx.get(token).refit());
|
|
1504
|
+
if (!feedActions) return;
|
|
1505
|
+
const actions = ctx.tryGet(ActionsToken$1);
|
|
1506
|
+
if (!actions) return;
|
|
1507
|
+
const snapshot = () => {
|
|
1508
|
+
const state = ctx.getState();
|
|
1509
|
+
if (!state.placed) return null;
|
|
1510
|
+
const stage = ctx.get(token);
|
|
1511
|
+
return {
|
|
1512
|
+
currentPon: stage.pages()[state.cursor]?.pon ?? null,
|
|
1513
|
+
visiblePons: stage.visiblePages().map((page) => page.pon),
|
|
1514
|
+
cause: state.motionCause
|
|
1515
|
+
};
|
|
1516
|
+
};
|
|
1517
|
+
ctx.watch(() => {
|
|
1518
|
+
const s = snapshot();
|
|
1519
|
+
return s === null ? "unplaced" : `${s.currentPon ?? -1}|${[...s.visiblePons].sort((a, b) => a - b).join(",")}`;
|
|
1520
|
+
}, () => {
|
|
1521
|
+
const s = snapshot();
|
|
1522
|
+
if (s === null) return;
|
|
1523
|
+
actions.reportPageState({
|
|
1524
|
+
currentPon: s.currentPon,
|
|
1525
|
+
visiblePons: s.visiblePons,
|
|
1526
|
+
placed: true,
|
|
1527
|
+
cause: s.cause
|
|
1528
|
+
});
|
|
1529
|
+
});
|
|
1484
1530
|
}
|
|
1485
1531
|
//#endregion
|
|
1486
1532
|
//#region src/reducer.ts
|
|
@@ -1500,6 +1546,7 @@ const initialStageState = (config) => {
|
|
|
1500
1546
|
},
|
|
1501
1547
|
dpr: 1,
|
|
1502
1548
|
cursor: 0,
|
|
1549
|
+
motionCause: "user",
|
|
1503
1550
|
activeRules: [],
|
|
1504
1551
|
...DEFAULT_SETTINGS,
|
|
1505
1552
|
...overrides
|
|
@@ -1546,6 +1593,10 @@ const stageReducer = (state, a) => {
|
|
|
1546
1593
|
...state,
|
|
1547
1594
|
cursor: a.cursor
|
|
1548
1595
|
};
|
|
1596
|
+
case "MOTION_CAUSE": return {
|
|
1597
|
+
...state,
|
|
1598
|
+
motionCause: a.cause
|
|
1599
|
+
};
|
|
1549
1600
|
case "PATCH": return applyPatch(state, a.patch);
|
|
1550
1601
|
case "RESPONSIVE": return {
|
|
1551
1602
|
...state,
|
|
@@ -1555,9 +1606,6 @@ const stageReducer = (state, a) => {
|
|
|
1555
1606
|
}
|
|
1556
1607
|
};
|
|
1557
1608
|
//#endregion
|
|
1558
|
-
//#region src/types.ts
|
|
1559
|
-
const StageToken = createCapabilityToken("stage");
|
|
1560
|
-
//#endregion
|
|
1561
1609
|
//#region src/stage.plugin.ts
|
|
1562
1610
|
/**
|
|
1563
1611
|
* Wires the parts into a kernel plugin. This file is the "manifest": it says what
|
|
@@ -1570,154 +1618,65 @@ const stagePlugin = (options = {}) => {
|
|
|
1570
1618
|
id,
|
|
1571
1619
|
token,
|
|
1572
1620
|
scope: "document",
|
|
1621
|
+
optional: [ActionsToken],
|
|
1573
1622
|
initialState: () => initialStageState(config),
|
|
1574
1623
|
reduce: stageReducer,
|
|
1575
1624
|
capability: (ctx) => createStageCapability(ctx, config),
|
|
1576
|
-
effects: (ctx) => registerStageEffects(ctx, token)
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
* resolve (engine-side) to an explicit `PdfDestination`; this maps it onto
|
|
1586
|
-
* ONE `reveal(pageIndex, options)` call — no destination-specific camera
|
|
1587
|
-
* code exists anywhere else.
|
|
1588
|
-
*
|
|
1589
|
-
* The whole protocol collapses onto three knobs:
|
|
1590
|
-
* rect — what to look at (a point for /XYZ, a rect for /FitR, the
|
|
1591
|
-
* page for /Fit, the content bounding box for /FitB*)
|
|
1592
|
-
* zoom — 'keep' | {level} | 'fit' | 'fit-width' | 'fit-height'
|
|
1593
|
-
* anchor — where it lands; 'keep' encodes the spec's null-means-retain
|
|
1594
|
-
*
|
|
1595
|
-
* Coordinates convert PDF user space (y-up, absolute) → content space
|
|
1596
|
-
* (y-down, crop-relative) through the SAME `pdfToContent` matrix selection
|
|
1597
|
-
* and search use, so a destination and an overlay can never disagree.
|
|
1598
|
-
*/
|
|
1599
|
-
/**
|
|
1600
|
-
* Translate one explicit destination against its target page's layout.
|
|
1601
|
-
* `bbox` is the page's content BOUNDING box in CONTENT space, for the
|
|
1602
|
-
* `/FitB*` kinds — pass it when known; the crop box is the spec-tolerated
|
|
1603
|
-
* fallback until the engine exposes it.
|
|
1604
|
-
*/
|
|
1605
|
-
function destinationToReveal(dest, layout, bbox) {
|
|
1606
|
-
const geo = pageGeometry({
|
|
1607
|
-
crop: layout.boxes.crop,
|
|
1608
|
-
rotation: layout.rotation,
|
|
1609
|
-
userUnit: layout.userUnit
|
|
1610
|
-
}, 1);
|
|
1611
|
-
const crop = layout.boxes.crop;
|
|
1612
|
-
const toContent = (x, y) => applyPoint(geo.pdfToContent, {
|
|
1613
|
-
x,
|
|
1614
|
-
y
|
|
1615
|
-
});
|
|
1616
|
-
const page = {
|
|
1617
|
-
x: 0,
|
|
1618
|
-
y: 0,
|
|
1619
|
-
width: layout.size.width,
|
|
1620
|
-
height: layout.size.height
|
|
1621
|
-
};
|
|
1622
|
-
const box = bbox ?? page;
|
|
1623
|
-
const options = (() => {
|
|
1624
|
-
switch (dest.kind) {
|
|
1625
|
-
case "xyz": {
|
|
1626
|
-
const hasLeft = dest.left != null;
|
|
1627
|
-
const hasTop = dest.top != null;
|
|
1628
|
-
const p = toContent(dest.left ?? crop.left, dest.top ?? crop.top);
|
|
1629
|
-
return {
|
|
1630
|
-
rect: {
|
|
1631
|
-
x: p.x,
|
|
1632
|
-
y: p.y,
|
|
1633
|
-
width: 0,
|
|
1634
|
-
height: 0
|
|
1635
|
-
},
|
|
1636
|
-
anchor: {
|
|
1637
|
-
x: hasLeft ? "start" : "keep",
|
|
1638
|
-
y: hasTop ? "start" : "keep"
|
|
1639
|
-
},
|
|
1640
|
-
zoom: dest.zoom != null && dest.zoom !== 0 ? { level: dest.zoom } : "keep"
|
|
1625
|
+
effects: (ctx) => registerStageEffects(ctx, token, id === "stage"),
|
|
1626
|
+
init: (ctx) => {
|
|
1627
|
+
if (id !== "stage") return;
|
|
1628
|
+
const actions = ctx.tryGet(ActionsToken$1);
|
|
1629
|
+
if (!actions) return;
|
|
1630
|
+
ctx.cleanup(actions.registerExecutor("goto", (node) => {
|
|
1631
|
+
if (node.type !== "goto") return {
|
|
1632
|
+
status: "inert",
|
|
1633
|
+
reason: "not a goto node"
|
|
1641
1634
|
};
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
rect: {
|
|
1648
|
-
x: 0,
|
|
1649
|
-
y: hasTop ? toContent(crop.left, dest.top).y : 0,
|
|
1650
|
-
width: page.width,
|
|
1651
|
-
height: 0
|
|
1652
|
-
},
|
|
1653
|
-
zoom: "fit-width",
|
|
1654
|
-
anchor: { y: hasTop ? "start" : "keep" }
|
|
1635
|
+
const stage = ctx.tryGet(token);
|
|
1636
|
+
const layout = ctx.document()?.pages.find((p) => p.pageObjectNumber === node.destination.pageObjectNumber);
|
|
1637
|
+
if (!stage || !layout) return {
|
|
1638
|
+
status: "failed",
|
|
1639
|
+
error: "no stage or destination page available"
|
|
1655
1640
|
};
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
anchor: { x: hasLeft ? "start" : "keep" }
|
|
1668
|
-
};
|
|
1669
|
-
}
|
|
1670
|
-
case "fitR": {
|
|
1671
|
-
const tl = toContent(dest.left, dest.top);
|
|
1672
|
-
const br = toContent(dest.right, dest.bottom);
|
|
1673
|
-
return {
|
|
1674
|
-
rect: {
|
|
1675
|
-
x: Math.min(tl.x, br.x),
|
|
1676
|
-
y: Math.min(tl.y, br.y),
|
|
1677
|
-
width: Math.abs(br.x - tl.x),
|
|
1678
|
-
height: Math.abs(br.y - tl.y)
|
|
1679
|
-
},
|
|
1680
|
-
zoom: "fit"
|
|
1681
|
-
};
|
|
1682
|
-
}
|
|
1683
|
-
case "fitB": return {
|
|
1684
|
-
rect: box,
|
|
1685
|
-
zoom: "fit"
|
|
1686
|
-
};
|
|
1687
|
-
case "fitBH": {
|
|
1688
|
-
const hasTop = dest.top != null;
|
|
1689
|
-
const y = hasTop ? toContent(crop.left, dest.top).y : box.y;
|
|
1690
|
-
return {
|
|
1691
|
-
rect: {
|
|
1692
|
-
x: box.x,
|
|
1693
|
-
y,
|
|
1694
|
-
width: box.width,
|
|
1695
|
-
height: 0
|
|
1696
|
-
},
|
|
1697
|
-
zoom: "fit-width",
|
|
1698
|
-
anchor: { y: hasTop ? "start" : "keep" }
|
|
1641
|
+
const { pageIndex, options: reveal } = destinationToReveal(node.destination, layout);
|
|
1642
|
+
stage.reveal(pageIndex, {
|
|
1643
|
+
...reveal,
|
|
1644
|
+
behavior: "smooth"
|
|
1645
|
+
});
|
|
1646
|
+
return { status: "executed" };
|
|
1647
|
+
}));
|
|
1648
|
+
ctx.cleanup(actions.registerExecutor("named", (node) => {
|
|
1649
|
+
if (node.type !== "named") return {
|
|
1650
|
+
status: "inert",
|
|
1651
|
+
reason: "not a named node"
|
|
1699
1652
|
};
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
rect: {
|
|
1705
|
-
x: hasLeft ? toContent(dest.left, crop.top).x : box.x,
|
|
1706
|
-
y: box.y,
|
|
1707
|
-
width: 0,
|
|
1708
|
-
height: box.height
|
|
1709
|
-
},
|
|
1710
|
-
zoom: "fit-height",
|
|
1711
|
-
anchor: { x: hasLeft ? "start" : "keep" }
|
|
1653
|
+
const stage = ctx.tryGet(token);
|
|
1654
|
+
if (!stage) return {
|
|
1655
|
+
status: "failed",
|
|
1656
|
+
error: "no stage available"
|
|
1712
1657
|
};
|
|
1713
|
-
|
|
1658
|
+
switch (node.name) {
|
|
1659
|
+
case "NextPage":
|
|
1660
|
+
stage.next({ behavior: "smooth" });
|
|
1661
|
+
return { status: "executed" };
|
|
1662
|
+
case "PrevPage":
|
|
1663
|
+
stage.prev({ behavior: "smooth" });
|
|
1664
|
+
return { status: "executed" };
|
|
1665
|
+
case "FirstPage":
|
|
1666
|
+
stage.goToPage(0, { behavior: "smooth" });
|
|
1667
|
+
return { status: "executed" };
|
|
1668
|
+
case "LastPage":
|
|
1669
|
+
stage.goToPage(Math.max(0, stage.pageCount() - 1), { behavior: "smooth" });
|
|
1670
|
+
return { status: "executed" };
|
|
1671
|
+
default: return {
|
|
1672
|
+
status: "inert",
|
|
1673
|
+
reason: `unknown named action '${node.name}'`
|
|
1674
|
+
};
|
|
1675
|
+
}
|
|
1676
|
+
}));
|
|
1714
1677
|
}
|
|
1715
|
-
})
|
|
1716
|
-
|
|
1717
|
-
pageIndex: layout.index,
|
|
1718
|
-
options
|
|
1719
|
-
};
|
|
1720
|
-
}
|
|
1678
|
+
});
|
|
1679
|
+
};
|
|
1721
1680
|
//#endregion
|
|
1722
1681
|
//#region src/scroll-handler.ts
|
|
1723
1682
|
/**
|