@flogeez/angular-tiptap-editor 3.6.0 → 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 (45) hide show
  1. package/CHANGELOG.md +18 -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/directives/ate-tooltip.directive.mjs +37 -2
  14. package/esm2022/lib/extensions/ate-paste.extension.mjs +125 -0
  15. package/esm2022/lib/extensions/ate-resizable-image.extension.mjs +44 -3
  16. package/esm2022/lib/models/ate-editor-config.model.mjs +1 -1
  17. package/esm2022/lib/models/ate-image.model.mjs +1 -1
  18. package/esm2022/lib/models/ate-paste.model.mjs +2 -0
  19. package/esm2022/lib/services/ate-editor-commands.service.mjs +7 -1
  20. package/esm2022/lib/services/ate-editor-i18n.service.mjs +36 -0
  21. package/esm2022/lib/services/ate-image.service.mjs +12 -5
  22. package/esm2022/lib/utils/ate-extensions.utils.mjs +3 -1
  23. package/esm2022/lib/utils/ate-image-src-resolver.utils.mjs +21 -0
  24. package/esm2022/public-api.mjs +2 -1
  25. package/fesm2022/flogeez-angular-tiptap-editor.mjs +333 -28
  26. package/fesm2022/flogeez-angular-tiptap-editor.mjs.map +1 -1
  27. package/lib/components/bubble-menus/base/ate-base-bubble-menu.d.ts +2 -2
  28. package/lib/components/bubble-menus/base/ate-base-sub-bubble-menu.d.ts +2 -2
  29. package/lib/components/bubble-menus/table/ate-cell-bubble-menu.component.d.ts +1 -1
  30. package/lib/components/editor/angular-tiptap-editor.component.d.ts +18 -2
  31. package/lib/components/editor/ate-block-controls.component.d.ts +2 -2
  32. package/lib/components/slash-commands/ate-slash-commands.component.d.ts +2 -2
  33. package/lib/config/ate-editor.config.d.ts +3 -0
  34. package/lib/config/ate-slash-commands.config.d.ts +2 -2
  35. package/lib/extensions/ate-paste.extension.d.ts +24 -0
  36. package/lib/extensions/ate-resizable-image.extension.d.ts +10 -1
  37. package/lib/models/ate-editor-config.model.d.ts +6 -1
  38. package/lib/models/ate-image.model.d.ts +12 -0
  39. package/lib/models/ate-paste.model.d.ts +36 -0
  40. package/lib/services/ate-editor-commands.service.d.ts +3 -1
  41. package/lib/services/ate-editor-i18n.service.d.ts +208 -0
  42. package/lib/services/ate-image.service.d.ts +3 -1
  43. package/lib/utils/ate-image-src-resolver.utils.d.ts +2 -0
  44. package/package.json +1 -1
  45. 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();
