@embedpdf/plugin-history 2.15.1 → 3.0.0-next.15

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.
Files changed (62) hide show
  1. package/LICENSE +192 -21
  2. package/README.md +65 -0
  3. package/dist/contract-B7QL_Q3D.js +11 -0
  4. package/dist/contract-B7QL_Q3D.js.map +1 -0
  5. package/dist/contract-DHipVkAd.cjs +21 -0
  6. package/dist/contract-DHipVkAd.cjs.map +1 -0
  7. package/dist/contract-J_XQyhyq.d.cts +66 -0
  8. package/dist/contract-J_XQyhyq.d.ts +66 -0
  9. package/dist/contract.cjs +4 -0
  10. package/dist/contract.d.cts +2 -0
  11. package/dist/contract.d.ts +2 -0
  12. package/dist/contract.js +2 -0
  13. package/dist/index.cjs +356 -2
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +56 -0
  16. package/dist/index.d.ts +56 -1
  17. package/dist/index.js +348 -377
  18. package/dist/index.js.map +1 -1
  19. package/package.json +25 -57
  20. package/dist/lib/actions.d.ts +0 -34
  21. package/dist/lib/history-plugin.d.ts +0 -33
  22. package/dist/lib/index.d.ts +0 -8
  23. package/dist/lib/manifest.d.ts +0 -4
  24. package/dist/lib/reducer.d.ts +0 -5
  25. package/dist/lib/types.d.ts +0 -151
  26. package/dist/preact/adapter.d.ts +0 -1
  27. package/dist/preact/core.d.ts +0 -1
  28. package/dist/preact/index.cjs +0 -2
  29. package/dist/preact/index.cjs.map +0 -1
  30. package/dist/preact/index.d.ts +0 -1
  31. package/dist/preact/index.js +0 -10
  32. package/dist/preact/index.js.map +0 -1
  33. package/dist/react/adapter.d.ts +0 -1
  34. package/dist/react/core.d.ts +0 -1
  35. package/dist/react/index.cjs +0 -2
  36. package/dist/react/index.cjs.map +0 -1
  37. package/dist/react/index.d.ts +0 -1
  38. package/dist/react/index.js +0 -10
  39. package/dist/react/index.js.map +0 -1
  40. package/dist/shared/hooks/index.d.ts +0 -1
  41. package/dist/shared/hooks/use-history.d.ts +0 -11
  42. package/dist/shared/index.d.ts +0 -2
  43. package/dist/shared-preact/hooks/index.d.ts +0 -1
  44. package/dist/shared-preact/hooks/use-history.d.ts +0 -11
  45. package/dist/shared-preact/index.d.ts +0 -2
  46. package/dist/shared-react/hooks/index.d.ts +0 -1
  47. package/dist/shared-react/hooks/use-history.d.ts +0 -11
  48. package/dist/shared-react/index.d.ts +0 -2
  49. package/dist/svelte/hooks/index.d.ts +0 -1
  50. package/dist/svelte/hooks/use-history.d.ts +0 -11
  51. package/dist/svelte/index.cjs +0 -2
  52. package/dist/svelte/index.cjs.map +0 -1
  53. package/dist/svelte/index.d.ts +0 -2
  54. package/dist/svelte/index.js +0 -10
  55. package/dist/svelte/index.js.map +0 -1
  56. package/dist/vue/hooks/index.d.ts +0 -1
  57. package/dist/vue/hooks/use-history.d.ts +0 -3
  58. package/dist/vue/index.cjs +0 -2
  59. package/dist/vue/index.cjs.map +0 -1
  60. package/dist/vue/index.d.ts +0 -2
  61. package/dist/vue/index.js +0 -10
  62. package/dist/vue/index.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,2 +1,356 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("@embedpdf/core"),e="history",o={id:e,name:"History Plugin",version:"1.0.0",provides:["history"],requires:[],optional:[],defaultConfig:{}},n="HISTORY/INIT_STATE",i="HISTORY/CLEANUP_STATE",s="HISTORY/SET_DOCUMENT_STATE",r=class extends t.BasePlugin{constructor(e,o){super(e,o),this.documentHistories=new Map,this.historyChange$=t.createEmitter()}async initialize(t){this.logger.info("HistoryPlugin","Initialize","History plugin initialized")}onDocumentLoadingStarted(t){this.dispatch((t=>({type:n,payload:{documentId:t}}))(t)),this.documentHistories.set(t,{topicHistories:new Map,globalTimeline:[],globalIndex:-1}),this.logger.debug("HistoryPlugin","DocumentOpened",`Initialized history state for document: ${t}`)}onDocumentClosed(t){this.dispatch((t=>({type:i,payload:{documentId:t}}))(t)),this.documentHistories.delete(t),this.logger.debug("HistoryPlugin","DocumentClosed",`Cleaned up history state for document: ${t}`)}getDocumentHistoryData(t){const e=t??this.getActiveDocumentId(),o=this.documentHistories.get(e);if(!o)throw new Error(`History data not found for document: ${e}`);return o}getDocumentHistoryState(t){const e=this.documentHistories.get(t);if(!e)throw new Error(`History data not found for document: ${t}`);const o={};return Array.from(e.topicHistories.entries()).forEach(([t,e])=>{o[t]={canUndo:e.currentIndex>-1,canRedo:e.currentIndex<e.commands.length-1}}),{global:{canUndo:e.globalIndex>-1,canRedo:e.globalIndex<e.globalTimeline.length-1},topics:o}}emitHistoryChange(t,e){const o=this.getDocumentHistoryState(t);this.dispatch(((t,e)=>({type:s,payload:{documentId:t,state:e}}))(t,o)),this.historyChange$.emit({documentId:t,topic:e,state:o})}register(t,e,o){const n=this.getDocumentHistoryData(o);n.topicHistories.has(e)||n.topicHistories.set(e,{commands:[],currentIndex:-1});const i=n.topicHistories.get(e);i.commands.splice(i.currentIndex+1),i.commands.push(t),i.currentIndex++;const s={command:t,topic:e};n.globalTimeline.splice(n.globalIndex+1),n.globalTimeline.push(s),n.globalIndex++,t.execute(),this.emitHistoryChange(o,e)}undo(t,e){const o=this.getDocumentHistoryData(e);let n;if(t){const e=o.topicHistories.get(t);e&&e.currentIndex>-1&&(e.commands[e.currentIndex].undo(),e.currentIndex--,n=t)}else if(o.globalIndex>-1){const t=o.globalTimeline[o.globalIndex];t.command.undo(),o.topicHistories.get(t.topic).currentIndex--,o.globalIndex--,n=t.topic}n&&this.emitHistoryChange(e,n)}redo(t,e){const o=this.getDocumentHistoryData(e);let n;if(t){const e=o.topicHistories.get(t);e&&e.currentIndex<e.commands.length-1&&(e.currentIndex++,e.commands[e.currentIndex].execute(),n=t)}else if(o.globalIndex<o.globalTimeline.length-1){o.globalIndex++;const t=o.globalTimeline[o.globalIndex];t.command.execute(),o.topicHistories.get(t.topic).currentIndex++,n=t.topic}n&&this.emitHistoryChange(e,n)}canUndo(t,e){const o=this.getDocumentHistoryData(e);if(t){const e=o.topicHistories.get(t);return!!e&&e.currentIndex>-1}return o.globalIndex>-1}canRedo(t,e){const o=this.getDocumentHistoryData(e);if(t){const e=o.topicHistories.get(t);return!!e&&e.currentIndex<e.commands.length-1}return o.globalIndex<o.globalTimeline.length-1}purgeByMetadata(t,e,o){const n=this.getDocumentHistoryData(o);let i=0;const s=e=>t(e.metadata),r=e?[e]:Array.from(n.topicHistories.keys());for(const d of r){const t=n.topicHistories.get(d);if(!t)continue;const e=[];let o=0;for(let n=0;n<t.commands.length;n++){const r=t.commands[n];s(r)?(n<=t.currentIndex&&o++,i++):e.push(r)}t.commands=e,t.currentIndex=Math.max(-1,t.currentIndex-o)}const c=[];let a=0;for(let d=0;d<n.globalTimeline.length;d++){const t=n.globalTimeline[d];(!e||t.topic===e)&&s(t.command)?d<=n.globalIndex&&a++:c.push(t)}return n.globalTimeline=c,n.globalIndex=Math.max(-1,n.globalIndex-a),i>0&&(this.emitHistoryChange(o,e),this.logger.debug("HistoryPlugin","PurgeByMetadata",`Purged ${i} history entries for document: ${o}${e?`, topic: ${e}`:""}`)),i}createHistoryScope(t){return{register:(e,o)=>this.register(e,o,t),undo:e=>this.undo(e,t),redo:e=>this.redo(e,t),canUndo:e=>this.canUndo(e,t),canRedo:e=>this.canRedo(e,t),getHistoryState:()=>this.getDocumentHistoryState(t),onHistoryChange:e=>this.historyChange$.on(o=>{o.documentId===t&&e(o.topic)}),purgeByMetadata:(e,o)=>this.purgeByMetadata(e,o,t)}}buildCapability(){return{register:(t,e)=>{const o=this.getActiveDocumentId();this.register(t,e,o)},undo:t=>{const e=this.getActiveDocumentId();this.undo(t,e)},redo:t=>{const e=this.getActiveDocumentId();this.redo(t,e)},canUndo:t=>{const e=this.getActiveDocumentId();return this.canUndo(t,e)},canRedo:t=>{const e=this.getActiveDocumentId();return this.canRedo(t,e)},getHistoryState:()=>{const t=this.getActiveDocumentId();return this.getDocumentHistoryState(t)},forDocument:t=>this.createHistoryScope(t),onHistoryChange:this.historyChange$.on,purgeByMetadata:(t,e)=>{const o=this.getActiveDocumentId();return this.purgeByMetadata(t,e,o)}}}async destroy(){this.historyChange$.clear(),this.documentHistories.clear(),super.destroy()}};r.id="history";let c=r;const a={global:{canUndo:!1,canRedo:!1},topics:{}},d={documents:{},activeDocumentId:null},u={manifest:o,create:t=>new c(e,t),reducer:(t=d,e)=>{switch(e.type){case n:{const{documentId:o}=e.payload;return{...t,documents:{...t.documents,[o]:{...a}}}}case i:{const{documentId:o}=e.payload,{[o]:n,...i}=t.documents;return{...t,documents:i,activeDocumentId:t.activeDocumentId===o?null:t.activeDocumentId}}case s:{const{documentId:o,state:n}=e.payload;return t.documents[o]?{...t,documents:{...t.documents,[o]:n}}:t}case"HISTORY/SET_ACTIVE_DOCUMENT":return{...t,activeDocumentId:e.payload};default:return t}},initialState:d};exports.HISTORY_PLUGIN_ID=e,exports.HistoryPlugin=c,exports.HistoryPluginPackage=u,exports.manifest=o;
