quoin-editor 0.3.0 → 0.4.1

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.cts CHANGED
@@ -109,17 +109,17 @@ declare function paletteBlocks(definitions: AnyBlockDefinition[]): AnyBlockDefin
109
109
  type BlockCanvasProps = {
110
110
  /**
111
111
  * The document. Update it synchronously from `onChange`, as with a controlled `<input value onChange>`.
112
- * Applying the edit late is tolerated as long as what comes back is one of the arrays the canvas emitted:
113
- * it recognises its own output even several keystrokes behind, so a transition or a debounced setter is safe.
114
- * A store that rebuilds the document on the way through hands back a structural copy instead, which arrives
115
- * behind the canvas's own state and reads as a foreign document. That does not revert the edit cleanly: the
116
- * characters the copy predates are dropped and the ones typed after them are kept.
112
+ * Applying the edit late is tolerated: the canvas recognises an array it emitted however late it comes back, and
113
+ * a structural copy of one within 64 edits, so a transition, a debounced setter or a store that clones the
114
+ * document on the way through is safe.
117
115
  * A new array that differs from the last one you supplied replaces what the canvas shows, which is how a host
118
116
  * loads, undoes or merges. An array structurally equal to the last one you supplied is ignored, so a "discard
119
- * changes" button that hands back the original document does nothing; remount the canvas with a React `key` to
120
- * force a reset. The comparison is shallow over each block's `data`: a host whose block `data` holds nested
121
- * objects rebuilt on every render, and which does not echo `onChange` back, can still lose an edit. The core
122
- * block types all have flat data, so this only affects custom blocks.
117
+ * changes" button that hands back the original document does nothing. A host that does not echo `onChange` back
118
+ * meets the same when it restores a copy of a state from the last 64 edits, which reads as a late echo. Remount
119
+ * the canvas with a React `key` to force a reset in either case. The comparison is shallow over each block's
120
+ * `data`: a host whose block `data` holds nested objects rebuilt on every render, and which does not echo
121
+ * `onChange` back, can still lose an edit. The core block types all have flat data, so this only affects custom
122
+ * blocks.
123
123
  */
124
124
  blocks: QuoinBlock[];
125
125
  onChange: (blocks: QuoinBlock[]) => void;
@@ -209,8 +209,8 @@ type UploadFieldProps = {
209
209
  label: string;
210
210
  /** An `accept` attribute for the file input, such as `'image/*'` or `'.pdf'`. */
211
211
  accept?: string;
212
- /** Called once the upload resolves. Throwing from it is reported through the canvas `onError`. */
213
- onUploaded: (result: UploadResult) => void;
212
+ /** Called once the upload resolves. The field stays busy until a returned promise settles, and throwing or rejecting is reported through the canvas `onError`. */
213
+ onUploaded: (result: UploadResult) => void | Promise<void>;
214
214
  };
215
215
  /** A file picker wired to the canvas `upload` function, with its own busy state and inline error. */
216
216
  declare function UploadField({ category, label, accept, onUploaded }: UploadFieldProps): React.JSX.Element;
@@ -267,7 +267,7 @@ type BlockList = {
267
267
  declare function useBlockList(initial: QuoinBlock[]): BlockList;
268
268
 
269
269
  /** Where a debounced save stands, for a "Saving..." or "Saved" indicator. */
270
- type AutosaveStatus = 'idle' | 'saving' | 'saved' | 'retrying';
270
+ type AutosaveStatus = 'idle' | 'saving' | 'saved' | 'retrying' | 'failed';
271
271
  /** What `useAutosave` tracks, how to build the payload, and how to persist it. */
272
272
  type UseAutosaveInput<T> = {
273
273
  /** The tracked fields. An edit to any of them marks dirty and restarts the debounce. */
@@ -280,24 +280,41 @@ type UseAutosaveInput<T> = {
280
280
  save: (payload: T) => unknown;
281
281
  debounceMs?: number;
282
282
  maxBackoffMs?: number;
283
+ /** Retries after a failed save before giving up with status `failed`. Defaults to 8. */
284
+ maxRetries?: number;
285
+ /**
286
+ * Whether a failed save is worth retrying. Receives the rejection, or the resolved
287
+ * `{ ok: false }` value. Return false for a permanent failure. Defaults to always.
288
+ */
289
+ isRetryable?: (error: unknown) => boolean;
290
+ /** Save a pending edit when the page is hidden or closed. Defaults to true. */
291
+ flushOnHide?: boolean;
283
292
  };
284
- /** The current save status and a way to retry a failed save immediately. */
293
+ /** The current save status, the last failure, and ways to save immediately. */
285
294
  type UseAutosaveResult = {
286
295
  status: AutosaveStatus;
296
+ /** The last failure, kept while a retry is pending and cleared by the next success. */
297
+ error: unknown;
298
+ /** Saves now, even when nothing is pending, and starts the retries over. */
287
299
  retry: () => void;
300
+ /** Saves a pending edit now and starts the retries over. A no-op when nothing is pending. */
301
+ flush: () => void;
288
302
  };
289
303
  /**
290
- * Debounced last-write-wins autosave. Nothing fires on mount, only after the first
291
- * real edit, and a rejected save backs off exponentially until it lands. `save` and
292
- * `build` are read at fire time, so passing fresh closures each render is safe.
304
+ * Debounced last-write-wins autosave. Nothing fires on mount, only after the first real
305
+ * edit. One save runs at a time: edits made while it runs are saved once it settles, as one
306
+ * save of the newest payload. A failed save backs off exponentially until it lands, stops
307
+ * being retryable or runs out of retries. An edit still waiting for its debounce is saved
308
+ * on unmount and when the page is hidden. `save` and `build` are read at fire time, so
309
+ * passing fresh closures each render is safe.
293
310
  */
294
- declare function useAutosave<T>({ deps, build, save, debounceMs, maxBackoffMs, }: UseAutosaveInput<T>): UseAutosaveResult;
311
+ declare function useAutosave<T>({ deps, build, save, debounceMs, maxBackoffMs, maxRetries, isRetryable, flushOnHide, }: UseAutosaveInput<T>): UseAutosaveResult;
295
312
 
296
313
  /** Height follows the content, and is recomputed when the box gets narrower or
297
314
  * wider, because a re-wrap changes the line count without changing the value.
298
315
  * Only a width change refits: reacting to the height this hook just set would
299
316
  * feed the observer its own output. */
300
- declare function useAutoGrow<T extends HTMLTextAreaElement>(value: string): React.RefObject<T | null>;
317
+ declare function useAutoGrow<T extends HTMLTextAreaElement>(value: string): React.RefObject<T>;
301
318
 
302
319
  /** A slash-palette entry: how it is shown and searched, and the markdown it inserts. */
303
320
  type TextCommand = {
package/dist/index.d.ts CHANGED
@@ -109,17 +109,17 @@ declare function paletteBlocks(definitions: AnyBlockDefinition[]): AnyBlockDefin
109
109
  type BlockCanvasProps = {
110
110
  /**
111
111
  * The document. Update it synchronously from `onChange`, as with a controlled `<input value onChange>`.
112
- * Applying the edit late is tolerated as long as what comes back is one of the arrays the canvas emitted:
113
- * it recognises its own output even several keystrokes behind, so a transition or a debounced setter is safe.
114
- * A store that rebuilds the document on the way through hands back a structural copy instead, which arrives
115
- * behind the canvas's own state and reads as a foreign document. That does not revert the edit cleanly: the
116
- * characters the copy predates are dropped and the ones typed after them are kept.
112
+ * Applying the edit late is tolerated: the canvas recognises an array it emitted however late it comes back, and
113
+ * a structural copy of one within 64 edits, so a transition, a debounced setter or a store that clones the
114
+ * document on the way through is safe.
117
115
  * A new array that differs from the last one you supplied replaces what the canvas shows, which is how a host
118
116
  * loads, undoes or merges. An array structurally equal to the last one you supplied is ignored, so a "discard
119
- * changes" button that hands back the original document does nothing; remount the canvas with a React `key` to
120
- * force a reset. The comparison is shallow over each block's `data`: a host whose block `data` holds nested
121
- * objects rebuilt on every render, and which does not echo `onChange` back, can still lose an edit. The core
122
- * block types all have flat data, so this only affects custom blocks.
117
+ * changes" button that hands back the original document does nothing. A host that does not echo `onChange` back
118
+ * meets the same when it restores a copy of a state from the last 64 edits, which reads as a late echo. Remount
119
+ * the canvas with a React `key` to force a reset in either case. The comparison is shallow over each block's
120
+ * `data`: a host whose block `data` holds nested objects rebuilt on every render, and which does not echo
121
+ * `onChange` back, can still lose an edit. The core block types all have flat data, so this only affects custom
122
+ * blocks.
123
123
  */
124
124
  blocks: QuoinBlock[];
125
125
  onChange: (blocks: QuoinBlock[]) => void;
@@ -209,8 +209,8 @@ type UploadFieldProps = {
209
209
  label: string;
210
210
  /** An `accept` attribute for the file input, such as `'image/*'` or `'.pdf'`. */
211
211
  accept?: string;
212
- /** Called once the upload resolves. Throwing from it is reported through the canvas `onError`. */
213
- onUploaded: (result: UploadResult) => void;
212
+ /** Called once the upload resolves. The field stays busy until a returned promise settles, and throwing or rejecting is reported through the canvas `onError`. */
213
+ onUploaded: (result: UploadResult) => void | Promise<void>;
214
214
  };
215
215
  /** A file picker wired to the canvas `upload` function, with its own busy state and inline error. */
216
216
  declare function UploadField({ category, label, accept, onUploaded }: UploadFieldProps): React.JSX.Element;
@@ -267,7 +267,7 @@ type BlockList = {
267
267
  declare function useBlockList(initial: QuoinBlock[]): BlockList;
268
268
 
269
269
  /** Where a debounced save stands, for a "Saving..." or "Saved" indicator. */
270
- type AutosaveStatus = 'idle' | 'saving' | 'saved' | 'retrying';
270
+ type AutosaveStatus = 'idle' | 'saving' | 'saved' | 'retrying' | 'failed';
271
271
  /** What `useAutosave` tracks, how to build the payload, and how to persist it. */
272
272
  type UseAutosaveInput<T> = {
273
273
  /** The tracked fields. An edit to any of them marks dirty and restarts the debounce. */
@@ -280,24 +280,41 @@ type UseAutosaveInput<T> = {
280
280
  save: (payload: T) => unknown;
281
281
  debounceMs?: number;
282
282
  maxBackoffMs?: number;
283
+ /** Retries after a failed save before giving up with status `failed`. Defaults to 8. */
284
+ maxRetries?: number;
285
+ /**
286
+ * Whether a failed save is worth retrying. Receives the rejection, or the resolved
287
+ * `{ ok: false }` value. Return false for a permanent failure. Defaults to always.
288
+ */
289
+ isRetryable?: (error: unknown) => boolean;
290
+ /** Save a pending edit when the page is hidden or closed. Defaults to true. */
291
+ flushOnHide?: boolean;
283
292
  };
284
- /** The current save status and a way to retry a failed save immediately. */
293
+ /** The current save status, the last failure, and ways to save immediately. */
285
294
  type UseAutosaveResult = {
286
295
  status: AutosaveStatus;
296
+ /** The last failure, kept while a retry is pending and cleared by the next success. */
297
+ error: unknown;
298
+ /** Saves now, even when nothing is pending, and starts the retries over. */
287
299
  retry: () => void;
300
+ /** Saves a pending edit now and starts the retries over. A no-op when nothing is pending. */
301
+ flush: () => void;
288
302
  };
289
303
  /**
290
- * Debounced last-write-wins autosave. Nothing fires on mount, only after the first
291
- * real edit, and a rejected save backs off exponentially until it lands. `save` and
292
- * `build` are read at fire time, so passing fresh closures each render is safe.
304
+ * Debounced last-write-wins autosave. Nothing fires on mount, only after the first real
305
+ * edit. One save runs at a time: edits made while it runs are saved once it settles, as one
306
+ * save of the newest payload. A failed save backs off exponentially until it lands, stops
307
+ * being retryable or runs out of retries. An edit still waiting for its debounce is saved
308
+ * on unmount and when the page is hidden. `save` and `build` are read at fire time, so
309
+ * passing fresh closures each render is safe.
293
310
  */
294
- declare function useAutosave<T>({ deps, build, save, debounceMs, maxBackoffMs, }: UseAutosaveInput<T>): UseAutosaveResult;
311
+ declare function useAutosave<T>({ deps, build, save, debounceMs, maxBackoffMs, maxRetries, isRetryable, flushOnHide, }: UseAutosaveInput<T>): UseAutosaveResult;
295
312
 
296
313
  /** Height follows the content, and is recomputed when the box gets narrower or
297
314
  * wider, because a re-wrap changes the line count without changing the value.
298
315
  * Only a width change refits: reacting to the height this hook just set would
299
316
  * feed the observer its own output. */
300
- declare function useAutoGrow<T extends HTMLTextAreaElement>(value: string): React.RefObject<T | null>;
317
+ declare function useAutoGrow<T extends HTMLTextAreaElement>(value: string): React.RefObject<T>;
301
318
 
302
319
  /** A slash-palette entry: how it is shown and searched, and the markdown it inserts. */
303
320
  type TextCommand = {