@yogiswara/honcho-editor-ui 3.4.3 → 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.
@@ -1,3 +1,4 @@
1
+ import { AdjustmentValues } from "../../lib/editor/honcho-editor";
1
2
  interface Log {
2
3
  created_at: string;
3
4
  updated_at: string;
@@ -59,10 +60,11 @@ export interface GallerySetup {
59
60
  sizes?: string | string[] | undefined;
60
61
  width: number;
61
62
  height: number;
62
- alt: string | undefined;
63
- key: string | undefined;
64
- frame?: string | undefined;
63
+ alt: string;
64
+ key: string;
65
+ frame?: string | null;
65
66
  isSelected?: boolean | undefined;
67
+ adjustments?: Partial<AdjustmentValues>;
66
68
  }
67
69
  export interface Gallery {
68
70
  id: string;
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
- // Remove item by ID (for cancellation)
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 false;
33
- // Replace with last element and heapify
34
- const removed = this.heap[index];
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
- else {
39
- this.heap[index] = this.heap.pop();
40
- // Re-heapify from this position
41
- this.heapifyDown(index);
42
- this.heapifyUp(index);
43
- }
44
- console.debug(`Removed task ${id} from queue`);
45
- return true;
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
- console.debug('EditorProcessingService created');
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
- console.debug('Editor processor set, queue length:', this.processingQueue.length);
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
- console.warn('No processor available, rejecting task:', task.id);
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
- console.debug(`Added task ${task.id} to queue. Queue length: ${this.processingQueue.length}`);
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
- console.debug('Starting queue processing...');
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
- // Check if the item was aborted before processing
174
- if (item.abortSignal?.aborted) {
175
- console.debug(`Skipping aborted task ${item.id}`);
176
- item.reject(new Error('Task aborted'));
177
- continue;
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
- // Final check before resolving
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
- console.error(`Failed to process task ${item.id}:`, error);
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
- console.debug('Queue processing complete');
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
- console.debug(`Cleared ${clearedItems.length} items from queue`);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yogiswara/honcho-editor-ui",
3
- "version": "3.4.3",
3
+ "version": "3.4.5",
4
4
  "description": "A complete UI component library for the Honcho photo editor.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",