2
- //# sourceMappingURL=index.cjs.map
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_contract = require("./contract-DHipVkAd.cjs");
3
+ let _embedpdf_core = require("@embedpdf/core");
4
+ let _embedpdf_engine_core_runtime = require("@embedpdf/engine-core/runtime");
5
+ //#region src/model.ts
6
+ const emptyHistory = () => ({
7
+ undo: [],
8
+ redo: [],
9
+ merging: null,
10
+ nextSeq: 0
11
+ });
12
+ /** The stack a step takes its entry from, and the one it puts it on. */
13
+ const stacksOf = (direction) => direction === "undo" ? {
14
+ from: "undo",
15
+ to: "redo"
16
+ } : {
17
+ from: "redo",
18
+ to: "undo"
19
+ };
20
+ /** The entry the next step of `direction` takes, or `null`. */
21
+ const nextOf = (history, direction) => history[direction].at(-1) ?? null;
22
+ /**
23
+ * A new change: it joins the newest entry when typing goes on under the same key, else it is a
24
+ * new entry on top, and the oldest go past `limit`. Anything that could be redone is forgotten.
25
+ */
26
+ function recorded(history, change, limit) {
27
+ const part = {
28
+ seq: history.nextSeq,
29
+ id: change.opId,
30
+ opId: change.opId,
31
+ forward: change.shows,
32
+ backward: change.undo
33
+ };
34
+ const top = history.undo.at(-1);
35
+ if (top && change.merge !== null && history.merging === change.merge) return {
36
+ undo: [...history.undo.slice(0, -1), {
37
+ ...top,
38
+ parts: [...top.parts, part]
39
+ }],
40
+ redo: [],
41
+ merging: change.merge,
42
+ nextSeq: history.nextSeq + 1
43
+ };
44
+ const entry = {
45
+ seq: part.seq,
46
+ label: change.label,
47
+ merge: change.merge,
48
+ parts: [part]
49
+ };
50
+ return {
51
+ undo: [...history.undo, entry].slice(-Math.max(1, limit)),
52
+ redo: [],
53
+ merging: change.merge,
54
+ nextSeq: history.nextSeq + 1
55
+ };
56
+ }
57
+ /**
58
+ * A step was staged: the entry `seq` leaves the top of its stack for the top of the other, each
59
+ * part now named by the change that staged it (`opIds`, by part id).
60
+ */
61
+ function stepped(history, direction, seq, opIds) {
62
+ const { from, to } = stacksOf(direction);
63
+ const entry = history[from].find((candidate) => candidate.seq === seq);
64
+ if (!entry) return history;
65
+ const moved = {
66
+ ...entry,
67
+ parts: entry.parts.map((part) => ({
68
+ ...part,
69
+ opId: opIds.get(part.id) ?? part.opId
70
+ }))
71
+ };
72
+ return {
73
+ ...history,
74
+ [from]: history[from].filter((candidate) => candidate !== entry),
75
+ [to]: [...history[to], moved],
76
+ merging: null
77
+ };
78
+ }
79
+ /** Every entry without the parts `drop` picks, and without the entries left with none. */
80
+ const withoutParts = (entries, drop) => {
81
+ let changed = false;
82
+ const kept = [];
83
+ for (const entry of entries) {
84
+ const parts = entry.parts.filter((part) => !drop(part));
85
+ if (parts.length === entry.parts.length) {
86
+ kept.push(entry);
87
+ continue;
88
+ }
89
+ changed = true;
90
+ if (parts.length) kept.push({
91
+ ...entry,
92
+ parts
93
+ });
94
+ }
95
+ return changed ? kept : entries;
96
+ };
97
+ /**
98
+ * The change `id` never happened (the engine refused it), or did nothing that can be undone:
99
+ * its part goes, from whichever stack holds it.
100
+ */
101
+ function forgotten(history, id) {
102
+ const undo = withoutParts(history.undo, (part) => part.id === id);
103
+ const redo = withoutParts(history.redo, (part) => part.id === id);
104
+ return undo === history.undo && redo === history.redo ? history : {
105
+ ...history,
106
+ undo,
107
+ redo
108
+ };
109
+ }
110
+ /**
111
+ * The engine refused one change of a step, so that part didn't move: it goes back where it
112
+ * was, as `part` was before the step, into its entry there (or a new one with the entry's
113
+ * number, in its place), wherever a later step had put it since. Its other parts stay where
114
+ * the step put them.
115
+ */
116
+ function stepRefused(history, direction, entry, part) {
117
+ const { from } = stacksOf(direction);
118
+ const { undo, redo } = forgotten(history, part.id);
119
+ const left = {
120
+ undo,
121
+ redo
122
+ };
123
+ const back = [...left[from]];
124
+ const at = back.findIndex((candidate) => candidate.seq === entry.seq);
125
+ if (at >= 0) {
126
+ const parts = [...back[at].parts, part].sort((left, right) => left.seq - right.seq);
127
+ back[at] = {
128
+ ...back[at],
129
+ parts
130
+ };
131
+ } else {
132
+ const before = (candidate) => from === "undo" ? candidate.seq < entry.seq : candidate.seq > entry.seq;
133
+ const index = back.filter(before).length;
134
+ back.splice(index, 0, {
135
+ ...entry,
136
+ parts: [part]
137
+ });
138
+ }
139
+ return {
140
+ ...history,
141
+ ...left,
142
+ [from]: back,
143
+ merging: null
144
+ };
145
+ }
146
+ /**
147
+ * The engine can no longer undo the entry `seq` (a final change or a new version came after
148
+ * it, or it is too old): it goes, and so does everything recorded before it.
149
+ */
150
+ function unavailable(history, seq) {
151
+ const newer = (entry) => entry.seq > seq;
152
+ return {
153
+ ...history,
154
+ undo: history.undo.filter(newer),
155
+ redo: history.redo.filter(newer),
156
+ merging: null
157
+ };
158
+ }
159
+ /** Nothing can be undone or redone. */
160
+ const cleared = (history) => history.undo.length === 0 && history.redo.length === 0 && history.merging === null ? history : {
161
+ ...history,
162
+ undo: [],
163
+ redo: [],
164
+ merging: null
165
+ };
166
+ //#endregion
167
+ //#region src/controller.ts
168
+ /** The document events after which nothing before them can be undone. */
169
+ const ENDS_UNDO = /* @__PURE__ */ new Set([
170
+ "redaction.applied",
171
+ "pages.flattened",
172
+ "signatures.completed",
173
+ "forms.repaired",
174
+ "document.versioned"
175
+ ]);
176
+ /** Whether the engine refused an undo because it can't be undone any more. */
177
+ const isUnavailable = (error) => _embedpdf_engine_core_runtime.EngineError.is(error.cause, _embedpdf_engine_core_runtime.EngineErrorCode.UndoUnavailable);
178
+ /** How many of an undo's parts were left alone, in whole or in part. */
179
+ const skippedIn = (result) => result.items.filter((item) => item.type === "skipped" || "skipped" in item && (item.skipped?.length ?? 0) > 0).length;
180
+ function createHistoryController(ctx) {
181
+ const settings = ctx.settings();
182
+ const undone = ctx.events.source();
183
+ const undoFailed = ctx.events.source();
184
+ /**
185
+ * The changes the engine will never apply, while a step is on its way: a refused change, and
186
+ * a step that undid one. A step that names one is refused too, and changes nothing more.
187
+ */
188
+ const dead = /* @__PURE__ */ new Set();
189
+ /** The parts forgotten while a step is on its way: a refusal of their step puts nothing back. */
190
+ const gone = /* @__PURE__ */ new Set();
191
+ let inFlight = 0;
192
+ const forget = (ids) => {
193
+ if (inFlight === 0) return;
194
+ for (const id of ids) gone.add(id);
195
+ };
196
+ const partsOf = (entries) => entries.flatMap((entry) => entry.parts.map((part) => part.id));
197
+ const clear = () => {
198
+ const history = ctx.state.get();
199
+ forget(partsOf([...history.undo, ...history.redo]));
200
+ ctx.state.update(cleared);
201
+ };
202
+ /**
203
+ * One step's change for one part, and what its answer does. `moot`: it named a change the
204
+ * engine never applied, or a part the history forgot, so its refusal says nothing new.
205
+ */
206
+ const answerOf = (direction, entry, part, change) => change.result.then((result) => {
207
+ if (!result.meta.undoable) {
208
+ forget([part.id]);
209
+ ctx.state.update(forgotten, part.id);
210
+ }
211
+ return { skipped: skippedIn(result) };
212
+ }, (error) => {
213
+ dead.add(change.opId);
214
+ if (dead.has(part.opId) || gone.has(part.id)) return { moot: true };
215
+ if (isUnavailable(error)) {
216
+ const history = ctx.state.get();
217
+ const older = [...history.undo, ...history.redo].filter((other) => other.seq <= entry.seq);
218
+ forget(partsOf(older));
219
+ ctx.state.update(unavailable, entry.seq);
220
+ } else ctx.state.update(stepRefused, direction, entry, part);
221
+ return { error };
222
+ });
223
+ /**
224
+ * Take one step: the undo of each part of the next entry, as one change each (undo goes
225
+ * newest first, redo oldest first), staged now, so the views show it at once.
226
+ */
227
+ const step = (direction) => {
228
+ ctx.changes.sendHolds();
229
+ const entry = nextOf(ctx.state.get(), direction);
230
+ if (!entry) return;
231
+ const parts = direction === "undo" ? [...entry.parts].reverse() : entry.parts;
232
+ const staged = [];
233
+ for (const part of parts) try {
234
+ const change = ctx.changes.stage({
235
+ label: entry.label,
236
+ undoOf: part.opId,
237
+ shows: direction === "undo" ? part.backward : part.forward
238
+ });
239
+ staged.push({
240
+ part,
241
+ change
242
+ });
243
+ } catch (error) {
244
+ if (staged.length === 0) throw error;
245
+ break;
246
+ }
247
+ ctx.state.update(stepped, direction, entry.seq, new Map(staged.map(({ part, change }) => [part.id, change.opId])));
248
+ inFlight += 1;
249
+ Promise.all(staged.map(({ part, change }) => answerOf(direction, entry, part, change))).then((answers) => {
250
+ inFlight -= 1;
251
+ if (inFlight === 0) {
252
+ dead.clear();
253
+ gone.clear();
254
+ }
255
+ const redo = direction === "redo";
256
+ if (answers.every((answer) => "moot" in answer)) return;
257
+ const failure = answers.find((answer) => "error" in answer);
258
+ if (failure && "error" in failure) {
259
+ undoFailed.emit({
260
+ label: entry.label,
261
+ redo,
262
+ reason: isUnavailable(failure.error) ? "unavailable" : "refused",
263
+ error: failure.error
264
+ });
265
+ return;
266
+ }
267
+ const skipped = answers.reduce((sum, answer) => sum + ("skipped" in answer ? answer.skipped : 0), 0);
268
+ undone.emit({
269
+ label: entry.label,
270
+ redo,
271
+ skipped
272
+ });
273
+ });
274
+ };
275
+ /** A change still being typed: what undo would undo before anything in the history. */
276
+ const openHold = () => ctx.changes.pending().find((change) => change.held && change.history) ?? null;
277
+ return {
278
+ api: {
279
+ ...settings.api,
280
+ undo: () => step("undo"),
281
+ redo: () => step("redo"),
282
+ canUndo: () => openHold() !== null || ctx.state.get().undo.length > 0,
283
+ canRedo: () => openHold() === null && ctx.state.get().redo.length > 0,
284
+ getUndoLabel: () => openHold()?.label ?? nextOf(ctx.state.get(), "undo")?.label ?? null,
285
+ getRedoLabel: () => openHold() === null ? nextOf(ctx.state.get(), "redo")?.label ?? null : null,
286
+ clear,
287
+ onUndone: undone.on,
288
+ onUndoFailed: undoFailed.on
289
+ },
290
+ connect() {
291
+ ctx.listen(ctx.changes.onStaged, (change) => {
292
+ if (change.undoOf !== null || !change.history) return;
293
+ ctx.state.update(recorded, change, settings.get().limit);
294
+ });
295
+ ctx.listen(ctx.changes.onSettled, (settled) => {
296
+ const { change } = settled;
297
+ if (change.undoOf !== null || !change.history) return;
298
+ if (settled.status === "refused" || !settled.result.meta.undoable) {
299
+ if (settled.status === "refused" && inFlight > 0) dead.add(change.opId);
300
+ forget([change.opId]);
301
+ ctx.state.update(forgotten, change.opId);
302
+ }
303
+ });
304
+ ctx.listen(ctx.doc.events, (event) => {
305
+ if (ENDS_UNDO.has(event.type)) clear();
306
+ });
307
+ }
308
+ };
309
+ }
310
+ //#endregion
311
+ //#region src/history.plugin.ts
312
+ /**
313
+ * Undo and redo, one history per open document, of what this session changes through the
314
+ * viewer's plugins: every change they stage on the document's change queue is a step. No
315
+ * dependencies and no engine state of its own. `config` is the settings the app registers,
316
+ * over {@link HISTORY_DEFAULTS}.
317
+ */
318
+ const historyPlugin = (config) => (0, _embedpdf_core.definePlugin)({
319
+ id: "history",
320
+ token: require_contract.HistoryToken,
321
+ scope: "document",
322
+ state: emptyHistory,
323
+ settings: {
324
+ defaults: require_contract.HISTORY_DEFAULTS,
325
+ registered: config
326
+ },
327
+ create: createHistoryController
328
+ });
329
+ //#endregion
330
+ //#region src/state.ts
331
+ /**
332
+ * The history page's State table as code: what `useHistoryState()` returns,
333
+ * and the same fields in every other framework. What the undo and redo
334
+ * buttons read.
335
+ */
336
+ const historyState = (0, _embedpdf_core.defineState)(require_contract.HistoryToken, {
337
+ read: (history) => ({
338
+ canUndo: history.canUndo(),
339
+ canRedo: history.canRedo(),
340
+ undoLabel: history.getUndoLabel(),
341
+ redoLabel: history.getRedoLabel()
342
+ }),
343
+ empty: {
344
+ canUndo: false,
345
+ canRedo: false,
346
+ undoLabel: null,
347
+ redoLabel: null
348
+ }
349
+ });
350
+ //#endregion
351
+ exports.HISTORY_DEFAULTS = require_contract.HISTORY_DEFAULTS;
352
+ exports.HistoryToken = require_contract.HistoryToken;
353
+ exports.historyPlugin = historyPlugin;
354
+ exports.historyState = historyState;
355
+
356
+ //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/lib/manifest.ts","../src/lib/actions.ts","../src/lib/history-plugin.ts","../src/lib/reducer.ts","../src/lib/index.ts"],"sourcesContent":["import { PluginManifest } from '@embedpdf/core';\nimport { HistoryPluginConfig } from './types';\n\nexport const HISTORY_PLUGIN_ID = 'history';\n\nexport const manifest: PluginManifest<HistoryPluginConfig> = {\n id: HISTORY_PLUGIN_ID,\n name: 'History Plugin',\n version: '1.0.0',\n provides: ['history'],\n requires: [],\n optional: [],\n defaultConfig: {},\n};\n","import { Action } from '@embedpdf/core';\nimport { HistoryDocumentState } from './types';\n\n// Document lifecycle actions\nexport const INIT_HISTORY_STATE = 'HISTORY/INIT_STATE';\nexport const CLEANUP_HISTORY_STATE = 'HISTORY/CLEANUP_STATE';\n\n// History state updates\nexport const SET_HISTORY_DOCUMENT_STATE = 'HISTORY/SET_DOCUMENT_STATE';\nexport const SET_ACTIVE_HISTORY_DOCUMENT = 'HISTORY/SET_ACTIVE_DOCUMENT';\n\n// Document lifecycle action interfaces\nexport interface InitHistoryStateAction extends Action {\n type: typeof INIT_HISTORY_STATE;\n payload: {\n documentId: string;\n };\n}\n\nexport interface CleanupHistoryStateAction extends Action {\n type: typeof CLEANUP_HISTORY_STATE;\n payload: {\n documentId: string;\n };\n}\n\n// State update action interfaces\nexport interface SetHistoryDocumentStateAction extends Action {\n type: typeof SET_HISTORY_DOCUMENT_STATE;\n payload: {\n documentId: string;\n state: HistoryDocumentState;\n };\n}\n\nexport interface SetActiveHistoryDocumentAction extends Action {\n type: typeof SET_ACTIVE_HISTORY_DOCUMENT;\n payload: string | null; // documentId\n}\n\nexport type HistoryAction =\n | InitHistoryStateAction\n | CleanupHistoryStateAction\n | SetHistoryDocumentStateAction\n | SetActiveHistoryDocumentAction;\n\n// Action creators\nexport const initHistoryState = (documentId: string): InitHistoryStateAction => ({\n type: INIT_HISTORY_STATE,\n payload: { documentId },\n});\n\nexport const cleanupHistoryState = (documentId: string): CleanupHistoryStateAction => ({\n type: CLEANUP_HISTORY_STATE,\n payload: { documentId },\n});\n\nexport const setHistoryDocumentState = (\n documentId: string,\n state: HistoryDocumentState,\n): SetHistoryDocumentStateAction => ({\n type: SET_HISTORY_DOCUMENT_STATE,\n payload: { documentId, state },\n});\n\nexport const setActiveHistoryDocument = (\n documentId: string | null,\n): SetActiveHistoryDocumentAction => ({\n type: SET_ACTIVE_HISTORY_DOCUMENT,\n payload: documentId,\n});\n","import { BasePlugin, createEmitter, Listener, PluginRegistry } from '@embedpdf/core';\nimport {\n Command,\n HistoryCapability,\n HistoryChangeEvent,\n HistoryDocumentState,\n HistoryEntry,\n HistoryPluginConfig,\n HistoryScope,\n HistoryState,\n} from './types';\nimport {\n HistoryAction,\n initHistoryState,\n cleanupHistoryState,\n setHistoryDocumentState,\n} from './actions';\n\ninterface DocumentHistoryData {\n topicHistories: Map<string, { commands: Command[]; currentIndex: number }>;\n globalTimeline: HistoryEntry[];\n globalIndex: number;\n}\n\nexport class HistoryPlugin extends BasePlugin<\n HistoryPluginConfig,\n HistoryCapability,\n HistoryState,\n HistoryAction\n> {\n static readonly id = 'history' as const;\n\n // Per-document history data (persisted with state)\n private readonly documentHistories = new Map<string, DocumentHistoryData>();\n\n // Event emitter for history changes\n private readonly historyChange$ = createEmitter<HistoryChangeEvent>();\n\n constructor(id: string, registry: PluginRegistry) {\n super(id, registry);\n }\n\n async initialize(_: HistoryPluginConfig): Promise<void> {\n this.logger.info('HistoryPlugin', 'Initialize', 'History plugin initialized');\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Lifecycle (from BasePlugin)\n // ─────────────────────────────────────────────────────────\n\n protected override onDocumentLoadingStarted(documentId: string): void {\n // Initialize history state for this document\n this.dispatch(initHistoryState(documentId));\n\n // Create document history data\n this.documentHistories.set(documentId, {\n topicHistories: new Map(),\n globalTimeline: [],\n globalIndex: -1,\n });\n\n this.logger.debug(\n 'HistoryPlugin',\n 'DocumentOpened',\n `Initialized history state for document: ${documentId}`,\n );\n }\n\n protected override onDocumentClosed(documentId: string): void {\n // Cleanup history state\n this.dispatch(cleanupHistoryState(documentId));\n\n // Cleanup document history data\n this.documentHistories.delete(documentId);\n\n this.logger.debug(\n 'HistoryPlugin',\n 'DocumentClosed',\n `Cleaned up history state for document: ${documentId}`,\n );\n }\n\n // ─────────────────────────────────────────────────────────\n // Helper Methods\n // ─────────────────────────────────────────────────────────\n\n private getDocumentHistoryData(documentId?: string): DocumentHistoryData {\n const id = documentId ?? this.getActiveDocumentId();\n const data = this.documentHistories.get(id);\n if (!data) {\n throw new Error(`History data not found for document: ${id}`);\n }\n return data;\n }\n\n private getDocumentHistoryState(documentId: string): HistoryDocumentState {\n const data = this.documentHistories.get(documentId);\n if (!data) {\n throw new Error(`History data not found for document: ${documentId}`);\n }\n\n const topics: HistoryDocumentState['topics'] = {};\n Array.from(data.topicHistories.entries()).forEach(([topic, history]) => {\n topics[topic] = {\n canUndo: history.currentIndex > -1,\n canRedo: history.currentIndex < history.commands.length - 1,\n };\n });\n\n return {\n global: {\n canUndo: data.globalIndex > -1,\n canRedo: data.globalIndex < data.globalTimeline.length - 1,\n },\n topics,\n };\n }\n\n private emitHistoryChange(documentId: string, topic: string | undefined) {\n // Update the state\n const state = this.getDocumentHistoryState(documentId);\n this.dispatch(setHistoryDocumentState(documentId, state));\n\n // Emit the event\n this.historyChange$.emit({\n documentId,\n topic,\n state,\n });\n }\n\n // ─────────────────────────────────────────────────────────\n // History Operations (per document)\n // ─────────────────────────────────────────────────────────\n\n private register(command: Command, topic: string, documentId: string): void {\n const data = this.getDocumentHistoryData(documentId);\n\n // 1. Manage Topic History\n if (!data.topicHistories.has(topic)) {\n data.topicHistories.set(topic, { commands: [], currentIndex: -1 });\n }\n const topicHistory = data.topicHistories.get(topic)!;\n topicHistory.commands.splice(topicHistory.currentIndex + 1);\n topicHistory.commands.push(command);\n topicHistory.currentIndex++;\n\n // 2. Manage Global History\n const historyEntry: HistoryEntry = { command, topic };\n data.globalTimeline.splice(data.globalIndex + 1);\n data.globalTimeline.push(historyEntry);\n data.globalIndex++;\n\n // 3. Execute and notify with the specific topic\n command.execute();\n this.emitHistoryChange(documentId, topic);\n }\n\n private undo(topic: string | undefined, documentId: string): void {\n const data = this.getDocumentHistoryData(documentId);\n let affectedTopic: string | undefined;\n\n if (topic) {\n // Scoped Undo\n const topicHistory = data.topicHistories.get(topic);\n if (topicHistory && topicHistory.currentIndex > -1) {\n topicHistory.commands[topicHistory.currentIndex].undo();\n topicHistory.currentIndex--;\n affectedTopic = topic;\n }\n } else {\n // Global Undo\n if (data.globalIndex > -1) {\n const entry = data.globalTimeline[data.globalIndex];\n entry.command.undo();\n data.topicHistories.get(entry.topic)!.currentIndex--;\n data.globalIndex--;\n affectedTopic = entry.topic;\n }\n }\n\n if (affectedTopic) {\n this.emitHistoryChange(documentId, affectedTopic);\n }\n }\n\n private redo(topic: string | undefined, documentId: string): void {\n const data = this.getDocumentHistoryData(documentId);\n let affectedTopic: string | undefined;\n\n if (topic) {\n // Scoped Redo\n const topicHistory = data.topicHistories.get(topic);\n if (topicHistory && topicHistory.currentIndex < topicHistory.commands.length - 1) {\n topicHistory.currentIndex++;\n topicHistory.commands[topicHistory.currentIndex].execute();\n affectedTopic = topic;\n }\n } else {\n // Global Redo\n if (data.globalIndex < data.globalTimeline.length - 1) {\n data.globalIndex++;\n const entry = data.globalTimeline[data.globalIndex];\n entry.command.execute();\n data.topicHistories.get(entry.topic)!.currentIndex++;\n affectedTopic = entry.topic;\n }\n }\n\n if (affectedTopic) {\n this.emitHistoryChange(documentId, affectedTopic);\n }\n }\n\n private canUndo(topic: string | undefined, documentId: string): boolean {\n const data = this.getDocumentHistoryData(documentId);\n\n if (topic) {\n const history = data.topicHistories.get(topic);\n return !!history && history.currentIndex > -1;\n }\n return data.globalIndex > -1;\n }\n\n private canRedo(topic: string | undefined, documentId: string): boolean {\n const data = this.getDocumentHistoryData(documentId);\n\n if (topic) {\n const history = data.topicHistories.get(topic);\n return !!history && history.currentIndex < history.commands.length - 1;\n }\n return data.globalIndex < data.globalTimeline.length - 1;\n }\n\n /**\n * Purges history entries that match the given predicate based on command metadata.\n * Used to remove commands that are no longer valid (e.g., after a permanent redaction commit).\n *\n * @param predicate A function that returns true for commands that should be purged\n * @param topic If provided, only purges entries for that specific topic\n * @param documentId The document to purge history for\n * @returns The number of entries that were purged\n */\n private purgeByMetadata<T>(\n predicate: (metadata: T | undefined) => boolean,\n topic: string | undefined,\n documentId: string,\n ): number {\n const data = this.getDocumentHistoryData(documentId);\n let purgedCount = 0;\n\n // Helper to check if a command should be purged\n const shouldPurge = (command: Command): boolean => {\n return predicate(command.metadata as T | undefined);\n };\n\n // 1. Purge from topic histories\n const topicsToProcess = topic ? [topic] : Array.from(data.topicHistories.keys());\n\n for (const topicName of topicsToProcess) {\n const topicHistory = data.topicHistories.get(topicName);\n if (!topicHistory) continue;\n\n const newCommands: Command[] = [];\n let indexAdjustment = 0;\n\n for (let i = 0; i < topicHistory.commands.length; i++) {\n const command = topicHistory.commands[i];\n if (shouldPurge(command)) {\n // If this entry is at or before currentIndex, we need to adjust\n if (i <= topicHistory.currentIndex) {\n indexAdjustment++;\n }\n purgedCount++;\n } else {\n newCommands.push(command);\n }\n }\n\n topicHistory.commands = newCommands;\n topicHistory.currentIndex = Math.max(-1, topicHistory.currentIndex - indexAdjustment);\n }\n\n // 2. Purge from global timeline\n const newTimeline: HistoryEntry[] = [];\n let globalIndexAdjustment = 0;\n\n for (let i = 0; i < data.globalTimeline.length; i++) {\n const entry = data.globalTimeline[i];\n const matchesTopic = !topic || entry.topic === topic;\n\n if (matchesTopic && shouldPurge(entry.command)) {\n // If this entry is at or before globalIndex, we need to adjust\n if (i <= data.globalIndex) {\n globalIndexAdjustment++;\n }\n } else {\n newTimeline.push(entry);\n }\n }\n\n data.globalTimeline = newTimeline;\n data.globalIndex = Math.max(-1, data.globalIndex - globalIndexAdjustment);\n\n // 3. Emit history change if anything was purged\n if (purgedCount > 0) {\n this.emitHistoryChange(documentId, topic);\n this.logger.debug(\n 'HistoryPlugin',\n 'PurgeByMetadata',\n `Purged ${purgedCount} history entries for document: ${documentId}${topic ? `, topic: ${topic}` : ''}`,\n );\n }\n\n return purgedCount;\n }\n\n // ─────────────────────────────────────────────────────────\n // Document Scoping\n // ─────────────────────────────────────────────────────────\n\n private createHistoryScope(documentId: string): HistoryScope {\n return {\n register: (command: Command, topic: string) => this.register(command, topic, documentId),\n undo: (topic?: string) => this.undo(topic, documentId),\n redo: (topic?: string) => this.redo(topic, documentId),\n canUndo: (topic?: string) => this.canUndo(topic, documentId),\n canRedo: (topic?: string) => this.canRedo(topic, documentId),\n getHistoryState: () => this.getDocumentHistoryState(documentId),\n onHistoryChange: (listener: Listener<string | undefined>) =>\n this.historyChange$.on((event) => {\n if (event.documentId === documentId) {\n listener(event.topic);\n }\n }),\n purgeByMetadata: <T>(predicate: (metadata: T | undefined) => boolean, topic?: string) =>\n this.purgeByMetadata(predicate, topic, documentId),\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Capability\n // ─────────────────────────────────────────────────────────\n\n protected buildCapability(): HistoryCapability {\n return {\n // Active document operations\n register: (command: Command, topic: string) => {\n const documentId = this.getActiveDocumentId();\n this.register(command, topic, documentId);\n },\n\n undo: (topic?: string) => {\n const documentId = this.getActiveDocumentId();\n this.undo(topic, documentId);\n },\n\n redo: (topic?: string) => {\n const documentId = this.getActiveDocumentId();\n this.redo(topic, documentId);\n },\n\n canUndo: (topic?: string) => {\n const documentId = this.getActiveDocumentId();\n return this.canUndo(topic, documentId);\n },\n\n canRedo: (topic?: string) => {\n const documentId = this.getActiveDocumentId();\n return this.canRedo(topic, documentId);\n },\n\n getHistoryState: () => {\n const documentId = this.getActiveDocumentId();\n return this.getDocumentHistoryState(documentId);\n },\n\n // Document-scoped operations\n forDocument: (documentId: string) => this.createHistoryScope(documentId),\n\n // Events\n onHistoryChange: this.historyChange$.on,\n\n // Purge operations\n purgeByMetadata: <T>(predicate: (metadata: T | undefined) => boolean, topic?: string) => {\n const documentId = this.getActiveDocumentId();\n return this.purgeByMetadata(predicate, topic, documentId);\n },\n };\n }\n\n // ─────────────────────────────────────────────────────────\n // Lifecycle\n // ─────────────────────────────────────────────────────────\n\n async destroy(): Promise<void> {\n // Clear all emitters\n this.historyChange$.clear();\n\n // Clear document histories\n this.documentHistories.clear();\n\n super.destroy();\n }\n}\n","import { Reducer } from '@embedpdf/core';\nimport {\n HistoryAction,\n INIT_HISTORY_STATE,\n CLEANUP_HISTORY_STATE,\n SET_HISTORY_DOCUMENT_STATE,\n SET_ACTIVE_HISTORY_DOCUMENT,\n} from './actions';\nimport { HistoryState, HistoryDocumentState } from './types';\n\nconst initialDocumentState: HistoryDocumentState = {\n global: {\n canUndo: false,\n canRedo: false,\n },\n topics: {},\n};\n\nexport const initialState: HistoryState = {\n documents: {},\n activeDocumentId: null,\n};\n\nexport const reducer: Reducer<HistoryState, HistoryAction> = (state = initialState, action) => {\n switch (action.type) {\n case INIT_HISTORY_STATE: {\n const { documentId } = action.payload;\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: { ...initialDocumentState },\n },\n };\n }\n\n case CLEANUP_HISTORY_STATE: {\n const { documentId } = action.payload;\n const { [documentId]: removed, ...remainingDocs } = state.documents;\n\n return {\n ...state,\n documents: remainingDocs,\n activeDocumentId: state.activeDocumentId === documentId ? null : state.activeDocumentId,\n };\n }\n\n case SET_HISTORY_DOCUMENT_STATE: {\n const { documentId, state: docState } = action.payload;\n if (!state.documents[documentId]) {\n return state;\n }\n\n return {\n ...state,\n documents: {\n ...state.documents,\n [documentId]: docState,\n },\n };\n }\n\n case SET_ACTIVE_HISTORY_DOCUMENT: {\n return {\n ...state,\n activeDocumentId: action.payload,\n };\n }\n\n default:\n return state;\n }\n};\n","import { PluginPackage } from '@embedpdf/core';\nimport { manifest, HISTORY_PLUGIN_ID } from './manifest';\nimport { HistoryPluginConfig, HistoryState } from './types';\nimport { HistoryPlugin } from './history-plugin';\nimport { initialState, reducer } from './reducer';\nimport { HistoryAction } from './actions';\n\nexport const HistoryPluginPackage: PluginPackage<\n HistoryPlugin,\n HistoryPluginConfig,\n HistoryState,\n HistoryAction\n> = {\n manifest,\n create: (registry) => new HistoryPlugin(HISTORY_PLUGIN_ID, registry),\n reducer,\n initialState,\n};\n\nexport * from './history-plugin';\nexport * from './types';\nexport * from './manifest';\n"],"names":["HISTORY_PLUGIN_ID","manifest","id","name","version","provides","requires","optional","defaultConfig","INIT_HISTORY_STATE","CLEANUP_HISTORY_STATE","SET_HISTORY_DOCUMENT_STATE","_HistoryPlugin","BasePlugin","constructor","registry","super","this","documentHistories","Map","historyChange$","createEmitter","initialize","_","logger","info","onDocumentLoadingStarted","documentId","dispatch","type","payload","initHistoryState","set","topicHistories","globalTimeline","globalIndex","debug","onDocumentClosed","cleanupHistoryState","delete","getDocumentHistoryData","getActiveDocumentId","data","get","Error","getDocumentHistoryState","topics","Array","from","entries","forEach","topic","history","canUndo","currentIndex","canRedo","commands","length","global","emitHistoryChange","state","setHistoryDocumentState","emit","register","command","has","topicHistory","splice","push","historyEntry","execute","undo","affectedTopic","entry","redo","purgeByMetadata","predicate","purgedCount","shouldPurge","metadata","topicsToProcess","keys","topicName","newCommands","indexAdjustment","i","Math","max","newTimeline","globalIndexAdjustment","createHistoryScope","getHistoryState","onHistoryChange","listener","on","event","buildCapability","forDocument","destroy","clear","HistoryPlugin","initialDocumentState","initialState","documents","activeDocumentId","HistoryPluginPackage","create","reducer","action","removed","remainingDocs","docState"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["EngineError","EngineErrorCode","HistoryToken","HISTORY_DEFAULTS","HistoryToken"],"sources":["../src/model.ts","../src/controller.ts","../src/history.plugin.ts","../src/state.ts"],"sourcesContent":["/**\n * The history as a value, and the pure steps that change it.\n *\n * An entry is one step of the history: one change this session staged, or the\n * changes of one typing session (its pauses). Each change is a part. A part is\n * named by the change the next step undoes: the change itself at first, then\n * the undo that undid it, then the redo that undid that undo, and so on. An\n * undo and a redo are the same thing to the engine: the undo of a change.\n *\n * Entries are numbered in the order they were recorded (`seq`). The undo\n * stack holds them oldest first and the redo stack newest first, so the next\n * step is always the last of its stack, and an entry put back after a refused\n * step finds its place by its number.\n */\nimport type { ChangeLabel, PredictedOp } from '@embedpdf/core';\n\nexport interface HistoryPart {\n /** The order it was recorded in. */\n readonly seq: number;\n /** The change it began as, which is what a refusal of that change names. Never changes. */\n readonly id: string;\n /** The change the next step undoes: the change itself, or its latest undo or redo. */\n readonly opId: string;\n /** What the change shows: what a redo of it shows until the engine answers. */\n readonly forward: readonly PredictedOp[];\n /** What undoing it shows until the engine answers. */\n readonly backward: readonly PredictedOp[];\n}\n\nexport interface HistoryEntry {\n /** The order it was recorded in. */\n readonly seq: number;\n readonly label: ChangeLabel;\n /** Consecutive changes with this key join it (typing); `null` for every other change. */\n readonly merge: string | null;\n /** Its changes, oldest first. */\n readonly parts: readonly HistoryPart[];\n}\n\nexport interface History {\n /** What undo undoes, oldest first: the last is next. */\n readonly undo: readonly HistoryEntry[];\n /** What redo redoes, newest first: the last is next. */\n readonly redo: readonly HistoryEntry[];\n /**\n * The merge key the newest entry still takes changes under: set when a change with a key is\n * recorded, `null` again once anything else happens.\n */\n readonly merging: string | null;\n readonly nextSeq: number;\n}\n\nexport type Direction = 'undo' | 'redo';\n\n/** A change this session staged, as the history records it. */\nexport interface RecordedChange {\n readonly opId: string;\n readonly label: ChangeLabel;\n readonly merge: string | null;\n readonly shows: readonly PredictedOp[];\n readonly undo: readonly PredictedOp[];\n}\n\nexport const emptyHistory = (): History => ({ undo: [], redo: [], merging: null, nextSeq: 0 });\n\n/** The stack a step takes its entry from, and the one it puts it on. */\nconst stacksOf = (direction: Direction) =>\n direction === 'undo'\n ? ({ from: 'undo', to: 'redo' } as const)\n : ({ from: 'redo', to: 'undo' } as const);\n\n/** The entry the next step of `direction` takes, or `null`. */\nexport const nextOf = (history: History, direction: Direction): HistoryEntry | null =>\n history[direction].at(-1) ?? null;\n\n/**\n * A new change: it joins the newest entry when typing goes on under the same key, else it is a\n * new entry on top, and the oldest go past `limit`. Anything that could be redone is forgotten.\n */\nexport function recorded(history: History, change: RecordedChange, limit: number): History {\n const part: HistoryPart = {\n seq: history.nextSeq,\n id: change.opId,\n opId: change.opId,\n forward: change.shows,\n backward: change.undo,\n };\n const top = history.undo.at(-1);\n if (top && change.merge !== null && history.merging === change.merge) {\n return {\n undo: [...history.undo.slice(0, -1), { ...top, parts: [...top.parts, part] }],\n redo: [],\n merging: change.merge,\n nextSeq: history.nextSeq + 1,\n };\n }\n const entry: HistoryEntry = {\n seq: part.seq,\n label: change.label,\n merge: change.merge,\n parts: [part],\n };\n return {\n undo: [...history.undo, entry].slice(-Math.max(1, limit)),\n redo: [],\n merging: change.merge,\n nextSeq: history.nextSeq + 1,\n };\n}\n\n/**\n * A step was staged: the entry `seq` leaves the top of its stack for the top of the other, each\n * part now named by the change that staged it (`opIds`, by part id).\n */\nexport function stepped(\n history: History,\n direction: Direction,\n seq: number,\n opIds: ReadonlyMap<string, string>,\n): History {\n const { from, to } = stacksOf(direction);\n const entry = history[from].find((candidate) => candidate.seq === seq);\n if (!entry) return history;\n const moved: HistoryEntry = {\n ...entry,\n parts: entry.parts.map((part) => ({ ...part, opId: opIds.get(part.id) ?? part.opId })),\n };\n return {\n ...history,\n [from]: history[from].filter((candidate) => candidate !== entry),\n [to]: [...history[to], moved],\n merging: null,\n } as History;\n}\n\n/** Every entry without the parts `drop` picks, and without the entries left with none. */\nconst withoutParts = (\n entries: readonly HistoryEntry[],\n drop: (part: HistoryPart) => boolean,\n): readonly HistoryEntry[] => {\n let changed = false;\n const kept: HistoryEntry[] = [];\n for (const entry of entries) {\n const parts = entry.parts.filter((part) => !drop(part));\n if (parts.length === entry.parts.length) {\n kept.push(entry);\n continue;\n }\n changed = true;\n if (parts.length) kept.push({ ...entry, parts });\n }\n return changed ? kept : entries;\n};\n\n/**\n * The change `id` never happened (the engine refused it), or did nothing that can be undone:\n * its part goes, from whichever stack holds it.\n */\nexport function forgotten(history: History, id: string): History {\n const undo = withoutParts(history.undo, (part) => part.id === id);\n const redo = withoutParts(history.redo, (part) => part.id === id);\n return undo === history.undo && redo === history.redo ? history : { ...history, undo, redo };\n}\n\n/**\n * The engine refused one change of a step, so that part didn't move: it goes back where it\n * was, as `part` was before the step, into its entry there (or a new one with the entry's\n * number, in its place), wherever a later step had put it since. Its other parts stay where\n * the step put them.\n */\nexport function stepRefused(\n history: History,\n direction: Direction,\n entry: Pick<HistoryEntry, 'seq' | 'label' | 'merge'>,\n part: HistoryPart,\n): History {\n const { from } = stacksOf(direction);\n const { undo, redo } = forgotten(history, part.id);\n const left = { undo, redo };\n const back = [...left[from]];\n const at = back.findIndex((candidate) => candidate.seq === entry.seq);\n if (at >= 0) {\n const parts = [...back[at]!.parts, part].sort((left, right) => left.seq - right.seq);\n back[at] = { ...back[at]!, parts };\n } else {\n // The undo stack is oldest first, the redo stack newest first.\n const before = (candidate: HistoryEntry) =>\n from === 'undo' ? candidate.seq < entry.seq : candidate.seq > entry.seq;\n const index = back.filter(before).length;\n back.splice(index, 0, { ...entry, parts: [part] });\n }\n return { ...history, ...left, [from]: back, merging: null };\n}\n\n/**\n * The engine can no longer undo the entry `seq` (a final change or a new version came after\n * it, or it is too old): it goes, and so does everything recorded before it.\n */\nexport function unavailable(history: History, seq: number): History {\n const newer = (entry: HistoryEntry) => entry.seq > seq;\n return {\n ...history,\n undo: history.undo.filter(newer),\n redo: history.redo.filter(newer),\n merging: null,\n };\n}\n\n/** Nothing can be undone or redone. */\nexport const cleared = (history: History): History =>\n history.undo.length === 0 && history.redo.length === 0 && history.merging === null\n ? history\n : { ...history, undo: [], redo: [], merging: null };\n","/**\n * The history controller. It records every change this session stages on the\n * document's change queue, as it is staged, so an undo right after an action\n * undoes that action even before the engine answered it. Undo and redo are\n * changes on the same queue: the undo of a change, by its `opId`. The views\n * show them at once, from what the change said undoing it looks like, and\n * the engine applies the reverse it recorded.\n *\n * The history keeps no copy of the document. What it holds is names, labels\n * and what each step looks like, and it changes on four things: a change\n * staged, a step staged, an answer, and a change that ends undo (a final\n * change, a new version).\n */\nimport type { PendingChange, PluginContext, PluginError } from '@embedpdf/core';\nimport { EngineError, EngineErrorCode, type ChangeResult } from '@embedpdf/engine-core/runtime';\n\nimport type {\n HistoryCapability,\n HistorySettings,\n HistoryUndoFailedEvent,\n HistoryUndoneEvent,\n} from './contract';\nimport {\n cleared,\n forgotten,\n nextOf,\n recorded,\n stepped,\n stepRefused,\n unavailable,\n type Direction,\n type History,\n type HistoryEntry,\n type HistoryPart,\n} from './model';\n\n/** The document events after which nothing before them can be undone. */\nconst ENDS_UNDO = new Set([\n 'redaction.applied',\n 'pages.flattened',\n 'signatures.completed',\n 'forms.repaired',\n 'document.versioned',\n]);\n\n/** Whether the engine refused an undo because it can't be undone any more. */\nconst isUnavailable = (error: PluginError): boolean =>\n EngineError.is((error as { cause?: unknown }).cause, EngineErrorCode.UndoUnavailable);\n\n/** How many of an undo's parts were left alone, in whole or in part. */\nconst skippedIn = (result: ChangeResult): number =>\n result.items.filter(\n (item) => item.type === 'skipped' || ('skipped' in item && (item.skipped?.length ?? 0) > 0),\n ).length;\n\nexport function createHistoryController(ctx: PluginContext<History, HistorySettings>) {\n const settings = ctx.settings();\n const undone = ctx.events.source<HistoryUndoneEvent>();\n const undoFailed = ctx.events.source<HistoryUndoFailedEvent>();\n\n /**\n * The changes the engine will never apply, while a step is on its way: a refused change, and\n * a step that undid one. A step that names one is refused too, and changes nothing more.\n */\n const dead = new Set<string>();\n /** The parts forgotten while a step is on its way: a refusal of their step puts nothing back. */\n const gone = new Set<string>();\n let inFlight = 0;\n\n const forget = (ids: Iterable<string>): void => {\n if (inFlight === 0) return;\n for (const id of ids) gone.add(id);\n };\n const partsOf = (entries: readonly HistoryEntry[]) =>\n entries.flatMap((entry) => entry.parts.map((part) => part.id));\n\n const clear = (): void => {\n const history = ctx.state.get();\n forget(partsOf([...history.undo, ...history.redo]));\n ctx.state.update(cleared);\n };\n\n /**\n * One step's change for one part, and what its answer does. `moot`: it named a change the\n * engine never applied, or a part the history forgot, so its refusal says nothing new.\n */\n const answerOf = (\n direction: Direction,\n entry: HistoryEntry,\n part: HistoryPart,\n change: PendingChange,\n ): Promise<{ skipped: number } | { error: PluginError } | { moot: true }> =>\n change.result.then(\n (result) => {\n if (!result.meta.undoable) {\n // It did nothing (every part was left alone): there is nothing to step back over.\n forget([part.id]);\n ctx.state.update(forgotten, part.id);\n }\n return { skipped: skippedIn(result) };\n },\n (error: PluginError) => {\n dead.add(change.opId);\n if (dead.has(part.opId) || gone.has(part.id)) return { moot: true };\n if (isUnavailable(error)) {\n const history = ctx.state.get();\n const older = [...history.undo, ...history.redo].filter(\n (other) => other.seq <= entry.seq,\n );\n forget(partsOf(older));\n ctx.state.update(unavailable, entry.seq);\n } else {\n // The part didn't move: it goes back, and the step can be tried again.\n ctx.state.update(stepRefused, direction, entry, part);\n }\n return { error };\n },\n );\n\n /**\n * Take one step: the undo of each part of the next entry, as one change each (undo goes\n * newest first, redo oldest first), staged now, so the views show it at once.\n */\n const step = (direction: Direction): void => {\n // What is still being typed is part of the history from now on: send it first.\n ctx.changes.sendHolds();\n const entry = nextOf(ctx.state.get(), direction);\n if (!entry) return;\n const parts = direction === 'undo' ? [...entry.parts].reverse() : entry.parts;\n const staged: { part: HistoryPart; change: PendingChange }[] = [];\n for (const part of parts) {\n try {\n const change = ctx.changes.stage({\n label: entry.label,\n undoOf: part.opId,\n shows: direction === 'undo' ? part.backward : part.forward,\n });\n staged.push({ part, change });\n } catch (error) {\n // The document closed: the parts staged so far move, the rest stay.\n if (staged.length === 0) throw error;\n break;\n }\n }\n ctx.state.update(\n stepped,\n direction,\n entry.seq,\n new Map(staged.map(({ part, change }) => [part.id, change.opId])),\n );\n inFlight += 1;\n void Promise.all(\n staged.map(({ part, change }) => answerOf(direction, entry, part, change)),\n ).then((answers) => {\n inFlight -= 1;\n if (inFlight === 0) {\n dead.clear();\n gone.clear();\n }\n const redo = direction === 'redo';\n if (answers.every((answer) => 'moot' in answer)) return;\n const failure = answers.find((answer) => 'error' in answer);\n if (failure && 'error' in failure) {\n undoFailed.emit({\n label: entry.label,\n redo,\n reason: isUnavailable(failure.error) ? 'unavailable' : 'refused',\n error: failure.error,\n });\n return;\n }\n const skipped = answers.reduce(\n (sum, answer) => sum + ('skipped' in answer ? answer.skipped : 0),\n 0,\n );\n undone.emit({ label: entry.label, redo, skipped });\n });\n };\n\n /** A change still being typed: what undo would undo before anything in the history. */\n const openHold = (): PendingChange | null =>\n ctx.changes.pending().find((change) => change.held && change.history) ?? null;\n\n const api: HistoryCapability = {\n ...settings.api,\n undo: () => step('undo'),\n redo: () => step('redo'),\n canUndo: () => openHold() !== null || ctx.state.get().undo.length > 0,\n canRedo: () => openHold() === null && ctx.state.get().redo.length > 0,\n getUndoLabel: () => openHold()?.label ?? nextOf(ctx.state.get(), 'undo')?.label ?? null,\n getRedoLabel: () =>\n openHold() === null ? (nextOf(ctx.state.get(), 'redo')?.label ?? null) : null,\n clear,\n onUndone: undone.on,\n onUndoFailed: undoFailed.on,\n };\n\n return {\n api,\n connect() {\n // A change this session staged is a step of the history, unless it moves along the\n // history (an undo or a redo) or isn't one (`history: false`).\n ctx.listen(ctx.changes.onStaged, (change) => {\n if (change.undoOf !== null || !change.history) return;\n ctx.state.update(recorded, change, settings.get().limit);\n });\n ctx.listen(ctx.changes.onSettled, (settled) => {\n const { change } = settled;\n if (change.undoOf !== null || !change.history) return;\n // A change the engine refused never happened; one that did nothing can't be undone.\n if (settled.status === 'refused' || !settled.result.meta.undoable) {\n if (settled.status === 'refused' && inFlight > 0) dead.add(change.opId);\n forget([change.opId]);\n ctx.state.update(forgotten, change.opId);\n }\n });\n ctx.listen(ctx.doc.events, (event) => {\n if (ENDS_UNDO.has(event.type)) clear();\n });\n },\n };\n}\n","import { definePlugin } from '@embedpdf/core';\n\nimport { HISTORY_DEFAULTS, HistoryToken, type HistoryConfig } from './contract';\nimport { createHistoryController } from './controller';\nimport { emptyHistory } from './model';\n\n/**\n * Undo and redo, one history per open document, of what this session changes through the\n * viewer's plugins: every change they stage on the document's change queue is a step. No\n * dependencies and no engine state of its own. `config` is the settings the app registers,\n * over {@link HISTORY_DEFAULTS}.\n */\nexport const historyPlugin = (config?: HistoryConfig) =>\n definePlugin({\n id: 'history',\n token: HistoryToken,\n scope: 'document',\n state: emptyHistory,\n settings: { defaults: HISTORY_DEFAULTS, registered: config },\n create: createHistoryController,\n });\n","/**\n * The history page's State table as code: what `useHistoryState()` returns,\n * and the same fields in every other framework. What the undo and redo\n * buttons read.\n */\nimport { defineState } from '@embedpdf/core';\n\nimport { HistoryToken } from './contract';\n\nexport const historyState = defineState(HistoryToken, {\n read: (history) => ({\n canUndo: history.canUndo(),\n canRedo: history.canRedo(),\n undoLabel: history.getUndoLabel(),\n redoLabel: history.getRedoLabel(),\n }),\n empty: {\n canUndo: false,\n canRedo: false,\n undoLabel: null,\n redoLabel: null,\n },\n});\n"],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { a as HistoryUndoFailedEvent, i as HistorySettings, n as HistoryCapability, o as HistoryUndoneEvent, r as HistoryConfig, s as HistoryToken, t as HISTORY_DEFAULTS } from "./contract-J_XQyhyq.cjs";
2
+ import { ChangeLabel, PredictedOp } from "@embedpdf/core";
3
+ //#region src/model.d.ts
4
+ interface HistoryPart {
5
+ /** The order it was recorded in. */
6
+ readonly seq: number;
7
+ /** The change it began as, which is what a refusal of that change names. Never changes. */
8
+ readonly id: string;
9
+ /** The change the next step undoes: the change itself, or its latest undo or redo. */
10
+ readonly opId: string;
11
+ /** What the change shows: what a redo of it shows until the engine answers. */
12
+ readonly forward: readonly PredictedOp[];
13
+ /** What undoing it shows until the engine answers. */
14
+ readonly backward: readonly PredictedOp[];
15
+ }
16
+ interface HistoryEntry {
17
+ /** The order it was recorded in. */
18
+ readonly seq: number;
19
+ readonly label: ChangeLabel;
20
+ /** Consecutive changes with this key join it (typing); `null` for every other change. */
21
+ readonly merge: string | null;
22
+ /** Its changes, oldest first. */
23
+ readonly parts: readonly HistoryPart[];
24
+ }
25
+ interface History {
26
+ /** What undo undoes, oldest first: the last is next. */
27
+ readonly undo: readonly HistoryEntry[];
28
+ /** What redo redoes, newest first: the last is next. */
29
+ readonly redo: readonly HistoryEntry[];
30
+ /**
31
+ * The merge key the newest entry still takes changes under: set when a change with a key is
32
+ * recorded, `null` again once anything else happens.
33
+ */
34
+ readonly merging: string | null;
35
+ readonly nextSeq: number;
36
+ }
37
+ //#endregion
38
+ //#region src/history.plugin.d.ts
39
+ /**
40
+ * Undo and redo, one history per open document, of what this session changes through the
41
+ * viewer's plugins: every change they stage on the document's change queue is a step. No
42
+ * dependencies and no engine state of its own. `config` is the settings the app registers,
43
+ * over {@link HISTORY_DEFAULTS}.
44
+ */
45
+ declare const historyPlugin: (config?: HistoryConfig) => import("@embedpdf/core").PluginDef<History, HistoryCapability, HistorySettings>;
46
+ //#endregion
47
+ //#region src/state.d.ts
48
+ declare const historyState: import("@embedpdf/core").StateDeclaration<HistoryCapability, {
49
+ canUndo: boolean;
50
+ canRedo: boolean;
51
+ undoLabel: import("@embedpdf/core").ChangeLabel | null;
52
+ redoLabel: import("@embedpdf/core").ChangeLabel | null;
53
+ }>;
54
+ //#endregion
55
+ export { HISTORY_DEFAULTS, HistoryCapability, HistoryConfig, HistorySettings, HistoryToken, HistoryUndoFailedEvent, HistoryUndoneEvent, historyPlugin, historyState };
56
+ //# sourceMappingURL=index.d.cts.map
package/dist/index.d.ts CHANGED
@@ -1 +1,56 @@
1
- export * from './lib';
1
+ import { a as HistoryUndoFailedEvent, i as HistorySettings, n as HistoryCapability, o as HistoryUndoneEvent, r as HistoryConfig, s as HistoryToken, t as HISTORY_DEFAULTS } from "./contract-J_XQyhyq.js";
2
+ import { ChangeLabel, PredictedOp } from "@embedpdf/core";
3
+ //#region src/model.d.ts
4
+ interface HistoryPart {
5
+ /** The order it was recorded in. */
6
+ readonly seq: number;
7
+ /** The change it began as, which is what a refusal of that change names. Never changes. */
8
+ readonly id: string;
9
+ /** The change the next step undoes: the change itself, or its latest undo or redo. */
10
+ readonly opId: string;
11
+ /** What the change shows: what a redo of it shows until the engine answers. */
12
+ readonly forward: readonly PredictedOp[];
13
+ /** What undoing it shows until the engine answers. */
14
+ readonly backward: readonly PredictedOp[];
15
+ }
16
+ interface HistoryEntry {
17
+ /** The order it was recorded in. */
18
+ readonly seq: number;
19
+ readonly label: ChangeLabel;
20
+ /** Consecutive changes with this key join it (typing); `null` for every other change. */
21
+ readonly merge: string | null;
22
+ /** Its changes, oldest first. */
23
+ readonly parts: readonly HistoryPart[];
24
+ }
25
+ interface History {
26
+ /** What undo undoes, oldest first: the last is next. */
27
+ readonly undo: readonly HistoryEntry[];
28
+ /** What redo redoes, newest first: the last is next. */
29
+ readonly redo: readonly HistoryEntry[];
30
+ /**
31
+ * The merge key the newest entry still takes changes under: set when a change with a key is
32
+ * recorded, `null` again once anything else happens.
33
+ */
34
+ readonly merging: string | null;
35
+ readonly nextSeq: number;
36
+ }
37
+ //#endregion
38
+ //#region src/history.plugin.d.ts
39
+ /**
40
+ * Undo and redo, one history per open document, of what this session changes through the
41
+ * viewer's plugins: every change they stage on the document's change queue is a step. No
42
+ * dependencies and no engine state of its own. `config` is the settings the app registers,
43
+ * over {@link HISTORY_DEFAULTS}.
44
+ */
45
+ declare const historyPlugin: (config?: HistoryConfig) => import("@embedpdf/core").PluginDef<History, HistoryCapability, HistorySettings>;
46
+ //#endregion
47
+ //#region src/state.d.ts
48
+ declare const historyState: import("@embedpdf/core").StateDeclaration<HistoryCapability, {
49
+ canUndo: boolean;
50
+ canRedo: boolean;
51
+ undoLabel: import("@embedpdf/core").ChangeLabel | null;
52
+ redoLabel: import("@embedpdf/core").ChangeLabel | null;
53
+ }>;
54
+ //#endregion
55
+ export { HISTORY_DEFAULTS, HistoryCapability, HistoryConfig, HistorySettings, HistoryToken, HistoryUndoFailedEvent, HistoryUndoneEvent, historyPlugin, historyState };
56
+ //# sourceMappingURL=index.d.ts.map