@flogeez/angular-tiptap-editor 3.5.4 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +2 -2
  3. package/esm2022/lib/components/bubble-menus/color/ate-color-bubble-menu.component.mjs +12 -2
  4. package/esm2022/lib/components/editor/angular-tiptap-editor.component.mjs +99 -24
  5. package/esm2022/lib/config/ate-editor.config.mjs +3 -1
  6. package/esm2022/lib/config/ate-slash-commands.config.mjs +2 -1
  7. package/esm2022/lib/extensions/ate-block-shortcuts.extension.mjs +100 -0
  8. package/esm2022/lib/extensions/ate-pasted-image.extension.mjs +7 -3
  9. package/esm2022/lib/i18n/ateTranslationsModel.mjs +1 -1
  10. package/esm2022/lib/i18n/es_ateTranslations.mjs +230 -0
  11. package/esm2022/lib/i18n/pt_ateTranslations.mjs +220 -0
  12. package/esm2022/lib/models/ate-editor-config.model.mjs +1 -1
  13. package/esm2022/lib/models/ate-editor-ref.mjs +3 -3
  14. package/esm2022/lib/models/ate-image.model.mjs +12 -2
  15. package/esm2022/lib/services/ate-editor-commands.service.mjs +6 -3
  16. package/esm2022/lib/services/ate-i18n.service.mjs +19 -9
  17. package/esm2022/lib/services/ate-image.service.mjs +18 -11
  18. package/esm2022/lib/utils/ate-equality.utils.mjs +14 -0
  19. package/esm2022/lib/utils/ate-extensions.utils.mjs +79 -0
  20. package/esm2022/public-api.mjs +2 -1
  21. package/fesm2022/flogeez-angular-tiptap-editor.mjs +808 -50
  22. package/fesm2022/flogeez-angular-tiptap-editor.mjs.map +1 -1
  23. package/lib/components/bubble-menus/color/ate-color-bubble-menu.component.d.ts +1 -0
  24. package/lib/components/editor/angular-tiptap-editor.component.d.ts +12 -2
  25. package/lib/config/ate-slash-commands.config.d.ts +2 -0
  26. package/lib/extensions/ate-block-shortcuts.extension.d.ts +32 -0
  27. package/lib/i18n/ateTranslationsModel.d.ts +1 -1
  28. package/lib/i18n/es_ateTranslations.d.ts +2 -0
  29. package/lib/i18n/pt_ateTranslations.d.ts +2 -0
  30. package/lib/models/ate-editor-config.model.d.ts +8 -0
  31. package/lib/models/ate-image.model.d.ts +30 -2
  32. package/lib/services/ate-editor-commands.service.d.ts +2 -1
  33. package/lib/services/ate-i18n.service.d.ts +3 -1
  34. package/lib/services/ate-image.service.d.ts +5 -3
  35. package/lib/utils/ate-equality.utils.d.ts +2 -0
  36. package/lib/utils/ate-extensions.utils.d.ts +13 -0
  37. package/package.json +1 -1
  38. package/public-api.d.ts +1 -0