@@ -3608,6 +3821,14 @@ const AteTiptapStateExtension = Extension.create({
3608
3821
 
3609
3822
  // Pre-calculate platform once to avoid repeated checks
3610
3823
  const IS_MAC = typeof window !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
3824
+ const DURATION = [100, 100];
3825
+ // After a tooltip closes, the next one still opens at once if hovered within this gap
3826
+ const WARM_GAP = 300;
3827
+ // Shared by every tooltip: the first one waits for its delay, the next ones open at once,
3828
+ // without animation, as long as the pointer moves from one control to the next.
3829
+ let openTooltip = null;
3830
+ let warm = false;
3831
+ let coolTimeout;
3611
3832
  class AteTooltipDirective {
3612
3833
  constructor() {
3613
3834
  this.el = inject(ElementRef);
@@ -3699,7 +3920,7 @@ class AteTooltipDirective {
3699
3920
  allowHTML,
3700
3921
  placement,
3701
3922
  delay,
3702
- duration: [100, 100],
3923
+ duration: DURATION,
3703
3924
  offset: [0, 6],
3704
3925
  theme: "ate-tooltip",
3705
3926
  touch: ["hold", 500],
@@ -3708,6 +3929,30 @@ class AteTooltipDirective {
3708
3929
  aria: {
3709
3930
  content: "describedby",
3710
3931
  },
3932
+ onTrigger: (instance, event) => {
3933
+ const base = this.delay();
3934
+ const instant = warm && event.type === "mouseenter";
3935
+ instance.setProps({
3936
+ delay: instant ? [0, Array.isArray(base) ? base[1] : base] : base,
3937
+ duration: instant ? [0, DURATION[1]] : DURATION,
3938
+ });
3939
+ },
3940
+ onShow: instance => {
3941
+ // Only one tooltip at a time: the previous one goes away at once
3942
+ if (openTooltip && openTooltip !== instance) {
3943
+ openTooltip.unmount();
3944
+ }
3945
+ openTooltip = instance;
3946
+ warm = true;
3947
+ clearTimeout(coolTimeout);
3948
+ },
3949
+ onHidden: instance => {
3950
+ if (openTooltip !== instance) {
3951
+ return;
3952
+ }
3953
+ openTooltip = null;
3954
+ coolTimeout = setTimeout(() => (warm = false), WARM_GAP);
3955
+ },
3711
3956
  popperOptions: {
3712
3957
  modifiers: [
3713
3958
  {
@@ -3723,6 +3968,9 @@ class AteTooltipDirective {
3723
3968
  }
3724
3969
  destroyTippy() {
3725
3970
  if (this.tippyInstance) {
3971
+ if (openTooltip === this.tippyInstance) {
3972
+ openTooltip = null;
3973
+ }
3726
3974
  this.tippyInstance.destroy();
3727
3975
  this.tippyInstance = null;
3728
3976
  }
@@ -3868,7 +4116,7 @@ class AteImageService {
3868
4116
  this.isImageSelected = computed(() => this.selectedImage() !== null);
3869
4117
  /** Resizing state */
3870
4118
  this.isResizing = signal(false);
3871
- this.i18n = inject(AteI18nService);
4119
+ this.i18n = inject(AteEditorI18n);
3872
4120
  this.t = this.i18n.imageUpload;
3873
4121
  /** Upload state signals */
3874
4122
  this.isUploading = signal(false);
@@ -3879,6 +4127,7 @@ class AteImageService {
3879
4127
  * If set, this handler replaces the default base64 conversion.
3880
4128
  */
3881
4129
  this.uploadHandler = null;
4130
+ this.srcResolver = null;
3882
4131
  this.currentEditor = null;
3883
4132
  }
3884
4133
  /** Select and track an image from the editor */
@@ -3975,7 +4224,10 @@ class AteImageService {
3975
4224
  img.onload = () => {
3976
4225
  this.resizeImage(editor, { width: img.naturalWidth, height: img.naturalHeight });
3977
4226
  };
3978
- 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
+ });
3979
4231
  }
3980
4232
  /** Get current image dimensions */
3981
4233
  getImageDimensions(editor) {
@@ -4001,8 +4253,10 @@ class AteImageService {
4001
4253
  if (!attrs || !attrs["src"]) {
4002
4254
  return;
4003
4255
  }
4004
- const src = attrs["src"];
4005
4256
  const fileName = attrs["alt"] || "image";
4257
+ ateResolveImageSrc(this.srcResolver, attrs["src"]).then(src => this.downloadResolvedImage(src, fileName));
4258
+ }
4259
+ downloadResolvedImage(src, fileName) {
4006
4260
  // Detect if it's base64 or remote URL
4007
4261
  if (src.startsWith("data:") ||
4008
4262
  src.startsWith("blob:") ||
@@ -5031,6 +5285,12 @@ class AteEditorCommandsService {
5031
5285
  set uploadHandler(handler) {
5032
5286
  this.imageService.uploadHandler = handler;
5033
5287
  }
5288
+ get srcResolver() {
5289
+ return this.imageService.srcResolver;
5290
+ }
5291
+ set srcResolver(resolver) {
5292
+ this.imageService.srcResolver = resolver;
5293
+ }
5034
5294
  /** Update state (called by TiptapStateExtension) */
5035
5295
  updateState(state) {
5036
5296
  this._editorState.set(state);
@@ -5464,7 +5724,7 @@ class AteColorPickerComponent {
5464
5724
  this.disabled = input(false);
5465
5725
  this.anchorToText = input(false);
5466
5726
  this.colorPickerSvc = inject(AteColorPickerService);
5467
- this.i18nService = inject(AteI18nService);
5727
+ this.i18nService = inject(AteEditorI18n);
5468
5728
  this.editorCommands = inject(AteEditorCommandsService);
5469
5729
  this.t = this.i18nService.toolbar;
5470
5730
  this.state = this.editorCommands.editorState;
@@ -5578,7 +5838,7 @@ class AteToolbarComponent {
5578
5838
  this.config = input.required();
5579
5839
  this.imageUpload = input({});
5580
5840
  this.floating = input(false);
5581
- this.i18nService = inject(AteI18nService);
5841
+ this.i18nService = inject(AteEditorI18n);
5582
5842
  this.editorCommands = inject(AteEditorCommandsService);
5583
5843
  this.t = this.i18nService.toolbar;
5584
5844
  this.state = this.editorCommands.editorState;
@@ -6183,6 +6443,11 @@ const ATE_DEFAULT_IMAGE_UPLOAD_CONFIG = {
6183
6443
  allowedTypes: ["image/jpeg", "image/png", "image/gif", "image/webp"],
6184
6444
  quality: 0.8,
6185
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
+ };
6186
6451
  /**
6187
6452
  * Ultimate default configuration for the Angular Tiptap Editor.
6188
6453
  * This serves as the 'Level 4' fallback in the configuration hierarchy:
@@ -6221,6 +6486,7 @@ const ATE_DEFAULT_CONFIG = {
6221
6486
  bubbleMenu: ATE_DEFAULT_BUBBLE_MENU_CONFIG,
6222
6487
  imageBubbleMenu: ATE_DEFAULT_IMAGE_BUBBLE_MENU_CONFIG,
6223
6488
  imageUpload: ATE_DEFAULT_IMAGE_UPLOAD_CONFIG,
6489
+ paste: ATE_DEFAULT_PASTE_CONFIG,
6224
6490
  tableBubbleMenu: ATE_DEFAULT_TABLE_MENU_CONFIG,
6225
6491
  cellBubbleMenu: ATE_DEFAULT_CELL_MENU_CONFIG,
6226
6492
  slashCommands: {},
@@ -6237,7 +6503,7 @@ const ATE_DEFAULT_CONFIG = {
6237
6503
  */
6238
6504
  class AteBaseBubbleMenu {
6239
6505
  constructor() {
6240
- this.i18nService = inject(AteI18nService);
6506
+ this.i18nService = inject(AteEditorI18n);
6241
6507
  this.editorCommands = inject(AteEditorCommandsService);
6242
6508
  // Core Inputs
6243
6509
  this.editor = input.required();
@@ -7442,7 +7708,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
7442
7708
  */
7443
7709
  class AteBaseSubBubbleMenu {
7444
7710
  constructor() {
7445
- this.i18nService = inject(AteI18nService);
7711
+ this.i18nService = inject(AteEditorI18n);
7446
7712
  this.editorCommands = inject(AteEditorCommandsService);
7447
7713
  // Core Inputs
7448
7714
  this.editor = input.required();
@@ -8337,7 +8603,7 @@ function filterSlashCommands(config, i18n, commands, imageOptions) {
8337
8603
  // Default command definitions are now centralized in src/lib/config/slash-commands.config.ts
8338
8604
  class AteSlashCommandsComponent {
8339
8605
  constructor() {
8340
- this.i18nService = inject(AteI18nService);
8606
+ this.i18nService = inject(AteEditorI18n);
8341
8607
  this.editor = input.required();
8342
8608
  this.config = input(undefined);
8343
8609
  this.menuRef = viewChild.required("menuRef");
@@ -8683,7 +8949,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
8683
8949
  */
8684
8950
  class AteBlockControlsComponent {
8685
8951
  constructor() {
8686
- this.i18n = inject(AteI18nService);
8952
+ this.i18n = inject(AteEditorI18n);
8687
8953
  this.editor = input.required();
8688
8954
  this.hoveredData = input(null);
8689
8955
  this.menuRef = viewChild.required("menuRef");
@@ -9521,6 +9787,8 @@ const ATE_BUILTIN_UI_DEPENDENCIES = {
9521
9787
  ateLinkShortcut: "Mod+K link editor shortcut",
9522
9788
  highlight: "toolbar/bubble menu highlight picker, AteMarksCalculator",
9523
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",
9524
9792
  atePastedImage: "pasted HTML image sanitization and base64 re-hosting",
9525
9793
  ateUploadProgress: "image upload progress overlay",
9526
9794
  tableExtension: "toolbar/slash menu table insertion, table/cell bubble menus, AteTableCalculator",
@@ -9635,6 +9903,11 @@ class AngularTiptapEditorComponent {
9635
9903
  this.stateCalculators = input(undefined);
9636
9904
  // Nouveau input pour la configuration de l'upload d'images
9637
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);
9638
9911
  /**
9639
9912
  * Custom handler for image uploads.
9640
9913
  * When provided, images will be processed through this handler instead of being converted to base64.
@@ -9655,6 +9928,11 @@ class AngularTiptapEditorComponent {
9655
9928
  * ```
9656
9929
  */
9657
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);
9658
9936
  /**
9659
9937
  * Optional unique identifier for the editor instance in the registry.
9660
9938
  * If not specified, a unique ID is automatically generated.
@@ -9814,7 +10092,7 @@ class AngularTiptapEditorComponent {
9814
10092
  ? fromInputOptions
9815
10093
  : fromConfigOptions;
9816
10094
  return {
9817
- commands: filterSlashCommands(baseConfig || {}, this.i18nService, this.editorCommandsService, this.finalImageUploadConfig()),
10095
+ commands: filterSlashCommands(baseConfig || {}, this.editorI18n, this.editorCommandsService, this.finalImageUploadConfig()),
9818
10096
  };
9819
10097
  });
9820
10098
  // Behavior
@@ -9833,6 +10111,12 @@ class AngularTiptapEditorComponent {
9833
10111
  this.finalAngularNodesConfig = computed(() => this.effectiveConfig().angularNodes ?? [], {
9834
10112
  equal: shallowArrayEqual,
9835
10113
  });
10114
+ // Paste
10115
+ this.finalPasteConfig = computed(() => ({
10116
+ ...ATE_DEFAULT_PASTE_CONFIG,
10117
+ ...this.effectiveConfig().paste,
10118
+ ...this.paste(),
10119
+ }));
9836
10120
  // Image Upload
9837
10121
  this.finalImageUploadConfig = computed(() => {
9838
10122
  const fromInput = this.imageUpload();
@@ -9848,15 +10132,11 @@ class AngularTiptapEditorComponent {
9848
10132
  maxSize: (merged.maxSize ?? 5) * 1024 * 1024, // Convert MB to bytes for internal service
9849
10133
  };
9850
10134
  });
10135
+ this.finalImageSrcResolver = computed(() => this.imageSrcResolver() ?? this.effectiveConfig().imageSrcResolver);
9851
10136
  this.finalImageUploadHandler = computed(() => this.imageUploadHandler() ?? this.effectiveConfig().imageUpload?.handler);
9852
- // Computed for current translations (allows per-instance override via config or input)
9853
- this.currentTranslations = computed(() => {
9854
- const localeOverride = this.finalLocale();
9855
- if (localeOverride) {
9856
- return this.i18nService.getTranslations(localeOverride) ?? this.i18nService.translations();
9857
- }
9858
- return this.i18nService.translations();
9859
- });
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;
9860
10140
  this._destroyRef = inject(DestroyRef);
9861
10141
  // NgControl for management of FormControls
9862
10142
  this.ngControl = inject(NgControl, { self: true, optional: true });
@@ -9880,6 +10160,7 @@ class AngularTiptapEditorComponent {
9880
10160
  const fromGlobal = this.globalConfig || {};
9881
10161
  return { ...ATE_DEFAULT_CONFIG, ...fromGlobal, ...fromInput };
9882
10162
  });
10163
+ this.editorI18n.bindLocale(this.finalLocale);
9883
10164
  // Effect to update editor content (with anti-echo)
9884
10165
  effect(() => {
9885
10166
  const content = this.content(); // Sole reactive dependency
@@ -9935,6 +10216,23 @@ class AngularTiptapEditorComponent {
9935
10216
  const handler = this.finalImageUploadHandler();
9936
10217
  this.editorCommandsService.uploadHandler = handler || null;
9937
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
+ });
9938
10236
  // Effect to update character count limit dynamically
9939
10237
  effect(() => {
9940
10238
  const editor = this.editor();
@@ -9993,6 +10291,7 @@ class AngularTiptapEditorComponent {
9993
10291
  }
9994
10292
  }
9995
10293
  initEditor() {
10294
+ this.editorCommandsService.srcResolver = this.finalImageSrcResolver() ?? null;
9996
10295
  const starterKitOptions = this.finalStarterKitOptions();
9997
10296
  const customExtensions = this.finalTiptapExtensions();
9998
10297
  const customNames = new Set(customExtensions.map(getExtensionName).filter((name) => !!name));
@@ -10038,7 +10337,7 @@ class AngularTiptapEditorComponent {
10038
10337
  types: ["textStyle"],
10039
10338
  }),
10040
10339
  Placeholder.configure({
10041
- placeholder: this.finalPlaceholder(),
10340
+ placeholder: () => this.finalPlaceholder(),
10042
10341
  }),
10043
10342
  Superscript,
10044
10343
  Subscript,
@@ -10049,6 +10348,9 @@ class AngularTiptapEditorComponent {
10049
10348
  AteBlockShortcuts.configure({
10050
10349
  enabled: () => this.finalEnableBlockShortcuts(),
10051
10350
  }),
10351
+ AtePaste.configure({
10352
+ config: () => this.finalPasteConfig(),
10353
+ }),
10052
10354
  AteLinkShortcut.configure({
10053
10355
  canOpen: () => this.editorCommandsService.editorState().can.toggleLink,
10054
10356
  open: editor => this.editorCommandsService.execute(editor, "toggleLink"),
@@ -10065,6 +10367,7 @@ class AngularTiptapEditorComponent {
10065
10367
  HTMLAttributes: {
10066
10368
  class: "ate-image",
10067
10369
  },
10370
+ srcResolver: () => this.editorCommandsService.srcResolver,
10068
10371
  }),
10069
10372
  AtePastedImage.configure({
10070
10373
  shouldRehost: () => this.editorCommandsService.hasUploadHandler,
@@ -10377,12 +10680,13 @@ class AngularTiptapEditorComponent {
10377
10680
  }
10378
10681
  }
10379
10682
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AngularTiptapEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10380
- 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: [
10381
10684
  AteEditorCommandsService,
10382
10685
  AteImageService,
10383
10686
  AteColorPickerService,
10384
10687
  AteLinkService,
10385
10688
  AteExportService,
10689
+ AteEditorI18n,
10386
10690
  ], viewQueries: [{ propertyName: "editorElement", first: true, predicate: ["editorElement"], descendants: true, isSignal: true }], hostDirectives: [{ directive: AteNoopValueAccessorDirective }], ngImport: i0, template: `
10387
10691
  <div class="ate-editor">
10388
10692
  <!-- Toolbar -->
@@ -10552,6 +10856,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
10552
10856
  AteColorPickerService,
10553
10857
  AteLinkService,
10554
10858
  AteExportService,
10859
+ AteEditorI18n,
10555
10860
  ], template: `
10556
10861
  <div class="ate-editor">
10557
10862
  <!-- Toolbar -->
@@ -10873,5 +11178,5 @@ const ATE_DEFAULT_TOC_CONFIG = {
10873
11178
  * Generated bundle index. Do not edit.
10874
11179
  */
10875
11180
 
10876
- 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 };
10877
11182
  //# sourceMappingURL=flogeez-angular-tiptap-editor.mjs.map