@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
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, signal, inject, Injectable, computed, Directive, ApplicationRef, EnvironmentInjector, createComponent, input, booleanAttribute, effect, untracked, Component, ChangeDetectionStrategy, HostListener, ElementRef, ViewEncapsulation, output, viewChild, DestroyRef, Injector, isDevMode, makeEnvironmentProviders, APP_INITIALIZER } from '@angular/core';
2
+ import { isDevMode, InjectionToken, signal, inject, Injectable, computed, Directive, ApplicationRef, EnvironmentInjector, createComponent, input, booleanAttribute, effect, untracked, Component, ChangeDetectionStrategy, HostListener, ElementRef, ViewEncapsulation, output, viewChild, DestroyRef, Injector, makeEnvironmentProviders, APP_INITIALIZER } from '@angular/core';
3
3
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
4
  import { Node as Node$1, mergeAttributes, nodeInputRule, Extension, getAttributes, Editor } from '@tiptap/core';
5
5
  import StarterKit from '@tiptap/starter-kit';
@@ -11,11 +11,11 @@ import { Highlight } from '@tiptap/extension-highlight';
11
11
  import { TextStyle } from '@tiptap/extension-text-style';
12
12
  import { Color } from '@tiptap/extension-color';
13
13
  import OfficePaste from '@intevation/tiptap-extension-office-paste';
14
+ import { isObservable, firstValueFrom, Subscription, concat, defer, of, tap } from 'rxjs';
14
15
  import { Plugin, PluginKey, NodeSelection, TextSelection } from '@tiptap/pm/state';
15
16
  import { DecorationSet, Decoration } from '@tiptap/pm/view';
16
- import { Fragment, Slice } from '@tiptap/pm/model';
17
+ import { Fragment, Slice, DOMSerializer } from '@tiptap/pm/model';
17
18
  import { Table, TableRow, TableHeader, TableCell } from '@tiptap/extension-table';
18
- import { Subscription, isObservable, firstValueFrom, concat, defer, of, tap } from 'rxjs';
19
19
  import tippy, { sticky } from 'tippy.js';
20
20
  import { DomSanitizer } from '@angular/platform-browser';
21
21
  import { CommonModule } from '@angular/common';
