@pixerate/editor 0.14.3 → 0.15.0
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/agent/index.cjs +28 -220
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +1 -1
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +5 -154
- package/dist/agent/index.js.map +1 -1
- package/dist/canvas/index.cjs +113 -377
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +2 -2
- package/dist/canvas/index.d.ts +2 -2
- package/dist/canvas/index.js +18 -208
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-33UVEDOS.cjs +233 -0
- package/dist/chunk-33UVEDOS.cjs.map +1 -0
- package/dist/chunk-7FWEWE6T.js +1888 -0
- package/dist/chunk-7FWEWE6T.js.map +1 -0
- package/dist/chunk-BYCOFZAA.js +514 -0
- package/dist/chunk-BYCOFZAA.js.map +1 -0
- package/dist/chunk-CYKQLDYH.cjs +1290 -0
- package/dist/chunk-CYKQLDYH.cjs.map +1 -0
- package/dist/chunk-GLSIZKOX.js +17 -0
- package/dist/chunk-GLSIZKOX.js.map +1 -0
- package/dist/chunk-GWHJVZPI.js +233 -0
- package/dist/chunk-GWHJVZPI.js.map +1 -0
- package/dist/chunk-LDES3EOS.cjs +1888 -0
- package/dist/chunk-LDES3EOS.cjs.map +1 -0
- package/dist/chunk-MPPXH7U5.cjs +336 -0
- package/dist/chunk-MPPXH7U5.cjs.map +1 -0
- package/dist/chunk-NNJRJJSC.js +1290 -0
- package/dist/chunk-NNJRJJSC.js.map +1 -0
- package/dist/chunk-O7LOPQMH.js +336 -0
- package/dist/chunk-O7LOPQMH.js.map +1 -0
- package/dist/chunk-SEDR75VH.cjs +17 -0
- package/dist/chunk-SEDR75VH.cjs.map +1 -0
- package/dist/chunk-SHWP2A43.cjs +514 -0
- package/dist/chunk-SHWP2A43.cjs.map +1 -0
- package/dist/chunk-VHZCXHS4.js +222 -0
- package/dist/chunk-VHZCXHS4.js.map +1 -0
- package/dist/chunk-WADCJ7A7.cjs +222 -0
- package/dist/chunk-WADCJ7A7.cjs.map +1 -0
- package/dist/extensions/index.cjs +25 -1920
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.js +22 -1868
- package/dist/extensions/index.js.map +1 -1
- package/dist/grammar/index.cjs +44 -387
- package/dist/grammar/index.cjs.map +1 -1
- package/dist/grammar/index.js +23 -321
- package/dist/grammar/index.js.map +1 -1
- package/dist/history/index.cjs +6 -257
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.js +4 -227
- package/dist/history/index.js.map +1 -1
- package/dist/image-editor/index.cjs +63 -106
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/index.cjs +324 -7232
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -4
- package/dist/index.d.ts +0 -4
- package/dist/index.js +258 -7007
- package/dist/index.js.map +1 -1
- package/dist/serializers/index.cjs +28 -558
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.js +16 -496
- package/dist/serializers/index.js.map +1 -1
- package/dist/spreadsheet/index.cjs +21 -1319
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.js +14 -1274
- package/dist/spreadsheet/index.js.map +1 -1
- package/dist/{types-lGtRobRV.d.cts → types-DC8b2TsA.d.cts} +1 -1
- package/dist/{types-lGtRobRV.d.ts → types-DC8b2TsA.d.ts} +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/history/index.ts","../../src/history/HistoryManager.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./HistoryManager\";\n","import {\n Command,\n HistoryManagerOptions,\n HistoryState,\n MergedExecutionOptions,\n SnapshotCommandOptions,\n} from \"./types\";\n\n/**\n * UI-agnostic History and Undo/Redo manager supporting command closures,\n * snapshot diffs, transaction batching, and high-frequency event coalescing.\n */\nexport class HistoryManager {\n private undoStack: Command[] = [];\n private redoStack: Command[] = [];\n private readonly maxDepth: number;\n private isApplying = false;\n private batchDepth = 0;\n private currentBatch: Command[] | null = null;\n private listeners: Set<(state: HistoryState) => void> = new Set();\n private onStateChange?: (state: HistoryState) => void;\n private cachedState: HistoryState;\n\n constructor(options: HistoryManagerOptions = {}) {\n this.maxDepth = options.maxDepth ?? 50;\n this.onStateChange = options.onStateChange;\n this.cachedState = this.computeState();\n }\n\n private computeState(): HistoryState {\n const canUndo = this.undoStack.length > 0;\n const canRedo = this.redoStack.length > 0;\n return {\n canUndo,\n canRedo,\n undoName: canUndo ? this.undoStack[this.undoStack.length - 1].name : \"\",\n redoName: canRedo ? this.redoStack[this.redoStack.length - 1].name : \"\",\n undoCount: this.undoStack.length,\n redoCount: this.redoStack.length,\n };\n }\n\n /**\n * Returns a snapshot of the current history state.\n * Cached to maintain referential equality across renders for useSyncExternalStore.\n */\n public getState(): HistoryState {\n return this.cachedState;\n }\n\n private notify(): void {\n this.cachedState = this.computeState();\n const state = this.cachedState;\n this.onStateChange?.(state);\n this.listeners.forEach((listener) => {\n try {\n listener(state);\n } catch (err) {\n console.error(\"[HistoryManager] Error in state listener:\", err);\n }\n });\n }\n\n /**\n * Executes a command and pushes it onto the undo stack.\n * Clears the redo stack.\n */\n public execute(command: Command): void {\n if (this.isApplying) {\n command.execute();\n return;\n }\n\n if (this.batchDepth > 0 && this.currentBatch) {\n command.execute();\n this.currentBatch.push({\n ...command,\n timestamp: command.timestamp ?? Date.now(),\n });\n return;\n }\n\n command.execute();\n\n this.undoStack.push({\n ...command,\n timestamp: command.timestamp ?? Date.now(),\n });\n\n if (this.undoStack.length > this.maxDepth) {\n this.undoStack.shift();\n }\n\n this.redoStack = [];\n this.notify();\n }\n\n /**\n * Executes a command, coalescing it with the previous command if they share\n * the same `tag` and occurred within `windowMs` (default: 300ms).\n * Useful for high-frequency interactions like slider scrubs or node drags.\n */\n public executeMerged(\n command: Command,\n options: MergedExecutionOptions = {},\n ): void {\n const windowMs = options.windowMs ?? 300;\n const now = Date.now();\n\n if (\n command.tag &&\n this.undoStack.length > 0 &&\n this.batchDepth === 0 &&\n !this.isApplying\n ) {\n const top = this.undoStack[this.undoStack.length - 1];\n const elapsed = top.timestamp ? now - top.timestamp : Infinity;\n\n if (top.tag === command.tag && elapsed <= windowMs) {\n command.execute();\n // Replace top with merged command keeping the original initial undo\n const initialUndo = top.undo;\n this.undoStack[this.undoStack.length - 1] = {\n name: command.name,\n tag: command.tag,\n timestamp: now,\n execute: command.execute,\n undo: initialUndo,\n redo: command.redo ?? command.execute,\n };\n this.redoStack = [];\n this.notify();\n return;\n }\n }\n\n this.execute({ ...command, timestamp: now });\n }\n\n /**\n * Batches multiple commands executed within `fn` into a single atomic undo entry.\n */\n public batch(name: string, fn: () => void): void {\n if (this.isApplying) {\n fn();\n return;\n }\n\n if (this.batchDepth > 0) {\n // Already inside a batch; execute directly\n fn();\n return;\n }\n\n this.batchDepth++;\n this.currentBatch = [];\n\n try {\n fn();\n } finally {\n this.batchDepth--;\n const batchCommands = this.currentBatch;\n this.currentBatch = null;\n\n if (batchCommands && batchCommands.length > 0) {\n const compoundCommand: Command = {\n name,\n timestamp: Date.now(),\n execute: () => {\n for (let i = 0; i < batchCommands.length; i++) {\n (batchCommands[i].redo ?? batchCommands[i].execute)();\n }\n },\n undo: () => {\n for (let i = batchCommands.length - 1; i >= 0; i--) {\n batchCommands[i].undo();\n }\n },\n redo: () => {\n for (let i = 0; i < batchCommands.length; i++) {\n (batchCommands[i].redo ?? batchCommands[i].execute)();\n }\n },\n };\n\n this.undoStack.push(compoundCommand);\n if (this.undoStack.length > this.maxDepth) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n this.notify();\n }\n }\n }\n\n /**\n * Reverts the most recent command on the undo stack.\n * Returns true if a command was undone, false otherwise.\n */\n public undo(): boolean {\n if (this.undoStack.length === 0 || this.isApplying || this.batchDepth > 0) {\n return false;\n }\n\n this.isApplying = true;\n try {\n const command = this.undoStack.pop()!;\n command.undo();\n this.redoStack.push(command);\n return true;\n } finally {\n this.isApplying = false;\n this.notify();\n }\n }\n\n /**\n * Re-applies the most recent undone command on the redo stack.\n * Returns true if a command was redone, false otherwise.\n */\n public redo(): boolean {\n if (this.redoStack.length === 0 || this.isApplying || this.batchDepth > 0) {\n return false;\n }\n\n this.isApplying = true;\n try {\n const command = this.redoStack.pop()!;\n (command.redo ?? command.execute)();\n this.undoStack.push(command);\n return true;\n } finally {\n this.isApplying = false;\n this.notify();\n }\n }\n\n /**\n * Clears both undo and redo stacks.\n */\n public clear(): void {\n this.undoStack = [];\n this.redoStack = [];\n this.notify();\n }\n\n /**\n * Subscribes to history state changes.\n * The listener is immediately invoked with the current state.\n * Returns an unsubscribe function.\n */\n public subscribe(listener: (state: HistoryState) => void): () => void {\n this.listeners.add(listener);\n listener(this.getState());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Utility helper to create a Command from before/after state snapshots.\n */\n public static createSnapshotCommand<T>(\n options: SnapshotCommandOptions<T>,\n ): Command {\n return {\n name: options.name,\n tag: options.tag,\n execute: () => options.apply(options.after),\n undo: () => options.apply(options.before),\n redo: () => options.apply(options.after),\n };\n }\n}\n\n/**\n * Creates and initializes a new HistoryManager instance.\n */\nexport function createHistoryManager(\n options: HistoryManagerOptions = {},\n): HistoryManager {\n return new HistoryManager(options);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/history/index.cjs"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,yDAA8B;AAC9B;AACE;AACA;AACF,iIAAC","file":"/home/runner/work/editor/editor/packages/core/dist/history/index.cjs"}
|
package/dist/history/index.js
CHANGED
|
@@ -1,230 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
maxDepth;
|
|
6
|
-
isApplying = false;
|
|
7
|
-
batchDepth = 0;
|
|
8
|
-
currentBatch = null;
|
|
9
|
-
listeners = /* @__PURE__ */ new Set();
|
|
10
|
-
onStateChange;
|
|
11
|
-
cachedState;
|
|
12
|
-
constructor(options = {}) {
|
|
13
|
-
this.maxDepth = options.maxDepth ?? 50;
|
|
14
|
-
this.onStateChange = options.onStateChange;
|
|
15
|
-
this.cachedState = this.computeState();
|
|
16
|
-
}
|
|
17
|
-
computeState() {
|
|
18
|
-
const canUndo = this.undoStack.length > 0;
|
|
19
|
-
const canRedo = this.redoStack.length > 0;
|
|
20
|
-
return {
|
|
21
|
-
canUndo,
|
|
22
|
-
canRedo,
|
|
23
|
-
undoName: canUndo ? this.undoStack[this.undoStack.length - 1].name : "",
|
|
24
|
-
redoName: canRedo ? this.redoStack[this.redoStack.length - 1].name : "",
|
|
25
|
-
undoCount: this.undoStack.length,
|
|
26
|
-
redoCount: this.redoStack.length
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Returns a snapshot of the current history state.
|
|
31
|
-
* Cached to maintain referential equality across renders for useSyncExternalStore.
|
|
32
|
-
*/
|
|
33
|
-
getState() {
|
|
34
|
-
return this.cachedState;
|
|
35
|
-
}
|
|
36
|
-
notify() {
|
|
37
|
-
this.cachedState = this.computeState();
|
|
38
|
-
const state = this.cachedState;
|
|
39
|
-
this.onStateChange?.(state);
|
|
40
|
-
this.listeners.forEach((listener) => {
|
|
41
|
-
try {
|
|
42
|
-
listener(state);
|
|
43
|
-
} catch (err) {
|
|
44
|
-
console.error("[HistoryManager] Error in state listener:", err);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Executes a command and pushes it onto the undo stack.
|
|
50
|
-
* Clears the redo stack.
|
|
51
|
-
*/
|
|
52
|
-
execute(command) {
|
|
53
|
-
if (this.isApplying) {
|
|
54
|
-
command.execute();
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (this.batchDepth > 0 && this.currentBatch) {
|
|
58
|
-
command.execute();
|
|
59
|
-
this.currentBatch.push({
|
|
60
|
-
...command,
|
|
61
|
-
timestamp: command.timestamp ?? Date.now()
|
|
62
|
-
});
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
command.execute();
|
|
66
|
-
this.undoStack.push({
|
|
67
|
-
...command,
|
|
68
|
-
timestamp: command.timestamp ?? Date.now()
|
|
69
|
-
});
|
|
70
|
-
if (this.undoStack.length > this.maxDepth) {
|
|
71
|
-
this.undoStack.shift();
|
|
72
|
-
}
|
|
73
|
-
this.redoStack = [];
|
|
74
|
-
this.notify();
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Executes a command, coalescing it with the previous command if they share
|
|
78
|
-
* the same `tag` and occurred within `windowMs` (default: 300ms).
|
|
79
|
-
* Useful for high-frequency interactions like slider scrubs or node drags.
|
|
80
|
-
*/
|
|
81
|
-
executeMerged(command, options = {}) {
|
|
82
|
-
const windowMs = options.windowMs ?? 300;
|
|
83
|
-
const now = Date.now();
|
|
84
|
-
if (command.tag && this.undoStack.length > 0 && this.batchDepth === 0 && !this.isApplying) {
|
|
85
|
-
const top = this.undoStack[this.undoStack.length - 1];
|
|
86
|
-
const elapsed = top.timestamp ? now - top.timestamp : Infinity;
|
|
87
|
-
if (top.tag === command.tag && elapsed <= windowMs) {
|
|
88
|
-
command.execute();
|
|
89
|
-
const initialUndo = top.undo;
|
|
90
|
-
this.undoStack[this.undoStack.length - 1] = {
|
|
91
|
-
name: command.name,
|
|
92
|
-
tag: command.tag,
|
|
93
|
-
timestamp: now,
|
|
94
|
-
execute: command.execute,
|
|
95
|
-
undo: initialUndo,
|
|
96
|
-
redo: command.redo ?? command.execute
|
|
97
|
-
};
|
|
98
|
-
this.redoStack = [];
|
|
99
|
-
this.notify();
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
this.execute({ ...command, timestamp: now });
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Batches multiple commands executed within `fn` into a single atomic undo entry.
|
|
107
|
-
*/
|
|
108
|
-
batch(name, fn) {
|
|
109
|
-
if (this.isApplying) {
|
|
110
|
-
fn();
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
if (this.batchDepth > 0) {
|
|
114
|
-
fn();
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
this.batchDepth++;
|
|
118
|
-
this.currentBatch = [];
|
|
119
|
-
try {
|
|
120
|
-
fn();
|
|
121
|
-
} finally {
|
|
122
|
-
this.batchDepth--;
|
|
123
|
-
const batchCommands = this.currentBatch;
|
|
124
|
-
this.currentBatch = null;
|
|
125
|
-
if (batchCommands && batchCommands.length > 0) {
|
|
126
|
-
const compoundCommand = {
|
|
127
|
-
name,
|
|
128
|
-
timestamp: Date.now(),
|
|
129
|
-
execute: () => {
|
|
130
|
-
for (let i = 0; i < batchCommands.length; i++) {
|
|
131
|
-
(batchCommands[i].redo ?? batchCommands[i].execute)();
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
undo: () => {
|
|
135
|
-
for (let i = batchCommands.length - 1; i >= 0; i--) {
|
|
136
|
-
batchCommands[i].undo();
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
redo: () => {
|
|
140
|
-
for (let i = 0; i < batchCommands.length; i++) {
|
|
141
|
-
(batchCommands[i].redo ?? batchCommands[i].execute)();
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
this.undoStack.push(compoundCommand);
|
|
146
|
-
if (this.undoStack.length > this.maxDepth) {
|
|
147
|
-
this.undoStack.shift();
|
|
148
|
-
}
|
|
149
|
-
this.redoStack = [];
|
|
150
|
-
this.notify();
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Reverts the most recent command on the undo stack.
|
|
156
|
-
* Returns true if a command was undone, false otherwise.
|
|
157
|
-
*/
|
|
158
|
-
undo() {
|
|
159
|
-
if (this.undoStack.length === 0 || this.isApplying || this.batchDepth > 0) {
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
this.isApplying = true;
|
|
163
|
-
try {
|
|
164
|
-
const command = this.undoStack.pop();
|
|
165
|
-
command.undo();
|
|
166
|
-
this.redoStack.push(command);
|
|
167
|
-
return true;
|
|
168
|
-
} finally {
|
|
169
|
-
this.isApplying = false;
|
|
170
|
-
this.notify();
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Re-applies the most recent undone command on the redo stack.
|
|
175
|
-
* Returns true if a command was redone, false otherwise.
|
|
176
|
-
*/
|
|
177
|
-
redo() {
|
|
178
|
-
if (this.redoStack.length === 0 || this.isApplying || this.batchDepth > 0) {
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
this.isApplying = true;
|
|
182
|
-
try {
|
|
183
|
-
const command = this.redoStack.pop();
|
|
184
|
-
(command.redo ?? command.execute)();
|
|
185
|
-
this.undoStack.push(command);
|
|
186
|
-
return true;
|
|
187
|
-
} finally {
|
|
188
|
-
this.isApplying = false;
|
|
189
|
-
this.notify();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Clears both undo and redo stacks.
|
|
194
|
-
*/
|
|
195
|
-
clear() {
|
|
196
|
-
this.undoStack = [];
|
|
197
|
-
this.redoStack = [];
|
|
198
|
-
this.notify();
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Subscribes to history state changes.
|
|
202
|
-
* The listener is immediately invoked with the current state.
|
|
203
|
-
* Returns an unsubscribe function.
|
|
204
|
-
*/
|
|
205
|
-
subscribe(listener) {
|
|
206
|
-
this.listeners.add(listener);
|
|
207
|
-
listener(this.getState());
|
|
208
|
-
return () => {
|
|
209
|
-
this.listeners.delete(listener);
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Utility helper to create a Command from before/after state snapshots.
|
|
214
|
-
*/
|
|
215
|
-
static createSnapshotCommand(options) {
|
|
216
|
-
return {
|
|
217
|
-
name: options.name,
|
|
218
|
-
tag: options.tag,
|
|
219
|
-
execute: () => options.apply(options.after),
|
|
220
|
-
undo: () => options.apply(options.before),
|
|
221
|
-
redo: () => options.apply(options.after)
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
function createHistoryManager(options = {}) {
|
|
226
|
-
return new HistoryManager(options);
|
|
227
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
HistoryManager,
|
|
3
|
+
createHistoryManager
|
|
4
|
+
} from "../chunk-GWHJVZPI.js";
|
|
228
5
|
export {
|
|
229
6
|
HistoryManager,
|
|
230
7
|
createHistoryManager
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/history/HistoryManager.ts"],"sourcesContent":["import {\n Command,\n HistoryManagerOptions,\n HistoryState,\n MergedExecutionOptions,\n SnapshotCommandOptions,\n} from \"./types\";\n\n/**\n * UI-agnostic History and Undo/Redo manager supporting command closures,\n * snapshot diffs, transaction batching, and high-frequency event coalescing.\n */\nexport class HistoryManager {\n private undoStack: Command[] = [];\n private redoStack: Command[] = [];\n private readonly maxDepth: number;\n private isApplying = false;\n private batchDepth = 0;\n private currentBatch: Command[] | null = null;\n private listeners: Set<(state: HistoryState) => void> = new Set();\n private onStateChange?: (state: HistoryState) => void;\n private cachedState: HistoryState;\n\n constructor(options: HistoryManagerOptions = {}) {\n this.maxDepth = options.maxDepth ?? 50;\n this.onStateChange = options.onStateChange;\n this.cachedState = this.computeState();\n }\n\n private computeState(): HistoryState {\n const canUndo = this.undoStack.length > 0;\n const canRedo = this.redoStack.length > 0;\n return {\n canUndo,\n canRedo,\n undoName: canUndo ? this.undoStack[this.undoStack.length - 1].name : \"\",\n redoName: canRedo ? this.redoStack[this.redoStack.length - 1].name : \"\",\n undoCount: this.undoStack.length,\n redoCount: this.redoStack.length,\n };\n }\n\n /**\n * Returns a snapshot of the current history state.\n * Cached to maintain referential equality across renders for useSyncExternalStore.\n */\n public getState(): HistoryState {\n return this.cachedState;\n }\n\n private notify(): void {\n this.cachedState = this.computeState();\n const state = this.cachedState;\n this.onStateChange?.(state);\n this.listeners.forEach((listener) => {\n try {\n listener(state);\n } catch (err) {\n console.error(\"[HistoryManager] Error in state listener:\", err);\n }\n });\n }\n\n /**\n * Executes a command and pushes it onto the undo stack.\n * Clears the redo stack.\n */\n public execute(command: Command): void {\n if (this.isApplying) {\n command.execute();\n return;\n }\n\n if (this.batchDepth > 0 && this.currentBatch) {\n command.execute();\n this.currentBatch.push({\n ...command,\n timestamp: command.timestamp ?? Date.now(),\n });\n return;\n }\n\n command.execute();\n\n this.undoStack.push({\n ...command,\n timestamp: command.timestamp ?? Date.now(),\n });\n\n if (this.undoStack.length > this.maxDepth) {\n this.undoStack.shift();\n }\n\n this.redoStack = [];\n this.notify();\n }\n\n /**\n * Executes a command, coalescing it with the previous command if they share\n * the same `tag` and occurred within `windowMs` (default: 300ms).\n * Useful for high-frequency interactions like slider scrubs or node drags.\n */\n public executeMerged(\n command: Command,\n options: MergedExecutionOptions = {},\n ): void {\n const windowMs = options.windowMs ?? 300;\n const now = Date.now();\n\n if (\n command.tag &&\n this.undoStack.length > 0 &&\n this.batchDepth === 0 &&\n !this.isApplying\n ) {\n const top = this.undoStack[this.undoStack.length - 1];\n const elapsed = top.timestamp ? now - top.timestamp : Infinity;\n\n if (top.tag === command.tag && elapsed <= windowMs) {\n command.execute();\n // Replace top with merged command keeping the original initial undo\n const initialUndo = top.undo;\n this.undoStack[this.undoStack.length - 1] = {\n name: command.name,\n tag: command.tag,\n timestamp: now,\n execute: command.execute,\n undo: initialUndo,\n redo: command.redo ?? command.execute,\n };\n this.redoStack = [];\n this.notify();\n return;\n }\n }\n\n this.execute({ ...command, timestamp: now });\n }\n\n /**\n * Batches multiple commands executed within `fn` into a single atomic undo entry.\n */\n public batch(name: string, fn: () => void): void {\n if (this.isApplying) {\n fn();\n return;\n }\n\n if (this.batchDepth > 0) {\n // Already inside a batch; execute directly\n fn();\n return;\n }\n\n this.batchDepth++;\n this.currentBatch = [];\n\n try {\n fn();\n } finally {\n this.batchDepth--;\n const batchCommands = this.currentBatch;\n this.currentBatch = null;\n\n if (batchCommands && batchCommands.length > 0) {\n const compoundCommand: Command = {\n name,\n timestamp: Date.now(),\n execute: () => {\n for (let i = 0; i < batchCommands.length; i++) {\n (batchCommands[i].redo ?? batchCommands[i].execute)();\n }\n },\n undo: () => {\n for (let i = batchCommands.length - 1; i >= 0; i--) {\n batchCommands[i].undo();\n }\n },\n redo: () => {\n for (let i = 0; i < batchCommands.length; i++) {\n (batchCommands[i].redo ?? batchCommands[i].execute)();\n }\n },\n };\n\n this.undoStack.push(compoundCommand);\n if (this.undoStack.length > this.maxDepth) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n this.notify();\n }\n }\n }\n\n /**\n * Reverts the most recent command on the undo stack.\n * Returns true if a command was undone, false otherwise.\n */\n public undo(): boolean {\n if (this.undoStack.length === 0 || this.isApplying || this.batchDepth > 0) {\n return false;\n }\n\n this.isApplying = true;\n try {\n const command = this.undoStack.pop()!;\n command.undo();\n this.redoStack.push(command);\n return true;\n } finally {\n this.isApplying = false;\n this.notify();\n }\n }\n\n /**\n * Re-applies the most recent undone command on the redo stack.\n * Returns true if a command was redone, false otherwise.\n */\n public redo(): boolean {\n if (this.redoStack.length === 0 || this.isApplying || this.batchDepth > 0) {\n return false;\n }\n\n this.isApplying = true;\n try {\n const command = this.redoStack.pop()!;\n (command.redo ?? command.execute)();\n this.undoStack.push(command);\n return true;\n } finally {\n this.isApplying = false;\n this.notify();\n }\n }\n\n /**\n * Clears both undo and redo stacks.\n */\n public clear(): void {\n this.undoStack = [];\n this.redoStack = [];\n this.notify();\n }\n\n /**\n * Subscribes to history state changes.\n * The listener is immediately invoked with the current state.\n * Returns an unsubscribe function.\n */\n public subscribe(listener: (state: HistoryState) => void): () => void {\n this.listeners.add(listener);\n listener(this.getState());\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Utility helper to create a Command from before/after state snapshots.\n */\n public static createSnapshotCommand<T>(\n options: SnapshotCommandOptions<T>,\n ): Command {\n return {\n name: options.name,\n tag: options.tag,\n execute: () => options.apply(options.after),\n undo: () => options.apply(options.before),\n redo: () => options.apply(options.after),\n };\n }\n}\n\n/**\n * Creates and initializes a new HistoryManager instance.\n */\nexport function createHistoryManager(\n options: HistoryManagerOptions = {},\n): HistoryManager {\n return new HistoryManager(options);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,46 +1,4 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/image-editor/index.ts
|
|
21
|
-
var image_editor_exports = {};
|
|
22
|
-
__export(image_editor_exports, {
|
|
23
|
-
DEFAULT_ADJUSTMENTS: () => DEFAULT_ADJUSTMENTS,
|
|
24
|
-
DEFAULT_DEPTH_MASK: () => DEFAULT_DEPTH_MASK,
|
|
25
|
-
DEFAULT_TRANSFORM: () => DEFAULT_TRANSFORM,
|
|
26
|
-
ImageEditorController: () => ImageEditorController,
|
|
27
|
-
ImageEditorRenderer: () => ImageEditorRenderer,
|
|
28
|
-
applyAdjustments: () => applyAdjustments,
|
|
29
|
-
applyDepthMask: () => applyDepthMask,
|
|
30
|
-
calculateCropDrag: () => calculateCropDrag,
|
|
31
|
-
canvasToImagePoint: () => canvasToImagePoint,
|
|
32
|
-
getAnnotationBounds: () => getAnnotationBounds,
|
|
33
|
-
getCropCursor: () => getCropCursor,
|
|
34
|
-
getViewportMetrics: () => getViewportMetrics,
|
|
35
|
-
globalImageCache: () => globalImageCache,
|
|
36
|
-
hitTestAnnotation: () => hitTestAnnotation,
|
|
37
|
-
hitTestCrop: () => hitTestCrop,
|
|
38
|
-
imageToCanvasPoint: () => imageToCanvasPoint,
|
|
39
|
-
removeAlpha: () => removeAlpha
|
|
40
|
-
});
|
|
41
|
-
module.exports = __toCommonJS(image_editor_exports);
|
|
42
|
-
|
|
43
|
-
// src/image-editor/filters.ts
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;// src/image-editor/filters.ts
|
|
44
2
|
function clamp(value, min = 0, max = 255) {
|
|
45
3
|
return Math.max(min, Math.min(max, value));
|
|
46
4
|
}
|
|
@@ -191,8 +149,8 @@ function removeAlpha(imageData) {
|
|
|
191
149
|
}
|
|
192
150
|
|
|
193
151
|
// src/image-editor/renderer.ts
|
|
194
|
-
var ImageCache = class {
|
|
195
|
-
cache = /* @__PURE__ */ new Map()
|
|
152
|
+
var ImageCache = (_class = class {constructor() { _class.prototype.__init.call(this); }
|
|
153
|
+
__init() {this.cache = /* @__PURE__ */ new Map()}
|
|
196
154
|
async get(src) {
|
|
197
155
|
const existing = this.cache.get(src);
|
|
198
156
|
if (existing && existing.complete) {
|
|
@@ -217,7 +175,7 @@ var ImageCache = class {
|
|
|
217
175
|
Object.defineProperty(img, "naturalWidth", { value: 800, configurable: true });
|
|
218
176
|
Object.defineProperty(img, "naturalHeight", { value: 600, configurable: true });
|
|
219
177
|
Object.defineProperty(img, "complete", { value: true, configurable: true });
|
|
220
|
-
} catch {
|
|
178
|
+
} catch (e2) {
|
|
221
179
|
}
|
|
222
180
|
this.cache.set(src, img);
|
|
223
181
|
resolve(img);
|
|
@@ -231,12 +189,12 @@ var ImageCache = class {
|
|
|
231
189
|
clear() {
|
|
232
190
|
this.cache.clear();
|
|
233
191
|
}
|
|
234
|
-
};
|
|
192
|
+
}, _class);
|
|
235
193
|
var globalImageCache = new ImageCache();
|
|
236
|
-
var ImageEditorRenderer = class {
|
|
237
|
-
imageCache = globalImageCache
|
|
238
|
-
cachedBaseCanvas = null
|
|
239
|
-
cachedBaseKey = ""
|
|
194
|
+
var ImageEditorRenderer = (_class2 = class {constructor() { _class2.prototype.__init2.call(this);_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this); }
|
|
195
|
+
__init2() {this.imageCache = globalImageCache}
|
|
196
|
+
__init3() {this.cachedBaseCanvas = null}
|
|
197
|
+
__init4() {this.cachedBaseKey = ""}
|
|
240
198
|
/**
|
|
241
199
|
* Helper to create a canvas element safely in browser/JSDOM.
|
|
242
200
|
*/
|
|
@@ -289,7 +247,7 @@ var ImageEditorRenderer = class {
|
|
|
289
247
|
const isSwapped = angle === 90 || angle === 270;
|
|
290
248
|
const baseW = isSwapped ? cropH : cropW;
|
|
291
249
|
const baseH = isSwapped ? cropW : cropH;
|
|
292
|
-
const baseKey = `${state.sourceUrl}-${naturalWidth}x${naturalHeight}-${cropX},${cropY},${cropW},${cropH}-${angle},${transform.flipH},${transform.flipV}-${adjustments.brightness},${adjustments.contrast},${adjustments.saturation},${adjustments.exposure},${adjustments.temperature},${adjustments.blur},${adjustments.opacity}-${depthMask.enabled},${depthMask.depthRange
|
|
250
|
+
const baseKey = `${state.sourceUrl}-${naturalWidth}x${naturalHeight}-${cropX},${cropY},${cropW},${cropH}-${angle},${transform.flipH},${transform.flipV}-${adjustments.brightness},${adjustments.contrast},${adjustments.saturation},${adjustments.exposure},${adjustments.temperature},${adjustments.blur},${adjustments.opacity}-${depthMask.enabled},${_optionalChain([depthMask, 'access', _ => _.depthRange, 'optionalAccess', _2 => _2[0]])}-${_optionalChain([depthMask, 'access', _3 => _3.depthRange, 'optionalAccess', _4 => _4[1]])},${depthMask.softness},${depthMask.invert},${depthMask.mode}`;
|
|
293
251
|
let baseCanvas = this.cachedBaseCanvas;
|
|
294
252
|
if (!baseCanvas || this.cachedBaseKey !== baseKey) {
|
|
295
253
|
baseCanvas = this.createCanvas(baseW, baseH);
|
|
@@ -327,7 +285,7 @@ var ImageEditorRenderer = class {
|
|
|
327
285
|
applyDepthMask(imageData, maskData, depthMask);
|
|
328
286
|
}
|
|
329
287
|
}
|
|
330
|
-
} catch {
|
|
288
|
+
} catch (e3) {
|
|
331
289
|
}
|
|
332
290
|
}
|
|
333
291
|
baseCtx.putImageData(imageData, 0, 0);
|
|
@@ -359,12 +317,12 @@ var ImageEditorRenderer = class {
|
|
|
359
317
|
ctx.restore();
|
|
360
318
|
ctx.restore();
|
|
361
319
|
const isCroppingActive = Boolean(options.showCropOverlay || state.activeTool === "crop");
|
|
362
|
-
const activeCrop = options.draftCrop !== void 0 && options.draftCrop !== null ? options.draftCrop : crop
|
|
320
|
+
const activeCrop = options.draftCrop !== void 0 && options.draftCrop !== null ? options.draftCrop : _nullishCoalesce(crop, () => ( (isCroppingActive ? {
|
|
363
321
|
x: 0,
|
|
364
322
|
y: 0,
|
|
365
323
|
width: naturalWidth,
|
|
366
324
|
height: naturalHeight
|
|
367
|
-
} : null);
|
|
325
|
+
} : null)));
|
|
368
326
|
if (isCroppingActive && activeCrop) {
|
|
369
327
|
this.drawCropOverlay(ctx, state, vpWidth, vpHeight, naturalWidth, naturalHeight, activeCrop);
|
|
370
328
|
}
|
|
@@ -475,7 +433,7 @@ var ImageEditorRenderer = class {
|
|
|
475
433
|
try {
|
|
476
434
|
const loadedImg = await this.imageCache.get(imgAnn.src);
|
|
477
435
|
ctx.drawImage(loadedImg, imgAnn.x, imgAnn.y, imgAnn.width, imgAnn.height);
|
|
478
|
-
} catch {
|
|
436
|
+
} catch (e4) {
|
|
479
437
|
}
|
|
480
438
|
break;
|
|
481
439
|
}
|
|
@@ -558,12 +516,12 @@ var ImageEditorRenderer = class {
|
|
|
558
516
|
* Draws a rule-of-thirds crop grid overlay.
|
|
559
517
|
*/
|
|
560
518
|
drawCropOverlay(ctx, state, vpWidth, vpHeight, imgWidth, imgHeight, cropOverride) {
|
|
561
|
-
const crop = cropOverride !== void 0 && cropOverride !== null ? cropOverride : state.crop
|
|
519
|
+
const crop = cropOverride !== void 0 && cropOverride !== null ? cropOverride : _nullishCoalesce(state.crop, () => ( {
|
|
562
520
|
x: 0,
|
|
563
521
|
y: 0,
|
|
564
522
|
width: imgWidth,
|
|
565
523
|
height: imgHeight
|
|
566
|
-
};
|
|
524
|
+
}));
|
|
567
525
|
if (!crop) return;
|
|
568
526
|
const { transform, pan, zoom } = state;
|
|
569
527
|
const angle = (transform.rotate % 360 + 360) % 360;
|
|
@@ -683,7 +641,7 @@ var ImageEditorRenderer = class {
|
|
|
683
641
|
applyDepthMask(imageData, maskData, depthMask);
|
|
684
642
|
}
|
|
685
643
|
}
|
|
686
|
-
} catch {
|
|
644
|
+
} catch (e5) {
|
|
687
645
|
}
|
|
688
646
|
}
|
|
689
647
|
interCtx.putImageData(imageData, 0, 0);
|
|
@@ -714,8 +672,8 @@ var ImageEditorRenderer = class {
|
|
|
714
672
|
if (!ctx) return maskCanvas;
|
|
715
673
|
ctx.fillStyle = "#000000";
|
|
716
674
|
ctx.fillRect(0, 0, naturalWidth, naturalHeight);
|
|
717
|
-
const useCrop = options
|
|
718
|
-
const useAnnotations = options
|
|
675
|
+
const useCrop = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _5 => _5.useCrop]), () => ( true));
|
|
676
|
+
const useAnnotations = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _6 => _6.useAnnotations]), () => ( true));
|
|
719
677
|
if (useCrop && state.crop) {
|
|
720
678
|
ctx.fillStyle = "#ffffff";
|
|
721
679
|
ctx.fillRect(state.crop.x, state.crop.y, state.crop.width, state.crop.height);
|
|
@@ -762,7 +720,7 @@ var ImageEditorRenderer = class {
|
|
|
762
720
|
}
|
|
763
721
|
return maskCanvas;
|
|
764
722
|
}
|
|
765
|
-
};
|
|
723
|
+
}, _class2);
|
|
766
724
|
|
|
767
725
|
// src/image-editor/controller.ts
|
|
768
726
|
var DEFAULT_ADJUSTMENTS = {
|
|
@@ -787,42 +745,42 @@ var DEFAULT_DEPTH_MASK = {
|
|
|
787
745
|
softness: 10,
|
|
788
746
|
invert: false
|
|
789
747
|
};
|
|
790
|
-
var ImageEditorController = class {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
imageElement = null
|
|
794
|
-
listeners = /* @__PURE__ */ new Set()
|
|
795
|
-
|
|
748
|
+
var ImageEditorController = (_class3 = class {
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
__init5() {this.imageElement = null}
|
|
752
|
+
__init6() {this.listeners = /* @__PURE__ */ new Set()}
|
|
753
|
+
|
|
796
754
|
// History stacks
|
|
797
|
-
undoStack = []
|
|
798
|
-
redoStack = []
|
|
799
|
-
|
|
800
|
-
isApplyingHistory = false
|
|
801
|
-
|
|
802
|
-
constructor(options = {}) {
|
|
803
|
-
this.maxHistoryDepth = options.maxHistoryDepth
|
|
755
|
+
__init7() {this.undoStack = []}
|
|
756
|
+
__init8() {this.redoStack = []}
|
|
757
|
+
|
|
758
|
+
__init9() {this.isApplyingHistory = false}
|
|
759
|
+
|
|
760
|
+
constructor(options = {}) {;_class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);
|
|
761
|
+
this.maxHistoryDepth = _nullishCoalesce(options.maxHistoryDepth, () => ( 50));
|
|
804
762
|
this.onStateChange = options.onStateChange;
|
|
805
763
|
this.renderer = new ImageEditorRenderer();
|
|
806
764
|
const initialAdjustments = {
|
|
807
765
|
...DEFAULT_ADJUSTMENTS,
|
|
808
|
-
...options.initialState
|
|
766
|
+
..._optionalChain([options, 'access', _7 => _7.initialState, 'optionalAccess', _8 => _8.adjustments])
|
|
809
767
|
};
|
|
810
768
|
const initialTransform = {
|
|
811
769
|
...DEFAULT_TRANSFORM,
|
|
812
|
-
...options.initialState
|
|
770
|
+
..._optionalChain([options, 'access', _9 => _9.initialState, 'optionalAccess', _10 => _10.transform])
|
|
813
771
|
};
|
|
814
772
|
const initialDepthMask = {
|
|
815
773
|
...DEFAULT_DEPTH_MASK,
|
|
816
|
-
...options.initialState
|
|
774
|
+
..._optionalChain([options, 'access', _11 => _11.initialState, 'optionalAccess', _12 => _12.depthMask])
|
|
817
775
|
};
|
|
818
776
|
this.state = {
|
|
819
777
|
sourceUrl: typeof options.image === "string" ? options.image : null,
|
|
820
778
|
imageDimensions: { width: 800, height: 600 },
|
|
821
|
-
crop: options.initialState
|
|
779
|
+
crop: _nullishCoalesce(_optionalChain([options, 'access', _13 => _13.initialState, 'optionalAccess', _14 => _14.crop]), () => ( null)),
|
|
822
780
|
transform: initialTransform,
|
|
823
781
|
adjustments: initialAdjustments,
|
|
824
782
|
depthMask: initialDepthMask,
|
|
825
|
-
annotations: options.initialState
|
|
783
|
+
annotations: _nullishCoalesce(_optionalChain([options, 'access', _15 => _15.initialState, 'optionalAccess', _16 => _16.annotations]), () => ( [])),
|
|
826
784
|
selectedAnnotationId: null,
|
|
827
785
|
activeTool: "select",
|
|
828
786
|
zoom: 1,
|
|
@@ -881,7 +839,7 @@ var ImageEditorController = class {
|
|
|
881
839
|
}
|
|
882
840
|
notify() {
|
|
883
841
|
const currentState = { ...this.state };
|
|
884
|
-
this.onStateChange
|
|
842
|
+
_optionalChain([this, 'access', _17 => _17.onStateChange, 'optionalCall', _18 => _18(currentState)]);
|
|
885
843
|
this.listeners.forEach((l) => l(currentState));
|
|
886
844
|
}
|
|
887
845
|
// --- History (Undo / Redo / Reset) ---
|
|
@@ -1106,7 +1064,7 @@ var ImageEditorController = class {
|
|
|
1106
1064
|
if (!source) return;
|
|
1107
1065
|
try {
|
|
1108
1066
|
await this.renderer.renderToCanvas(targetCanvas, source, this.state, options);
|
|
1109
|
-
} catch {
|
|
1067
|
+
} catch (e6) {
|
|
1110
1068
|
}
|
|
1111
1069
|
}
|
|
1112
1070
|
async renderExport(options) {
|
|
@@ -1119,13 +1077,13 @@ var ImageEditorController = class {
|
|
|
1119
1077
|
async toDataURL(options = {}) {
|
|
1120
1078
|
const canvas = await this.renderExport(options);
|
|
1121
1079
|
const format = options.format || "image/png";
|
|
1122
|
-
const quality = options.quality
|
|
1080
|
+
const quality = _nullishCoalesce(options.quality, () => ( 0.92));
|
|
1123
1081
|
return canvas.toDataURL(format, quality);
|
|
1124
1082
|
}
|
|
1125
1083
|
async toBlob(options = {}) {
|
|
1126
1084
|
const canvas = await this.renderExport(options);
|
|
1127
1085
|
const format = options.format || "image/png";
|
|
1128
|
-
const quality = options.quality
|
|
1086
|
+
const quality = _nullishCoalesce(options.quality, () => ( 0.92));
|
|
1129
1087
|
return new Promise((resolve, reject) => {
|
|
1130
1088
|
if (canvas.toBlob) {
|
|
1131
1089
|
canvas.toBlob((blob) => {
|
|
@@ -1198,14 +1156,14 @@ var ImageEditorController = class {
|
|
|
1198
1156
|
};
|
|
1199
1157
|
this.state.annotations = JSON.parse(JSON.stringify(data.annotations || []));
|
|
1200
1158
|
}
|
|
1201
|
-
};
|
|
1159
|
+
}, _class3);
|
|
1202
1160
|
|
|
1203
1161
|
// src/image-editor/geometry.ts
|
|
1204
1162
|
function getViewportMetrics(state, vpWidth, vpHeight, isActivelyCropping = false, cropOverride) {
|
|
1205
1163
|
const { imageDimensions, crop: stateCrop, transform, zoom, pan } = state;
|
|
1206
1164
|
const naturalWidth = imageDimensions.width || 800;
|
|
1207
1165
|
const naturalHeight = imageDimensions.height || 600;
|
|
1208
|
-
const activeCrop = cropOverride !== void 0 && cropOverride !== null ? cropOverride : stateCrop
|
|
1166
|
+
const activeCrop = cropOverride !== void 0 && cropOverride !== null ? cropOverride : _nullishCoalesce(stateCrop, () => ( (isActivelyCropping ? { x: 0, y: 0, width: naturalWidth, height: naturalHeight } : null)));
|
|
1209
1167
|
const cropW = !isActivelyCropping && activeCrop ? activeCrop.width : naturalWidth;
|
|
1210
1168
|
const cropH = !isActivelyCropping && activeCrop ? activeCrop.height : naturalHeight;
|
|
1211
1169
|
const angle = (transform.rotate % 360 + 360) % 360;
|
|
@@ -1419,24 +1377,23 @@ function hitTestAnnotation(imgPt, ann, padding = 10) {
|
|
|
1419
1377
|
const b = getAnnotationBounds(ann);
|
|
1420
1378
|
return imgPt.x >= b.x - padding && imgPt.x <= b.x + b.width + padding && imgPt.y >= b.y - padding && imgPt.y <= b.y + b.height + padding;
|
|
1421
1379
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
});
|
|
1380
|
+
|
|
1381
|
+
|
|
1382
|
+
|
|
1383
|
+
|
|
1384
|
+
|
|
1385
|
+
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
|
|
1389
|
+
|
|
1390
|
+
|
|
1391
|
+
|
|
1392
|
+
|
|
1393
|
+
|
|
1394
|
+
|
|
1395
|
+
|
|
1396
|
+
|
|
1397
|
+
|
|
1398
|
+
exports.DEFAULT_ADJUSTMENTS = DEFAULT_ADJUSTMENTS; exports.DEFAULT_DEPTH_MASK = DEFAULT_DEPTH_MASK; exports.DEFAULT_TRANSFORM = DEFAULT_TRANSFORM; exports.ImageEditorController = ImageEditorController; exports.ImageEditorRenderer = ImageEditorRenderer; exports.applyAdjustments = applyAdjustments; exports.applyDepthMask = applyDepthMask; exports.calculateCropDrag = calculateCropDrag; exports.canvasToImagePoint = canvasToImagePoint; exports.getAnnotationBounds = getAnnotationBounds; exports.getCropCursor = getCropCursor; exports.getViewportMetrics = getViewportMetrics; exports.globalImageCache = globalImageCache; exports.hitTestAnnotation = hitTestAnnotation; exports.hitTestCrop = hitTestCrop; exports.imageToCanvasPoint = imageToCanvasPoint; exports.removeAlpha = removeAlpha;
|
|
1442
1399
|
//# sourceMappingURL=index.cjs.map
|