@@ -55,6 +55,7 @@ export declare class AteColorBubbleMenuComponent extends AteBaseSubBubbleMenu {
55
55
  activeMode: import("@angular/core").WritableSignal<"highlight" | "text">;
56
56
  protected onStateChange(): void;
57
57
  shouldShow(): boolean;
58
+ private findColoredElementInContent;
58
59
  getSelectionRect(): DOMRect;
59
60
  protected onTippyShow(_instance: TippyInstance): void;
60
61
  protected onTippyHide(_instance: TippyInstance): void;
@@ -1,5 +1,6 @@
1
1
  import { ElementRef, OnDestroy, AfterViewInit } from "@angular/core";
2
2
  import { Editor, EditorOptions, Extension, Node, Mark, JSONContent } from "@tiptap/core";
3
+ import { type StarterKitOptions } from "@tiptap/starter-kit";
3
4
  import { Node as PMNode } from "@tiptap/pm/model";
4
5
  import { AteCustomSlashCommands } from "../../models/ate-slash-command.model";
5
6
  import { AteI18nService } from "../../services/ate-i18n.service";
@@ -9,7 +10,7 @@ import { AteSlashCommandsConfig } from "../../config/ate-slash-commands.config";
9
10
  import { AteToolbarConfig } from "../../models/ate-toolbar.model";
10
11
  import { AteBubbleMenuConfig } from "../../models/ate-bubble-menu.model";
11
12
  import { AteEditorConfig, AteAngularNode, AteBlockControlsMode, AteAutofocusMode } from "../../models/ate-editor-config.model";
12
- import { AteImageUploadHandler, AteImageUploadOptions, AteImageUploadResult, AtePastedImageRejection } from "../../models/ate-image.model";
13
+ import { AteImageUploadHandler, AteImageUploadOptions, AteImageUploadResult, AteImageRejection, AtePastedImageRejection } from "../../models/ate-image.model";
13
14
  import { SupportedLocale } from "../../i18n/ateTranslationsModel";
14
15
  import * as i0 from "@angular/core";
15
16
  import * as i1 from "../../directives/ate-noop-value-accessor.directive";
@@ -39,6 +40,7 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
39
40
  maxCharacters: import("@angular/core").InputSignal<number | undefined>;
40
41
  enableOfficePaste: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
41
42
  enableSlashCommands: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
43
+ enableBlockShortcuts: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
42
44
  slashCommands: import("@angular/core").InputSignal<AteSlashCommandsConfig | undefined>;
43
45
  customSlashCommands: import("@angular/core").InputSignal<AteCustomSlashCommands | undefined>;
44
46
  blockControls: import("@angular/core").InputSignal<AteBlockControlsMode | undefined>;
@@ -51,6 +53,7 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
51
53
  spellcheck: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
52
54
  tiptapExtensions: import("@angular/core").InputSignal<(Mark<any, any> | Node<any, any> | Extension<any, any>)[] | undefined>;
53
55
  tiptapOptions: import("@angular/core").InputSignal<Partial<EditorOptions> | undefined>;
56
+ starterKitOptions: import("@angular/core").InputSignal<Partial<StarterKitOptions> | undefined>;
54
57
  showBubbleMenu: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
55
58
  bubbleMenu: import("@angular/core").InputSignal<Partial<AteBubbleMenuConfig> | undefined>;
56
59
  showImageBubbleMenu: import("@angular/core").InputSignalWithTransform<boolean | undefined, unknown>;
@@ -106,6 +109,8 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
106
109
  }>;
107
110
  editableChange: import("@angular/core").OutputEmitterRef<boolean>;
108
111
  imageUploaded: import("@angular/core").OutputEmitterRef<AteImageUploadResult>;
112
+ /** Emitted when an image file is rejected (disallowed type, too large or failed upload) */
113
+ imageRejected: import("@angular/core").OutputEmitterRef<AteImageRejection>;
109
114
  /** Emitted when an image from pasted HTML is dropped (unreachable source or failed upload) */
110
115
  pastedImageRejected: import("@angular/core").OutputEmitterRef<AtePastedImageRejection>;
111
116
  editorElement: import("@angular/core").Signal<ElementRef<any>>;
@@ -158,12 +163,14 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
158
163
  readonly finalSlashCommandsConfig: import("@angular/core").Signal<AteCustomSlashCommands>;
159
164
  readonly finalAutofocus: import("@angular/core").Signal<AteAutofocusMode | undefined>;
160
165
  readonly finalMaxCharacters: import("@angular/core").Signal<number | undefined>;
166
+ readonly finalEnableBlockShortcuts: import("@angular/core").Signal<boolean>;
161
167
  readonly finalBlockControls: import("@angular/core").Signal<AteBlockControlsMode>;
162
168
  readonly finalShowCharacterCount: import("@angular/core").Signal<boolean>;
163
169
  readonly finalShowWordCount: import("@angular/core").Signal<boolean>;
164
170
  readonly finalLocale: import("@angular/core").Signal<SupportedLocale>;
165
171
  readonly finalTiptapExtensions: import("@angular/core").Signal<(Mark<any, any> | Node<any, any> | Extension<any, any>)[]>;
