@flogeez/angular-tiptap-editor 3.6.1 → 3.7.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 (44) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +6 -2
  3. package/esm2022/lib/components/bubble-menus/base/ate-base-bubble-menu.mjs +3 -3
  4. package/esm2022/lib/components/bubble-menus/base/ate-base-sub-bubble-menu.mjs +3 -3
  5. package/esm2022/lib/components/color-picker/ate-color-picker.component.mjs +3 -3
  6. package/esm2022/lib/components/editor/angular-tiptap-editor.component.mjs +52 -13
  7. package/esm2022/lib/components/editor/ate-block-controls.component.mjs +3 -3
  8. package/esm2022/lib/components/slash-commands/ate-slash-commands.component.mjs +3 -3
  9. package/esm2022/lib/components/table-of-contents/ate-table-of-contents.component.mjs +3 -3
  10. package/esm2022/lib/components/toolbar/ate-toolbar.component.mjs +3 -3
  11. package/esm2022/lib/config/ate-editor.config.mjs +7 -1
  12. package/esm2022/lib/config/ate-slash-commands.config.mjs +1 -1
  13. package/esm2022/lib/extensions/ate-paste.extension.mjs +125 -0
  14. package/esm2022/lib/extensions/ate-resizable-image.extension.mjs +44 -3
  15. package/esm2022/lib/models/ate-editor-config.model.mjs +1 -1
  16. package/esm2022/lib/models/ate-image.model.mjs +1 -1
  17. package/esm2022/lib/models/ate-paste.model.mjs +2 -0
  18. package/esm2022/lib/services/ate-editor-commands.service.mjs +7 -1
  19. package/esm2022/lib/services/ate-editor-i18n.service.mjs +36 -0
  20. package/esm2022/lib/services/ate-image.service.mjs +12 -5
  21. package/esm2022/lib/utils/ate-extensions.utils.mjs +3 -1
  22. package/esm2022/lib/utils/ate-image-src-resolver.utils.mjs +21 -0
  23. package/esm2022/public-api.mjs +2 -1
  24. package/fesm2022/flogeez-angular-tiptap-editor.mjs +297 -27
  25. package/fesm2022/flogeez-angular-tiptap-editor.mjs.map +1 -1
  26. package/lib/components/bubble-menus/base/ate-base-bubble-menu.d.ts +2 -2
  27. package/lib/components/bubble-menus/base/ate-base-sub-bubble-menu.d.ts +2 -2
  28. package/lib/components/bubble-menus/table/ate-cell-bubble-menu.component.d.ts +1 -1
  29. package/lib/components/editor/angular-tiptap-editor.component.d.ts +18 -2
  30. package/lib/components/editor/ate-block-controls.component.d.ts +2 -2
  31. package/lib/components/slash-commands/ate-slash-commands.component.d.ts +2 -2
  32. package/lib/config/ate-editor.config.d.ts +3 -0
  33. package/lib/config/ate-slash-commands.config.d.ts +2 -2
  34. package/lib/extensions/ate-paste.extension.d.ts +24 -0
  35. package/lib/extensions/ate-resizable-image.extension.d.ts +10 -1
  36. package/lib/models/ate-editor-config.model.d.ts +6 -1
  37. package/lib/models/ate-image.model.d.ts +12 -0
  38. package/lib/models/ate-paste.model.d.ts +36 -0
  39. package/lib/services/ate-editor-commands.service.d.ts +3 -1
  40. package/lib/services/ate-editor-i18n.service.d.ts +208 -0
  41. package/lib/services/ate-image.service.d.ts +3 -1
  42. package/lib/utils/ate-image-src-resolver.utils.d.ts +2 -0
  43. package/package.json +1 -1
  44. package/public-api.d.ts +1 -0
@@ -2,14 +2,14 @@ import { ElementRef, OnDestroy, AfterViewInit } from "@angular/core";
2
2
  import { Instance as TippyInstance } from "tippy.js";
3
3
  import { Editor } from "@tiptap/core";
4
4
  import { AteEditorCommandsService } from "../../../services/ate-editor-commands.service";
