@malva-ui/editor 0.1.13 → 0.1.14
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/README.md +5 -5
- package/fesm2022/malva-ui-editor-ai.mjs +17 -0
- package/fesm2022/malva-ui-editor-ai.mjs.map +1 -0
- package/fesm2022/malva-ui-editor.mjs +10227 -0
- package/fesm2022/malva-ui-editor.mjs.map +1 -0
- package/package.json +35 -26
- package/types/malva-ui-editor-ai.d.ts +1 -0
- package/types/malva-ui-editor.d.ts +2995 -0
- package/ai/index.d.ts +0 -12
- package/ai/malva-ui-editor-ai.d.ts +0 -5
- package/esm2022/ai/index.js +0 -12
- package/esm2022/ai/index.js.map +0 -1
- package/esm2022/ai/malva-ui-editor-ai.js +0 -5
- package/esm2022/ai/malva-ui-editor-ai.js.map +0 -1
- package/esm2022/index.js +0 -37
- package/esm2022/index.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-actions.js +0 -69
- package/esm2022/lib/ai/editor-ai-actions.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-context.js +0 -612
- package/esm2022/lib/ai/editor-ai-context.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-menu.js +0 -288
- package/esm2022/lib/ai/editor-ai-menu.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-review-bar.js +0 -223
- package/esm2022/lib/ai/editor-ai-review-bar.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-stream.js +0 -677
- package/esm2022/lib/ai/editor-ai-stream.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-suggestions.js +0 -751
- package/esm2022/lib/ai/editor-ai-suggestions.js.map +0 -1
- package/esm2022/lib/ai/editor-ai.tokens.js +0 -10
- package/esm2022/lib/ai/editor-ai.tokens.js.map +0 -1
- package/esm2022/lib/ai/editor-ai.types.js +0 -1
- package/esm2022/lib/ai/editor-ai.types.js.map +0 -1
- package/esm2022/lib/editor/editor.js +0 -985
- package/esm2022/lib/editor/editor.js.map +0 -1
- package/esm2022/lib/editor-extension-preflight.js +0 -83
- package/esm2022/lib/editor-extension-preflight.js.map +0 -1
- package/esm2022/lib/editor-serialization.js +0 -152
- package/esm2022/lib/editor-serialization.js.map +0 -1
- package/esm2022/lib/editor-toolbar-context.js +0 -259
- package/esm2022/lib/editor-toolbar-context.js.map +0 -1
- package/esm2022/lib/editor.tokens.js +0 -10
- package/esm2022/lib/editor.tokens.js.map +0 -1
- package/esm2022/lib/editor.types.js +0 -48
- package/esm2022/lib/editor.types.js.map +0 -1
- package/esm2022/lib/extensions/editor-block-handle.js +0 -760
- package/esm2022/lib/extensions/editor-block-handle.js.map +0 -1
- package/esm2022/lib/extensions/editor-extensions.js +0 -321
- package/esm2022/lib/extensions/editor-extensions.js.map +0 -1
- package/esm2022/lib/extensions/editor-markdown-compatibility.js +0 -162
- package/esm2022/lib/extensions/editor-markdown-compatibility.js.map +0 -1
- package/esm2022/lib/extensions/editor-upload-placeholder.js +0 -156
- package/esm2022/lib/extensions/editor-upload-placeholder.js.map +0 -1
- package/esm2022/lib/status/editor-status.js +0 -64
- package/esm2022/lib/status/editor-status.js.map +0 -1
- package/esm2022/lib/toolbar/editor-alignment.js +0 -203
- package/esm2022/lib/toolbar/editor-alignment.js.map +0 -1
- package/esm2022/lib/toolbar/editor-block-insert.js +0 -101
- package/esm2022/lib/toolbar/editor-block-insert.js.map +0 -1
- package/esm2022/lib/toolbar/editor-color-control.js +0 -71
- package/esm2022/lib/toolbar/editor-color-control.js.map +0 -1
- package/esm2022/lib/toolbar/editor-command-button.js +0 -115
- package/esm2022/lib/toolbar/editor-command-button.js.map +0 -1
- package/esm2022/lib/toolbar/editor-heading.js +0 -266
- package/esm2022/lib/toolbar/editor-heading.js.map +0 -1
- package/esm2022/lib/toolbar/editor-highlight.js +0 -84
- package/esm2022/lib/toolbar/editor-highlight.js.map +0 -1
- package/esm2022/lib/toolbar/editor-image-upload-dialog.js +0 -289
- package/esm2022/lib/toolbar/editor-image-upload-dialog.js.map +0 -1
- package/esm2022/lib/toolbar/editor-image-upload.js +0 -315
- package/esm2022/lib/toolbar/editor-image-upload.js.map +0 -1
- package/esm2022/lib/toolbar/editor-inline-marks.js +0 -124
- package/esm2022/lib/toolbar/editor-inline-marks.js.map +0 -1
- package/esm2022/lib/toolbar/editor-link.js +0 -265
- package/esm2022/lib/toolbar/editor-link.js.map +0 -1
- package/esm2022/lib/toolbar/editor-list.js +0 -249
- package/esm2022/lib/toolbar/editor-list.js.map +0 -1
- package/esm2022/lib/toolbar/editor-table.js +0 -292
- package/esm2022/lib/toolbar/editor-table.js.map +0 -1
- package/esm2022/lib/toolbar/editor-text-color.js +0 -82
- package/esm2022/lib/toolbar/editor-text-color.js.map +0 -1
- package/esm2022/lib/toolbar/editor-toolbar-widget.js +0 -41
- package/esm2022/lib/toolbar/editor-toolbar-widget.js.map +0 -1
- package/esm2022/lib/toolbar/editor-toolbar.defs.js +0 -60
- package/esm2022/lib/toolbar/editor-toolbar.defs.js.map +0 -1
- package/esm2022/lib/toolbar/editor-toolbar.js +0 -826
- package/esm2022/lib/toolbar/editor-toolbar.js.map +0 -1
- package/esm2022/lib/toolbar/editor-undo-redo.js +0 -72
- package/esm2022/lib/toolbar/editor-undo-redo.js.map +0 -1
- package/esm2022/lib/toolbar/editor-zoom.js +0 -330
- package/esm2022/lib/toolbar/editor-zoom.js.map +0 -1
- package/esm2022/lib/upload/editor-image-upload-coordinator.js +0 -480
- package/esm2022/lib/upload/editor-image-upload-coordinator.js.map +0 -1
- package/esm2022/malva-ui-editor.js +0 -5
- package/esm2022/malva-ui-editor.js.map +0 -1
- package/index.d.ts +0 -35
- package/lib/ai/editor-ai-actions.d.ts +0 -27
- package/lib/ai/editor-ai-context.d.ts +0 -307
- package/lib/ai/editor-ai-menu.d.ts +0 -142
- package/lib/ai/editor-ai-review-bar.d.ts +0 -116
- package/lib/ai/editor-ai-stream.d.ts +0 -207
- package/lib/ai/editor-ai-suggestions.d.ts +0 -219
- package/lib/ai/editor-ai.tokens.d.ts +0 -10
- package/lib/ai/editor-ai.types.d.ts +0 -176
- package/lib/editor/editor.d.ts +0 -243
- package/lib/editor-extension-preflight.d.ts +0 -23
- package/lib/editor-serialization.d.ts +0 -74
- package/lib/editor-toolbar-context.d.ts +0 -116
- package/lib/editor.tokens.d.ts +0 -6
- package/lib/editor.types.d.ts +0 -194
- package/lib/extensions/editor-block-handle.d.ts +0 -59
- package/lib/extensions/editor-extensions.d.ts +0 -165
- package/lib/extensions/editor-markdown-compatibility.d.ts +0 -11
- package/lib/extensions/editor-upload-placeholder.d.ts +0 -75
- package/lib/status/editor-status.d.ts +0 -24
- package/lib/toolbar/editor-alignment.d.ts +0 -61
- package/lib/toolbar/editor-block-insert.d.ts +0 -26
- package/lib/toolbar/editor-color-control.d.ts +0 -32
- package/lib/toolbar/editor-command-button.d.ts +0 -29
- package/lib/toolbar/editor-heading.d.ts +0 -68
- package/lib/toolbar/editor-highlight.d.ts +0 -21
- package/lib/toolbar/editor-image-upload-dialog.d.ts +0 -96
- package/lib/toolbar/editor-image-upload.d.ts +0 -71
- package/lib/toolbar/editor-inline-marks.d.ts +0 -30
- package/lib/toolbar/editor-link.d.ts +0 -78
- package/lib/toolbar/editor-list.d.ts +0 -67
- package/lib/toolbar/editor-table.d.ts +0 -133
- package/lib/toolbar/editor-text-color.d.ts +0 -21
- package/lib/toolbar/editor-toolbar-widget.d.ts +0 -19
- package/lib/toolbar/editor-toolbar.d.ts +0 -157
- package/lib/toolbar/editor-toolbar.defs.d.ts +0 -35
- package/lib/toolbar/editor-undo-redo.d.ts +0 -26
- package/lib/toolbar/editor-zoom.d.ts +0 -108
- package/lib/upload/editor-image-upload-coordinator.d.ts +0 -55
- package/malva-ui-editor.d.ts +0 -5
|
@@ -1,480 +0,0 @@
|
|
|
1
|
-
import { computed, InjectionToken, signal } from '@angular/core';
|
|
2
|
-
import { MlvEditorImageUploadControl, mlvEditorDefaultImageUrlPolicy, } from '../editor.types';
|
|
3
|
-
const editorImageUploadCoordinators = new WeakMap();
|
|
4
|
-
/** @internal Resolves only capabilities created by the Malva coordinator factory. */
|
|
5
|
-
export function resolveMlvEditorImageUploadCoordinator(control) {
|
|
6
|
-
return control ? editorImageUploadCoordinators.get(control) : undefined;
|
|
7
|
-
}
|
|
8
|
-
/** Internal editor-scoped coordinator token consumed by upload entry points. */
|
|
9
|
-
export const MLV_EDITOR_IMAGE_UPLOAD_COORDINATOR = new InjectionToken('MLV_EDITOR_IMAGE_UPLOAD_COORDINATOR');
|
|
10
|
-
let nextUploadId = 0;
|
|
11
|
-
const nextStableUploadId = () => {
|
|
12
|
-
nextUploadId += 1;
|
|
13
|
-
return `mlv-editor-upload-${nextUploadId}`;
|
|
14
|
-
};
|
|
15
|
-
const isMimePattern = (value) => value === '*/*' || /^[^\s/*]+\/(?:\*|[^\s/*]+)$/u.test(value);
|
|
16
|
-
const createError = (code, message, cause) => ({
|
|
17
|
-
code,
|
|
18
|
-
message,
|
|
19
|
-
recoverable: true,
|
|
20
|
-
...(cause === undefined ? {} : { cause }),
|
|
21
|
-
});
|
|
22
|
-
/**
|
|
23
|
-
* Creates one editor-scoped upload coordinator.
|
|
24
|
-
*
|
|
25
|
-
* @internal The returned value is exposed only through
|
|
26
|
-
* `MLV_EDITOR_IMAGE_UPLOAD_COORDINATOR`.
|
|
27
|
-
*/
|
|
28
|
-
export function createMlvEditorImageUploadCoordinator(connection, injectedUploader, abortRegistry) {
|
|
29
|
-
const coordinator = new EditorImageUploadCoordinator(connection, injectedUploader, abortRegistry);
|
|
30
|
-
editorImageUploadCoordinators.set(coordinator, coordinator);
|
|
31
|
-
return coordinator;
|
|
32
|
-
}
|
|
33
|
-
class EditorImageUploadCoordinator extends MlvEditorImageUploadControl {
|
|
34
|
-
_connection;
|
|
35
|
-
_injectedUploader;
|
|
36
|
-
_abortRegistry;
|
|
37
|
-
/** @private Mutable upload list backing the public read-only signal. */
|
|
38
|
-
_pending = signal([], /* @ts-ignore */
|
|
39
|
-
...(ngDevMode ? [{ debugName: "_pending" }] : /* istanbul ignore next */ []));
|
|
40
|
-
/** Active and retryable uploads owned by this editor. */
|
|
41
|
-
pending = this._pending.asReadonly();
|
|
42
|
-
/** @private Retry metadata and active request generation keyed by stable ID. */
|
|
43
|
-
_states = new Map();
|
|
44
|
-
/** @private Synchronous terminal observers used by editor-owned UI. */
|
|
45
|
-
_terminalListeners = new Set();
|
|
46
|
-
/** @private Live dialog-owner reference count keyed by upload ID. */
|
|
47
|
-
_dialogOwners = new Map();
|
|
48
|
-
/** @private Ownership snapshot retained for the complete terminal delivery. */
|
|
49
|
-
_terminalDialogOwners = new Map();
|
|
50
|
-
/** @private Currently resolved adapter, with the input taking precedence. */
|
|
51
|
-
_resolvedUploader = computed(() => this._connection.imageUploader() ?? this._injectedUploader, /* @ts-ignore */
|
|
52
|
-
...(ngDevMode ? [{ debugName: "_resolvedUploader" }] : /* istanbul ignore next */ []));
|
|
53
|
-
/** Whether either the input or injected adapter resolves for this editor. */
|
|
54
|
-
adapterAvailable = computed(() => this._resolvedUploader() !== undefined, /* @ts-ignore */
|
|
55
|
-
...(ngDevMode ? [{ debugName: "adapterAvailable" }] : /* istanbul ignore next */ []));
|
|
56
|
-
/** Current editor-scoped validation options. */
|
|
57
|
-
get options() {
|
|
58
|
-
return this._connection.imageUploadOptions;
|
|
59
|
-
}
|
|
60
|
-
/** Whether this editor can currently begin image uploads. */
|
|
61
|
-
available = computed(() => this._connection.editor() !== null &&
|
|
62
|
-
this._resolvedUploader() !== undefined &&
|
|
63
|
-
!this._connection.disabled() &&
|
|
64
|
-
!this._connection.readonly(), /* @ts-ignore */
|
|
65
|
-
...(ngDevMode ? [{ debugName: "available" }] : /* istanbul ignore next */ []));
|
|
66
|
-
constructor(_connection, _injectedUploader, _abortRegistry) {
|
|
67
|
-
super();
|
|
68
|
-
this._connection = _connection;
|
|
69
|
-
this._injectedUploader = _injectedUploader;
|
|
70
|
-
this._abortRegistry = _abortRegistry;
|
|
71
|
-
}
|
|
72
|
-
/** Observes explicit terminal transitions synchronously. */
|
|
73
|
-
onTerminal(listener) {
|
|
74
|
-
this._terminalListeners.add(listener);
|
|
75
|
-
return () => this._terminalListeners.delete(listener);
|
|
76
|
-
}
|
|
77
|
-
/** Claims live dialog ownership until the returned release is called. */
|
|
78
|
-
claimDialogOwnership(id) {
|
|
79
|
-
this._dialogOwners.set(id, (this._dialogOwners.get(id) ?? 0) + 1);
|
|
80
|
-
let released = false;
|
|
81
|
-
return () => {
|
|
82
|
-
if (released)
|
|
83
|
-
return;
|
|
84
|
-
released = true;
|
|
85
|
-
const owners = this._dialogOwners.get(id) ?? 0;
|
|
86
|
-
if (owners <= 1)
|
|
87
|
-
this._dialogOwners.delete(id);
|
|
88
|
-
else
|
|
89
|
-
this._dialogOwners.set(id, owners - 1);
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
/** Whether a live dialog owns terminal announcements for an upload. */
|
|
93
|
-
hasDialogOwner(id) {
|
|
94
|
-
return (this._terminalDialogOwners.get(id) ??
|
|
95
|
-
(this._dialogOwners.get(id) ?? 0) > 0);
|
|
96
|
-
}
|
|
97
|
-
/** Validates and begins one file interaction. */
|
|
98
|
-
start(files, source, metadata = {}) {
|
|
99
|
-
const editor = this._connection.editor();
|
|
100
|
-
const uploader = this._resolvedUploader();
|
|
101
|
-
if (!editor ||
|
|
102
|
-
!uploader ||
|
|
103
|
-
this._connection.disabled() ||
|
|
104
|
-
this._connection.readonly()) {
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
const options = this._validateOptions(this._connection.imageUploadOptions());
|
|
108
|
-
if (!options) {
|
|
109
|
-
for (const file of files) {
|
|
110
|
-
this._emitValidationFailure(file, source, 'The image upload configuration is invalid.');
|
|
111
|
-
}
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
const ids = [];
|
|
115
|
-
const acceptedInteractionFiles = files.slice(0, options.maxFiles);
|
|
116
|
-
const excessFiles = files.slice(options.maxFiles);
|
|
117
|
-
for (const file of excessFiles) {
|
|
118
|
-
this._emitValidationFailure(file, source, `Only ${options.maxFiles} image file(s) may be uploaded at once.`);
|
|
119
|
-
}
|
|
120
|
-
for (const file of acceptedInteractionFiles) {
|
|
121
|
-
const validationMessage = this._validateFile(file, options);
|
|
122
|
-
if (validationMessage) {
|
|
123
|
-
this._emitValidationFailure(file, source, validationMessage);
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
const id = nextStableUploadId();
|
|
127
|
-
const state = {
|
|
128
|
-
id,
|
|
129
|
-
file,
|
|
130
|
-
source,
|
|
131
|
-
generation: 0,
|
|
132
|
-
status: 'uploading',
|
|
133
|
-
...metadata,
|
|
134
|
-
};
|
|
135
|
-
this._states.set(id, state);
|
|
136
|
-
this._pending.update((pending) => [
|
|
137
|
-
...pending,
|
|
138
|
-
this._toPendingUpload(state, 0),
|
|
139
|
-
]);
|
|
140
|
-
ids.push(id);
|
|
141
|
-
if (!this._insertPlaceholder(editor, state)) {
|
|
142
|
-
this._fail(state, state.generation, createError('upload-result', 'The editor cannot create an image upload placeholder.'));
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
this._beginRequest(state, uploader);
|
|
146
|
-
}
|
|
147
|
-
return ids;
|
|
148
|
-
}
|
|
149
|
-
/** Cancels one active request and emits its cancellation event once. */
|
|
150
|
-
cancel(id) {
|
|
151
|
-
const state = this._states.get(id);
|
|
152
|
-
if (!state || state.status !== 'uploading' || !state.controller)
|
|
153
|
-
return;
|
|
154
|
-
this._terminate(state, 'cancelled');
|
|
155
|
-
}
|
|
156
|
-
/** Retries one recoverable failure with a fresh request generation. */
|
|
157
|
-
retry(id) {
|
|
158
|
-
const state = this._states.get(id);
|
|
159
|
-
const editor = this._connection.editor();
|
|
160
|
-
const uploader = this._resolvedUploader();
|
|
161
|
-
if (!state ||
|
|
162
|
-
state.status !== 'failed' ||
|
|
163
|
-
!editor ||
|
|
164
|
-
!uploader ||
|
|
165
|
-
this._connection.disabled() ||
|
|
166
|
-
this._connection.readonly()) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
state.status = 'uploading';
|
|
170
|
-
this._replacePending(this._toPendingUpload(state, 0));
|
|
171
|
-
const placeholderReset = this._updatePlaceholder(editor, state.id, 0);
|
|
172
|
-
if (!placeholderReset && !this._insertPlaceholder(editor, state)) {
|
|
173
|
-
this._fail(state, state.generation, createError('upload-result', 'The editor cannot restore the image upload placeholder.'));
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
this._beginRequest(state, uploader);
|
|
177
|
-
}
|
|
178
|
-
/** Removes one failed item without emitting a cancellation event. */
|
|
179
|
-
remove(id) {
|
|
180
|
-
const state = this._states.get(id);
|
|
181
|
-
if (!state || state.status !== 'failed')
|
|
182
|
-
return;
|
|
183
|
-
this._states.delete(id);
|
|
184
|
-
this._removePending(id);
|
|
185
|
-
this._removePlaceholder(this._connection.editor(), id);
|
|
186
|
-
this._emitTerminal({ id, reason: 'removed' });
|
|
187
|
-
}
|
|
188
|
-
/** @private Validates and normalizes host upload configuration. */
|
|
189
|
-
_validateOptions(options) {
|
|
190
|
-
const accept = Array.isArray(options?.accept)
|
|
191
|
-
? options.accept.map((value) => typeof value === 'string' ? value.trim().toLowerCase() : '')
|
|
192
|
-
: [];
|
|
193
|
-
if (accept.length === 0 ||
|
|
194
|
-
accept.some((value) => !value || !isMimePattern(value)) ||
|
|
195
|
-
!Number.isFinite(options?.maxSize) ||
|
|
196
|
-
options.maxSize < 0 ||
|
|
197
|
-
!Number.isInteger(options?.maxFiles) ||
|
|
198
|
-
options.maxFiles < 1 ||
|
|
199
|
-
(options.urlPolicy !== undefined &&
|
|
200
|
-
typeof options.urlPolicy !== 'function')) {
|
|
201
|
-
return undefined;
|
|
202
|
-
}
|
|
203
|
-
return {
|
|
204
|
-
accept,
|
|
205
|
-
maxSize: options.maxSize,
|
|
206
|
-
maxFiles: options.maxFiles,
|
|
207
|
-
urlPolicy: options.urlPolicy ?? mlvEditorDefaultImageUrlPolicy,
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
/** @private Returns the user-facing validation failure for one file. */
|
|
211
|
-
_validateFile(file, options) {
|
|
212
|
-
const mimeType = typeof file?.type === 'string' ? file.type.trim().toLowerCase() : '';
|
|
213
|
-
const mimeAccepted = options.accept.some((pattern) => {
|
|
214
|
-
if (pattern === '*/*')
|
|
215
|
-
return true;
|
|
216
|
-
if (!mimeType)
|
|
217
|
-
return false;
|
|
218
|
-
if (pattern.endsWith('/*')) {
|
|
219
|
-
return mimeType.startsWith(`${pattern.slice(0, -1)}`);
|
|
220
|
-
}
|
|
221
|
-
return pattern === mimeType;
|
|
222
|
-
});
|
|
223
|
-
if (!mimeAccepted) {
|
|
224
|
-
return 'The selected file type is not accepted for image upload.';
|
|
225
|
-
}
|
|
226
|
-
if (!Number.isFinite(file?.size) ||
|
|
227
|
-
file.size < 0 ||
|
|
228
|
-
file.size > options.maxSize) {
|
|
229
|
-
return `The selected image exceeds the ${options.maxSize}-byte size limit.`;
|
|
230
|
-
}
|
|
231
|
-
return undefined;
|
|
232
|
-
}
|
|
233
|
-
/** @private Emits a validation failure without creating upload state. */
|
|
234
|
-
_emitValidationFailure(file, source, message) {
|
|
235
|
-
const error = createError('upload-validation', message);
|
|
236
|
-
this._connection.emitFailure({ file, source, error });
|
|
237
|
-
this._connection.emitError(error);
|
|
238
|
-
}
|
|
239
|
-
/** @private Creates a fresh controller and invokes the resolved adapter. */
|
|
240
|
-
_beginRequest(state, uploader) {
|
|
241
|
-
state.generation += 1;
|
|
242
|
-
const generation = state.generation;
|
|
243
|
-
const controller = new AbortController();
|
|
244
|
-
state.controller = controller;
|
|
245
|
-
state.unregister = this._abortRegistry.register(() => this._terminateForLifecycle(state.id, generation));
|
|
246
|
-
let request;
|
|
247
|
-
try {
|
|
248
|
-
request = uploader.upload(state.file, {
|
|
249
|
-
source: state.source,
|
|
250
|
-
signal: controller.signal,
|
|
251
|
-
reportProgress: (progress) => this._reportProgress(state.id, generation, progress),
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
catch (cause) {
|
|
255
|
-
this._fail(state, generation, createError('upload-transport', 'The image upload adapter failed before starting the request.', cause));
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
Promise.resolve(request).then((result) => this._complete(state.id, generation, result), (cause) => this._fail(state, generation, createError('upload-transport', 'The image upload request failed.', cause)));
|
|
259
|
-
}
|
|
260
|
-
/** @private Applies finite progress only to the current request generation. */
|
|
261
|
-
_reportProgress(id, generation, progress) {
|
|
262
|
-
const state = this._currentActiveState(id, generation);
|
|
263
|
-
if (!state || !Number.isFinite(progress))
|
|
264
|
-
return;
|
|
265
|
-
const normalizedProgress = Math.max(0, Math.min(100, progress));
|
|
266
|
-
this._replacePending(this._toPendingUpload(state, normalizedProgress));
|
|
267
|
-
this._updatePlaceholder(this._connection.editor(), id, normalizedProgress);
|
|
268
|
-
}
|
|
269
|
-
/** @private Validates a result and atomically replaces its mapped placeholder. */
|
|
270
|
-
_complete(id, generation, candidate) {
|
|
271
|
-
const state = this._currentActiveState(id, generation);
|
|
272
|
-
if (!state)
|
|
273
|
-
return;
|
|
274
|
-
const options = this._validateOptions(this._connection.imageUploadOptions());
|
|
275
|
-
const result = options
|
|
276
|
-
? this._validateResult(candidate, state, options)
|
|
277
|
-
: undefined;
|
|
278
|
-
if (!result) {
|
|
279
|
-
this._fail(state, generation, createError('upload-result', 'The image uploader returned invalid or unsafe image metadata.', candidate));
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const editor = this._connection.editor();
|
|
283
|
-
if (!editor || !this._replacePlaceholder(editor, id, result)) {
|
|
284
|
-
this._fail(state, generation, createError('upload-result', 'The editor cannot atomically insert the uploaded image.'));
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
this._endActiveRequest(state);
|
|
288
|
-
this._states.delete(id);
|
|
289
|
-
this._removePending(id);
|
|
290
|
-
this._connection.emitSuccess({
|
|
291
|
-
file: state.file,
|
|
292
|
-
source: state.source,
|
|
293
|
-
result,
|
|
294
|
-
});
|
|
295
|
-
this._emitTerminal({ id, reason: 'success' });
|
|
296
|
-
}
|
|
297
|
-
/** @private Returns normalized metadata only when the complete result is valid. */
|
|
298
|
-
_validateResult(candidate, state, options) {
|
|
299
|
-
if (!candidate ||
|
|
300
|
-
typeof candidate !== 'object' ||
|
|
301
|
-
Array.isArray(candidate)) {
|
|
302
|
-
return undefined;
|
|
303
|
-
}
|
|
304
|
-
const value = candidate;
|
|
305
|
-
if (typeof value['src'] !== 'string')
|
|
306
|
-
return undefined;
|
|
307
|
-
const src = value['src'].trim();
|
|
308
|
-
if (!src)
|
|
309
|
-
return undefined;
|
|
310
|
-
if ((value['alt'] !== undefined && typeof value['alt'] !== 'string') ||
|
|
311
|
-
(value['title'] !== undefined && typeof value['title'] !== 'string') ||
|
|
312
|
-
!this._isOptionalPositiveNumber(value['width']) ||
|
|
313
|
-
!this._isOptionalPositiveNumber(value['height'])) {
|
|
314
|
-
return undefined;
|
|
315
|
-
}
|
|
316
|
-
try {
|
|
317
|
-
if (!options.urlPolicy(src))
|
|
318
|
-
return undefined;
|
|
319
|
-
}
|
|
320
|
-
catch {
|
|
321
|
-
return undefined;
|
|
322
|
-
}
|
|
323
|
-
const alt = typeof value['alt'] === 'string' ? value['alt'] : state.alt;
|
|
324
|
-
const title = typeof value['title'] === 'string' ? value['title'] : state.title;
|
|
325
|
-
return {
|
|
326
|
-
src,
|
|
327
|
-
...(alt === undefined ? {} : { alt }),
|
|
328
|
-
...(title === undefined ? {} : { title }),
|
|
329
|
-
...(typeof value['width'] === 'number' ? { width: value['width'] } : {}),
|
|
330
|
-
...(typeof value['height'] === 'number'
|
|
331
|
-
? { height: value['height'] }
|
|
332
|
-
: {}),
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
/** @private Whether an optional dimension is absent or finite and positive. */
|
|
336
|
-
_isOptionalPositiveNumber(value) {
|
|
337
|
-
return (value === undefined ||
|
|
338
|
-
(typeof value === 'number' && Number.isFinite(value) && value > 0));
|
|
339
|
-
}
|
|
340
|
-
/** @private Converts a current request failure into retryable pending state. */
|
|
341
|
-
_fail(state, generation, error) {
|
|
342
|
-
if (this._states.get(state.id) !== state ||
|
|
343
|
-
state.generation !== generation ||
|
|
344
|
-
state.status !== 'uploading') {
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
this._endActiveRequest(state);
|
|
348
|
-
state.status = 'failed';
|
|
349
|
-
this._replacePending(this._toPendingUpload(state, 0, error));
|
|
350
|
-
this._connection.emitFailure({
|
|
351
|
-
file: state.file,
|
|
352
|
-
source: state.source,
|
|
353
|
-
error,
|
|
354
|
-
});
|
|
355
|
-
this._connection.emitError(error);
|
|
356
|
-
}
|
|
357
|
-
/** @private Stops explicit cancellation after first invalidating its generation. */
|
|
358
|
-
_terminate(state, reason) {
|
|
359
|
-
state.generation += 1;
|
|
360
|
-
this._endActiveRequest(state, true);
|
|
361
|
-
this._states.delete(state.id);
|
|
362
|
-
this._removePending(state.id);
|
|
363
|
-
this._removePlaceholder(this._connection.editor(), state.id);
|
|
364
|
-
if (reason === 'cancelled') {
|
|
365
|
-
this._connection.emitCancelled({
|
|
366
|
-
file: state.file,
|
|
367
|
-
source: state.source,
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
this._emitTerminal({ id: state.id, reason });
|
|
371
|
-
}
|
|
372
|
-
/** @private Stops a registry-owned request without inventing a cancel event. */
|
|
373
|
-
_terminateForLifecycle(id, generation) {
|
|
374
|
-
const state = this._currentActiveState(id, generation);
|
|
375
|
-
if (!state)
|
|
376
|
-
return;
|
|
377
|
-
this._terminate(state, 'lifecycle-abort');
|
|
378
|
-
}
|
|
379
|
-
/** @private Delivers one terminal reason to current UI observers. */
|
|
380
|
-
_emitTerminal(event) {
|
|
381
|
-
this._terminalDialogOwners.set(event.id, (this._dialogOwners.get(event.id) ?? 0) > 0);
|
|
382
|
-
try {
|
|
383
|
-
for (const listener of [...this._terminalListeners])
|
|
384
|
-
listener(event);
|
|
385
|
-
}
|
|
386
|
-
finally {
|
|
387
|
-
this._terminalDialogOwners.delete(event.id);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
/** @private Unregisters and optionally aborts one controller. */
|
|
391
|
-
_endActiveRequest(state, abort = false) {
|
|
392
|
-
const unregister = state.unregister;
|
|
393
|
-
const controller = state.controller;
|
|
394
|
-
state.unregister = undefined;
|
|
395
|
-
state.controller = undefined;
|
|
396
|
-
unregister?.();
|
|
397
|
-
if (abort && controller && !controller.signal.aborted)
|
|
398
|
-
controller.abort();
|
|
399
|
-
}
|
|
400
|
-
/** @private Gets a live generation and rejects cancelled or superseded work. */
|
|
401
|
-
_currentActiveState(id, generation) {
|
|
402
|
-
const state = this._states.get(id);
|
|
403
|
-
return state &&
|
|
404
|
-
state.generation === generation &&
|
|
405
|
-
state.status === 'uploading' &&
|
|
406
|
-
state.controller &&
|
|
407
|
-
!state.controller.signal.aborted
|
|
408
|
-
? state
|
|
409
|
-
: undefined;
|
|
410
|
-
}
|
|
411
|
-
/** @private Builds the exact public pending shape. */
|
|
412
|
-
_toPendingUpload(state, progress, error) {
|
|
413
|
-
return {
|
|
414
|
-
id: state.id,
|
|
415
|
-
file: state.file,
|
|
416
|
-
source: state.source,
|
|
417
|
-
progress,
|
|
418
|
-
status: state.status,
|
|
419
|
-
...(error === undefined ? {} : { error }),
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
/** @private Replaces one immutable public pending entry. */
|
|
423
|
-
_replacePending(upload) {
|
|
424
|
-
this._pending.update((pending) => pending.map((candidate) => candidate.id === upload.id ? upload : candidate));
|
|
425
|
-
}
|
|
426
|
-
/** @private Removes one public pending entry. */
|
|
427
|
-
_removePending(id) {
|
|
428
|
-
this._pending.update((pending) => pending.filter((candidate) => candidate.id !== id));
|
|
429
|
-
}
|
|
430
|
-
/** @private Inserts a non-serializable placeholder before invoking an adapter. */
|
|
431
|
-
_insertPlaceholder(editor, state) {
|
|
432
|
-
try {
|
|
433
|
-
return (typeof editor.commands.insertUploadPlaceholder === 'function' &&
|
|
434
|
-
editor.commands.insertUploadPlaceholder({
|
|
435
|
-
id: state.id,
|
|
436
|
-
...(state.position === undefined ? {} : { position: state.position }),
|
|
437
|
-
progress: 0,
|
|
438
|
-
}));
|
|
439
|
-
}
|
|
440
|
-
catch {
|
|
441
|
-
return false;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
/** @private Updates one current placeholder without mutating the document. */
|
|
445
|
-
_updatePlaceholder(editor, id, progress) {
|
|
446
|
-
try {
|
|
447
|
-
return (!!editor &&
|
|
448
|
-
typeof editor.commands.updateUploadPlaceholder === 'function' &&
|
|
449
|
-
editor.commands.updateUploadPlaceholder({ id, progress }));
|
|
450
|
-
}
|
|
451
|
-
catch {
|
|
452
|
-
return false;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
/** @private Removes one placeholder when its extension remains available. */
|
|
456
|
-
_removePlaceholder(editor, id) {
|
|
457
|
-
try {
|
|
458
|
-
if (typeof editor?.commands.removeUploadPlaceholder === 'function') {
|
|
459
|
-
editor.commands.removeUploadPlaceholder({ id });
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
catch {
|
|
463
|
-
// A missing custom extension must not prevent coordinator cleanup.
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
/** @private Atomically inserts an image at the placeholder's mapped position. */
|
|
467
|
-
_replacePlaceholder(editor, id, result) {
|
|
468
|
-
try {
|
|
469
|
-
return (typeof editor.commands.replaceUploadPlaceholder === 'function' &&
|
|
470
|
-
editor.commands.replaceUploadPlaceholder({
|
|
471
|
-
id,
|
|
472
|
-
attributes: result,
|
|
473
|
-
}));
|
|
474
|
-
}
|
|
475
|
-
catch {
|
|
476
|
-
return false;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
//# sourceMappingURL=editor-image-upload-coordinator.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"editor-image-upload-coordinator.js","sourceRoot":"","sources":["../../../../../../libs/editor/src/lib/upload/editor-image-upload-coordinator.ts"],"names":[],"mappings":"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","sourcesContent":["import { computed, InjectionToken, signal, type Signal } from '@angular/core';\nimport type { Editor } from '@tiptap/core';\nimport {\n MlvEditorImageUploadControl,\n mlvEditorDefaultImageUrlPolicy,\n type MlvEditorError,\n type MlvEditorImageUploadCancelled,\n type MlvEditorImageUploadFailure,\n type MlvEditorImageUploadOptions,\n type MlvEditorImageUploadResult,\n type MlvEditorImageUploadSource,\n type MlvEditorImageUploadSuccess,\n type MlvEditorImageUploader,\n type MlvEditorPendingUpload,\n} from '../editor.types';\n\ntype MlvEditorImageUploadTerminalReason =\n | 'success'\n | 'cancelled'\n | 'lifecycle-abort'\n | 'removed';\n\ninterface MlvEditorImageUploadTerminalEvent {\n readonly id: string;\n readonly reason: MlvEditorImageUploadTerminalReason;\n}\n\n/** Internal upload coordinator shared by every editor image entry point. */\nexport interface MlvEditorImageUploadCoordinator\n extends MlvEditorImageUploadControl {\n /** Observes explicit terminal transitions synchronously. */\n onTerminal(\n listener: (event: MlvEditorImageUploadTerminalEvent) => void,\n ): () => void;\n\n /** Claims live dialog ownership until the returned release is called. */\n claimDialogOwnership(id: string): () => void;\n\n /** Whether a live dialog owned this upload at the current terminal delivery. */\n hasDialogOwner(id: string): boolean;\n}\n\nconst editorImageUploadCoordinators = new WeakMap<\n MlvEditorImageUploadControl,\n MlvEditorImageUploadCoordinator\n>();\n\n/** @internal Resolves only capabilities created by the Malva coordinator factory. */\nexport function resolveMlvEditorImageUploadCoordinator(\n control: MlvEditorImageUploadControl | undefined,\n): MlvEditorImageUploadCoordinator | undefined {\n return control ? editorImageUploadCoordinators.get(control) : undefined;\n}\n\n/** Internal lifecycle and event connection supplied by the owning editor. */\nexport interface MlvEditorImageUploadCoordinatorConnection {\n /** Current Tiptap editor instance. */\n readonly editor: Signal<Editor | null>;\n\n /** Per-editor uploader input. */\n readonly imageUploader: Signal<MlvEditorImageUploader | undefined>;\n\n /** Current validation and URL-policy options. */\n readonly imageUploadOptions: Signal<MlvEditorImageUploadOptions>;\n\n /** Whether the full editor composite is disabled. */\n readonly disabled: () => boolean;\n\n /** Whether the editor content is read-only. */\n readonly readonly: () => boolean;\n\n /** Emits one successful upload payload. */\n readonly emitSuccess: (event: MlvEditorImageUploadSuccess) => void;\n\n /** Emits one failed upload payload. */\n readonly emitFailure: (event: MlvEditorImageUploadFailure) => void;\n\n /** Emits one explicit cancellation payload. */\n readonly emitCancelled: (event: MlvEditorImageUploadCancelled) => void;\n\n /** Emits the shared recoverable editor error. */\n readonly emitError: (error: MlvEditorError) => void;\n}\n\n/** Minimal abort registry contract used without coupling consumers to its class. */\nexport interface MlvEditorImageUploadAbortRegistryLike {\n /** Registers one active abort callback and returns its unregister function. */\n register(abort: () => void): () => void;\n}\n\n/** Internal editor-scoped coordinator token consumed by upload entry points. */\nexport const MLV_EDITOR_IMAGE_UPLOAD_COORDINATOR =\n new InjectionToken<MlvEditorImageUploadCoordinator>(\n 'MLV_EDITOR_IMAGE_UPLOAD_COORDINATOR',\n );\n\ninterface ValidatedUploadOptions {\n readonly accept: readonly string[];\n readonly maxSize: number;\n readonly maxFiles: number;\n readonly urlPolicy: (url: string) => boolean;\n}\n\ninterface UploadMetadata {\n readonly position?: number;\n readonly alt?: string;\n readonly title?: string;\n}\n\ninterface UploadState extends UploadMetadata {\n readonly id: string;\n readonly file: File;\n readonly source: MlvEditorImageUploadSource;\n generation: number;\n status: 'uploading' | 'failed';\n controller?: AbortController;\n unregister?: () => void;\n}\n\nlet nextUploadId = 0;\n\nconst nextStableUploadId = (): string => {\n nextUploadId += 1;\n return `mlv-editor-upload-${nextUploadId}`;\n};\n\nconst isMimePattern = (value: string): boolean =>\n value === '*/*' || /^[^\\s/*]+\\/(?:\\*|[^\\s/*]+)$/u.test(value);\n\nconst createError = (\n code: Extract<\n MlvEditorError['code'],\n 'upload-validation' | 'upload-transport' | 'upload-result'\n >,\n message: string,\n cause?: unknown,\n): MlvEditorError => ({\n code,\n message,\n recoverable: true,\n ...(cause === undefined ? {} : { cause }),\n});\n\n/**\n * Creates one editor-scoped upload coordinator.\n *\n * @internal The returned value is exposed only through\n * `MLV_EDITOR_IMAGE_UPLOAD_COORDINATOR`.\n */\nexport function createMlvEditorImageUploadCoordinator(\n connection: MlvEditorImageUploadCoordinatorConnection,\n injectedUploader: MlvEditorImageUploader | undefined,\n abortRegistry: MlvEditorImageUploadAbortRegistryLike,\n): MlvEditorImageUploadCoordinator {\n const coordinator = new EditorImageUploadCoordinator(\n connection,\n injectedUploader,\n abortRegistry,\n );\n editorImageUploadCoordinators.set(coordinator, coordinator);\n return coordinator;\n}\n\nclass EditorImageUploadCoordinator\n extends MlvEditorImageUploadControl\n implements MlvEditorImageUploadCoordinator\n{\n /** @private Mutable upload list backing the public read-only signal. */\n private readonly _pending = signal<readonly MlvEditorPendingUpload[]>([]);\n\n /** Active and retryable uploads owned by this editor. */\n readonly pending = this._pending.asReadonly();\n\n /** @private Retry metadata and active request generation keyed by stable ID. */\n private readonly _states = new Map<string, UploadState>();\n\n /** @private Synchronous terminal observers used by editor-owned UI. */\n private readonly _terminalListeners = new Set<\n (event: MlvEditorImageUploadTerminalEvent) => void\n >();\n\n /** @private Live dialog-owner reference count keyed by upload ID. */\n private readonly _dialogOwners = new Map<string, number>();\n\n /** @private Ownership snapshot retained for the complete terminal delivery. */\n private readonly _terminalDialogOwners = new Map<string, boolean>();\n\n /** @private Currently resolved adapter, with the input taking precedence. */\n private readonly _resolvedUploader = computed(\n () => this._connection.imageUploader() ?? this._injectedUploader,\n );\n\n /** Whether either the input or injected adapter resolves for this editor. */\n readonly adapterAvailable = computed(\n () => this._resolvedUploader() !== undefined,\n );\n\n /** Current editor-scoped validation options. */\n get options(): Signal<MlvEditorImageUploadOptions> {\n return this._connection.imageUploadOptions;\n }\n\n /** Whether this editor can currently begin image uploads. */\n readonly available = computed(\n () =>\n this._connection.editor() !== null &&\n this._resolvedUploader() !== undefined &&\n !this._connection.disabled() &&\n !this._connection.readonly(),\n );\n\n constructor(\n private readonly _connection: MlvEditorImageUploadCoordinatorConnection,\n private readonly _injectedUploader: MlvEditorImageUploader | undefined,\n private readonly _abortRegistry: MlvEditorImageUploadAbortRegistryLike,\n ) {\n super();\n }\n\n /** Observes explicit terminal transitions synchronously. */\n onTerminal(\n listener: (event: MlvEditorImageUploadTerminalEvent) => void,\n ): () => void {\n this._terminalListeners.add(listener);\n return () => this._terminalListeners.delete(listener);\n }\n\n /** Claims live dialog ownership until the returned release is called. */\n claimDialogOwnership(id: string): () => void {\n this._dialogOwners.set(id, (this._dialogOwners.get(id) ?? 0) + 1);\n let released = false;\n return () => {\n if (released) return;\n released = true;\n const owners = this._dialogOwners.get(id) ?? 0;\n if (owners <= 1) this._dialogOwners.delete(id);\n else this._dialogOwners.set(id, owners - 1);\n };\n }\n\n /** Whether a live dialog owns terminal announcements for an upload. */\n hasDialogOwner(id: string): boolean {\n return (\n this._terminalDialogOwners.get(id) ??\n (this._dialogOwners.get(id) ?? 0) > 0\n );\n }\n\n /** Validates and begins one file interaction. */\n start(\n files: readonly File[],\n source: MlvEditorImageUploadSource,\n metadata: UploadMetadata = {},\n ): readonly string[] {\n const editor = this._connection.editor();\n const uploader = this._resolvedUploader();\n if (\n !editor ||\n !uploader ||\n this._connection.disabled() ||\n this._connection.readonly()\n ) {\n return [];\n }\n\n const options = this._validateOptions(\n this._connection.imageUploadOptions(),\n );\n if (!options) {\n for (const file of files) {\n this._emitValidationFailure(\n file,\n source,\n 'The image upload configuration is invalid.',\n );\n }\n return [];\n }\n\n const ids: string[] = [];\n const acceptedInteractionFiles = files.slice(0, options.maxFiles);\n const excessFiles = files.slice(options.maxFiles);\n\n for (const file of excessFiles) {\n this._emitValidationFailure(\n file,\n source,\n `Only ${options.maxFiles} image file(s) may be uploaded at once.`,\n );\n }\n\n for (const file of acceptedInteractionFiles) {\n const validationMessage = this._validateFile(file, options);\n if (validationMessage) {\n this._emitValidationFailure(file, source, validationMessage);\n continue;\n }\n\n const id = nextStableUploadId();\n const state: UploadState = {\n id,\n file,\n source,\n generation: 0,\n status: 'uploading',\n ...metadata,\n };\n this._states.set(id, state);\n this._pending.update((pending) => [\n ...pending,\n this._toPendingUpload(state, 0),\n ]);\n ids.push(id);\n\n if (!this._insertPlaceholder(editor, state)) {\n this._fail(\n state,\n state.generation,\n createError(\n 'upload-result',\n 'The editor cannot create an image upload placeholder.',\n ),\n );\n continue;\n }\n\n this._beginRequest(state, uploader);\n }\n\n return ids;\n }\n\n /** Cancels one active request and emits its cancellation event once. */\n cancel(id: string): void {\n const state = this._states.get(id);\n if (!state || state.status !== 'uploading' || !state.controller) return;\n this._terminate(state, 'cancelled');\n }\n\n /** Retries one recoverable failure with a fresh request generation. */\n retry(id: string): void {\n const state = this._states.get(id);\n const editor = this._connection.editor();\n const uploader = this._resolvedUploader();\n if (\n !state ||\n state.status !== 'failed' ||\n !editor ||\n !uploader ||\n this._connection.disabled() ||\n this._connection.readonly()\n ) {\n return;\n }\n\n state.status = 'uploading';\n this._replacePending(this._toPendingUpload(state, 0));\n\n const placeholderReset = this._updatePlaceholder(editor, state.id, 0);\n if (!placeholderReset && !this._insertPlaceholder(editor, state)) {\n this._fail(\n state,\n state.generation,\n createError(\n 'upload-result',\n 'The editor cannot restore the image upload placeholder.',\n ),\n );\n return;\n }\n\n this._beginRequest(state, uploader);\n }\n\n /** Removes one failed item without emitting a cancellation event. */\n remove(id: string): void {\n const state = this._states.get(id);\n if (!state || state.status !== 'failed') return;\n this._states.delete(id);\n this._removePending(id);\n this._removePlaceholder(this._connection.editor(), id);\n this._emitTerminal({ id, reason: 'removed' });\n }\n\n /** @private Validates and normalizes host upload configuration. */\n private _validateOptions(\n options: MlvEditorImageUploadOptions,\n ): ValidatedUploadOptions | undefined {\n const accept = Array.isArray(options?.accept)\n ? options.accept.map((value) =>\n typeof value === 'string' ? value.trim().toLowerCase() : '',\n )\n : [];\n if (\n accept.length === 0 ||\n accept.some((value) => !value || !isMimePattern(value)) ||\n !Number.isFinite(options?.maxSize) ||\n options.maxSize < 0 ||\n !Number.isInteger(options?.maxFiles) ||\n options.maxFiles < 1 ||\n (options.urlPolicy !== undefined &&\n typeof options.urlPolicy !== 'function')\n ) {\n return undefined;\n }\n\n return {\n accept,\n maxSize: options.maxSize,\n maxFiles: options.maxFiles,\n urlPolicy: options.urlPolicy ?? mlvEditorDefaultImageUrlPolicy,\n };\n }\n\n /** @private Returns the user-facing validation failure for one file. */\n private _validateFile(\n file: File,\n options: ValidatedUploadOptions,\n ): string | undefined {\n const mimeType =\n typeof file?.type === 'string' ? file.type.trim().toLowerCase() : '';\n const mimeAccepted = options.accept.some((pattern) => {\n if (pattern === '*/*') return true;\n if (!mimeType) return false;\n if (pattern.endsWith('/*')) {\n return mimeType.startsWith(`${pattern.slice(0, -1)}`);\n }\n return pattern === mimeType;\n });\n if (!mimeAccepted) {\n return 'The selected file type is not accepted for image upload.';\n }\n if (\n !Number.isFinite(file?.size) ||\n file.size < 0 ||\n file.size > options.maxSize\n ) {\n return `The selected image exceeds the ${options.maxSize}-byte size limit.`;\n }\n return undefined;\n }\n\n /** @private Emits a validation failure without creating upload state. */\n private _emitValidationFailure(\n file: File,\n source: MlvEditorImageUploadSource,\n message: string,\n ): void {\n const error = createError('upload-validation', message);\n this._connection.emitFailure({ file, source, error });\n this._connection.emitError(error);\n }\n\n /** @private Creates a fresh controller and invokes the resolved adapter. */\n private _beginRequest(\n state: UploadState,\n uploader: MlvEditorImageUploader,\n ): void {\n state.generation += 1;\n const generation = state.generation;\n const controller = new AbortController();\n state.controller = controller;\n state.unregister = this._abortRegistry.register(() =>\n this._terminateForLifecycle(state.id, generation),\n );\n\n let request: Promise<MlvEditorImageUploadResult>;\n try {\n request = uploader.upload(state.file, {\n source: state.source,\n signal: controller.signal,\n reportProgress: (progress) =>\n this._reportProgress(state.id, generation, progress),\n });\n } catch (cause: unknown) {\n this._fail(\n state,\n generation,\n createError(\n 'upload-transport',\n 'The image upload adapter failed before starting the request.',\n cause,\n ),\n );\n return;\n }\n\n Promise.resolve(request).then(\n (result) => this._complete(state.id, generation, result),\n (cause: unknown) =>\n this._fail(\n state,\n generation,\n createError(\n 'upload-transport',\n 'The image upload request failed.',\n cause,\n ),\n ),\n );\n }\n\n /** @private Applies finite progress only to the current request generation. */\n private _reportProgress(\n id: string,\n generation: number,\n progress: number,\n ): void {\n const state = this._currentActiveState(id, generation);\n if (!state || !Number.isFinite(progress)) return;\n const normalizedProgress = Math.max(0, Math.min(100, progress));\n this._replacePending(this._toPendingUpload(state, normalizedProgress));\n this._updatePlaceholder(this._connection.editor(), id, normalizedProgress);\n }\n\n /** @private Validates a result and atomically replaces its mapped placeholder. */\n private _complete(id: string, generation: number, candidate: unknown): void {\n const state = this._currentActiveState(id, generation);\n if (!state) return;\n const options = this._validateOptions(\n this._connection.imageUploadOptions(),\n );\n const result = options\n ? this._validateResult(candidate, state, options)\n : undefined;\n if (!result) {\n this._fail(\n state,\n generation,\n createError(\n 'upload-result',\n 'The image uploader returned invalid or unsafe image metadata.',\n candidate,\n ),\n );\n return;\n }\n\n const editor = this._connection.editor();\n if (!editor || !this._replacePlaceholder(editor, id, result)) {\n this._fail(\n state,\n generation,\n createError(\n 'upload-result',\n 'The editor cannot atomically insert the uploaded image.',\n ),\n );\n return;\n }\n\n this._endActiveRequest(state);\n this._states.delete(id);\n this._removePending(id);\n this._connection.emitSuccess({\n file: state.file,\n source: state.source,\n result,\n });\n this._emitTerminal({ id, reason: 'success' });\n }\n\n /** @private Returns normalized metadata only when the complete result is valid. */\n private _validateResult(\n candidate: unknown,\n state: UploadState,\n options: ValidatedUploadOptions,\n ): MlvEditorImageUploadResult | undefined {\n if (\n !candidate ||\n typeof candidate !== 'object' ||\n Array.isArray(candidate)\n ) {\n return undefined;\n }\n const value = candidate as Record<string, unknown>;\n if (typeof value['src'] !== 'string') return undefined;\n const src = value['src'].trim();\n if (!src) return undefined;\n if (\n (value['alt'] !== undefined && typeof value['alt'] !== 'string') ||\n (value['title'] !== undefined && typeof value['title'] !== 'string') ||\n !this._isOptionalPositiveNumber(value['width']) ||\n !this._isOptionalPositiveNumber(value['height'])\n ) {\n return undefined;\n }\n\n try {\n if (!options.urlPolicy(src)) return undefined;\n } catch {\n return undefined;\n }\n\n const alt = typeof value['alt'] === 'string' ? value['alt'] : state.alt;\n const title =\n typeof value['title'] === 'string' ? value['title'] : state.title;\n return {\n src,\n ...(alt === undefined ? {} : { alt }),\n ...(title === undefined ? {} : { title }),\n ...(typeof value['width'] === 'number' ? { width: value['width'] } : {}),\n ...(typeof value['height'] === 'number'\n ? { height: value['height'] }\n : {}),\n };\n }\n\n /** @private Whether an optional dimension is absent or finite and positive. */\n private _isOptionalPositiveNumber(value: unknown): boolean {\n return (\n value === undefined ||\n (typeof value === 'number' && Number.isFinite(value) && value > 0)\n );\n }\n\n /** @private Converts a current request failure into retryable pending state. */\n private _fail(\n state: UploadState,\n generation: number,\n error: MlvEditorError,\n ): void {\n if (\n this._states.get(state.id) !== state ||\n state.generation !== generation ||\n state.status !== 'uploading'\n ) {\n return;\n }\n this._endActiveRequest(state);\n state.status = 'failed';\n this._replacePending(this._toPendingUpload(state, 0, error));\n this._connection.emitFailure({\n file: state.file,\n source: state.source,\n error,\n });\n this._connection.emitError(error);\n }\n\n /** @private Stops explicit cancellation after first invalidating its generation. */\n private _terminate(\n state: UploadState,\n reason: Extract<\n MlvEditorImageUploadTerminalEvent['reason'],\n 'cancelled' | 'lifecycle-abort'\n >,\n ): void {\n state.generation += 1;\n this._endActiveRequest(state, true);\n this._states.delete(state.id);\n this._removePending(state.id);\n this._removePlaceholder(this._connection.editor(), state.id);\n if (reason === 'cancelled') {\n this._connection.emitCancelled({\n file: state.file,\n source: state.source,\n });\n }\n this._emitTerminal({ id: state.id, reason });\n }\n\n /** @private Stops a registry-owned request without inventing a cancel event. */\n private _terminateForLifecycle(id: string, generation: number): void {\n const state = this._currentActiveState(id, generation);\n if (!state) return;\n this._terminate(state, 'lifecycle-abort');\n }\n\n /** @private Delivers one terminal reason to current UI observers. */\n private _emitTerminal(event: MlvEditorImageUploadTerminalEvent): void {\n this._terminalDialogOwners.set(\n event.id,\n (this._dialogOwners.get(event.id) ?? 0) > 0,\n );\n try {\n for (const listener of [...this._terminalListeners]) listener(event);\n } finally {\n this._terminalDialogOwners.delete(event.id);\n }\n }\n\n /** @private Unregisters and optionally aborts one controller. */\n private _endActiveRequest(state: UploadState, abort = false): void {\n const unregister = state.unregister;\n const controller = state.controller;\n state.unregister = undefined;\n state.controller = undefined;\n unregister?.();\n if (abort && controller && !controller.signal.aborted) controller.abort();\n }\n\n /** @private Gets a live generation and rejects cancelled or superseded work. */\n private _currentActiveState(\n id: string,\n generation: number,\n ): UploadState | undefined {\n const state = this._states.get(id);\n return state &&\n state.generation === generation &&\n state.status === 'uploading' &&\n state.controller &&\n !state.controller.signal.aborted\n ? state\n : undefined;\n }\n\n /** @private Builds the exact public pending shape. */\n private _toPendingUpload(\n state: UploadState,\n progress: number,\n error?: MlvEditorError,\n ): MlvEditorPendingUpload {\n return {\n id: state.id,\n file: state.file,\n source: state.source,\n progress,\n status: state.status,\n ...(error === undefined ? {} : { error }),\n };\n }\n\n /** @private Replaces one immutable public pending entry. */\n private _replacePending(upload: MlvEditorPendingUpload): void {\n this._pending.update((pending) =>\n pending.map((candidate) =>\n candidate.id === upload.id ? upload : candidate,\n ),\n );\n }\n\n /** @private Removes one public pending entry. */\n private _removePending(id: string): void {\n this._pending.update((pending) =>\n pending.filter((candidate) => candidate.id !== id),\n );\n }\n\n /** @private Inserts a non-serializable placeholder before invoking an adapter. */\n private _insertPlaceholder(editor: Editor, state: UploadState): boolean {\n try {\n return (\n typeof editor.commands.insertUploadPlaceholder === 'function' &&\n editor.commands.insertUploadPlaceholder({\n id: state.id,\n ...(state.position === undefined ? {} : { position: state.position }),\n progress: 0,\n })\n );\n } catch {\n return false;\n }\n }\n\n /** @private Updates one current placeholder without mutating the document. */\n private _updatePlaceholder(\n editor: Editor | null,\n id: string,\n progress: number,\n ): boolean {\n try {\n return (\n !!editor &&\n typeof editor.commands.updateUploadPlaceholder === 'function' &&\n editor.commands.updateUploadPlaceholder({ id, progress })\n );\n } catch {\n return false;\n }\n }\n\n /** @private Removes one placeholder when its extension remains available. */\n private _removePlaceholder(editor: Editor | null, id: string): void {\n try {\n if (typeof editor?.commands.removeUploadPlaceholder === 'function') {\n editor.commands.removeUploadPlaceholder({ id });\n }\n } catch {\n // A missing custom extension must not prevent coordinator cleanup.\n }\n }\n\n /** @private Atomically inserts an image at the placeholder's mapped position. */\n private _replacePlaceholder(\n editor: Editor,\n id: string,\n result: MlvEditorImageUploadResult,\n ): boolean {\n try {\n return (\n typeof editor.commands.replaceUploadPlaceholder === 'function' &&\n editor.commands.replaceUploadPlaceholder({\n id,\n attributes: result,\n })\n );\n } catch {\n return false;\n }\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"malva-ui-editor.js","sourceRoot":"","sources":["../../../../libs/editor/src/malva-ui-editor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,SAAS,CAAC","sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"]}
|
package/index.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
export * from './lib/editor.tokens';
|
|
2
|
-
export * from './lib/editor.types';
|
|
3
|
-
export * from './lib/ai/editor-ai-actions';
|
|
4
|
-
export { MLV_EDITOR_AI_CONTEXT, MlvEditorAiContext, } from './lib/ai/editor-ai-context';
|
|
5
|
-
export type { MlvEditorAiStatus, MlvEditorAiTransformOptions, } from './lib/ai/editor-ai-context';
|
|
6
|
-
export * from './lib/ai/editor-ai-menu';
|
|
7
|
-
export * from './lib/ai/editor-ai-review-bar';
|
|
8
|
-
export * from './lib/ai/editor-ai-stream';
|
|
9
|
-
export { applyMlvEditorAiSuggestions, MLV_EDITOR_AI_SUGGESTION_CURRENT_CLASS, MLV_EDITOR_AI_SUGGESTION_DELETE_CLASS, MLV_EDITOR_AI_SUGGESTION_INSERT_CLASS, } from './lib/ai/editor-ai-suggestions';
|
|
10
|
-
export type { MlvEditorAiSuggestion, MlvEditorAiSuggestionKind, MlvEditorAiSuggestionRange, MlvEditorAiSuggestionsOptions, MlvEditorAiSuggestionsSession, } from './lib/ai/editor-ai-suggestions';
|
|
11
|
-
export * from './lib/ai/editor-ai.tokens';
|
|
12
|
-
export * from './lib/ai/editor-ai.types';
|
|
13
|
-
export { MLV_EDITOR_TOOLBAR_CONTEXT } from './lib/editor-toolbar-context';
|
|
14
|
-
export type { MlvEditorToolbarContext } from './lib/editor-toolbar-context';
|
|
15
|
-
export * from './lib/editor/editor';
|
|
16
|
-
export * from './lib/extensions/editor-block-handle';
|
|
17
|
-
export * from './lib/extensions/editor-extensions';
|
|
18
|
-
export * from './lib/extensions/editor-upload-placeholder';
|
|
19
|
-
export * from './lib/status/editor-status';
|
|
20
|
-
export * from './lib/toolbar/editor-alignment';
|
|
21
|
-
export * from './lib/toolbar/editor-block-insert';
|
|
22
|
-
export * from './lib/toolbar/editor-heading';
|
|
23
|
-
export * from './lib/toolbar/editor-highlight';
|
|
24
|
-
export * from './lib/toolbar/editor-inline-marks';
|
|
25
|
-
export * from './lib/toolbar/editor-list';
|
|
26
|
-
export * from './lib/toolbar/editor-link';
|
|
27
|
-
export { MlvEditorImageUpload } from './lib/toolbar/editor-image-upload';
|
|
28
|
-
export * from './lib/toolbar/editor-table';
|
|
29
|
-
export * from './lib/toolbar/editor-toolbar.defs';
|
|
30
|
-
export { MlvEditorToolbar } from './lib/toolbar/editor-toolbar';
|
|
31
|
-
export * from './lib/toolbar/editor-toolbar-widget';
|
|
32
|
-
export * from './lib/toolbar/editor-undo-redo';
|
|
33
|
-
export * from './lib/toolbar/editor-zoom';
|
|
34
|
-
export * from './lib/toolbar/editor-text-color';
|
|
35
|
-
export type { Editor, EditorOptions, Extension, Extensions, } from '@tiptap/core';
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { MlvEditorI18n } from '@malva-ui/i18n';
|
|
2
|
-
import type { MlvEditorAiAction } from './editor-ai.types';
|
|
3
|
-
/**
|
|
4
|
-
* Builds the seven built-in AI menu actions, labelled from the supplied
|
|
5
|
-
* editor copy.
|
|
6
|
-
*
|
|
7
|
-
* This is the exact list `MlvEditorAiMenu` renders when its `actions` input is
|
|
8
|
-
* omitted, so a host that wants to *extend* rather than replace the built-ins
|
|
9
|
-
* spreads the result instead of retyping it:
|
|
10
|
-
*
|
|
11
|
-
* ```ts
|
|
12
|
-
* private readonly _copy = inject(MLV_EDITOR_I18N, { optional: true });
|
|
13
|
-
*
|
|
14
|
-
* readonly actions = computed<readonly MlvEditorAiAction[]>(() => [
|
|
15
|
-
* ...mlvEditorAiDefaultActions(this._copy?.()),
|
|
16
|
-
* { kind: 'legal-review', label: 'Legal review', output: 'review' },
|
|
17
|
-
* ]);
|
|
18
|
-
* ```
|
|
19
|
-
*
|
|
20
|
-
* The custom-prompt entry is not part of this list: the menu renders it
|
|
21
|
-
* separately and it is dropped with `showCustomPrompt="false"`.
|
|
22
|
-
*
|
|
23
|
-
* @param copy Resolved editor copy, typically `inject(MLV_EDITOR_I18N)()`.
|
|
24
|
-
* `null`/`undefined`, or any missing key, falls back to the English label.
|
|
25
|
-
* @returns A fresh array of the seven built-in actions, in menu order.
|
|
26
|
-
*/
|
|
27
|
-
export declare function mlvEditorAiDefaultActions(copy: MlvEditorI18n | null | undefined): readonly MlvEditorAiAction[];
|