166
172
  readonly finalTiptapOptions: import("@angular/core").Signal<Partial<EditorOptions>>;
173
+ readonly finalStarterKitOptions: import("@angular/core").Signal<Partial<StarterKitOptions>>;
167
174
  readonly finalStateCalculators: import("@angular/core").Signal<AteStateCalculator[]>;
168
175
  readonly finalAngularNodesConfig: import("@angular/core").Signal<AteAngularNode[]>;
169
176
  readonly finalImageUploadConfig: import("@angular/core").Signal<{
@@ -182,6 +189,7 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
182
189
  readonly editorCommandsService: AteEditorCommandsService;
183
190
  readonly editorState: import("@angular/core").Signal<import("../../models/ate-editor-state.model").AteEditorStateSnapshot>;
184
191
  private editorRegistry;
192
+ private readonly warnedExtensionOverrides;
185
193
  private iconRegistry;
186
194
  private _registeredId;
187
195
  readonly registeredId: import("@angular/core").Signal<string | null>;
@@ -216,6 +224,7 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
216
224
  showCellMenu?: boolean | undefined;
217
225
  downloadImage?: boolean | undefined;
218
226
  enableSlashCommands?: boolean | undefined;
227
+ enableBlockShortcuts?: boolean | undefined;
219
228
  maxCharacters?: number | undefined;
220
229
  blockControls?: AteBlockControlsMode | undefined;
221
230
  toolbar?: AteToolbarConfig | undefined;
@@ -230,6 +239,7 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
230
239
  angularNodes?: AteAngularNode[] | undefined;
231
240
  tiptapExtensions?: (Mark<any, any> | Node<any, any> | Extension<any, any>)[] | undefined;
232
241
  tiptapOptions?: Partial<EditorOptions> | undefined;
242
+ starterKitOptions?: Partial<StarterKitOptions> | undefined;
233
243
  stateCalculators?: AteStateCalculator[] | undefined;
234
244
  customSlashCommands?: AteCustomSlashCommands | undefined;
235
245
  }>;
@@ -253,5 +263,5 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
253
263
  private setupFormControlSubscription;
254
264
  onEditorClick(event: Event): void;
255
265
  static ɵfac: i0.ɵɵFactoryDeclaration<AngularTiptapEditorComponent, never>;