5
- import { AteI18nService } from "../../../services/ate-i18n.service";
5
+ import { AteEditorI18n } from "../../../services/ate-editor-i18n.service";
6
6
  import * as i0 from "@angular/core";
7
7
  /**
8
8
  * Base abstract class for all Bubble Menus (Text, Image, Table, Cell).
9
9
  * Handles common logic for Tippy.js initialization, positioning, and visibility.
10
10
  */
11
11
  export declare abstract class AteBaseBubbleMenu implements AfterViewInit, OnDestroy {
12
- protected readonly i18nService: AteI18nService;
12
+ protected readonly i18nService: AteEditorI18n;
13
13
  protected readonly editorCommands: AteEditorCommandsService;
14
14
  editor: import("@angular/core").InputSignal<Editor>;
15
15
  menuRef: import("@angular/core").Signal<ElementRef<HTMLDivElement>>;
@@ -2,14 +2,14 @@ import { ElementRef, OnDestroy, AfterViewInit } from "@angular/core";
2
2
  import { Instance as TippyInstance } from "tippy.js";
3
3
  import { Editor } from "@tiptap/core";
4
4
  import { AteEditorCommandsService } from "../../../services/ate-editor-commands.service";
5
- import { AteI18nService } from "../../../services/ate-i18n.service";
5
+ import { AteEditorI18n } from "../../../services/ate-editor-i18n.service";
6
6
  import * as i0 from "@angular/core";
7
7
  /**
8
8
  * Base abstract class for Sub-Bubble Menus (Link, Color).
9
9
  * These menus are "sub-menus" of the main bubble menu or triggered/anchored by specific UI elements.
10
10
  */
11
11
  export declare abstract class AteBaseSubBubbleMenu implements AfterViewInit, OnDestroy {
12
- protected readonly i18nService: AteI18nService;
12
+ protected readonly i18nService: AteEditorI18n;
13
13
  protected readonly editorCommands: AteEditorCommandsService;
14
14
  editor: import("@angular/core").InputSignal<Editor>;
15
15
  menuRef: import("@angular/core").Signal<ElementRef<HTMLDivElement>>;
@@ -7,7 +7,7 @@ export declare class AteCellBubbleMenuComponent extends AteBaseBubbleMenu {
7
7
  mergeCells: boolean;
8
8
  splitCell: boolean;
9
9
  }>;
10
- readonly i18n: () => import("@flogeez/angular-tiptap-editor").AteI18nService;
10
+ readonly i18n: () => import("../../../services/ate-editor-i18n.service").AteEditorI18n;
11
11
  shouldShow(): boolean;
12
12
  getSelectionRect(): DOMRect;
13
13
  protected executeCommand(editor: Editor, command: string, ...args: unknown[]): void;
@@ -10,7 +10,8 @@ import { AteSlashCommandsConfig } from "../../config/ate-slash-commands.config";
10
10
  import { AteToolbarConfig } from "../../models/ate-toolbar.model";
11
11
  import { AteBubbleMenuConfig } from "../../models/ate-bubble-menu.model";
12
12
  import { AteEditorConfig, AteAngularNode, AteBlockControlsMode, AteAutofocusMode } from "../../models/ate-editor-config.model";
13
- import { AteImageUploadHandler, AteImageUploadOptions, AteImageUploadResult, AteImageRejection, AtePastedImageRejection } from "../../models/ate-image.model";
13
+ import { AteImageUploadHandler, AteImageSrcResolver, AteImageUploadOptions, AteImageUploadResult, AteImageRejection, AtePastedImageRejection } from "../../models/ate-image.model";
14
+ import { AtePasteConfig } from "../../models/ate-paste.model";
14
15
  import { SupportedLocale } from "../../i18n/ateTranslationsModel";
15
16
  import * as i0 from "@angular/core";
16
17
  import * as i1 from "../../directives/ate-noop-value-accessor.directive";
@@ -68,6 +69,11 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
68
69
  */
69
70
  stateCalculators: import("@angular/core").InputSignal<AteStateCalculator[] | undefined>;
70
71
  imageUpload: import("@angular/core").InputSignal<Partial<AteImageUploadOptions> | undefined>;
72
+ /**
73
+ * What is kept from HTML pasted or dropped from other apps (`colors`, `highlights`),
74
+ * and an optional `transformHTML` hook. Merged over `config.paste`.
75
+ */
76
+ paste: import("@angular/core").InputSignal<AtePasteConfig | undefined>;
71
77
  /**
72
78
  * Custom handler for image uploads.
73
79
  * When provided, images will be processed through this handler instead of being converted to base64.
@@ -88,6 +94,11 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
88
94
  * ```
89
95
  */
90
96
  imageUploadHandler: import("@angular/core").InputSignal<AteImageUploadHandler | undefined>;
97
+ /**
98
+ * Maps the `src` stored in the document to the URL the editor displays and downloads.
99
+ * The document, `getHTML()` and exports keep the original `src`.
100
+ */
101
+ imageSrcResolver: import("@angular/core").InputSignal<AteImageSrcResolver | undefined>;
91
102
  /**
92
103
  * Optional unique identifier for the editor instance in the registry.
93
104
  * If not specified, a unique ID is automatically generated.
@@ -173,6 +184,7 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
173
184
  readonly finalStarterKitOptions: import("@angular/core").Signal<Partial<StarterKitOptions>>;
174
185
  readonly finalStateCalculators: import("@angular/core").Signal<AteStateCalculator[]>;
175
186
  readonly finalAngularNodesConfig: import("@angular/core").Signal<AteAngularNode[]>;
187
+ readonly finalPasteConfig: import("@angular/core").Signal<AtePasteConfig>;
176
188
  readonly finalImageUploadConfig: import("@angular/core").Signal<{
177
189
  maxSize: number;
178
190
  quality?: number | undefined;
@@ -181,7 +193,9 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
181
193
  allowedTypes?: string[] | undefined;
182
194
  handler?: AteImageUploadHandler | undefined;
183
195
  }>;
196
+ readonly finalImageSrcResolver: import("@angular/core").Signal<AteImageSrcResolver | undefined>;
184
197
  readonly finalImageUploadHandler: import("@angular/core").Signal<AteImageUploadHandler | undefined>;
198
+ private readonly editorI18n;
185
199
  readonly currentTranslations: import("@angular/core").Signal<import("../../i18n/ateTranslationsModel").AteTranslations>;
186
200
  private _destroyRef;
187
201
  private ngControl;
@@ -235,7 +249,9 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
235
249
  slashCommands?: AteSlashCommandsConfig | undefined;
236
250
  tableOfContents?: import("@flogeez/angular-tiptap-editor").AteTocConfig | undefined;
237
251
  floatingToolbar?: boolean | undefined;
252
+ paste?: AtePasteConfig | undefined;
238
253
  imageUpload?: import("../../models/ate-image.model").AteImageUploadConfig | undefined;
254
+ imageSrcResolver?: AteImageSrcResolver | undefined;
239
255
  angularNodes?: AteAngularNode[] | undefined;
240
256
  tiptapExtensions?: (Mark<any, any> | Node<any, any> | Extension<any, any>)[] | undefined;
241
257
  tiptapOptions?: Partial<EditorOptions> | undefined;
@@ -263,5 +279,5 @@ export declare class AngularTiptapEditorComponent implements AfterViewInit, OnDe
263
279
  private setupFormControlSubscription;
264
280
  onEditorClick(event: Event): void;
265
281
  static ɵfac: i0.ɵɵFactoryDeclaration<AngularTiptapEditorComponent, never>;
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: {}; }]>;
282
+ 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; }; "paste": { "alias": "paste"; "required": false; "isSignal": true; }; "imageUploadHandler": { "alias": "imageUploadHandler"; "required": false; "isSignal": true; }; "imageSrcResolver": { "alias": "imageSrcResolver"; "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: {}; }]>;
267
283
  }
@@ -1,13 +1,13 @@
1
1
  import { ElementRef, OnDestroy, AfterViewInit } from "@angular/core";
2
2
  import { Editor } from "@tiptap/core";
3
3
  import { Node as PMNode } from "@tiptap/pm/model";
4
- import { AteI18nService } from "../../services/ate-i18n.service";
4
+ import { AteEditorI18n } from "../../services/ate-editor-i18n.service";
5
5
  import * as i0 from "@angular/core";
6
6
  /**
7
7
  * Component providing Notion-like block controls (Plus button and Drag Handle).
8
8
  */
9
9
  export declare class AteBlockControlsComponent implements AfterViewInit, OnDestroy {
10
- protected readonly i18n: AteI18nService;
10
+ protected readonly i18n: AteEditorI18n;
11
11
  editor: import("@angular/core").InputSignal<Editor>;
12
12
  hoveredData: import("@angular/core").InputSignal<{
13
13
  node: PMNode;
@@ -1,10 +1,10 @@
1
1
  import { ElementRef, OnDestroy, AfterViewInit } from "@angular/core";
2
2
  import { Editor } from "@tiptap/core";
3
- import { AteI18nService } from "../../services/ate-i18n.service";
3
+ import { AteEditorI18n } from "../../services/ate-editor-i18n.service";
4
4
  import { AteSlashCommandItem, AteCustomSlashCommands } from "../../models/ate-slash-command.model";
5
5
  import * as i0 from "@angular/core";
6
6
  export declare class AteSlashCommandsComponent implements AfterViewInit, OnDestroy {
7
- readonly i18nService: AteI18nService;
7
+ readonly i18nService: AteEditorI18n;
8
8
  editor: import("@angular/core").InputSignal<Editor>;
9
9
  config: import("@angular/core").InputSignal<AteCustomSlashCommands | undefined>;
10
10
  menuRef: import("@angular/core").Signal<ElementRef<HTMLDivElement>>;
@@ -8,6 +8,9 @@ export declare const ATE_DEFAULT_TABLE_MENU_CONFIG: AteTableBubbleMenuConfig;
8
8
  export declare const ATE_DEFAULT_CELL_MENU_CONFIG: AteCellBubbleMenuConfig;
9
9
  export declare const ATE_DEFAULT_IMAGE_UPLOAD_CONFIG: AteImageUploadConfig;
10
10
  import { AteEditorConfig } from "../models/ate-editor-config.model";
11
+ import { AtePasteConfig } from "../models/ate-paste.model";
12
+ /** Default paste settings: everything pasted from other apps is kept */
13
+ export declare const ATE_DEFAULT_PASTE_CONFIG: AtePasteConfig;
11
14
  /**
12
15
  * Ultimate default configuration for the Angular Tiptap Editor.
13
16
  * This serves as the 'Level 4' fallback in the configuration hierarchy:
@@ -24,7 +24,7 @@ export declare const ATE_DEFAULT_SLASH_COMMANDS_CONFIG: AteSlashCommandsConfig;
24
24
  * Factory pour créer les commandes natives avec leurs traductions et leur logique d'exécution.
25
25
  * Utilise les services de l'éditeur pour garantir une cohérence de comportement.
26
26
  */
27
- export declare function createDefaultSlashCommands(i18n: AteI18nService, commands: AteEditorCommandsService, imageOptions?: {
27
+ export declare function createDefaultSlashCommands(i18n: Pick<AteI18nService, "slashCommands">, commands: AteEditorCommandsService, imageOptions?: {
28
28
  quality?: number;
29
29
  maxWidth?: number;
30
30
  maxHeight?: number;
@@ -34,7 +34,7 @@ export declare function createDefaultSlashCommands(i18n: AteI18nService, command
34
34
  /**
35
35
  * Filtre et assemble les commandes selon la configuration fournie.
36
36
  */
37
- export declare function filterSlashCommands(config: AteSlashCommandsConfig, i18n: AteI18nService, commands: AteEditorCommandsService, imageOptions?: {
37
+ export declare function filterSlashCommands(config: AteSlashCommandsConfig, i18n: Pick<AteI18nService, "slashCommands">, commands: AteEditorCommandsService, imageOptions?: {
38
38
  quality?: number;
39
39
  maxWidth?: number;
40
40
  maxHeight?: number;
@@ -0,0 +1,24 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { AtePasteConfig, AtePasteSource } from "../models/ate-paste.model";
3
+ export interface AtePasteOptions {
4
+ /** Paste settings, read on every paste so they can change at runtime */
5
+ config: () => AtePasteConfig;
6
+ }
7
+ export interface AtePasteStorage {
8
+ /** Source of the paste being processed, detected before any cleanup */
9
+ source: AtePasteSource;
10
+ }
11
+ declare module "@tiptap/core" {
12
+ interface Storage {
13
+ atePaste: AtePasteStorage;
14
+ }
15
+ }
16
+ /** Detects where clipboard HTML comes from. Must run on the HTML as copied, before any cleanup */
17
+ export declare function detectPasteSource(html: string): AtePasteSource;
18
+ /** Removes the formats turned off in `config` (`colors`, `highlights`) from pasted HTML */
19
+ export declare function stripPastedFormats(html: string, config: AtePasteConfig): string;
20
+ /**
21
+ * Applies `AtePasteConfig` to pasted and dropped HTML, after the Office cleanup:
22
+ * removes the formats turned off for content from other apps, then calls `transformHTML`.
23
+ */
24
+ export declare const AtePaste: Extension<AtePasteOptions, AtePasteStorage>;
@@ -1,10 +1,19 @@
1
1
  import { Node } from "@tiptap/core";
2
+ import { AteImageSrcResolver } from "../models/ate-image.model";
2
3
  export interface AteResizableImageOptions {
3
4
  inline: boolean;
4
5
  allowBase64: boolean;
5
6
  HTMLAttributes: Record<string, unknown>;
7
+ srcResolver: () => AteImageSrcResolver | null | undefined;
8
+ }
9
+ export interface AteResizableImageStorage {
10
+ /** One callback per image node view: re-applies the resolver when it changes */
11
+ srcRefreshers: Set<() => void>;
6
12
  }
7
13
  declare module "@tiptap/core" {
14
+ interface Storage {
15
+ resizableImage: AteResizableImageStorage;
16
+ }
8
17
  interface Commands<ReturnType> {
9
18
  resizableImage: {
10
19
  setResizableImage: (options: {
@@ -24,4 +33,4 @@ declare module "@tiptap/core" {
24
33
  };
25
34
  }
26
35
  }
27
- export declare const AteResizableImage: Node<AteResizableImageOptions, any>;
36
+ export declare const AteResizableImage: Node<AteResizableImageOptions, AteResizableImageStorage>;
@@ -3,7 +3,8 @@ import { AteToolbarConfig } from "./ate-toolbar.model";
3
3
  import { RegisterAngularComponentOptions } from "../node-view/ate-node-view.models";
4
4
  import { AteBubbleMenuConfig, AteImageBubbleMenuConfig, AteTableBubbleMenuConfig, AteCellBubbleMenuConfig } from "./ate-bubble-menu.model";
5
5
  import { AteSlashCommandsConfig } from "../config/ate-slash-commands.config";
6
- import { AteImageUploadConfig } from "./ate-image.model";
6
+ import { AteImageSrcResolver, AteImageUploadConfig } from "./ate-image.model";
7
+ import { AtePasteConfig } from "./ate-paste.model";
7
8
  import { AteTocConfig } from "./ate-toc.model";
8
9
  import { Extension, Node, Mark, EditorOptions } from "@tiptap/core";
9
10
  import type { StarterKitOptions } from "@tiptap/starter-kit";
@@ -101,8 +102,12 @@ export interface AteEditorConfig {
101
102
  tableOfContents?: AteTocConfig;
102
103
  /** If true, shows the floating toolbar on focus */
103
104
  floatingToolbar?: boolean;
105
+ /** What is kept from HTML pasted or dropped from other apps, and an optional `transformHTML` hook */
106
+ paste?: AtePasteConfig;
104
107
  /** Technical configuration for image uploads */
105
108
  imageUpload?: AteImageUploadConfig;
109
+ /** Maps the `src` stored in the document to the URL the editor displays and downloads (e.g. a `blob:` fetched with a token) */
110
+ imageSrcResolver?: AteImageSrcResolver;
106
111
  /**
107
112
  * List of Angular components to automatically register as interactive editor nodes.
108
113
  * This is the preferred way to extend the editor with custom Angular logic.
@@ -139,3 +139,15 @@ export interface AteImageUploadConfig {
139
139
  /** Accepted file types (e.g., ['image/jpeg', 'image/png']) */
140
140
  allowedTypes?: string[];
141
141
  }
142
+ /**
143
+ * Maps the `src` stored in the document to the URL the editor displays (and downloads).
144
+ * The document, `getHTML()`, `getJSON()` and exports always keep the original `src`.
145
+ *
146
+ * Use it for images that cannot be loaded by a plain `<img src>`, e.g. endpoints that need an
147
+ * authorization header: fetch the image and resolve to a `blob:` URL.
148
+ *
149
+ * Can return a string, a Promise or an Observable (only the first emission is used).
150
+ * When it throws, rejects or emits nothing, the original `src` is displayed instead.
151
+ * The library never revokes the URLs it returns: if you create `blob:` URLs, you own their lifetime.
152
+ */
153
+ export type AteImageSrcResolver = (src: string) => string | Promise<string> | Observable<string>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Where pasted (or dropped) HTML comes from.
3
+ * - `editor`: copied from an Angular Tiptap Editor (this one or another one, in any app)
4
+ * - `office`: copied from Microsoft Word or Excel
5
+ * - `external`: anything else (Google Docs, web pages, other editors such as Confluence)
6
+ */
7
+ export type AtePasteSource = "editor" | "office" | "external";
8
+ /** Context given to `AtePasteConfig.transformHTML` */
9
+ export interface AtePasteContext {
10
+ /** Where the HTML comes from, detected on the clipboard HTML as copied */
11
+ source: AtePasteSource;
12
+ }
13
+ /**
14
+ * Controls what is kept from HTML pasted or dropped into the editor.
15
+ * Format keys only apply to content from other apps (`office` and `external` sources):
16
+ * content copied from an editor always keeps its formatting.
17
+ * Every key is read at paste time, so changes apply to the next paste.
18
+ */
19
+ export interface AtePasteConfig {
20
+ /** Keep text colors from other apps (`color` styles, `<font color>`). Default: `true` */
21
+ colors?: boolean;
22
+ /** Keep highlights and background colors from other apps (`<mark>`, `background-color`). Default: `true` */
23
+ highlights?: boolean;
24
+ /**
25
+ * Last step before the HTML is parsed: receives the HTML (after the Office cleanup and the format keys above)
26
+ * and returns the HTML to insert. Called for every source, including `editor`.
27
+ * If it throws or does not return a string, the HTML is inserted as it was before this step.
28
+ *
29
+ * @example Drop images pasted from web pages
30
+ * ```typescript
31
+ * transformHTML: (html, { source }) =>
32
+ * source === 'external' ? html.replace(/<img[^>]*>/g, '') : html
33
+ * ```
34
+ */
35
+ transformHTML?: (html: string, context: AtePasteContext) => string;
36
+ }
@@ -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, AteImageRejection } from "../models/ate-image.model";
4
+ import { AteImageUploadHandler, AteImageSrcResolver, 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;
@@ -22,6 +22,8 @@ export declare class AteEditorCommandsService {
22
22
  set onImageUploaded(callback: ((result: AteImageUploadResult) => void) | null);
23
23
  set onImageRejected(callback: ((rejection: AteImageRejection) => void) | null);
24
24
  set uploadHandler(handler: AteImageUploadHandler | null);
25
+ get srcResolver(): AteImageSrcResolver | null;
26
+ set srcResolver(resolver: AteImageSrcResolver | null);
25
27
  /** Update state (called by TiptapStateExtension) */
26
28
  updateState(state: AteEditorStateSnapshot): void;
27
29
  /** Signal to toggle link edit mode from UI (Toolbar) - Immediate response */
@@ -0,0 +1,208 @@
1
+ import { AteTranslations, SupportedLocale } from "../i18n/ateTranslationsModel";
2
+ import * as i0 from "@angular/core";
3
+ /**
4
+ * Translations seen by one editor instance. The editor provides its own copy and binds its
5
+ * `locale` input to it; without a locale (or outside an editor) it follows the global
6
+ * `AteI18nService` locale. Internal: not part of the public API.
7
+ */
8
+ export declare class AteEditorI18n {
9
+ private readonly global;
10
+ private readonly localeSource;
11
+ readonly translations: import("@angular/core").Signal<AteTranslations>;
12
+ readonly toolbar: import("@angular/core").Signal<{
13
+ bold: string;
14
+ italic: string;
15
+ underline: string;
16
+ strike: string;
17
+ code: string;
18
+ codeBlock: string;
19
+ superscript: string;
20
+ subscript: string;
21
+ highlight: string;
22
+ highlightPicker: string;
23
+ heading1: string;
24
+ heading2: string;
25
+ heading3: string;
26
+ bulletList: string;
27
+ orderedList: string;
28
+ blockquote: string;
29
+ alignLeft: string;
30
+ alignCenter: string;
31
+ alignRight: string;
32
+ alignJustify: string;
33
+ link: string;
34
+ image: string;
35
+ horizontalRule: string;
36
+ table: string;
37
+ undo: string;
38
+ redo: string;
39
+ clear: string;
40
+ textColor: string;
41
+ customColor: string;
42
+ presets: string;
43
+ }>;
44
+ readonly bubbleMenu: import("@angular/core").Signal<{
45
+ bold: string;
46
+ italic: string;
47
+ underline: string;
48
+ strike: string;
49
+ code: string;
50
+ superscript: string;
51
+ subscript: string;
52
+ highlight: string;
53
+ highlightPicker: string;
54
+ textColor: string;
55
+ link: string;
56
+ addLink: string;
57
+ editLink: string;
58
+ removeLink: string;
59
+ linkUrl: string;
60
+ linkText: string;
61
+ openLink: string;
62
+ customColor: string;
63
+ presets: string;
64
+ }>;
65
+ readonly slashCommands: import("@angular/core").Signal<{
66
+ heading1: {
67
+ title: string;
68
+ description: string;
69
+ keywords: string[];
70
+ };
71
+ heading2: {
72
+ title: string;
73
+ description: string;
74
+ keywords: string[];
75
+ };
76
+ heading3: {
77
+ title: string;
78
+ description: string;
79
+ keywords: string[];
80
+ };
81
+ bulletList: {
82
+ title: string;
83
+ description: string;
84
+ keywords: string[];
85
+ };
86
+ orderedList: {
87
+ title: string;
88
+ description: string;
89
+ keywords: string[];
90
+ };
91
+ blockquote: {
92
+ title: string;
93
+ description: string;
94
+ keywords: string[];
95
+ };
96
+ code: {
97
+ title: string;
98
+ description: string;
99
+ keywords: string[];
100
+ };
101
+ image: {
102
+ title: string;
103
+ description: string;
104
+ keywords: string[];
105
+ };
106
+ horizontalRule: {
107
+ title: string;
108
+ description: string;
109
+ keywords: string[];
110
+ };
111
+ table: {
112
+ title: string;
113
+ description: string;
114
+ keywords: string[];
115
+ };
116
+ tableOfContents: {
117
+ title: string;
118
+ description: string;
119
+ keywords: string[];
120
+ };
121
+ filterPlaceholder?: string | undefined;
122
+ }>;
123
+ readonly table: import("@angular/core").Signal<{
124
+ addRowBefore: string;
125
+ addRowAfter: string;
126
+ deleteRow: string;
127
+ addColumnBefore: string;
128
+ addColumnAfter: string;
129
+ deleteColumn: string;
130
+ deleteTable: string;
131
+ toggleHeaderRow: string;
132
+ toggleHeaderColumn: string;
133
+ mergeCells: string;
134
+ splitCell: string;
135
+ }>;
136
+ readonly imageUpload: import("@angular/core").Signal<{
137
+ selectImage: string;
138
+ loadError: string;
139
+ uploadingImage: string;
140
+ uploadProgress: string;
141
+ uploadError: string;
142
+ uploadSuccess: string;
143
+ imageTooLarge: string;
144
+ invalidFileType: string;
145
+ dragDropText: string;
146
+ changeImage: string;
147
+ downloadImage: string;
148
+ deleteImage: string;
149
+ resizeSmall: string;
150
+ resizeMedium: string;
151
+ resizeLarge: string;
152
+ resizeOriginal: string;
153
+ alignLeft: string;
154
+ alignCenter: string;
155
+ alignRight: string;
156
+ resizing: string;
157
+ compressing: string;
158
+ compressionError: string;
159
+ validating: string;
160
+ uploadingToServer: string;
161
+ replacingImage: string;
162
+ insertingImage: string;
163
+ noFileSelected: string;
164
+ selectionCancelled: string;
165
+ }>;
166
+ readonly editor: import("@angular/core").Signal<{
167
+ placeholder: string;
168
+ character: string;
169
+ characterPlural: string;
170
+ word: string;
171
+ wordPlural: string;
172
+ linkPrompt: string;
173
+ linkUrlPrompt: string;
174
+ confirmDelete: string;
175
+ toggleEdit: string;
176
+ viewMode: string;
177
+ blockAdd: string;
178
+ blockDrag: string;
179
+ tableOfContents: string;
180
+ }>;
181
+ readonly common: import("@angular/core").Signal<{
182
+ cancel: string;
183
+ confirm: string;
184
+ apply: string;
185
+ delete: string;
186
+ save: string;
187
+ close: string;
188
+ loading: string;
189
+ error: string;
190
+ success: string;
191
+ }>;
192
+ readonly export: import("@angular/core").Signal<{
193
+ export: string;
194
+ html: string;
195
+ markdown: string;
196
+ text: string;
197
+ copyHtml: string;
198
+ copyMarkdown: string;
199
+ copyText: string;
200
+ downloadHtml: string;
201
+ downloadMarkdown: string;
202
+ downloadText: string;
203
+ copiedSuccess: string;
204
+ }>;
205
+ bindLocale(source: () => SupportedLocale | undefined): void;
206
+ static ɵfac: i0.ɵɵFactoryDeclaration<AteEditorI18n, never>;
207
+ static ɵprov: i0.ɵɵInjectableDeclaration<AteEditorI18n>;
208
+ }
@@ -1,5 +1,5 @@
1
1
  import { Editor } from "@tiptap/core";
2
- import { AteResizeOptions, AteImageUploadOptions, AteImageUploadHandler, AteImageUploadResult, AteImageData, AteImageRejection, AteImageSource, AteImageValidationReason } from "../models/ate-image.model";
2
+ import { AteResizeOptions, AteImageUploadOptions, AteImageUploadHandler, AteImageSrcResolver, 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;
@@ -20,6 +20,7 @@ export declare class AteImageService {
20
20
  * If set, this handler replaces the default base64 conversion.
21
21
  */
22
22
  uploadHandler: AteImageUploadHandler | null;
23
+ srcResolver: AteImageSrcResolver | null;
23
24
  private currentEditor;
24
25
  /** Select and track an image from the editor */
25
26
  selectImage(editor: Editor): void;
@@ -44,6 +45,7 @@ export declare class AteImageService {
44
45
  deleteImage(editor: Editor): void;
45
46
  /** Download the current image */
46
47
  downloadImage(editor: Editor): void;
48
+ private downloadResolvedImage;
47
49
  private updateSelectedImage;
48
50
  /** Validate file type and size */
49
51
  validateImage(file: File, options?: {
@@ -0,0 +1,2 @@
1
+ import { AteImageSrcResolver } from "../models/ate-image.model";
2
+ export declare function ateResolveImageSrc(resolver: AteImageSrcResolver | null | undefined, src: string): Promise<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flogeez/angular-tiptap-editor",
3
- "version": "3.6.1",
3
+ "version": "3.7.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
@@ -28,6 +28,7 @@ export { AteLinkShortcut, isLinkShortcut, type AteLinkShortcutOptions, } from ".
28
28
  export { AteBlockShortcuts, type AteBlockShortcutsOptions, } from "./lib/extensions/ate-block-shortcuts.extension";
29
29
  export * from "./lib/models/ate-toolbar.model";
30
30
  export * from "./lib/models/ate-image.model";
31
+ export * from "./lib/models/ate-paste.model";
31
32
  export * from "./lib/models/ate-bubble-menu.model";
32
33
  export * from "./lib/models/ate-editor-config.model";
33
34
  export * from "./lib/models/ate-slash-command.model";