@@ -23,6 +23,25 @@ import * as i1 from '@angular/forms';
23
23
  import { FormsModule, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
24
24
  import { CellSelection } from '@tiptap/pm/tables';
25
25
 
26
+ async function ateResolveImageSrc(resolver, src) {
27
+ if (!resolver || !src) {
28
+ return src;
29
+ }
30
+ try {
31
+ const result = resolver(src);
32
+ const resolved = isObservable(result) ? await firstValueFrom(result) : await result;
33
+ if (typeof resolved === "string" && resolved) {
34
+ return resolved;
35
+ }
36
+ }
37
+ catch (error) {
38
+ if (isDevMode()) {
39
+ console.warn("[ATE] imageSrcResolver failed, showing the original src instead.", src, error);
40
+ }
41
+ }
42
+ return src;
43
+ }
44
+
26
45
  const AteResizableImage = Node$1.create({
27
46
  name: "resizableImage",
28
47
  addOptions() {
@@ -30,6 +49,12 @@ const AteResizableImage = Node$1.create({
30
49
  inline: false,
31
50
  allowBase64: false,
32
51
  HTMLAttributes: {},
52
+ srcResolver: () => null,
53
+ };
54
+ },
55
+ addStorage() {
56
+ return {
57
+ srcRefreshers: new Set(),
33
58
  };
34
59
  },
35
60
  inline() {
@@ -120,8 +145,13 @@ const AteResizableImage = Node$1.create({
120
145
  ];
121
146
  },
122
147
  addNodeView() {
148
+ const options = this.options;
149
+ const storage = this.storage;
123
150
  return ({ node, getPos, editor }) => {
124
151
  let currentNode = node;
152
+ let requestedSrc = null;
153
+ let requestedWith = null;
154
+ let destroyed = false;
125
155
  const wrapper = document.createElement("div");
126
156
  wrapper.className = "resizable-image-wrapper";
127
157
  wrapper.style.display = "block";
@@ -134,7 +164,6 @@ const AteResizableImage = Node$1.create({
134
164
  container.style.position = "relative";
135
165
  container.style.display = "inline-block";
136
166
  const img = document.createElement("img");
137
- img.src = currentNode.attrs["src"];
138
167
  img.alt = currentNode.attrs["alt"] || "";
139
168
  img.title = currentNode.attrs["title"] || "";
140
169
  img.className = "ate-image";
@@ -145,6 +174,32 @@ const AteResizableImage = Node$1.create({
145
174
  if (currentNode.attrs["height"]) {
146
175
  img.height = currentNode.attrs["height"];
147
176
  }
177
+ const applySrc = (src) => {
178
+ const resolver = options.srcResolver() ?? null;
179
+ if (!resolver || !src) {
180
+ requestedSrc = null;
181
+ requestedWith = null;
182
+ img.src = src ?? "";
183
+ return;
184
+ }
185
+ if (src === requestedSrc && resolver === requestedWith) {
186
+ return;
187
+ }
188
+ // A new resolver for the same src keeps the current image until the new URL is ready, so it does not blink
189
+ if (src !== requestedSrc) {
190
+ img.removeAttribute("src");
191
+ }
192
+ requestedSrc = src;
193
+ requestedWith = resolver;
194
+ ateResolveImageSrc(resolver, src).then(resolved => {
195
+ if (!destroyed && requestedSrc === src && requestedWith === resolver) {
196
+ img.src = resolved;
197
+ }
198
+ });
199
+ };
200
+ const refreshSrc = () => applySrc(currentNode.attrs["src"]);
201
+ refreshSrc();
202
+ storage.srcRefreshers.add(refreshSrc);
148
203
  wrapper.appendChild(container);
149
204
  container.appendChild(img);
150
205
  // Allow selection in read-only mode to show bubble menu/download
@@ -276,7 +331,7 @@ const AteResizableImage = Node$1.create({
276
331
  return false;
277
332
  }
278
333
  currentNode = updatedNode;
279
- img.src = updatedNode.attrs["src"];
334
+ applySrc(updatedNode.attrs["src"]);
280
335
  img.alt = updatedNode.attrs["alt"] || "";
281
336
  img.title = updatedNode.attrs["title"] || "";
282
337
  if (updatedNode.attrs["width"]) {
@@ -300,6 +355,10 @@ const AteResizableImage = Node$1.create({
300
355
  return !!target.closest(".resize-controls");
301
356
  },
302
357
  ignoreMutation: () => true,
358
+ destroy: () => {
359
+ destroyed = true;
360
+ storage.srcRefreshers.delete(refreshSrc);
361
+ },
303
362
  };
304
363
  };
305
364
  },
@@ -996,6 +1055,127 @@ const AteBlockShortcuts = Extension.create({
996
1055
  },
997
1056
  });
998
1057
 
1058
+ /** Set on HTML copied from an editor, so a later paste knows it comes from an editor */
1059
+ const EDITOR_CLIPBOARD_MARKER = "data-ate-copy";
1060
+ const OFFICE_HTML = /urn:schemas-microsoft-com:office|class="?Mso|mso-/i;
1061
+ /** Detects where clipboard HTML comes from. Must run on the HTML as copied, before any cleanup */
1062
+ function detectPasteSource(html) {
1063
+ if (html.includes(EDITOR_CLIPBOARD_MARKER)) {
1064
+ return "editor";
1065
+ }
1066
+ return OFFICE_HTML.test(html) ? "office" : "external";
1067
+ }
1068
+ /** Removes the formats turned off in `config` (`colors`, `highlights`) from pasted HTML */
1069
+ function stripPastedFormats(html, config) {
1070
+ const keepColors = config.colors !== false;
1071
+ const keepHighlights = config.highlights !== false;
1072
+ if (keepColors && keepHighlights) {
1073
+ return html;
1074
+ }
1075
+ const template = document.createElement("template");
1076
+ template.innerHTML = html;
1077
+ const content = template.content;
1078
+ const styleProperties = [
1079
+ ...(keepColors ? [] : ["color"]),
1080
+ ...(keepHighlights ? [] : ["background-color", "background"]),
1081
+ ];
1082
+ content.querySelectorAll("[style]").forEach(element => {
1083
+ styleProperties.forEach(property => element.style.removeProperty(property));
1084
+ if (!element.getAttribute("style")?.trim()) {
1085
+ element.removeAttribute("style");
1086
+ }
1087
+ });
1088
+ if (!keepColors) {
1089
+ content.querySelectorAll("font[color]").forEach(font => font.removeAttribute("color"));
1090
+ }
1091
+ if (!keepHighlights) {
1092
+ content.querySelectorAll("[bgcolor]").forEach(element => element.removeAttribute("bgcolor"));
1093
+ content
1094
+ .querySelectorAll("mark")
1095
+ .forEach(mark => mark.replaceWith(...Array.from(mark.childNodes)));
1096
+ }
1097
+ return template.innerHTML;
1098
+ }
1099
+ /**
1100
+ * Runs before every other paste cleanup (the Office cleanup removes the markers it relies on):
1101
+ * records the source of each paste, and marks HTML copied from the editor.
1102
+ */
1103
+ const AtePasteSourceTracker = Extension.create({
1104
+ name: "atePasteSourceTracker",
1105
+ priority: 100000,
1106
+ addProseMirrorPlugins() {
1107
+ const editor = this.editor;
1108
+ const serializer = DOMSerializer.fromSchema(editor.schema);
1109
+ return [
1110
+ new Plugin({
1111
+ key: new PluginKey("atePasteSourceTracker"),
1112
+ props: {
1113
+ transformPastedHTML: html => {
1114
+ editor.storage.atePaste.source = detectPasteSource(html);
1115
+ return html;
1116
+ },
1117
+ clipboardSerializer: {
1118
+ serializeFragment: (fragment, options) => {
1119
+ const dom = serializer.serializeFragment(fragment, options);
1120
+ dom.firstElementChild?.setAttribute(EDITOR_CLIPBOARD_MARKER, "");
1121
+ return dom;
1122
+ },
1123
+ },
1124
+ },
1125
+ }),
1126
+ ];
1127
+ },
1128
+ });
1129
+ /**
1130
+ * Applies `AtePasteConfig` to pasted and dropped HTML, after the Office cleanup:
1131
+ * removes the formats turned off for content from other apps, then calls `transformHTML`.
1132
+ */
1133
+ const AtePaste = Extension.create({
1134
+ name: "atePaste",
1135
+ addOptions() {
1136
+ return {
1137
+ config: () => ({}),
1138
+ };
1139
+ },
1140
+ addStorage() {
1141
+ return {
1142
+ source: "external",
1143
+ };
1144
+ },
1145
+ addExtensions() {
1146
+ return [AtePasteSourceTracker];
1147
+ },
1148
+ addProseMirrorPlugins() {
1149
+ const options = this.options;
1150
+ const storage = this.storage;
1151
+ return [
1152
+ new Plugin({
1153
+ key: new PluginKey("atePaste"),
1154
+ props: {
1155
+ transformPastedHTML: html => {
1156
+ const config = options.config();
1157
+ const source = storage.source;
1158
+ const stripped = source === "editor" ? html : stripPastedFormats(html, config);
1159
+ if (!config.transformHTML) {
1160
+ return stripped;
1161
+ }
1162
+ try {
1163
+ const transformed = config.transformHTML(stripped, { source });
1164
+ return typeof transformed === "string" ? transformed : stripped;
1165
+ }
1166
+ catch (error) {
1167
+ if (isDevMode()) {
1168
+ console.warn("[ATE] paste.transformHTML failed, inserting the HTML unchanged.", error);
1169
+ }
1170
+ return stripped;
1171
+ }
1172
+ },
1173
+ },
1174
+ }),
1175
+ ];
1176
+ },
1177
+ });
1178
+
999
1179
  const AteTableExtension = Extension.create({
1000
1180
  name: "tableExtension",
1001
1181
  addExtensions() {
@@ -2995,6 +3175,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2995
3175
  }]
2996
3176
  }], ctorParameters: () => [] });
2997
3177
 
3178
+ /**
3179
+ * Translations seen by one editor instance. The editor provides its own copy and binds its
3180
+ * `locale` input to it; without a locale (or outside an editor) it follows the global
3181
+ * `AteI18nService` locale. Internal: not part of the public API.
3182
+ */
3183
+ class AteEditorI18n {
3184
+ constructor() {
3185
+ this.global = inject(AteI18nService);
3186
+ this.localeSource = signal(() => undefined);
3187
+ this.translations = computed(() => {
3188
+ const locale = this.localeSource()();
3189
+ return (locale && this.global.getTranslations(locale)) || this.global.translations();
3190
+ });
3191
+ this.toolbar = computed(() => this.translations().toolbar);
3192
+ this.bubbleMenu = computed(() => this.translations().bubbleMenu);
3193
+ this.slashCommands = computed(() => this.translations().slashCommands);
3194
+ this.table = computed(() => this.translations().table);
3195
+ this.imageUpload = computed(() => this.translations().imageUpload);
3196
+ this.editor = computed(() => this.translations().editor);
3197
+ this.common = computed(() => this.translations().common);
3198
+ this.export = computed(() => this.translations().export);
3199
+ }
3200
+ bindLocale(source) {
3201
+ this.localeSource.set(source);
3202
+ }
3203
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AteEditorI18n, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3204
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AteEditorI18n, providedIn: "root" }); }
3205
+ }
3206
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AteEditorI18n, decorators: [{
3207
+ type: Injectable,
3208
+ args: [{ providedIn: "root" }]
3209
+ }] });
3210
+
2998
3211
  /**
2999
3212
  * Table of Contents Component (Notion-style)
3000
3213
  * Dynamically extracts headings from the active editor and displays them.
@@ -3046,7 +3259,7 @@ class AteTableOfContentsComponent {
3046
3259
  },
3047
3260
  });
3048
3261
  this.registry = inject(AteEditorRegistry);
3049
- this.i18n = inject(AteI18nService);
3262
+ this.i18n = inject(AteEditorI18n);
3050
3263
  // Computed header title with fallback to i18n
3051
3264
  this.headerTitle = computed(() => {
3052
3265
  const custom = this.title();
@@ -3903,7 +4116,7 @@ class AteImageService {
3903
4116
  this.isImageSelected = computed(() => this.selectedImage() !== null);
3904
4117
  /** Resizing state */
3905
4118
  this.isResizing = signal(false);
3906
- this.i18n = inject(AteI18nService);
4119
+ this.i18n = inject(AteEditorI18n);
3907
4120
  this.t = this.i18n.imageUpload;
3908
4121
  /** Upload state signals */
3909
4122
  this.isUploading = signal(false);
@@ -3914,6 +4127,7 @@ class AteImageService {
3914
4127
  * If set, this handler replaces the default base64 conversion.
3915
4128
  */
3916
4129
  this.uploadHandler = null;
4130
+ this.srcResolver = null;
3917
4131
  this.currentEditor = null;
3918
4132
  }
3919
4133
  /** Select and track an image from the editor */
@@ -4010,7 +4224,10 @@ class AteImageService {
4010
4224
  img.onload = () => {
4011
4225
  this.resizeImage(editor, { width: img.naturalWidth, height: img.naturalHeight });
4012
4226
  };
4013
- img.src = editor.getAttributes("resizableImage")["src"];
4227
+ const storedSrc = editor.getAttributes("resizableImage")["src"];
4228
+ ateResolveImageSrc(this.srcResolver, storedSrc).then(src => {
4229
+ img.src = src;
4230
+ });
4014
4231
  }
4015
4232
  /** Get current image dimensions */
4016
4233
  getImageDimensions(editor) {
@@ -4036,8 +4253,10 @@ class AteImageService {
4036
4253
  if (!attrs || !attrs["src"]) {
4037
4254
  return;
4038
4255
  }
4039
- const src = attrs["src"];
4040
4256
  const fileName = attrs["alt"] || "image";
4257
+ ateResolveImageSrc(this.srcResolver, attrs["src"]).then(src => this.downloadResolvedImage(src, fileName));
4258
+ }
4259
+ downloadResolvedImage(src, fileName) {
4041
4260
  // Detect if it's base64 or remote URL
4042
4261
  if (src.startsWith("data:") ||
4043
4262
  src.startsWith("blob:") ||
@@ -5066,6 +5285,12 @@ class AteEditorCommandsService {
5066
5285
  set uploadHandler(handler) {
5067
5286
  this.imageService.uploadHandler = handler;
5068
5287
  }
5288
+ get srcResolver() {
5289
+ return this.imageService.srcResolver;
5290
+ }
5291
+ set srcResolver(resolver) {
5292
+ this.imageService.srcResolver = resolver;
5293
+ }
5069
5294
  /** Update state (called by TiptapStateExtension) */
5070
5295
  updateState(state) {
5071
5296
  this._editorState.set(state);
@@ -5499,7 +5724,7 @@ class AteColorPickerComponent {
5499
5724
  this.disabled = input(false);
5500
5725
  this.anchorToText = input(false);
5501
5726
  this.colorPickerSvc = inject(AteColorPickerService);
5502
- this.i18nService = inject(AteI18nService);
5727
+ this.i18nService = inject(AteEditorI18n);
5503
5728
  this.editorCommands = inject(AteEditorCommandsService);
5504
5729
  this.t = this.i18nService.toolbar;
5505
5730
  this.state = this.editorCommands.editorState;
@@ -5613,7 +5838,7 @@ class AteToolbarComponent {
5613
5838
  this.config = input.required();
5614
5839
  this.imageUpload = input({});
5615
5840
  this.floating = input(false);
5616
- this.i18nService = inject(AteI18nService);
5841
+ this.i18nService = inject(AteEditorI18n);
5617
5842
  this.editorCommands = inject(AteEditorCommandsService);
5618
5843
  this.t = this.i18nService.toolbar;
5619
5844
  this.state = this.editorCommands.editorState;
@@ -6218,6 +6443,11 @@ const ATE_DEFAULT_IMAGE_UPLOAD_CONFIG = {
6218
6443
  allowedTypes: ["image/jpeg", "image/png", "image/gif", "image/webp"],
6219
6444
  quality: 0.8,
6220
6445
  };
6446
+ /** Default paste settings: everything pasted from other apps is kept */
6447
+ const ATE_DEFAULT_PASTE_CONFIG = {
6448
+ colors: true,
6449
+ highlights: true,
6450
+ };
6221
6451
  /**
6222
6452
  * Ultimate default configuration for the Angular Tiptap Editor.
6223
6453
  * This serves as the 'Level 4' fallback in the configuration hierarchy:
@@ -6256,6 +6486,7 @@ const ATE_DEFAULT_CONFIG = {
6256
6486
  bubbleMenu: ATE_DEFAULT_BUBBLE_MENU_CONFIG,
6257
6487
  imageBubbleMenu: ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG,
6258
6488
  imageUpload: ATE_DEFAULT_IMAGE_UPLOAD_CONFIG,
6489
+ paste: ATE_DEFAULT_PASTE_CONFIG,
6259
6490
  tableBubbleMenu: ATE_DEFAULT_TABLE_MENU_CONFIG,
6260
6491
  cellBubbleMenu: ATE_DEFAULT_CELL_MENU_CONFIG,
6261
6492
  slashCommands: {},
@@ -6272,7 +6503,7 @@ const ATE_DEFAULT_CONFIG = {
6272
6503
  */
6273
6504
  class AteBaseBubbleMenu {
6274
6505
  constructor() {
6275
- this.i18nService = inject(AteI18nService);
6506
+ this.i18nService = inject(AteEditorI18n);
6276
6507
  this.editorCommands = inject(AteEditorCommandsService);
6277
6508
  // Core Inputs
6278
6509
  this.editor = input.required();
@@ -7477,7 +7708,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
7477
7708
  */
7478
7709
  class AteBaseSubBubbleMenu {
7479
7710
  constructor() {
7480
- this.i18nService = inject(AteI18nService);
7711
+ this.i18nService = inject(AteEditorI18n);
7481
7712
  this.editorCommands = inject(AteEditorCommandsService);
7482
7713
  // Core Inputs
7483
7714
  this.editor = input.required();
@@ -8372,7 +8603,7 @@ function filterSlashCommands(config, i18n, commands, imageOptions) {
8372
8603
  // Default command definitions are now centralized in src/lib/config/slash-commands.config.ts
8373
8604
  class AteSlashCommandsComponent {
8374
8605
  constructor() {
8375
- this.i18nService = inject(AteI18nService);
8606
+ this.i18nService = inject(AteEditorI18n);
8376
8607
  this.editor = input.required();
8377
8608
  this.config = input(undefined);
8378
8609
  this.menuRef = viewChild.required("menuRef");
@@ -8718,7 +8949,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
8718
8949
  */
8719
8950
  class AteBlockControlsComponent {
8720
8951
  constructor() {
8721
- this.i18n = inject(AteI18nService);
8952
+ this.i18n = inject(AteEditorI18n);
8722
8953
  this.editor = input.required();
8723
8954
  this.hoveredData = input(null);
8724
8955
  this.menuRef = viewChild.required("menuRef");
@@ -9556,6 +9787,8 @@ const ATE_BUILTIN_UI_DEPENDENCIES = {
9556
9787
  ateLinkShortcut: "Mod+K link editor shortcut",
9557
9788
  highlight: "toolbar/bubble menu highlight picker, AteMarksCalculator",
9558
9789
  resizableImage: "image node view, image bubble menu, image upload/resize service, AteImageCalculator",
9790
+ atePaste: "paste settings (`paste` input / config): format filters and `transformHTML`",
9791
+ atePasteSourceTracker: "paste source detection (`editor` / `office` / `external`) for the `paste` settings",
9559
9792
  atePastedImage: "pasted HTML image sanitization and base64 re-hosting",
9560
9793
  ateUploadProgress: "image upload progress overlay",
9561
9794
  tableExtension: "toolbar/slash menu table insertion, table/cell bubble menus, AteTableCalculator",
@@ -9670,6 +9903,11 @@ class AngularTiptapEditorComponent {
9670
9903
  this.stateCalculators = input(undefined);
9671
9904
  // Nouveau input pour la configuration de l'upload d'images
9672
9905
  this.imageUpload = input(undefined);
9906
+ /**
9907
+ * What is kept from HTML pasted or dropped from other apps (`colors`, `highlights`),
9908
+ * and an optional `transformHTML` hook. Merged over `config.paste`.
9909
+ */
9910
+ this.paste = input(undefined);
9673
9911
  /**
9674
9912
  * Custom handler for image uploads.
9675
9913
  * When provided, images will be processed through this handler instead of being converted to base64.
@@ -9690,6 +9928,11 @@ class AngularTiptapEditorComponent {
9690
9928
  * ```
9691
9929
  */
9692
9930
  this.imageUploadHandler = input(undefined);
9931
+ /**
9932
+ * Maps the `src` stored in the document to the URL the editor displays and downloads.
9933
+ * The document, `getHTML()` and exports keep the original `src`.
9934
+ */
9935
+ this.imageSrcResolver = input(undefined);
9693
9936
  /**
9694
9937
  * Optional unique identifier for the editor instance in the registry.
9695
9938
  * If not specified, a unique ID is automatically generated.
@@ -9849,7 +10092,7 @@ class AngularTiptapEditorComponent {
9849
10092
  ? fromInputOptions
9850
10093
  : fromConfigOptions;
9851
10094
  return {
9852
- commands: filterSlashCommands(baseConfig || {}, this.i18nService, this.editorCommandsService, this.finalImageUploadConfig()),
10095
+ commands: filterSlashCommands(baseConfig || {}, this.editorI18n, this.editorCommandsService, this.finalImageUploadConfig()),
9853
10096
  };
9854
10097
  });
9855
10098
  // Behavior
@@ -9868,6 +10111,12 @@ class AngularTiptapEditorComponent {
9868
10111
  this.finalAngularNodesConfig = computed(() => this.effectiveConfig().angularNodes ?? [], {
9869
10112
  equal: shallowArrayEqual,
9870
10113
  });
10114
+ // Paste
10115
+ this.finalPasteConfig = computed(() => ({
10116
+ ...ATE_DEFAULT_PASTE_CONFIG,
10117
+ ...this.effectiveConfig().paste,
10118
+ ...this.paste(),
10119
+ }));
9871
10120
  // Image Upload
9872
10121
  this.finalImageUploadConfig = computed(() => {
9873
10122
  const fromInput = this.imageUpload();
@@ -9883,15 +10132,11 @@ class AngularTiptapEditorComponent {
9883
10132
  maxSize: (merged.maxSize ?? 5) * 1024 * 1024, // Convert MB to bytes for internal service
9884
10133
  };
9885
10134
  });
10135
+ this.finalImageSrcResolver = computed(() => this.imageSrcResolver() ?? this.effectiveConfig().imageSrcResolver);
9886
10136
  this.finalImageUploadHandler = computed(() => this.imageUploadHandler() ?? this.effectiveConfig().imageUpload?.handler);
9887
- // Computed for current translations (allows per-instance override via config or input)
9888
- this.currentTranslations = computed(() => {
9889
- const localeOverride = this.finalLocale();
9890
- if (localeOverride) {
9891
- return this.i18nService.getTranslations(localeOverride) ?? this.i18nService.translations();
9892
- }
9893
- return this.i18nService.translations();
9894
- });
10137
+ this.editorI18n = inject(AteEditorI18n);
10138
+ // Current translations (per-instance override via config or input, else the global locale)
10139
+ this.currentTranslations = this.editorI18n.translations;
9895
10140
  this._destroyRef = inject(DestroyRef);
9896
10141
  // NgControl for management of FormControls
9897
10142
  this.ngControl = inject(NgControl, { self: true, optional: true });
@@ -9915,6 +10160,7 @@ class AngularTiptapEditorComponent {
9915
10160
  const fromGlobal = this.globalConfig || {};
9916
10161
  return { ...ATE_DEFAULT_CONFIG, ...fromGlobal, ...fromInput };
9917
10162
  });
10163
+ this.editorI18n.bindLocale(this.finalLocale);
9918
10164
  // Effect to update editor content (with anti-echo)
9919
10165
  effect(() => {
9920
10166
  const content = this.content(); // Sole reactive dependency
@@ -9970,6 +10216,23 @@ class AngularTiptapEditorComponent {
9970
10216
  const handler = this.finalImageUploadHandler();
9971
10217
  this.editorCommandsService.uploadHandler = handler || null;
9972
10218
  }, { allowSignalWrites: true });
10219
+ // Effect to refresh the placeholder decoration when its resolved text changes
10220
+ effect(() => {
10221
+ const editor = this.editor();
10222
+ this.finalPlaceholder();
10223
+ if (editor && !editor.isDestroyed) {
10224
+ untracked(() => editor.view.dispatch(editor.state.tr));
10225
+ }
10226
+ });
10227
+ // Effect to re-apply the image src resolver to the displayed images when it changes
10228
+ // (e.g. a resolver set after the editor was created, or a new one after a token refresh)
10229
+ effect(() => {
10230
+ this.editorCommandsService.srcResolver = this.finalImageSrcResolver() ?? null;
10231
+ const editor = untracked(() => this.editor());
10232
+ if (editor && !editor.isDestroyed) {
10233
+ editor.storage.resizableImage?.srcRefreshers.forEach(refresh => refresh());
10234
+ }
10235
+ });
9973
10236
  // Effect to update character count limit dynamically
9974
10237
  effect(() => {
9975
10238
  const editor = this.editor();
@@ -10028,6 +10291,7 @@ class AngularTiptapEditorComponent {
10028
10291
  }
10029
10292
  }
10030
10293
  initEditor() {
10294
+ this.editorCommandsService.srcResolver = this.finalImageSrcResolver() ?? null;
10031
10295
  const starterKitOptions = this.finalStarterKitOptions();
10032
10296
  const customExtensions = this.finalTiptapExtensions();
10033
10297
  const customNames = new Set(customExtensions.map(getExtensionName).filter((name) => !!name));
@@ -10073,7 +10337,7 @@ class AngularTiptapEditorComponent {
10073
10337
  types: ["textStyle"],
10074
10338
  }),
10075
10339
  Placeholder.configure({
10076
- placeholder: this.finalPlaceholder(),
10340
+ placeholder: () => this.finalPlaceholder(),
10077
10341
  }),
10078
10342
  Superscript,
10079
10343
  Subscript,
@@ -10084,6 +10348,9 @@ class AngularTiptapEditorComponent {
10084
10348
  AteBlockShortcuts.configure({
10085
10349
  enabled: () => this.finalEnableBlockShortcuts(),
10086
10350
  }),
10351
+ AtePaste.configure({
10352
+ config: () => this.finalPasteConfig(),
10353
+ }),
10087
10354
  AteLinkShortcut.configure({
10088
10355
  canOpen: () => this.editorCommandsService.editorState().can.toggleLink,
10089
10356
  open: editor => this.editorCommandsService.execute(editor, "toggleLink"),
@@ -10100,6 +10367,7 @@ class AngularTiptapEditorComponent {
10100
10367
  HTMLAttributes: {
10101
10368
  class: "ate-image",
10102
10369
  },
10370
+ srcResolver: () => this.editorCommandsService.srcResolver,
10103
10371
  }),
10104
10372
  AtePastedImage.configure({
10105
10373
  shouldRehost: () => this.editorCommandsService.hasUploadHandler,
@@ -10412,12 +10680,13 @@ class AngularTiptapEditorComponent {
10412
10680
  }
10413
10681
  }
10414
10682
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AngularTiptapEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10415
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AngularTiptapEditorComponent, isStandalone: true, selector: "angular-tiptap-editor", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, fillContainer: { classPropertyName: "fillContainer", publicName: "fillContainer", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showFooter: { classPropertyName: "showFooter", publicName: "showFooter", isSignal: true, isRequired: false, transformFunction: null }, showCharacterCount: { classPropertyName: "showCharacterCount", publicName: "showCharacterCount", isSignal: true, isRequired: false, transformFunction: null }, showWordCount: { classPropertyName: "showWordCount", publicName: "showWordCount", isSignal: true, isRequired: false, transformFunction: null }, maxCharacters: { classPropertyName: "maxCharacters", publicName: "maxCharacters", isSignal: true, isRequired: false, transformFunction: null }, enableOfficePaste: { classPropertyName: "enableOfficePaste", publicName: "enableOfficePaste", isSignal: true, isRequired: false, transformFunction: null }, enableSlashCommands: { classPropertyName: "enableSlashCommands", publicName: "enableSlashCommands", isSignal: true, isRequired: false, transformFunction: null }, enableBlockShortcuts: { classPropertyName: "enableBlockShortcuts", publicName: "enableBlockShortcuts", isSignal: true, isRequired: false, transformFunction: null }, slashCommands: { classPropertyName: "slashCommands", publicName: "slashCommands", isSignal: true, isRequired: false, transformFunction: null }, customSlashCommands: { classPropertyName: "customSlashCommands", publicName: "customSlashCommands", isSignal: true, isRequired: false, transformFunction: null }, blockControls: { classPropertyName: "blockControls", publicName: "blockControls", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, seamless: { classPropertyName: "seamless", publicName: "seamless", isSignal: true, isRequired: false, transformFunction: null }, floatingToolbar: { classPropertyName: "floatingToolbar", publicName: "floatingToolbar", isSignal: true, isRequired: false, transformFunction: null }, showEditToggle: { classPropertyName: "showEditToggle", publicName: "showEditToggle", isSignal: true, isRequired: false, transformFunction: null }, spellcheck: { classPropertyName: "spellcheck", publicName: "spellcheck", isSignal: true, isRequired: false, transformFunction: null }, tiptapExtensions: { classPropertyName: "tiptapExtensions", publicName: "tiptapExtensions", isSignal: true, isRequired: false, transformFunction: null }, tiptapOptions: { classPropertyName: "tiptapOptions", publicName: "tiptapOptions", isSignal: true, isRequired: false, transformFunction: null }, starterKitOptions: { classPropertyName: "starterKitOptions", publicName: "starterKitOptions", isSignal: true, isRequired: false, transformFunction: null }, showBubbleMenu: { classPropertyName: "showBubbleMenu", publicName: "showBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, bubbleMenu: { classPropertyName: "bubbleMenu", publicName: "bubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, showImageBubbleMenu: { classPropertyName: "showImageBubbleMenu", publicName: "showImageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, imageBubbleMenu: { classPropertyName: "imageBubbleMenu", publicName: "imageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null }, showTableBubbleMenu: { classPropertyName: "showTableBubbleMenu", publicName: "showTableBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, tableBubbleMenu: { classPropertyName: "tableBubbleMenu", publicName: "tableBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, showCellBubbleMenu: { classPropertyName: "showCellBubbleMenu", publicName: "showCellBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, cellBubbleMenu: { classPropertyName: "cellBubbleMenu", publicName: "cellBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, stateCalculators: { classPropertyName: "stateCalculators", publicName: "stateCalculators", isSignal: true, isRequired: false, transformFunction: null }, imageUpload: { classPropertyName: "imageUpload", publicName: "imageUpload", isSignal: true, isRequired: false, transformFunction: null }, imageUploadHandler: { classPropertyName: "imageUploadHandler", publicName: "imageUploadHandler", isSignal: true, isRequired: false, transformFunction: null }, editorId: { classPropertyName: "editorId", publicName: "editorId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { contentChange: "contentChange", editorCreated: "editorCreated", editorUpdate: "editorUpdate", editorFocus: "editorFocus", editorBlur: "editorBlur", editableChange: "editableChange", imageUploaded: "imageUploaded", imageRejected: "imageRejected", pastedImageRejected: "pastedImageRejected" }, host: { properties: { "class.fill-container": "finalFillContainer()", "class.floating-toolbar": "finalFloatingToolbar()", "class.is-readonly": "!finalEditable() && !mergedDisabled()", "class.is-disabled": "mergedDisabled()", "style.--ate-border-width": "finalSeamless() || mergedDisabled() ? '0' : null", "style.--ate-background": "finalSeamless() ? 'transparent' : (mergedDisabled() ? 'var(--ate-surface-tertiary)' : null)", "style.--ate-toolbar-border-color": "finalSeamless() ? 'transparent' : null", "style.--ate-counter-background": "finalSeamless() ? 'transparent' : null", "style.--ate-counter-border-color": "finalSeamless() ? 'transparent' : null", "class.dark": "config().theme === 'dark'", "class.ate-blocks-inside": "finalBlockControls() === 'inside'", "class.ate-blocks-outside": "finalBlockControls() === 'outside'", "attr.data-theme": "config().theme" } }, providers: [
10683
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: AngularTiptapEditorComponent, isStandalone: true, selector: "angular-tiptap-editor", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, fillContainer: { classPropertyName: "fillContainer", publicName: "fillContainer", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showFooter: { classPropertyName: "showFooter", publicName: "showFooter", isSignal: true, isRequired: false, transformFunction: null }, showCharacterCount: { classPropertyName: "showCharacterCount", publicName: "showCharacterCount", isSignal: true, isRequired: false, transformFunction: null }, showWordCount: { classPropertyName: "showWordCount", publicName: "showWordCount", isSignal: true, isRequired: false, transformFunction: null }, maxCharacters: { classPropertyName: "maxCharacters", publicName: "maxCharacters", isSignal: true, isRequired: false, transformFunction: null }, enableOfficePaste: { classPropertyName: "enableOfficePaste", publicName: "enableOfficePaste", isSignal: true, isRequired: false, transformFunction: null }, enableSlashCommands: { classPropertyName: "enableSlashCommands", publicName: "enableSlashCommands", isSignal: true, isRequired: false, transformFunction: null }, enableBlockShortcuts: { classPropertyName: "enableBlockShortcuts", publicName: "enableBlockShortcuts", isSignal: true, isRequired: false, transformFunction: null }, slashCommands: { classPropertyName: "slashCommands", publicName: "slashCommands", isSignal: true, isRequired: false, transformFunction: null }, customSlashCommands: { classPropertyName: "customSlashCommands", publicName: "customSlashCommands", isSignal: true, isRequired: false, transformFunction: null }, blockControls: { classPropertyName: "blockControls", publicName: "blockControls", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, seamless: { classPropertyName: "seamless", publicName: "seamless", isSignal: true, isRequired: false, transformFunction: null }, floatingToolbar: { classPropertyName: "floatingToolbar", publicName: "floatingToolbar", isSignal: true, isRequired: false, transformFunction: null }, showEditToggle: { classPropertyName: "showEditToggle", publicName: "showEditToggle", isSignal: true, isRequired: false, transformFunction: null }, spellcheck: { classPropertyName: "spellcheck", publicName: "spellcheck", isSignal: true, isRequired: false, transformFunction: null }, tiptapExtensions: { classPropertyName: "tiptapExtensions", publicName: "tiptapExtensions", isSignal: true, isRequired: false, transformFunction: null }, tiptapOptions: { classPropertyName: "tiptapOptions", publicName: "tiptapOptions", isSignal: true, isRequired: false, transformFunction: null }, starterKitOptions: { classPropertyName: "starterKitOptions", publicName: "starterKitOptions", isSignal: true, isRequired: false, transformFunction: null }, showBubbleMenu: { classPropertyName: "showBubbleMenu", publicName: "showBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, bubbleMenu: { classPropertyName: "bubbleMenu", publicName: "bubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, showImageBubbleMenu: { classPropertyName: "showImageBubbleMenu", publicName: "showImageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, imageBubbleMenu: { classPropertyName: "imageBubbleMenu", publicName: "imageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null }, showTableBubbleMenu: { classPropertyName: "showTableBubbleMenu", publicName: "showTableBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, tableBubbleMenu: { classPropertyName: "tableBubbleMenu", publicName: "tableBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, showCellBubbleMenu: { classPropertyName: "showCellBubbleMenu", publicName: "showCellBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, cellBubbleMenu: { classPropertyName: "cellBubbleMenu", publicName: "cellBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, stateCalculators: { classPropertyName: "stateCalculators", publicName: "stateCalculators", isSignal: true, isRequired: false, transformFunction: null }, imageUpload: { classPropertyName: "imageUpload", publicName: "imageUpload", isSignal: true, isRequired: false, transformFunction: null }, paste: { classPropertyName: "paste", publicName: "paste", isSignal: true, isRequired: false, transformFunction: null }, imageUploadHandler: { classPropertyName: "imageUploadHandler", publicName: "imageUploadHandler", isSignal: true, isRequired: false, transformFunction: null }, imageSrcResolver: { classPropertyName: "imageSrcResolver", publicName: "imageSrcResolver", isSignal: true, isRequired: false, transformFunction: null }, editorId: { classPropertyName: "editorId", publicName: "editorId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { contentChange: "contentChange", editorCreated: "editorCreated", editorUpdate: "editorUpdate", editorFocus: "editorFocus", editorBlur: "editorBlur", editableChange: "editableChange", imageUploaded: "imageUploaded", imageRejected: "imageRejected", pastedImageRejected: "pastedImageRejected" }, host: { properties: { "class.fill-container": "finalFillContainer()", "class.floating-toolbar": "finalFloatingToolbar()", "class.is-readonly": "!finalEditable() && !mergedDisabled()", "class.is-disabled": "mergedDisabled()", "style.--ate-border-width": "finalSeamless() || mergedDisabled() ? '0' : null", "style.--ate-background": "finalSeamless() ? 'transparent' : (mergedDisabled() ? 'var(--ate-surface-tertiary)' : null)", "style.--ate-toolbar-border-color": "finalSeamless() ? 'transparent' : null", "style.--ate-counter-background": "finalSeamless() ? 'transparent' : null", "style.--ate-counter-border-color": "finalSeamless() ? 'transparent' : null", "class.dark": "config().theme === 'dark'", "class.ate-blocks-inside": "finalBlockControls() === 'inside'", "class.ate-blocks-outside": "finalBlockControls() === 'outside'", "attr.data-theme": "config().theme" } }, providers: [
10416
10684
  AteEditorCommandsService,
10417
10685
  AteImageService,
10418
10686
  AteColorPickerService,
10419
10687
  AteLinkService,
10420
10688
  AteExportService,
10689
+ AteEditorI18n,
10421
10690
  ], viewQueries: [{ propertyName: "editorElement", first: true, predicate: ["editorElement"], descendants: true, isSignal: true }], hostDirectives: [{ directive: AteNoopValueAccessorDirective }], ngImport: i0, template: `
10422
10691
  <div class="ate-editor">
10423
10692
  <!-- Toolbar -->
@@ -10587,6 +10856,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
10587
10856
  AteColorPickerService,
10588
10857
  AteLinkService,
10589
10858
  AteExportService,
10859
+ AteEditorI18n,
10590
10860
  ], template: `
10591
10861
  <div class="ate-editor">
10592
10862
  <!-- Toolbar -->
@@ -10908,5 +11178,5 @@ const ATE_DEFAULT_TOC_CONFIG = {
10908
11178
  * Generated bundle index. Do not edit.
10909
11179
  */
10910
11180
 
10911
- export { ATE_BUBBLE_MENU_KEYS, ATE_CELL_BUBBLE_MENU_KEYS, ATE_DEFAULT_BUBBLE_MENU_CONFIG, ATE_DEFAULT_CELL_MENU_CONFIG, ATE_DEFAULT_CONFIG, ATE_DEFAULT_ICONS, ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, ATE_DEFAULT_IMAGE_UPLOAD_CONFIG, ATE_DEFAULT_SLASH_COMMANDS_CONFIG, ATE_DEFAULT_TABLE_MENU_CONFIG, ATE_DEFAULT_TOC_CONFIG, ATE_DEFAULT_TOOLBAR_CONFIG, ATE_GLOBAL_CONFIG, ATE_ICONS, ATE_IMAGE_BUBBLE_MENU_KEYS, ATE_INITIAL_EDITOR_STATE, ATE_MATERIAL_SYMBOLS_VIEWBOX, ATE_SLASH_COMMAND_KEYS, ATE_STROKE_ICONS, ATE_TABLE_BUBBLE_MENU_KEYS, ATE_TOOLBAR_KEYS, AngularTiptapEditorComponent, AteAngularNodeView, AteBlockShortcuts, AteColorPickerService, AteDiscoveryCalculator, AteEditorCommandsService, AteEditorRef, AteEditorRegistry, AteExportService, AteI18nService, AteIconComponent, AteIconRegistry, AteImageCalculator, AteImageService, AteImageValidationError, AteLinkService, AteLinkShortcut, AteMarksCalculator, AteNodeViewRenderer, AteNoopValueAccessorDirective, AtePastedImage, AteSelectionCalculator, AteStructureCalculator, AteTableCalculator, AteTableOfContentsComponent, AteTocNodeComponent, AteTocNodeOptions, AteTooltipDirective, classifyPastedImageSrc, createAngularComponentExtension, createDefaultSlashCommands, dataUriToFile, filterSlashCommands, getAteTocNodeExtension, isLinkShortcut, provideAteEditor, provideAteIcons, registerAngularComponent, sanitizePastedImages };
11181
+ export { ATE_BUBBLE_MENU_KEYS, ATE_CELL_BUBBLE_MENU_KEYS, ATE_DEFAULT_BUBBLE_MENU_CONFIG, ATE_DEFAULT_CELL_MENU_CONFIG, ATE_DEFAULT_CONFIG, ATE_DEFAULT_ICONS, ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, ATE_DEFAULT_IMAGE_UPLOAD_CONFIG, ATE_DEFAULT_PASTE_CONFIG, ATE_DEFAULT_SLASH_COMMANDS_CONFIG, ATE_DEFAULT_TABLE_MENU_CONFIG, ATE_DEFAULT_TOC_CONFIG, ATE_DEFAULT_TOOLBAR_CONFIG, ATE_GLOBAL_CONFIG, ATE_ICONS, ATE_IMAGE_BUBBLE_MENU_KEYS, ATE_INITIAL_EDITOR_STATE, ATE_MATERIAL_SYMBOLS_VIEWBOX, ATE_SLASH_COMMAND_KEYS, ATE_STROKE_ICONS, ATE_TABLE_BUBBLE_MENU_KEYS, ATE_TOOLBAR_KEYS, AngularTiptapEditorComponent, AteAngularNodeView, AteBlockShortcuts, AteColorPickerService, AteDiscoveryCalculator, AteEditorCommandsService, AteEditorRef, AteEditorRegistry, AteExportService, AteI18nService, AteIconComponent, AteIconRegistry, AteImageCalculator, AteImageService, AteImageValidationError, AteLinkService, AteLinkShortcut, AteMarksCalculator, AteNodeViewRenderer, AteNoopValueAccessorDirective, AtePastedImage, AteSelectionCalculator, AteStructureCalculator, AteTableCalculator, AteTableOfContentsComponent, AteTocNodeComponent, AteTocNodeOptions, AteTooltipDirective, classifyPastedImageSrc, createAngularComponentExtension, createDefaultSlashCommands, dataUriToFile, filterSlashCommands, getAteTocNodeExtension, isLinkShortcut, provideAteEditor, provideAteIcons, registerAngularComponent, sanitizePastedImages };
10912
11182
  //# sourceMappingURL=flogeez-angular-tiptap-editor.mjs.map