256
- static ɵcmp: i0.ɵɵComponentDeclaration<AngularTiptapEditorComponent, "angular-tiptap-editor", never, { "config": { "alias": "config"; "required": false; "isSignal": true; }; "content": { "alias": "content"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "minHeight": { "alias": "minHeight"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "fillContainer": { "alias": "fillContainer"; "required": false; "isSignal": true; }; "showToolbar": { "alias": "showToolbar"; "required": false; "isSignal": true; }; "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "showCharacterCount": { "alias": "showCharacterCount"; "required": false; "isSignal": true; }; "showWordCount": { "alias": "showWordCount"; "required": false; "isSignal": true; }; "maxCharacters": { "alias": "maxCharacters"; "required": false; "isSignal": true; }; "enableOfficePaste": { "alias": "enableOfficePaste"; "required": false; "isSignal": true; }; "enableSlashCommands": { "alias": "enableSlashCommands"; "required": false; "isSignal": true; }; "slashCommands": { "alias": "slashCommands"; "required": false; "isSignal": true; }; "customSlashCommands": { "alias": "customSlashCommands"; "required": false; "isSignal": true; }; "blockControls": { "alias": "blockControls"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "autofocus": { "alias": "autofocus"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "seamless": { "alias": "seamless"; "required": false; "isSignal": true; }; "floatingToolbar": { "alias": "floatingToolbar"; "required": false; "isSignal": true; }; "showEditToggle": { "alias": "showEditToggle"; "required": false; "isSignal": true; }; "spellcheck": { "alias": "spellcheck"; "required": false; "isSignal": true; }; "tiptapExtensions": { "alias": "tiptapExtensions"; "required": false; "isSignal": true; }; "tiptapOptions": { "alias": "tiptapOptions"; "required": false; "isSignal": true; }; "showBubbleMenu": { "alias": "showBubbleMenu"; "required": false; "isSignal": true; }; "bubbleMenu": { "alias": "bubbleMenu"; "required": false; "isSignal": true; }; "showImageBubbleMenu": { "alias": "showImageBubbleMenu"; "required": false; "isSignal": true; }; "imageBubbleMenu": { "alias": "imageBubbleMenu"; "required": false; "isSignal": true; }; "toolbar": { "alias": "toolbar"; "required": false; "isSignal": true; }; "showTableBubbleMenu": { "alias": "showTableBubbleMenu"; "required": false; "isSignal": true; }; "tableBubbleMenu": { "alias": "tableBubbleMenu"; "required": false; "isSignal": true; }; "showCellBubbleMenu": { "alias": "showCellBubbleMenu"; "required": false; "isSignal": true; }; "cellBubbleMenu": { "alias": "cellBubbleMenu"; "required": false; "isSignal": true; }; "stateCalculators": { "alias": "stateCalculators"; "required": false; "isSignal": true; }; "imageUpload": { "alias": "imageUpload"; "required": false; "isSignal": true; }; "imageUploadHandler": { "alias": "imageUploadHandler"; "required": false; "isSignal": true; }; "editorId": { "alias": "editorId"; "required": false; "isSignal": true; }; }, { "contentChange": "contentChange"; "editorCreated": "editorCreated"; "editorUpdate": "editorUpdate"; "editorFocus": "editorFocus"; "editorBlur": "editorBlur"; "editableChange": "editableChange"; "imageUploaded": "imageUploaded"; "pastedImageRejected": "pastedImageRejected"; }, never, never, true, [{ directive: typeof i1.AteNoopValueAccessorDirective; inputs: {}; outputs: {}; }]>;
266
+ static ɵcmp: i0.ɵɵComponentDeclaration<AngularTiptapEditorComponent, "angular-tiptap-editor", never, { "config": { "alias": "config"; "required": false; "isSignal": true; }; "content": { "alias": "content"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "minHeight": { "alias": "minHeight"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "fillContainer": { "alias": "fillContainer"; "required": false; "isSignal": true; }; "showToolbar": { "alias": "showToolbar"; "required": false; "isSignal": true; }; "showFooter": { "alias": "showFooter"; "required": false; "isSignal": true; }; "showCharacterCount": { "alias": "showCharacterCount"; "required": false; "isSignal": true; }; "showWordCount": { "alias": "showWordCount"; "required": false; "isSignal": true; }; "maxCharacters": { "alias": "maxCharacters"; "required": false; "isSignal": true; }; "enableOfficePaste": { "alias": "enableOfficePaste"; "required": false; "isSignal": true; }; "enableSlashCommands": { "alias": "enableSlashCommands"; "required": false; "isSignal": true; }; "enableBlockShortcuts": { "alias": "enableBlockShortcuts"; "required": false; "isSignal": true; }; "slashCommands": { "alias": "slashCommands"; "required": false; "isSignal": true; }; "customSlashCommands": { "alias": "customSlashCommands"; "required": false; "isSignal": true; }; "blockControls": { "alias": "blockControls"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "autofocus": { "alias": "autofocus"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "seamless": { "alias": "seamless"; "required": false; "isSignal": true; }; "floatingToolbar": { "alias": "floatingToolbar"; "required": false; "isSignal": true; }; "showEditToggle": { "alias": "showEditToggle"; "required": false; "isSignal": true; }; "spellcheck": { "alias": "spellcheck"; "required": false; "isSignal": true; }; "tiptapExtensions": { "alias": "tiptapExtensions"; "required": false; "isSignal": true; }; "tiptapOptions": { "alias": "tiptapOptions"; "required": false; "isSignal": true; }; "starterKitOptions": { "alias": "starterKitOptions"; "required": false; "isSignal": true; }; "showBubbleMenu": { "alias": "showBubbleMenu"; "required": false; "isSignal": true; }; "bubbleMenu": { "alias": "bubbleMenu"; "required": false; "isSignal": true; }; "showImageBubbleMenu": { "alias": "showImageBubbleMenu"; "required": false; "isSignal": true; }; "imageBubbleMenu": { "alias": "imageBubbleMenu"; "required": false; "isSignal": true; }; "toolbar": { "alias": "toolbar"; "required": false; "isSignal": true; }; "showTableBubbleMenu": { "alias": "showTableBubbleMenu"; "required": false; "isSignal": true; }; "tableBubbleMenu": { "alias": "tableBubbleMenu"; "required": false; "isSignal": true; }; "showCellBubbleMenu": { "alias": "showCellBubbleMenu"; "required": false; "isSignal": true; }; "cellBubbleMenu": { "alias": "cellBubbleMenu"; "required": false; "isSignal": true; }; "stateCalculators": { "alias": "stateCalculators"; "required": false; "isSignal": true; }; "imageUpload": { "alias": "imageUpload"; "required": false; "isSignal": true; }; "imageUploadHandler": { "alias": "imageUploadHandler"; "required": false; "isSignal": true; }; "editorId": { "alias": "editorId"; "required": false; "isSignal": true; }; }, { "contentChange": "contentChange"; "editorCreated": "editorCreated"; "editorUpdate": "editorUpdate"; "editorFocus": "editorFocus"; "editorBlur": "editorBlur"; "editableChange": "editableChange"; "imageUploaded": "imageUploaded"; "imageRejected": "imageRejected"; "pastedImageRejected": "pastedImageRejected"; }, never, never, true, [{ directive: typeof i1.AteNoopValueAccessorDirective; inputs: {}; outputs: {}; }]>;
257
267
  }
@@ -28,6 +28,7 @@ export declare function createDefaultSlashCommands(i18n: AteI18nService, command
28
28
  quality?: number;
29
29
  maxWidth?: number;
30
30
  maxHeight?: number;
31
+ maxSize?: number;
31
32
  allowedTypes?: string[];
32
33
  }): AteSlashCommandItem[];
33
34
  /**
@@ -37,5 +38,6 @@ export declare function filterSlashCommands(config: AteSlashCommandsConfig, i18n
37
38
  quality?: number;
38
39
  maxWidth?: number;
39
40
  maxHeight?: number;
41
+ maxSize?: number;
40
42
  allowedTypes?: string[];
41
43
  }): AteSlashCommandItem[];
@@ -0,0 +1,32 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { Node as ProseMirrorNode } from "@tiptap/pm/model";
3
+ import { EditorState, Selection, Transaction } from "@tiptap/pm/state";
4
+ export interface AteBlockShortcutsOptions {
5
+ /** Whether the shortcuts are active. Evaluated on every key press, so it can change at runtime */
6
+ enabled: () => boolean;
7
+ }
8
+ export interface AteBlockAtSelection {
9
+ node: ProseMirrorNode;
10
+ /** Document position right before the block */
11
+ pos: number;
12
+ parent: ProseMirrorNode;
13
+ /** Index of the block among its parent's children */
14
+ index: number;
15
+ }
16
+ /**
17
+ * Resolves the block the shortcuts act on: a selected block node, the list or task item
18
+ * holding the cursor, or else the top-level block holding the cursor.
19
+ */
20
+ export declare function findBlockAtSelection(selection: Selection): AteBlockAtSelection | null;
21
+ /** Inserts a copy of the current block right after it */
22
+ export declare function duplicateBlock(state: EditorState): Transaction | null;
23
+ /** Swaps the current block with its previous sibling, or returns `null` at the first position */
24
+ export declare function moveBlockUp(state: EditorState): Transaction | null;
25
+ /** Swaps the current block with its next sibling, or returns `null` at the last position */
26
+ export declare function moveBlockDown(state: EditorState): Transaction | null;
27
+ /**
28
+ * Notion-style block shortcuts: `Mod+D` duplicates the current block, `Mod+Shift+ArrowUp` and
29
+ * `Mod+Shift+ArrowDown` move it one sibling up or down. Inside a list the block is the list
30
+ * or task item, which moves within its list. At the edges the key event is left alone.
31
+ */
32
+ export declare const AteBlockShortcuts: Extension<AteBlockShortcutsOptions, any>;
@@ -1,4 +1,4 @@
1
- export type SupportedLocale = "en" | "fr" | "de" | (string & {});
1
+ export type SupportedLocale = "en" | "fr" | "de" | "pt" | "es" | (string & {});
2
2
  export interface AteTranslations {
3
3
  toolbar: {
4
4
  bold: string;
@@ -0,0 +1,2 @@
1
+ import { AteTranslations } from "./ateTranslationsModel";
2
+ export declare const SPANISH_TRANSLATIONS: AteTranslations;
@@ -0,0 +1,2 @@
1
+ import { AteTranslations } from "./ateTranslationsModel";
2
+ export declare const PORTUGUESE_TRANSLATIONS: AteTranslations;
@@ -6,6 +6,7 @@ import { AteSlashCommandsConfig } from "../config/ate-slash-commands.config";
6
6
  import { AteImageUploadConfig } from "./ate-image.model";
7
7
  import { AteTocConfig } from "./ate-toc.model";
8
8
  import { Extension, Node, Mark, EditorOptions } from "@tiptap/core";
9
+ import type { StarterKitOptions } from "@tiptap/starter-kit";
9
10
  import { AteStateCalculator } from "./ate-editor-state.model";
10
11
  import { AteCustomSlashCommands } from "./ate-slash-command.model";
11
12
  /**
@@ -70,6 +71,11 @@ export interface AteEditorConfig {
70
71
  downloadImage?: boolean;
71
72
  /** Enable or disable slash commands (/) */
72
73
  enableSlashCommands?: boolean;
74
+ /**
75
+ * Enable the block keyboard shortcuts: `Mod+D` duplicates the current block,
76
+ * `Mod+Shift+ArrowUp` / `Mod+Shift+ArrowDown` move it up or down.
77
+ */
78
+ enableBlockShortcuts?: boolean;
73
79
  /** Maximum number of characters allowed */
74
80
  maxCharacters?: number;
75
81
  /**
@@ -106,6 +112,8 @@ export interface AteEditorConfig {
106
112
  tiptapExtensions?: (Extension | Node | Mark)[];
107
113
  /** Raw TipTap editor options (e.g., enableInputRules, injectCSS, etc.) */
108
114
  tiptapOptions?: Partial<EditorOptions>;
115
+ /** Options for Tiptap's StarterKit, merged over the library defaults (e.g. `{ undoRedo: false }` for collaboration) */
116
+ starterKitOptions?: Partial<StarterKitOptions>;
109
117
  /** Reactive state calculators to extend the editor's live state */
110
118
  stateCalculators?: AteStateCalculator[];
111
119
  /**
@@ -15,13 +15,41 @@ export interface AteImageUploadResult {
15
15
  height?: number;
16
16
  originalSize?: number;
17
17
  }
18
+ /** Why an image failed validation: its type is not in `allowedTypes`, or it exceeds `maxSize` */
19
+ export type AteImageValidationReason = "invalid-type" | "too-large";
20
+ /**
21
+ * Thrown when an image fails validation, before compression and before `imageUploadHandler` runs.
22
+ * `message` is the translated, user-facing text.
23
+ */
24
+ export declare class AteImageValidationError extends Error {
25
+ readonly reason: AteImageValidationReason;
26
+ constructor(reason: AteImageValidationReason, message: string);
27
+ }
28
+ /** Where a rejected image file came from */
29
+ export type AteImageSource = "paste" | "drop" | "picker" | "api";
30
+ /**
31
+ * Emitted when an image file cannot be inserted.
32
+ * - `invalid-type`: the file type is not in `allowedTypes`
33
+ * - `too-large`: the file exceeds `maxSize`
34
+ * - `upload-failed`: processing or `imageUploadHandler` failed
35
+ *
36
+ * `source` is `paste` (clipboard file), `drop`, `picker` (toolbar, slash menu, image bubble menu)
37
+ * or `api` (`uploadImage`).
38
+ */
39
+ export interface AteImageRejection {
40
+ reason: AteImageValidationReason | "upload-failed";
41
+ file: File;
42
+ source: AteImageSource;
43
+ }
18
44
  /**
19
45
  * Emitted when an image found in pasted HTML cannot be kept.
20
46
  * - `unreachable`: the page cannot load its source (e.g. `attachment:`, `file:`, `cid:`, foreign `blob:`, malformed base64)
21
- * - `upload-failed`: a pasted base64 image was rejected by validation or by the upload handler
47
+ * - `invalid-type`: a pasted base64 image has a type that is not in `allowedTypes`
48
+ * - `too-large`: a pasted base64 image exceeds `maxSize`
49
+ * - `upload-failed`: a pasted base64 image was rejected by the upload handler
22
50
  */
23
51
  export interface AtePastedImageRejection {
24
- reason: "unreachable" | "upload-failed";
52
+ reason: "unreachable" | AteImageValidationReason | "upload-failed";
25
53
  src: string;
26
54
  }
27
55
  export interface AteResizeOptions {
@@ -1,7 +1,7 @@
1
1
  import { Editor } from "@tiptap/core";
2
2
  import { AteExportFormat, AteExportOptions } from "./ate-export.service";
3
3
  import { AteEditorStateSnapshot } from "../models/ate-editor-state.model";
4
- import { AteImageUploadHandler, AteImageUploadOptions, AteImageUploadResult } from "../models/ate-image.model";
4
+ import { AteImageUploadHandler, AteImageUploadOptions, AteImageUploadResult, AteImageRejection } from "../models/ate-image.model";
5
5
  import * as i0 from "@angular/core";
6
6
  export declare class AteEditorCommandsService {
7
7
  private imageService;
@@ -20,6 +20,7 @@ export declare class AteEditorCommandsService {
20
20
  readonly uploadProgress: import("@angular/core").Signal<number>;
21
21
  readonly uploadMessage: import("@angular/core").Signal<string>;
22
22
  set onImageUploaded(callback: ((result: AteImageUploadResult) => void) | null);
23
+ set onImageRejected(callback: ((rejection: AteImageRejection) => void) | null);
23
24
  set uploadHandler(handler: AteImageUploadHandler | null);
24
25
  /** Update state (called by TiptapStateExtension) */
25
26
  updateState(state: AteEditorStateSnapshot): void;
@@ -35,7 +35,7 @@ export declare class AteI18nService {
35
35
  table: string;
36
36
  undo: string;
37
37
  redo: string;
38
- clear: string; /** All loaded translations (useful for dynamic key access) */
38
+ clear: string;
39
39
  textColor: string;
40
40
  customColor: string;
41
41
  presets: string;
@@ -206,6 +206,8 @@ export declare class AteI18nService {
206
206
  autoDetectLocale(): void;
207
207
  getSupportedLocales(): SupportedLocale[];
208
208
  addTranslations(locale: string, translations: AteTranslations | Partial<AteTranslations>): void;
209
+ getTranslations(locale: SupportedLocale): AteTranslations | undefined;
210
+ private resolveLocale;
209
211
  private detectBrowserLanguage;
210
212
  getToolbarTitle(key: keyof AteTranslations["toolbar"]): string;
211
213
  getBubbleMenuTitle(key: keyof AteTranslations["bubbleMenu"]): string;
@@ -1,8 +1,9 @@
1
1
  import { Editor } from "@tiptap/core";
2
- import { AteResizeOptions, AteImageUploadOptions, AteImageUploadHandler, AteImageUploadResult, AteImageData } from "../models/ate-image.model";
2
+ import { AteResizeOptions, AteImageUploadOptions, AteImageUploadHandler, AteImageUploadResult, AteImageData, AteImageRejection, AteImageSource, AteImageValidationReason } from "../models/ate-image.model";
3
3
  import * as i0 from "@angular/core";
4
4
  export declare class AteImageService {
5
5
  onImageUploadedCallback?: (result: AteImageUploadResult) => void;
6
+ onImageRejectedCallback?: (rejection: AteImageRejection) => void;
6
7
  /** Signals for image state */
7
8
  selectedImage: import("@angular/core").WritableSignal<AteImageData | null>;
8
9
  isImageSelected: import("@angular/core").Signal<boolean>;
@@ -51,6 +52,7 @@ export declare class AteImageService {
51
52
  }): {
52
53
  valid: boolean;
53
54
  error?: string;
55
+ reason?: AteImageValidationReason;
54
56
  };
55
57
  /** Compress and process image on client side */
56
58
  compressImage(file: File, quality?: number, maxWidth?: number, maxHeight?: number): Promise<AteImageUploadResult>;
@@ -65,7 +67,7 @@ export declare class AteImageService {
65
67
  rehostImage(file: File, options?: AteImageUploadOptions): Promise<string>;
66
68
  private resetUploadState;
67
69
  /** Main entry point for file upload and insertion */
68
- uploadAndInsertImage(editor: Editor, file: File, options?: AteImageUploadOptions): Promise<void>;
70
+ uploadAndInsertImage(editor: Editor, file: File, options?: AteImageUploadOptions, source?: AteImageSource): Promise<void>;
69
71
  /** Trigger an editor transaction to force decoration update */
70
72
  private forceEditorUpdate;
71
73
  /** Generic helper to open file picker and process selection */
@@ -75,7 +77,7 @@ export declare class AteImageService {
75
77
  /** Select file and replace currently selected image */
76
78
  selectAndReplaceImage(editor: Editor, options?: AteImageUploadOptions): Promise<void>;
77
79
  /** Internal helper used by replacement logic */
78
- uploadAndReplaceImage(editor: Editor, file: File, options?: AteImageUploadOptions): Promise<void>;
80
+ uploadAndReplaceImage(editor: Editor, file: File, options?: AteImageUploadOptions, source?: AteImageSource): Promise<void>;
79
81
  /**
80
82
  * Handle image drop event.
81
83
  * Calculates the position from coordinates and sets the selection accordingly.
@@ -0,0 +1,2 @@
1
+ export declare function shallowArrayEqual<T>(a: readonly T[], b: readonly T[]): boolean;
2
+ export declare function shallowObjectEqual<T extends object>(a: T, b: T): boolean;
@@ -0,0 +1,13 @@
1
+ import type { Extension, Mark, Node } from "@tiptap/core";
2
+ import type { StarterKitOptions } from "@tiptap/starter-kit";
3
+ /**
4
+ * Maps the runtime `.name` of each extension bundled in `@tiptap/starter-kit`
5
+ * to its corresponding option key in `StarterKitOptions`.
6
+ */
7
+ export declare const STARTER_KIT_NAME_TO_OPTION: Readonly<Record<string, keyof StarterKitOptions>>;
8
+ /**
9
+ * Describes which parts of Angular Tiptap Editor's UI or state calculators
10
+ * depend on a given built-in or StarterKit extension name.
11
+ */
12
+ export declare const ATE_BUILTIN_UI_DEPENDENCIES: Readonly<Record<string, string>>;
13
+ export declare function getExtensionName(ext: Extension | Node | Mark): string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flogeez/angular-tiptap-editor",
3
- "version": "3.5.4",
3
+ "version": "3.6.0",
4
4
  "description": "A modern, customizable rich-text editor for Angular (18+), built with Tiptap and featuring complete internationalization support",
5
5
  "keywords": [
6
6
  "angular",
package/public-api.d.ts CHANGED
@@ -25,6 +25,7 @@ export * from "./lib/extensions/calculators/ate-structure.calculator";
25
25
  export * from "./lib/extensions/calculators/ate-table.calculator";
26
26
  export { AtePastedImage, classifyPastedImageSrc, dataUriToFile, sanitizePastedImages, type AtePastedImageOptions, type AtePastedImageSource, } from "./lib/extensions/ate-pasted-image.extension";
27
27
  export { AteLinkShortcut, isLinkShortcut, type AteLinkShortcutOptions, } from "./lib/extensions/ate-link-shortcut.extension";
28
+ export { AteBlockShortcuts, type AteBlockShortcutsOptions, } from "./lib/extensions/ate-block-shortcuts.extension";
28
29
  export * from "./lib/models/ate-toolbar.model";
29
30
  export * from "./lib/models/ate-image.model";
30
31
  export * from "./lib/models/ate-bubble-menu.model";