@yogiswara/honcho-editor-ui 3.4.4 → 3.4.5
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/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { useHonchoEditorSingle } from './hooks/editor/useHonchoEditorSingle';
|
|
2
2
|
export { useHonchoEditorBulk } from './hooks/editor/useHonchoEditorBulk';
|
|
3
|
+
export { log } from './utils/logger';
|
|
3
4
|
export type { Controller, AdjustmentState, Preset, ImageItem, ColorAdjustment, CreateEditorTaskRequest, EditorHistoryEntry, GetGalleryUpdateTimestampResponse, GetHistoryResponse, EditorConfig, GallerySetup, ResponseGalleryPaging, Gallery, Content } from './hooks/editor/type';
|
|
4
5
|
export type { PhotoData } from './hooks/editor/useHonchoEditorBulk';
|
|
5
6
|
export { default as AlbumImageGalleryInfinite } from './components/editor/GalleryAlbum/AlbumImageGallery';
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// END
|
|
5
5
|
export { useHonchoEditorSingle } from './hooks/editor/useHonchoEditorSingle';
|
|
6
6
|
export { useHonchoEditorBulk } from './hooks/editor/useHonchoEditorBulk';
|
|
7
|
+
export { log } from './utils/logger';
|
|
7
8
|
export { default as AlbumImageGalleryInfinite } from './components/editor/GalleryAlbum/AlbumImageGallery';
|
|
8
9
|
export { default as ImageItemComponents } from './components/editor/GalleryAlbum/ImageItemComponents';
|
|
9
10
|
export { partialAdjustmentStateToAdjustmentState, mapAdjustmentStateToAdjustmentEditor, mapColorAdjustmentToAdjustmentState, mapAdjustmentStateToColorAdjustment } from './utils/adjustment';
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { AdjustmentValues } from '../editor/honcho-editor';
|
|
2
|
+
export declare class TaskReplacedError extends Error {
|
|
3
|
+
constructor(taskId: string);
|
|
4
|
+
}
|
|
2
5
|
export interface EditorTask {
|
|
3
6
|
id: string;
|
|
4
7
|
path: string;
|
|
5
8
|
frame: string | null;
|
|
6
9
|
adjustments?: Partial<AdjustmentValues>;
|
|
7
10
|
priority?: number;
|
|
8
|
-
abortSignal?: AbortSignal;
|
|
9
11
|
}
|
|
10
12
|
export interface EditorResponse {
|
|
11
13
|
id: string;
|
|
@@ -14,6 +16,7 @@ export interface EditorResponse {
|
|
|
14
16
|
export declare class EditorProcessingService {
|
|
15
17
|
private processingQueue;
|
|
16
18
|
private isProcessing;
|
|
19
|
+
private currentProcessingTaskId?;
|
|
17
20
|
private processImage?;
|
|
18
21
|
private pendingProcessingTimeout?;
|
|
19
22
|
private statusChangeListeners;
|
|
@@ -25,10 +28,10 @@ export declare class EditorProcessingService {
|
|
|
25
28
|
requestProcessing(task: EditorTask): Promise<EditorResponse>;
|
|
26
29
|
private scheduleProcessing;
|
|
27
30
|
private processQueue;
|
|
28
|
-
private removeFromQueue;
|
|
29
31
|
getQueueStatus(): {
|
|
30
32
|
queueLength: number;
|
|
31
33
|
isProcessing: boolean;
|
|
34
|
+
currentProcessingTaskId: string | undefined;
|
|
32
35
|
hasProcessor: boolean;
|
|
33
36
|
};
|
|
34
37
|
clearQueue(): void;
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import { log } from '../../utils/logger';
|
|
2
|
+
export class TaskReplacedError extends Error {
|
|
3
|
+
constructor(taskId) {
|
|
4
|
+
super(`Task ${taskId} was replaced by a newer request`);
|
|
5
|
+
this.name = 'TaskReplacedError';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
1
8
|
// Simple priority queue implementation using binary heap
|
|
2
9
|
class PriorityQueue {
|
|
3
10
|
constructor() {
|
|
@@ -25,24 +32,53 @@ class PriorityQueue {
|
|
|
25
32
|
clear() {
|
|
26
33
|
return this.heap.splice(0);
|
|
27
34
|
}
|
|
28
|
-
//
|
|
35
|
+
// Find and remove an existing task by ID and priority - O(n)
|
|
36
|
+
removeByIdAndPriority(id, priority) {
|
|
37
|
+
const index = this.heap.findIndex(item => item.id === id && (item.priority || 0) === priority);
|
|
38
|
+
if (index === -1)
|
|
39
|
+
return undefined;
|
|
40
|
+
const removedItem = this.heap[index];
|
|
41
|
+
// If it's the last item, just pop it
|
|
42
|
+
if (index === this.heap.length - 1) {
|
|
43
|
+
this.heap.pop();
|
|
44
|
+
return removedItem;
|
|
45
|
+
}
|
|
46
|
+
// Replace with last item and restore heap property
|
|
47
|
+
this.heap[index] = this.heap.pop();
|
|
48
|
+
// Restore heap property by bubbling up or down as needed
|
|
49
|
+
this.heapifyUp(index);
|
|
50
|
+
this.heapifyDown(index);
|
|
51
|
+
return removedItem;
|
|
52
|
+
}
|
|
53
|
+
// Find and remove an existing task by ID - O(n)
|
|
29
54
|
removeById(id) {
|
|
30
55
|
const index = this.heap.findIndex(item => item.id === id);
|
|
31
56
|
if (index === -1)
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
|
|
57
|
+
return undefined;
|
|
58
|
+
const removedItem = this.heap[index];
|
|
59
|
+
// If it's the last item, just pop it
|
|
35
60
|
if (index === this.heap.length - 1) {
|
|
36
61
|
this.heap.pop();
|
|
62
|
+
return removedItem;
|
|
37
63
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
64
|
+
// Replace with last item and restore heap property
|
|
65
|
+
this.heap[index] = this.heap.pop();
|
|
66
|
+
// Restore heap property by bubbling up or down as needed
|
|
67
|
+
this.heapifyUp(index);
|
|
68
|
+
this.heapifyDown(index);
|
|
69
|
+
return removedItem;
|
|
70
|
+
}
|
|
71
|
+
// Check if a task with the given ID exists in the queue
|
|
72
|
+
hasId(id) {
|
|
73
|
+
return this.heap.some(item => item.id === id);
|
|
74
|
+
}
|
|
75
|
+
// Find an existing task by ID and priority without removing it - O(n)
|
|
76
|
+
findByIdAndPriority(id, priority) {
|
|
77
|
+
return this.heap.find(item => item.id === id && (item.priority || 0) === priority);
|
|
78
|
+
}
|
|
79
|
+
// Find an existing task by ID without removing it - O(n)
|
|
80
|
+
findById(id) {
|
|
81
|
+
return this.heap.find(item => item.id === id);
|
|
46
82
|
}
|
|
47
83
|
heapifyUp(index) {
|
|
48
84
|
while (index > 0) {
|
|
@@ -83,12 +119,12 @@ export class EditorProcessingService {
|
|
|
83
119
|
this.processingQueue = new PriorityQueue();
|
|
84
120
|
this.isProcessing = false;
|
|
85
121
|
this.statusChangeListeners = [];
|
|
86
|
-
|
|
122
|
+
log.debug('EditorProcessingService created');
|
|
87
123
|
}
|
|
88
124
|
// Set the processing function from the editor
|
|
89
125
|
setProcessor(processImage) {
|
|
90
126
|
this.processImage = processImage;
|
|
91
|
-
|
|
127
|
+
log.debug({ queueLength: this.processingQueue.length }, 'Editor processor set');
|
|
92
128
|
// Start processing if there are queued items
|
|
93
129
|
if (this.processingQueue.length > 0) {
|
|
94
130
|
this.scheduleProcessing();
|
|
@@ -112,38 +148,59 @@ export class EditorProcessingService {
|
|
|
112
148
|
// Add task to processing queue
|
|
113
149
|
async requestProcessing(task) {
|
|
114
150
|
return new Promise((resolve, reject) => {
|
|
115
|
-
// Check if already aborted before adding to queue
|
|
116
|
-
if (task.abortSignal?.aborted) {
|
|
117
|
-
console.debug(`Task ${task.id} already aborted, not adding to queue`);
|
|
118
|
-
reject(new Error('Task aborted before processing'));
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
151
|
// Validate that we have a processor
|
|
122
152
|
if (!this.processImage) {
|
|
123
|
-
|
|
153
|
+
log.warn({ taskId: task.id }, 'No processor available, rejecting task');
|
|
124
154
|
reject(new Error('Editor not ready - processor not set'));
|
|
125
155
|
return;
|
|
126
156
|
}
|
|
157
|
+
// Check if we're currently processing this task with higher priority
|
|
158
|
+
if (this.currentProcessingTaskId === task.id && this.isProcessing) {
|
|
159
|
+
log.debug({ taskId: task.id }, 'Task already being processed - ignoring duplicate request');
|
|
160
|
+
reject(new Error('Task already being processed'));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Check if task already exists in queue with same priority level
|
|
164
|
+
const newPriority = task.priority || 0;
|
|
165
|
+
const existingTaskSamePriority = this.processingQueue.findByIdAndPriority(task.id, newPriority);
|
|
166
|
+
if (existingTaskSamePriority) {
|
|
167
|
+
// Only replace if same priority level
|
|
168
|
+
const isHighPriority = newPriority >= 10;
|
|
169
|
+
const isLowPriority = newPriority < 10;
|
|
170
|
+
if (isHighPriority || isLowPriority) {
|
|
171
|
+
// Remove existing task with same priority and replace it
|
|
172
|
+
this.processingQueue.removeByIdAndPriority(task.id, newPriority);
|
|
173
|
+
log.debug({
|
|
174
|
+
taskId: task.id,
|
|
175
|
+
priority: newPriority,
|
|
176
|
+
priorityLevel: isHighPriority ? 'high' : 'low'
|
|
177
|
+
}, 'Replacing existing task with same priority level');
|
|
178
|
+
existingTaskSamePriority.reject(new TaskReplacedError(task.id));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// Different priority or no existing task - check if different priority exists
|
|
183
|
+
const existingTaskDifferentPriority = this.processingQueue.findById(task.id);
|
|
184
|
+
if (existingTaskDifferentPriority) {
|
|
185
|
+
const existingPriority = existingTaskDifferentPriority.priority || 0;
|
|
186
|
+
log.debug({
|
|
187
|
+
taskId: task.id,
|
|
188
|
+
existingPriority,
|
|
189
|
+
newPriority
|
|
190
|
+
}, 'Task with different priority already exists - queuing new task separately');
|
|
191
|
+
// Don't reject, just queue the new task alongside the existing one
|
|
192
|
+
}
|
|
193
|
+
}
|
|
127
194
|
const queueItem = {
|
|
128
195
|
...task,
|
|
129
196
|
priority: task.priority || 0,
|
|
130
197
|
resolve,
|
|
131
198
|
reject,
|
|
132
199
|
timestamp: Date.now(),
|
|
133
|
-
abortSignal: task.abortSignal,
|
|
134
200
|
};
|
|
135
|
-
// Set up abort listener to remove from queue if cancelled
|
|
136
|
-
if (task.abortSignal) {
|
|
137
|
-
const abortHandler = () => {
|
|
138
|
-
console.debug(`Task ${task.id} aborted, removing from queue if still pending`);
|
|
139
|
-
this.removeFromQueue(task.id);
|
|
140
|
-
reject(new Error('Task aborted'));
|
|
141
|
-
};
|
|
142
|
-
task.abortSignal.addEventListener('abort', abortHandler, { once: true });
|
|
143
|
-
}
|
|
144
201
|
// Add to priority queue - O(log n)
|
|
145
202
|
this.processingQueue.enqueue(queueItem);
|
|
146
|
-
|
|
203
|
+
log.debug({ taskId: task.id, queueLength: this.processingQueue.length }, 'Added task to queue');
|
|
147
204
|
// Notify status change
|
|
148
205
|
this.notifyStatusChange();
|
|
149
206
|
// Schedule processing with debouncing
|
|
@@ -166,35 +223,27 @@ export class EditorProcessingService {
|
|
|
166
223
|
}
|
|
167
224
|
this.isProcessing = true;
|
|
168
225
|
this.notifyStatusChange();
|
|
169
|
-
|
|
226
|
+
log.debug({ queueLength: this.processingQueue.length }, 'Starting queue processing');
|
|
170
227
|
while (this.processingQueue.length > 0) {
|
|
171
228
|
// Get highest priority item - O(log n) vs O(n log n) sorting
|
|
172
229
|
const item = this.processingQueue.dequeue();
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
item.
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
console.debug(`Processing task ${item.id} (priority: ${item.priority || 0}, queue remaining: ${this.processingQueue.length})`);
|
|
230
|
+
this.currentProcessingTaskId = item.id; // Track currently processing task
|
|
231
|
+
log.debug({
|
|
232
|
+
taskId: item.id,
|
|
233
|
+
priority: item.priority || 0,
|
|
234
|
+
queueRemaining: this.processingQueue.length
|
|
235
|
+
}, 'Processing task');
|
|
180
236
|
try {
|
|
181
|
-
// Check abort signal again before actual processing
|
|
182
|
-
if (item.abortSignal?.aborted) {
|
|
183
|
-
throw new Error('Task aborted during processing');
|
|
184
|
-
}
|
|
185
237
|
const result = await this.processImage(item);
|
|
186
|
-
|
|
187
|
-
if (!item.abortSignal?.aborted) {
|
|
188
|
-
item.resolve(result);
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
item.reject(new Error('Task aborted after processing'));
|
|
192
|
-
}
|
|
238
|
+
item.resolve(result);
|
|
193
239
|
}
|
|
194
240
|
catch (error) {
|
|
195
|
-
|
|
241
|
+
log.error({ taskId: item.id, error }, 'Failed to process task');
|
|
196
242
|
item.reject(error instanceof Error ? error : new Error(String(error)));
|
|
197
243
|
}
|
|
244
|
+
finally {
|
|
245
|
+
this.currentProcessingTaskId = undefined; // Clear tracking when done
|
|
246
|
+
}
|
|
198
247
|
// Yield control to browser for UI updates - more efficient than setTimeout
|
|
199
248
|
await new Promise(resolve => {
|
|
200
249
|
if (typeof requestIdleCallback !== 'undefined') {
|
|
@@ -210,21 +259,14 @@ export class EditorProcessingService {
|
|
|
210
259
|
}
|
|
211
260
|
this.isProcessing = false;
|
|
212
261
|
this.notifyStatusChange();
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
// Remove task from queue by ID
|
|
216
|
-
removeFromQueue(id) {
|
|
217
|
-
const removed = this.processingQueue.removeById(id);
|
|
218
|
-
if (removed) {
|
|
219
|
-
this.notifyStatusChange();
|
|
220
|
-
}
|
|
221
|
-
return removed;
|
|
262
|
+
log.debug('Queue processing complete');
|
|
222
263
|
}
|
|
223
264
|
// Get current queue status
|
|
224
265
|
getQueueStatus() {
|
|
225
266
|
return {
|
|
226
267
|
queueLength: this.processingQueue.length,
|
|
227
268
|
isProcessing: this.isProcessing,
|
|
269
|
+
currentProcessingTaskId: this.currentProcessingTaskId,
|
|
228
270
|
hasProcessor: !!this.processImage,
|
|
229
271
|
};
|
|
230
272
|
}
|
|
@@ -235,7 +277,7 @@ export class EditorProcessingService {
|
|
|
235
277
|
item.reject(new Error('Queue cleared'));
|
|
236
278
|
});
|
|
237
279
|
this.notifyStatusChange();
|
|
238
|
-
|
|
280
|
+
log.debug({ clearedItemsCount: clearedItems.length }, 'Cleared items from queue');
|
|
239
281
|
}
|
|
240
282
|
// Cleanup method for removing timeouts
|
|
241
283
|
cleanup() {
|
|
@@ -244,6 +286,7 @@ export class EditorProcessingService {
|
|
|
244
286
|
this.pendingProcessingTimeout = undefined;
|
|
245
287
|
}
|
|
246
288
|
this.clearQueue();
|
|
289
|
+
this.currentProcessingTaskId = undefined;
|
|
247
290
|
this.statusChangeListeners.length = 0;
|
|
248
291
|
}
|
|
249
292
|
}
|