@flogeez/angular-tiptap-editor 0.4.0 → 0.5.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.
@@ -361,9 +361,9 @@ const UploadProgress = Extension.create({
361
361
  max-width: 400px;
362
362
  }
363
363
  .upload-skeleton {
364
- background: #f8f9fa;
365
- border: 2px dashed #e2e8f0;
366
- border-radius: 8px;
364
+ background: var(--ate-surface-secondary);
365
+ border: 2px dashed var(--ate-border-color);
366
+ border-radius: var(--ate-border-radius);
367
367
  padding: 16px;
368
368
  display: flex;
369
369
  align-items: center;
@@ -372,8 +372,8 @@ const UploadProgress = Extension.create({
372
372
  animation: pulse 2s infinite;
373
373
  }
374
374
  @keyframes pulse {
375
- 0%, 100% { background-color: #f8f9fa; }
376
- 50% { background-color: #f1f5f9; }
375
+ 0%, 100% { background-color: var(--ate-surface-secondary); }
376
+ 50% { background-color: var(--ate-surface-tertiary); }
377
377
  }
378
378
  .upload-content {
379
379
  display: flex;
@@ -388,9 +388,9 @@ const UploadProgress = Extension.create({
388
388
  justify-content: center;
389
389
  width: 48px;
390
390
  height: 48px;
391
- background: #e6f3ff;
391
+ background: var(--ate-primary-light);
392
392
  border-radius: 50%;
393
- color: #3182ce;
393
+ color: var(--ate-primary);
394
394
  }
395
395
  .upload-icon .material-symbols-outlined {
396
396
  font-size: 24px;
@@ -411,25 +411,25 @@ const UploadProgress = Extension.create({
411
411
  }
412
412
  .upload-message {
413
413
  font-size: 14px;
414
- color: #4a5568;
414
+ color: var(--ate-text);
415
415
  font-weight: 500;
416
416
  }
417
417
  .progress-bar {
418
418
  width: 100%;
419
419
  height: 6px;
420
- background: #e2e8f0;
420
+ background: var(--ate-border-color);
421
421
  border-radius: 3px;
422
422
  overflow: hidden;
423
423
  }
424
424
  .progress-fill {
425
425
  height: 100%;
426
- background: linear-gradient(90deg, #3182ce, #4299e1);
426
+ background: var(--ate-primary);
427
427
  border-radius: 3px;
428
428
  transition: width 0.3s ease;
429
429
  }
430
430
  .progress-text {
431
431
  font-size: 12px;
432
- color: #718096;
432
+ color: var(--ate-text-secondary);
433
433
  font-weight: 500;
434
434
  }
435
435
  `;
@@ -548,6 +548,7 @@ class TiptapButtonComponent {
548
548
  [class.small]="size() === 'small'"
549
549
  [class.medium]="size() === 'medium'"
550
550
  [class.large]="size() === 'large'"
551
+ [class.has-custom-color]="!!color()"
551
552
  [disabled]="disabled()"
552
553
  [style.color]="color()"
553
554
  [attr.title]="title()"
@@ -564,7 +565,7 @@ class TiptapButtonComponent {
564
565
  >
565
566
  <ng-content></ng-content>
566
567
  </button>
567
- `, isInline: true, styles: [".tiptap-button{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:transparent;border-radius:8px;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);color:#64748b;position:relative;overflow:hidden}.tiptap-button:before{content:\"\";position:absolute;inset:0;background:linear-gradient(135deg,#6366f1,#8b5cf6);opacity:0;transition:opacity .2s ease;border-radius:8px}.tiptap-button:hover{color:#6366f1;transform:translateY(-1px)}.tiptap-button:hover:before{opacity:.1}.tiptap-button:active{transform:translateY(0)}.tiptap-button.is-active{color:#6366f1;background:#6366f11a}.tiptap-button.is-active:before{opacity:.15}.tiptap-button.is-active:hover{background:#6366f126}.tiptap-button:disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.tiptap-button:disabled:hover{transform:none;color:#64748b}.tiptap-button:disabled:before{opacity:0}.tiptap-button .material-symbols-outlined{font-size:20px;position:relative;z-index:1}.tiptap-button .material-symbols-outlined.icon-small{font-size:16px}.tiptap-button .material-symbols-outlined.icon-medium{font-size:20px}.tiptap-button .material-symbols-outlined.icon-large{font-size:24px}.tiptap-button.text-button{width:auto;padding:0 12px;font-size:14px;font-weight:500}.tiptap-button.color-button{width:28px;height:28px;border-radius:50%;border:2px solid transparent;transition:all .2s ease}.tiptap-button.color-button:hover{border-color:#e2e8f0;transform:scale(1.1)}.tiptap-button.color-button.is-active{border-color:#6366f1;box-shadow:0 0 0 2px #6366f133}.tiptap-button.primary{background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff}.tiptap-button.primary:hover{background:linear-gradient(135deg,#5b21b6,#7c3aed);color:#fff}.tiptap-button.secondary{background:#f1f5f9;color:#64748b}.tiptap-button.secondary:hover{background:#e2e8f0;color:#475569}.tiptap-button.danger{color:#ef4444}.tiptap-button.danger:hover{color:#dc2626;background:#ef44441a}.tiptap-button.danger:before{background:linear-gradient(135deg,#ef4444,#dc2626)}.tiptap-button.small{width:24px;height:24px}.tiptap-button.medium{width:32px;height:32px}.tiptap-button.large{width:40px;height:40px}.tiptap-button.has-badge{position:relative}.tiptap-button .badge{position:absolute;top:-4px;right:-4px;background:#ef4444;color:#fff;font-size:10px;padding:2px 4px;border-radius:8px;min-width:16px;text-align:center;line-height:1}.tiptap-button.has-tooltip{position:relative}.tiptap-button .tooltip{position:absolute;bottom:-30px;left:50%;transform:translate(-50%);background:#000c;color:#fff;padding:4px 8px;border-radius:4px;font-size:12px;white-space:nowrap;opacity:0;visibility:hidden;transition:all .2s ease;z-index:1000}.tiptap-button:hover .tooltip{opacity:1;visibility:visible}@keyframes pulse{0%,to{box-shadow:0 0 #6366f166}50%{box-shadow:0 0 0 4px #6366f100}}.tiptap-button.is-active.pulse{animation:pulse 2s infinite}@media (max-width: 768px){.tiptap-button{width:32px;height:32px}.tiptap-button .material-symbols-outlined{font-size:18px}.tiptap-button.text-button{padding:0 8px;font-size:13px}}\n"] }); }
568
+ `, isInline: true, styles: [".tiptap-button{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:transparent;border-radius:var(--ate-border-radius, 8px);cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);color:var(--ate-toolbar-button-color, var(--ate-text-secondary));position:relative;overflow:hidden}.tiptap-button:before{content:\"\";position:absolute;inset:0;background:var(--ate-primary);opacity:0;transition:opacity .2s ease;border-radius:var(--ate-border-radius, 8px)}.tiptap-button:hover:not(.has-custom-color){color:var(--ate-toolbar-button-active-color, var(--ate-primary));background:var(--ate-toolbar-button-hover-background, transparent);transform:translateY(-1px)}.tiptap-button.has-custom-color:hover{background:var(--ate-toolbar-button-hover-background, transparent);transform:translateY(-1px)}.tiptap-button:hover:before{opacity:.1}.tiptap-button:active{transform:translateY(0)}.tiptap-button.is-active:not(.has-custom-color){color:var(--ate-toolbar-button-active-color, var(--ate-primary));background:var(--ate-toolbar-button-active-background, var(--ate-primary-light))}.tiptap-button.is-active.has-custom-color{background:var(--ate-toolbar-button-active-background, var(--ate-primary-light))}.tiptap-button:disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.tiptap-button .material-symbols-outlined{font-size:20px;position:relative;z-index:1}.tiptap-button .material-symbols-outlined.icon-small{font-size:16px}.tiptap-button .material-symbols-outlined.icon-medium{font-size:20px}.tiptap-button .material-symbols-outlined.icon-large{font-size:24px}.tiptap-button.text-button{width:auto;padding:0 12px;font-size:14px;font-weight:500;gap:8px}.tiptap-button.color-button{width:28px;height:28px;border-radius:50%;border:2px solid transparent;transition:all .2s ease}.tiptap-button.color-button:hover{border-color:var(--ate-border);transform:scale(1.1)}.tiptap-button.color-button.is-active{border-color:var(--ate-primary);box-shadow:0 0 0 2px var(--ate-primary-light)}.tiptap-button.danger{color:var(--ate-error-color, #ef4444)}.tiptap-button.danger:hover{background:var(--ate-error-bg, rgba(239, 68, 68, .1))}.tiptap-button.danger:before{background:var(--ate-error-color, #ef4444)}.tiptap-button.small{width:24px;height:24px}.tiptap-button.medium{width:32px;height:32px}.tiptap-button.large{width:40px;height:40px}@keyframes pulse{0%,to{box-shadow:0 0 0 0 var(--ate-primary-light-alpha)}50%{box-shadow:0 0 0 4px transparent}}.tiptap-button.is-active.pulse{animation:pulse 2s infinite}@media (max-width: 768px){.tiptap-button{width:32px;height:32px}.tiptap-button .material-symbols-outlined{font-size:18px}.tiptap-button.text-button{padding:0 8px;font-size:13px}}\n"] }); }
568
569
  }
569
570
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: TiptapButtonComponent, decorators: [{
570
571
  type: Component,
@@ -578,6 +579,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
578
579
  [class.small]="size() === 'small'"
579
580
  [class.medium]="size() === 'medium'"
580
581
  [class.large]="size() === 'large'"
582
+ [class.has-custom-color]="!!color()"
581
583
  [disabled]="disabled()"
582
584
  [style.color]="color()"
583
585
  [attr.title]="title()"
@@ -594,7 +596,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
594
596
  >
595
597
  <ng-content></ng-content>
596
598
  </button>
597
- `, styles: [".tiptap-button{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:transparent;border-radius:8px;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);color:#64748b;position:relative;overflow:hidden}.tiptap-button:before{content:\"\";position:absolute;inset:0;background:linear-gradient(135deg,#6366f1,#8b5cf6);opacity:0;transition:opacity .2s ease;border-radius:8px}.tiptap-button:hover{color:#6366f1;transform:translateY(-1px)}.tiptap-button:hover:before{opacity:.1}.tiptap-button:active{transform:translateY(0)}.tiptap-button.is-active{color:#6366f1;background:#6366f11a}.tiptap-button.is-active:before{opacity:.15}.tiptap-button.is-active:hover{background:#6366f126}.tiptap-button:disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.tiptap-button:disabled:hover{transform:none;color:#64748b}.tiptap-button:disabled:before{opacity:0}.tiptap-button .material-symbols-outlined{font-size:20px;position:relative;z-index:1}.tiptap-button .material-symbols-outlined.icon-small{font-size:16px}.tiptap-button .material-symbols-outlined.icon-medium{font-size:20px}.tiptap-button .material-symbols-outlined.icon-large{font-size:24px}.tiptap-button.text-button{width:auto;padding:0 12px;font-size:14px;font-weight:500}.tiptap-button.color-button{width:28px;height:28px;border-radius:50%;border:2px solid transparent;transition:all .2s ease}.tiptap-button.color-button:hover{border-color:#e2e8f0;transform:scale(1.1)}.tiptap-button.color-button.is-active{border-color:#6366f1;box-shadow:0 0 0 2px #6366f133}.tiptap-button.primary{background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff}.tiptap-button.primary:hover{background:linear-gradient(135deg,#5b21b6,#7c3aed);color:#fff}.tiptap-button.secondary{background:#f1f5f9;color:#64748b}.tiptap-button.secondary:hover{background:#e2e8f0;color:#475569}.tiptap-button.danger{color:#ef4444}.tiptap-button.danger:hover{color:#dc2626;background:#ef44441a}.tiptap-button.danger:before{background:linear-gradient(135deg,#ef4444,#dc2626)}.tiptap-button.small{width:24px;height:24px}.tiptap-button.medium{width:32px;height:32px}.tiptap-button.large{width:40px;height:40px}.tiptap-button.has-badge{position:relative}.tiptap-button .badge{position:absolute;top:-4px;right:-4px;background:#ef4444;color:#fff;font-size:10px;padding:2px 4px;border-radius:8px;min-width:16px;text-align:center;line-height:1}.tiptap-button.has-tooltip{position:relative}.tiptap-button .tooltip{position:absolute;bottom:-30px;left:50%;transform:translate(-50%);background:#000c;color:#fff;padding:4px 8px;border-radius:4px;font-size:12px;white-space:nowrap;opacity:0;visibility:hidden;transition:all .2s ease;z-index:1000}.tiptap-button:hover .tooltip{opacity:1;visibility:visible}@keyframes pulse{0%,to{box-shadow:0 0 #6366f166}50%{box-shadow:0 0 0 4px #6366f100}}.tiptap-button.is-active.pulse{animation:pulse 2s infinite}@media (max-width: 768px){.tiptap-button{width:32px;height:32px}.tiptap-button .material-symbols-outlined{font-size:18px}.tiptap-button.text-button{padding:0 8px;font-size:13px}}\n"] }]
599
+ `, styles: [".tiptap-button{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;background:transparent;border-radius:var(--ate-border-radius, 8px);cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);color:var(--ate-toolbar-button-color, var(--ate-text-secondary));position:relative;overflow:hidden}.tiptap-button:before{content:\"\";position:absolute;inset:0;background:var(--ate-primary);opacity:0;transition:opacity .2s ease;border-radius:var(--ate-border-radius, 8px)}.tiptap-button:hover:not(.has-custom-color){color:var(--ate-toolbar-button-active-color, var(--ate-primary));background:var(--ate-toolbar-button-hover-background, transparent);transform:translateY(-1px)}.tiptap-button.has-custom-color:hover{background:var(--ate-toolbar-button-hover-background, transparent);transform:translateY(-1px)}.tiptap-button:hover:before{opacity:.1}.tiptap-button:active{transform:translateY(0)}.tiptap-button.is-active:not(.has-custom-color){color:var(--ate-toolbar-button-active-color, var(--ate-primary));background:var(--ate-toolbar-button-active-background, var(--ate-primary-light))}.tiptap-button.is-active.has-custom-color{background:var(--ate-toolbar-button-active-background, var(--ate-primary-light))}.tiptap-button:disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.tiptap-button .material-symbols-outlined{font-size:20px;position:relative;z-index:1}.tiptap-button .material-symbols-outlined.icon-small{font-size:16px}.tiptap-button .material-symbols-outlined.icon-medium{font-size:20px}.tiptap-button .material-symbols-outlined.icon-large{font-size:24px}.tiptap-button.text-button{width:auto;padding:0 12px;font-size:14px;font-weight:500;gap:8px}.tiptap-button.color-button{width:28px;height:28px;border-radius:50%;border:2px solid transparent;transition:all .2s ease}.tiptap-button.color-button:hover{border-color:var(--ate-border);transform:scale(1.1)}.tiptap-button.color-button.is-active{border-color:var(--ate-primary);box-shadow:0 0 0 2px var(--ate-primary-light)}.tiptap-button.danger{color:var(--ate-error-color, #ef4444)}.tiptap-button.danger:hover{background:var(--ate-error-bg, rgba(239, 68, 68, .1))}.tiptap-button.danger:before{background:var(--ate-error-color, #ef4444)}.tiptap-button.small{width:24px;height:24px}.tiptap-button.medium{width:32px;height:32px}.tiptap-button.large{width:40px;height:40px}@keyframes pulse{0%,to{box-shadow:0 0 0 0 var(--ate-primary-light-alpha)}50%{box-shadow:0 0 0 4px transparent}}.tiptap-button.is-active.pulse{animation:pulse 2s infinite}@media (max-width: 768px){.tiptap-button{width:32px;height:32px}.tiptap-button .material-symbols-outlined{font-size:18px}.tiptap-button.text-button{padding:0 8px;font-size:13px}}\n"] }]
598
600
  }] });
599
601
 
600
602
  class ColorPickerService {
@@ -658,7 +660,19 @@ class ColorPickerService {
658
660
  if (found)
659
661
  return found;
660
662
  const attrs = editor.getAttributes("textStyle") || {};
661
- return attrs.color ? this.normalizeColor(attrs.color) : "#000000";
663
+ if (attrs.color)
664
+ return this.normalizeColor(attrs.color);
665
+ // Dynamic fallback: get current computed text color from editor's DOM
666
+ try {
667
+ if (typeof window !== "undefined" && editor.view?.dom) {
668
+ const computedColor = window.getComputedStyle(editor.view.dom).color;
669
+ return this.normalizeColor(computedColor);
670
+ }
671
+ }
672
+ catch (e) {
673
+ // Fail silently and use basic fallback
674
+ }
675
+ return "#000000";
662
676
  }
663
677
  /**
664
678
  * Check if a color is explicitly applied on the selection.
@@ -856,6 +870,7 @@ const ENGLISH_TRANSLATIONS = {
856
870
  },
857
871
  imageUpload: {
858
872
  selectImage: "Select Image",
873
+ loadError: "Error loading image",
859
874
  uploadingImage: "Uploading image...",
860
875
  uploadProgress: "Upload Progress",
861
876
  uploadError: "Upload Error",
@@ -869,12 +884,20 @@ const ENGLISH_TRANSLATIONS = {
869
884
  resizeMedium: "Medium",
870
885
  resizeLarge: "Large",
871
886
  resizeOriginal: "Original",
887
+ resizing: "Resizing...",
888
+ compressing: "Compressing...",
889
+ compressionError: "Error during compression",
890
+ validating: "Validating file...",
891
+ uploadingToServer: "Uploading to server...",
892
+ replacingImage: "Replacing image...",
893
+ insertingImage: "Inserting into editor...",
894
+ noFileSelected: "No image file selected",
895
+ selectionCancelled: "Selection cancelled",
872
896
  },
873
897
  editor: {
874
898
  placeholder: "Start typing...",
875
899
  character: "character",
876
900
  word: "word",
877
- imageLoadError: "Error loading image",
878
901
  linkPrompt: "Enter link URL",
879
902
  linkUrlPrompt: "Enter URL",
880
903
  confirmDelete: "Are you sure you want to delete this?",
@@ -1014,6 +1037,7 @@ const FRENCH_TRANSLATIONS = {
1014
1037
  },
1015
1038
  imageUpload: {
1016
1039
  selectImage: "Sélectionner une image",
1040
+ loadError: "Erreur de chargement de l'image",
1017
1041
  uploadingImage: "Téléchargement de l'image...",
1018
1042
  uploadProgress: "Progression du téléchargement",
1019
1043
  uploadError: "Erreur de téléchargement",
@@ -1027,12 +1051,20 @@ const FRENCH_TRANSLATIONS = {
1027
1051
  resizeMedium: "Moyen",
1028
1052
  resizeLarge: "Grand",
1029
1053
  resizeOriginal: "Original",
1054
+ resizing: "Redimensionnement...",
1055
+ compressing: "Compression...",
1056
+ compressionError: "Erreur lors de la compression",
1057
+ validating: "Validation du fichier...",
1058
+ uploadingToServer: "Upload vers le serveur...",
1059
+ replacingImage: "Remplacement de l'image...",
1060
+ insertingImage: "Insertion dans l'éditeur...",
1061
+ noFileSelected: "Aucun fichier image sélectionné",
1062
+ selectionCancelled: "Sélection annulée",
1030
1063
  },
1031
1064
  editor: {
1032
1065
  placeholder: "Commencez à écrire...",
1033
1066
  character: "caractère",
1034
1067
  word: "mot",
1035
- imageLoadError: "Erreur de chargement de l'image",
1036
1068
  linkPrompt: "Entrez l'URL du lien",
1037
1069
  linkUrlPrompt: "Entrez l'URL",
1038
1070
  confirmDelete: "Êtes-vous sûr de vouloir supprimer ceci ?",
@@ -1132,7 +1164,9 @@ class TiptapTextColorPickerComponent {
1132
1164
  this.editorChange = signal(0);
1133
1165
  this.currentColor = computed(() => {
1134
1166
  this.editorChange();
1135
- return (this.previewColor() ?? this.colorPickerSvc.getCurrentColor(this.editor()));
1167
+ if (this.previewColor())
1168
+ return this.previewColor();
1169
+ return this.colorPickerSvc.getCurrentColor(this.editor());
1136
1170
  });
1137
1171
  this.hasColorApplied = computed(() => {
1138
1172
  this.editorChange();
@@ -1252,7 +1286,7 @@ class TiptapTextColorPickerComponent {
1252
1286
  <tiptap-button
1253
1287
  icon="format_color_text"
1254
1288
  [title]="t().textColor"
1255
- [color]="hasColorApplied() ? currentColor() : undefined"
1289
+ [color]="hasColorApplied() ? currentColor() : 'var(--ate-text)'"
1256
1290
  (onClick)="triggerPicker()"
1257
1291
  >
1258
1292
  <input
@@ -1287,7 +1321,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
1287
1321
  <tiptap-button
1288
1322
  icon="format_color_text"
1289
1323
  [title]="t().textColor"
1290
- [color]="hasColorApplied() ? currentColor() : undefined"
1324
+ [color]="hasColorApplied() ? currentColor() : 'var(--ate-text)'"
1291
1325
  (onClick)="triggerPicker()"
1292
1326
  >
1293
1327
  <input
@@ -1325,6 +1359,8 @@ class TiptapBubbleMenuComponent {
1325
1359
  }
1326
1360
  // Effect comme propriété de classe pour éviter l'erreur d'injection context
1327
1361
  constructor() {
1362
+ this.i18nService = inject(TiptapI18nService);
1363
+ this.t = this.i18nService.bubbleMenu;
1328
1364
  this.editor = input.required();
1329
1365
  this.config = input({
1330
1366
  bold: true,
@@ -1356,8 +1392,6 @@ class TiptapBubbleMenuComponent {
1356
1392
  separator: true,
1357
1393
  ...this.config(),
1358
1394
  }));
1359
- this.i18nService = inject(TiptapI18nService);
1360
- this.t = this.i18nService.bubbleMenu;
1361
1395
  this.updateMenu = () => {
1362
1396
  // Debounce pour éviter les appels trop fréquents
1363
1397
  if (this.updateTimeout) {
@@ -1461,7 +1495,10 @@ class TiptapBubbleMenuComponent {
1461
1495
  content: menuElement,
1462
1496
  trigger: "manual",
1463
1497
  placement: "top-start",
1464
- appendTo: () => document.body,
1498
+ appendTo: (ref) => {
1499
+ const host = this.editor().options.element.closest("angular-tiptap-editor");
1500
+ return host || document.body;
1501
+ },
1465
1502
  interactive: true,
1466
1503
  arrow: false,
1467
1504
  offset: [0, 8],
@@ -1573,7 +1610,7 @@ class TiptapBubbleMenuComponent {
1573
1610
  ed.chain().focus().toggleHighlight().run();
1574
1611
  break;
1575
1612
  case "link":
1576
- const href = window.prompt("URL du lien:");
1613
+ const href = window.prompt(this.i18nService.editor().linkPrompt);
1577
1614
  if (href) {
1578
1615
  ed.chain().focus().toggleLink({ href }).run();
1579
1616
  }
@@ -1768,7 +1805,7 @@ class TiptapSeparatorComponent {
1768
1805
  [class.medium]="size() === 'medium'"
1769
1806
  [class.large]="size() === 'large'"
1770
1807
  ></div>
1771
- `, isInline: true, styles: [".tiptap-separator{background-color:#e2e8f0;margin:0}.tiptap-separator.vertical{width:1px;height:24px;margin:0 8px}.tiptap-separator.horizontal{height:1px;width:100%;margin:8px 0}.tiptap-separator.small.vertical{height:16px;margin:0 4px}.tiptap-separator.small.horizontal{margin:4px 0}.tiptap-separator.medium.vertical{height:24px;margin:0 8px}.tiptap-separator.medium.horizontal{margin:8px 0}.tiptap-separator.large.vertical{height:32px;margin:0 12px}.tiptap-separator.large.horizontal{margin:12px 0}\n"] }); }
1808
+ `, isInline: true, styles: [".tiptap-separator{background-color:var(--ate-border, #e2e8f0);margin:0}.tiptap-separator.vertical{width:1px;height:24px;margin:0 8px}.tiptap-separator.horizontal{height:1px;width:100%;margin:8px 0}.tiptap-separator.small.vertical{height:16px;margin:0 4px}.tiptap-separator.small.horizontal{margin:4px 0}.tiptap-separator.medium.vertical{height:24px;margin:0 8px}.tiptap-separator.medium.horizontal{margin:8px 0}.tiptap-separator.large.vertical{height:32px;margin:0 12px}.tiptap-separator.large.horizontal{margin:12px 0}\n"] }); }
1772
1809
  }
1773
1810
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: TiptapSeparatorComponent, decorators: [{
1774
1811
  type: Component,
@@ -1781,7 +1818,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
1781
1818
  [class.medium]="size() === 'medium'"
1782
1819
  [class.large]="size() === 'large'"
1783
1820
  ></div>
1784
- `, styles: [".tiptap-separator{background-color:#e2e8f0;margin:0}.tiptap-separator.vertical{width:1px;height:24px;margin:0 8px}.tiptap-separator.horizontal{height:1px;width:100%;margin:8px 0}.tiptap-separator.small.vertical{height:16px;margin:0 4px}.tiptap-separator.small.horizontal{margin:4px 0}.tiptap-separator.medium.vertical{height:24px;margin:0 8px}.tiptap-separator.medium.horizontal{margin:8px 0}.tiptap-separator.large.vertical{height:32px;margin:0 12px}.tiptap-separator.large.horizontal{margin:12px 0}\n"] }]
1821
+ `, styles: [".tiptap-separator{background-color:var(--ate-border, #e2e8f0);margin:0}.tiptap-separator.vertical{width:1px;height:24px;margin:0 8px}.tiptap-separator.horizontal{height:1px;width:100%;margin:8px 0}.tiptap-separator.small.vertical{height:16px;margin:0 4px}.tiptap-separator.small.horizontal{margin:4px 0}.tiptap-separator.medium.vertical{height:24px;margin:0 8px}.tiptap-separator.medium.horizontal{margin:8px 0}.tiptap-separator.large.vertical{height:32px;margin:0 12px}.tiptap-separator.large.horizontal{margin:12px 0}\n"] }]
1785
1822
  }] });
1786
1823
 
1787
1824
  class ImageService {
@@ -1789,7 +1826,10 @@ class ImageService {
1789
1826
  // Signals pour l'état des images
1790
1827
  this.selectedImage = signal(null);
1791
1828
  this.isImageSelected = computed(() => this.selectedImage() !== null);
1829
+ // Resizing signals
1792
1830
  this.isResizing = signal(false);
1831
+ this.i18n = inject(TiptapI18nService);
1832
+ this.t = this.i18n.imageUpload;
1793
1833
  // Signaux pour l'upload
1794
1834
  this.isUploading = signal(false);
1795
1835
  this.uploadProgress = signal(0);
@@ -1947,7 +1987,7 @@ class ImageService {
1947
1987
  resolve({ width: img.naturalWidth, height: img.naturalHeight });
1948
1988
  };
1949
1989
  img.onerror = () => {
1950
- reject(new Error("Impossible de charger l'image"));
1990
+ reject(new Error(this.t().loadError));
1951
1991
  };
1952
1992
  img.src = src;
1953
1993
  });
@@ -1968,12 +2008,12 @@ class ImageService {
1968
2008
  // Validation des images
1969
2009
  validateImage(file, maxSize = 5 * 1024 * 1024) {
1970
2010
  if (!file.type.startsWith("image/")) {
1971
- return { valid: false, error: "Le fichier doit être une image" };
2011
+ return { valid: false, error: this.t().invalidFileType };
1972
2012
  }
1973
2013
  if (file.size > maxSize) {
1974
2014
  return {
1975
2015
  valid: false,
1976
- error: `L'image est trop volumineuse (max ${maxSize / 1024 / 1024}MB)`,
2016
+ error: `${this.t().imageTooLarge} (max ${maxSize / 1024 / 1024}MB)`,
1977
2017
  };
1978
2018
  }
1979
2019
  return { valid: true };
@@ -1988,7 +2028,7 @@ class ImageService {
1988
2028
  // Mise à jour du progrès
1989
2029
  if (this.isUploading()) {
1990
2030
  this.uploadProgress.set(40);
1991
- this.uploadMessage.set("Redimensionnement...");
2031
+ this.uploadMessage.set(this.t().resizing);
1992
2032
  this.forceEditorUpdate();
1993
2033
  }
1994
2034
  let { width, height } = img;
@@ -2005,7 +2045,7 @@ class ImageService {
2005
2045
  // Mise à jour du progrès
2006
2046
  if (this.isUploading()) {
2007
2047
  this.uploadProgress.set(60);
2008
- this.uploadMessage.set("Compression...");
2048
+ this.uploadMessage.set(this.t().compressing);
2009
2049
  this.forceEditorUpdate();
2010
2050
  }
2011
2051
  // Convertir en base64 avec compression
@@ -2027,17 +2067,17 @@ class ImageService {
2027
2067
  resolve(result);
2028
2068
  }
2029
2069
  else {
2030
- reject(new Error("Erreur lors de la compression"));
2070
+ reject(new Error(this.t().compressionError));
2031
2071
  }
2032
2072
  };
2033
2073
  reader.readAsDataURL(blob);
2034
2074
  }
2035
2075
  else {
2036
- reject(new Error("Erreur lors de la compression"));
2076
+ reject(new Error(this.t().compressionError));
2037
2077
  }
2038
2078
  }, file.type, quality);
2039
2079
  };
2040
- img.onerror = () => reject(new Error("Erreur lors du chargement de l'image"));
2080
+ img.onerror = () => reject(new Error(this.t().loadError));
2041
2081
  img.src = URL.createObjectURL(file);
2042
2082
  });
2043
2083
  }
@@ -2048,7 +2088,7 @@ class ImageService {
2048
2088
  this.currentEditor = editor;
2049
2089
  this.isUploading.set(true);
2050
2090
  this.uploadProgress.set(0);
2051
- this.uploadMessage.set("Validation du fichier...");
2091
+ this.uploadMessage.set(this.t().validating);
2052
2092
  this.forceEditorUpdate();
2053
2093
  // Validation
2054
2094
  const validation = this.validateImage(file);
@@ -2056,7 +2096,7 @@ class ImageService {
2056
2096
  throw new Error(validation.error);
2057
2097
  }
2058
2098
  this.uploadProgress.set(20);
2059
- this.uploadMessage.set("Compression en cours...");
2099
+ this.uploadMessage.set(this.t().compressing);
2060
2100
  this.forceEditorUpdate();
2061
2101
  // Petit délai pour permettre à l'utilisateur de voir la progression
2062
2102
  await new Promise((resolve) => setTimeout(resolve, 200));
@@ -2064,7 +2104,7 @@ class ImageService {
2064
2104
  this.uploadProgress.set(80);
2065
2105
  // Si un handler personnalisé est défini, l'utiliser pour l'upload
2066
2106
  if (this.uploadHandler) {
2067
- this.uploadMessage.set("Upload vers le serveur...");
2107
+ this.uploadMessage.set(this.t().uploadingToServer);
2068
2108
  this.forceEditorUpdate();
2069
2109
  try {
2070
2110
  const handlerResponse = this.uploadHandler({
@@ -2086,7 +2126,7 @@ class ImageService {
2086
2126
  }
2087
2127
  }
2088
2128
  catch (handlerError) {
2089
- console.error("Erreur lors de l'upload personnalisé:", handlerError);
2129
+ console.error(this.t().uploadError, handlerError);
2090
2130
  throw handlerError;
2091
2131
  }
2092
2132
  }
@@ -2109,7 +2149,7 @@ class ImageService {
2109
2149
  this.uploadMessage.set("");
2110
2150
  this.forceEditorUpdate();
2111
2151
  this.currentEditor = null;
2112
- console.error("Erreur lors de l'upload d'image:", error);
2152
+ console.error(this.t().uploadError, error);
2113
2153
  throw error;
2114
2154
  }
2115
2155
  }
@@ -2123,7 +2163,7 @@ class ImageService {
2123
2163
  width: result.width,
2124
2164
  height: result.height,
2125
2165
  });
2126
- }, "Insertion dans l'éditeur...", options);
2166
+ }, this.t().insertingImage, options);
2127
2167
  }
2128
2168
  // Méthode pour forcer la mise à jour de l'éditeur
2129
2169
  forceEditorUpdate() {
@@ -2152,13 +2192,13 @@ class ImageService {
2152
2192
  }
2153
2193
  }
2154
2194
  else {
2155
- reject(new Error("Aucun fichier image sélectionné"));
2195
+ reject(new Error(this.t().noFileSelected));
2156
2196
  }
2157
2197
  document.body.removeChild(input);
2158
2198
  });
2159
2199
  input.addEventListener("cancel", () => {
2160
2200
  document.body.removeChild(input);
2161
- reject(new Error("Sélection annulée"));
2201
+ reject(new Error(this.t().selectionCancelled));
2162
2202
  });
2163
2203
  document.body.appendChild(input);
2164
2204
  input.click();
@@ -2180,60 +2220,28 @@ class ImageService {
2180
2220
  try {
2181
2221
  // Supprimer visuellement l'ancienne image immédiatement
2182
2222
  editor.chain().focus().deleteSelection().run();
2183
- // Stocker la référence à l'éditeur
2184
- this.currentEditor = editor;
2185
- this.isUploading.set(true);
2186
- this.uploadProgress.set(0);
2187
- this.uploadMessage.set("Validation du fichier...");
2188
- this.forceEditorUpdate();
2189
- // Validation
2190
- const validation = this.validateImage(file);
2191
- if (!validation.valid) {
2192
- throw new Error(validation.error);
2193
- }
2194
- this.uploadProgress.set(20);
2195
- this.uploadMessage.set("Compression en cours...");
2196
- this.forceEditorUpdate();
2197
- // Petit délai pour permettre à l'utilisateur de voir la progression
2198
- await new Promise((resolve) => setTimeout(resolve, 200));
2199
- const result = await this.compressImage(file, options?.quality || 0.8, options?.maxWidth || 1920, options?.maxHeight || 1080);
2200
- this.uploadProgress.set(80);
2201
- this.uploadMessage.set("Remplacement de l'image...");
2202
- this.forceEditorUpdate();
2203
- // Petit délai pour le remplacement
2204
- await new Promise((resolve) => setTimeout(resolve, 100));
2205
- // Insérer la nouvelle image à la position actuelle
2206
- this.insertImage(editor, {
2207
- src: result.src,
2208
- alt: result.name,
2209
- title: `${result.name} (${result.width}×${result.height})`,
2210
- width: result.width,
2211
- height: result.height,
2212
- });
2213
- // L'image est remplacée, maintenant on peut cacher l'indicateur
2214
- this.isUploading.set(false);
2215
- this.uploadProgress.set(0);
2216
- this.uploadMessage.set("");
2217
- this.forceEditorUpdate();
2218
- this.currentEditor = null;
2223
+ await this.uploadImageWithProgress(editor, file, (editor, result) => {
2224
+ this.insertImage(editor, {
2225
+ src: result.src,
2226
+ alt: result.name,
2227
+ title: `${result.name} (${result.width}×${result.height})`,
2228
+ width: result.width,
2229
+ height: result.height,
2230
+ });
2231
+ }, this.t().replacingImage, options);
2219
2232
  }
2220
2233
  catch (error) {
2221
- // En cas d'erreur, restaurer l'image originale
2234
+ // En cas d'erreur, restaurer l'image originale si elle existait
2222
2235
  if (backupImage["src"]) {
2223
2236
  this.insertImage(editor, {
2224
2237
  src: backupImage["src"],
2225
- alt: backupImage["alt"],
2226
- title: backupImage["title"],
2238
+ alt: backupImage["alt"] || "",
2239
+ title: backupImage["title"] || "",
2227
2240
  width: backupImage["width"],
2228
2241
  height: backupImage["height"],
2229
2242
  });
2230
2243
  }
2231
- this.isUploading.set(false);
2232
- this.uploadProgress.set(0);
2233
- this.uploadMessage.set("");
2234
- this.forceEditorUpdate();
2235
- this.currentEditor = null;
2236
- console.error("Erreur lors du remplacement d'image:", error);
2244
+ console.error("Error during image replacement:", error);
2237
2245
  throw error;
2238
2246
  }
2239
2247
  }
@@ -2249,6 +2257,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
2249
2257
 
2250
2258
  class TiptapImageBubbleMenuComponent {
2251
2259
  constructor() {
2260
+ this.i18nService = inject(TiptapI18nService);
2261
+ this.t = this.i18nService.imageUpload;
2252
2262
  this.editor = input.required();
2253
2263
  this.config = input({
2254
2264
  changeImage: true,
@@ -2289,8 +2299,6 @@ class TiptapImageBubbleMenuComponent {
2289
2299
  if (!ed)
2290
2300
  return;
2291
2301
  const isImageSelected = ed.isActive("resizableImage") || ed.isActive("image");
2292
- const { from, to } = ed.state.selection;
2293
- const hasTextSelection = from !== to;
2294
2302
  // Ne montrer le menu image que si :
2295
2303
  // - Une image est sélectionnée
2296
2304
  // - L'éditeur est éditable
@@ -2365,7 +2373,10 @@ class TiptapImageBubbleMenuComponent {
2365
2373
  content: menuElement,
2366
2374
  trigger: "manual",
2367
2375
  placement: "top-start",
2368
- appendTo: () => document.body,
2376
+ appendTo: (ref) => {
2377
+ const host = this.editor().options.element.closest("angular-tiptap-editor");
2378
+ return host || document.body;
2379
+ },
2369
2380
  interactive: true,
2370
2381
  arrow: false,
2371
2382
  offset: [0, 8],
@@ -2492,7 +2503,7 @@ class TiptapImageBubbleMenuComponent {
2492
2503
  });
2493
2504
  }
2494
2505
  catch (error) {
2495
- console.error("Erreur lors du changement d'image:", error);
2506
+ console.error(this.i18nService.imageUpload().uploadError, error);
2496
2507
  }
2497
2508
  }
2498
2509
  deleteImage() {
@@ -2507,7 +2518,7 @@ class TiptapImageBubbleMenuComponent {
2507
2518
  @if (imageBubbleMenuConfig().changeImage) {
2508
2519
  <tiptap-button
2509
2520
  icon="drive_file_rename_outline"
2510
- title="Changer l'image"
2521
+ [title]="t().changeImage"
2511
2522
  (click)="onCommand('changeImage', $event)"
2512
2523
  ></tiptap-button>
2513
2524
  } @if (imageBubbleMenuConfig().separator && hasResizeButtons()) {
@@ -2516,27 +2527,27 @@ class TiptapImageBubbleMenuComponent {
2516
2527
  <tiptap-button
2517
2528
  icon="crop_square"
2518
2529
  iconSize="small"
2519
- title="Petite (300×200)"
2530
+ [title]="t().resizeSmall"
2520
2531
  (click)="onCommand('resizeSmall', $event)"
2521
2532
  ></tiptap-button>
2522
2533
  } @if (imageBubbleMenuConfig().resizeMedium) {
2523
2534
  <tiptap-button
2524
2535
  icon="crop_square"
2525
2536
  iconSize="medium"
2526
- title="Moyenne (500×350)"
2537
+ [title]="t().resizeMedium"
2527
2538
  (click)="onCommand('resizeMedium', $event)"
2528
2539
  ></tiptap-button>
2529
2540
  } @if (imageBubbleMenuConfig().resizeLarge) {
2530
2541
  <tiptap-button
2531
2542
  icon="crop_square"
2532
2543
  iconSize="large"
2533
- title="Grande (800×600)"
2544
+ [title]="t().resizeLarge"
2534
2545
  (click)="onCommand('resizeLarge', $event)"
2535
2546
  ></tiptap-button>
2536
2547
  } @if (imageBubbleMenuConfig().resizeOriginal) {
2537
2548
  <tiptap-button
2538
2549
  icon="photo_size_select_actual"
2539
- title="Taille originale"
2550
+ [title]="t().resizeOriginal"
2540
2551
  (click)="onCommand('resizeOriginal', $event)"
2541
2552
  ></tiptap-button>
2542
2553
  } @if (imageBubbleMenuConfig().separator &&
@@ -2545,7 +2556,7 @@ class TiptapImageBubbleMenuComponent {
2545
2556
  } @if (imageBubbleMenuConfig().deleteImage) {
2546
2557
  <tiptap-button
2547
2558
  icon="delete"
2548
- title="Supprimer l'image"
2559
+ [title]="t().deleteImage"
2549
2560
  variant="danger"
2550
2561
  (click)="onCommand('deleteImage', $event)"
2551
2562
  ></tiptap-button>
@@ -2560,7 +2571,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
2560
2571
  @if (imageBubbleMenuConfig().changeImage) {
2561
2572
  <tiptap-button
2562
2573
  icon="drive_file_rename_outline"
2563
- title="Changer l'image"
2574
+ [title]="t().changeImage"
2564
2575
  (click)="onCommand('changeImage', $event)"
2565
2576
  ></tiptap-button>
2566
2577
  } @if (imageBubbleMenuConfig().separator && hasResizeButtons()) {
@@ -2569,27 +2580,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
2569
2580
  <tiptap-button
2570
2581
  icon="crop_square"
2571
2582
  iconSize="small"
2572
- title="Petite (300×200)"
2583
+ [title]="t().resizeSmall"
2573
2584
  (click)="onCommand('resizeSmall', $event)"
2574
2585
  ></tiptap-button>
2575
2586
  } @if (imageBubbleMenuConfig().resizeMedium) {
2576
2587
  <tiptap-button
2577
2588
  icon="crop_square"
2578
2589
  iconSize="medium"
2579
- title="Moyenne (500×350)"
2590
+ [title]="t().resizeMedium"
2580
2591
  (click)="onCommand('resizeMedium', $event)"
2581
2592
  ></tiptap-button>
2582
2593
  } @if (imageBubbleMenuConfig().resizeLarge) {
2583
2594
  <tiptap-button
2584
2595
  icon="crop_square"
2585
2596
  iconSize="large"
2586
- title="Grande (800×600)"
2597
+ [title]="t().resizeLarge"
2587
2598
  (click)="onCommand('resizeLarge', $event)"
2588
2599
  ></tiptap-button>
2589
2600
  } @if (imageBubbleMenuConfig().resizeOriginal) {
2590
2601
  <tiptap-button
2591
2602
  icon="photo_size_select_actual"
2592
- title="Taille originale"
2603
+ [title]="t().resizeOriginal"
2593
2604
  (click)="onCommand('resizeOriginal', $event)"
2594
2605
  ></tiptap-button>
2595
2606
  } @if (imageBubbleMenuConfig().separator &&
@@ -2598,7 +2609,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
2598
2609
  } @if (imageBubbleMenuConfig().deleteImage) {
2599
2610
  <tiptap-button
2600
2611
  icon="delete"
2601
- title="Supprimer l'image"
2612
+ [title]="t().deleteImage"
2602
2613
  variant="danger"
2603
2614
  (click)="onCommand('deleteImage', $event)"
2604
2615
  ></tiptap-button>
@@ -2785,7 +2796,7 @@ class EditorCommandsService {
2785
2796
  }
2786
2797
  // Méthode pour vider le contenu
2787
2798
  clearContent(editor) {
2788
- editor.chain().focus().setContent("", true).run(); // ✅ Forcer l'émission de l'événement
2799
+ editor.commands.setContent("", true);
2789
2800
  }
2790
2801
  // Méthodes de base de l'éditeur
2791
2802
  focus(editor) {
@@ -2795,11 +2806,14 @@ class EditorCommandsService {
2795
2806
  editor.chain().blur().run();
2796
2807
  }
2797
2808
  setContent(editor, content, emitUpdate = true) {
2798
- editor.chain().focus().setContent(content, emitUpdate).run();
2809
+ editor.commands.setContent(content, emitUpdate);
2799
2810
  }
2800
2811
  setEditable(editor, editable) {
2801
2812
  editor.setEditable(editable);
2802
2813
  }
2814
+ insertContent(editor, content) {
2815
+ editor.chain().focus().insertContent(content).run();
2816
+ }
2803
2817
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: EditorCommandsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2804
2818
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: EditorCommandsService, providedIn: "root" }); }
2805
2819
  }
@@ -2822,7 +2836,7 @@ class TiptapTableBubbleMenuComponent {
2822
2836
  this.tippyInstance = null;
2823
2837
  this.updateTimeout = null;
2824
2838
  // Signaux
2825
- this.i18n = this.i18nService;
2839
+ this.t = this.i18nService.table;
2826
2840
  this.updateMenu = () => {
2827
2841
  // Debounce pour éviter les appels trop fréquents
2828
2842
  if (this.updateTimeout) {
@@ -2904,8 +2918,11 @@ class TiptapTableBubbleMenuComponent {
2904
2918
  this.tippyInstance = tippy(document.body, {
2905
2919
  content: menuElement,
2906
2920
  trigger: "manual",
2907
- placement: "top-start",
2908
- appendTo: () => document.body,
2921
+ placement: "bottom-start",
2922
+ appendTo: (ref) => {
2923
+ const host = this.editor().options.element.closest("angular-tiptap-editor");
2924
+ return host || document.body;
2925
+ },
2909
2926
  interactive: true,
2910
2927
  arrow: false,
2911
2928
  offset: [0, 8],
@@ -3034,19 +3051,19 @@ class TiptapTableBubbleMenuComponent {
3034
3051
  @if (config().addRowBefore !== false) {
3035
3052
  <tiptap-button
3036
3053
  icon="add_row_above"
3037
- title="{{ i18n.table().addRowBefore }}"
3054
+ title="{{ t().addRowBefore }}"
3038
3055
  (click)="addRowBefore()"
3039
3056
  ></tiptap-button>
3040
3057
  } @if (config().addRowAfter !== false) {
3041
3058
  <tiptap-button
3042
3059
  icon="add_row_below"
3043
- title="{{ i18n.table().addRowAfter }}"
3060
+ title="{{ t().addRowAfter }}"
3044
3061
  (click)="addRowAfter()"
3045
3062
  ></tiptap-button>
3046
3063
  } @if (config().deleteRow !== false) {
3047
3064
  <tiptap-button
3048
3065
  icon="delete"
3049
- title="{{ i18n.table().deleteRow }}"
3066
+ title="{{ t().deleteRow }}"
3050
3067
  variant="danger"
3051
3068
  (click)="deleteRow()"
3052
3069
  ></tiptap-button>
@@ -3058,19 +3075,19 @@ class TiptapTableBubbleMenuComponent {
3058
3075
  @if (config().addColumnBefore !== false) {
3059
3076
  <tiptap-button
3060
3077
  icon="add_column_left"
3061
- title="{{ i18n.table().addColumnBefore }}"
3078
+ title="{{ t().addColumnBefore }}"
3062
3079
  (click)="addColumnBefore()"
3063
3080
  ></tiptap-button>
3064
3081
  } @if (config().addColumnAfter !== false) {
3065
3082
  <tiptap-button
3066
3083
  icon="add_column_right"
3067
- title="{{ i18n.table().addColumnAfter }}"
3084
+ title="{{ t().addColumnAfter }}"
3068
3085
  (click)="addColumnAfter()"
3069
3086
  ></tiptap-button>
3070
3087
  } @if (config().deleteColumn !== false) {
3071
3088
  <tiptap-button
3072
3089
  icon="delete"
3073
- title="{{ i18n.table().deleteColumn }}"
3090
+ title="{{ t().deleteColumn }}"
3074
3091
  variant="danger"
3075
3092
  (click)="deleteColumn()"
3076
3093
  ></tiptap-button>
@@ -3082,13 +3099,13 @@ class TiptapTableBubbleMenuComponent {
3082
3099
  @if (config().toggleHeaderRow !== false) {
3083
3100
  <tiptap-button
3084
3101
  icon="toolbar"
3085
- title="{{ i18n.table().toggleHeaderRow }}"
3102
+ title="{{ t().toggleHeaderRow }}"
3086
3103
  (click)="toggleHeaderRow()"
3087
3104
  ></tiptap-button>
3088
3105
  } @if (config().toggleHeaderColumn !== false) {
3089
3106
  <tiptap-button
3090
3107
  icon="dock_to_right"
3091
- title="{{ i18n.table().toggleHeaderColumn }}"
3108
+ title="{{ t().toggleHeaderColumn }}"
3092
3109
  (click)="toggleHeaderColumn()"
3093
3110
  ></tiptap-button>
3094
3111
  } @if (config().separator !== false && config().deleteTable !== false) {
@@ -3099,7 +3116,7 @@ class TiptapTableBubbleMenuComponent {
3099
3116
  @if (config().deleteTable !== false) {
3100
3117
  <tiptap-button
3101
3118
  icon="delete_forever"
3102
- title="{{ i18n.table().deleteTable }}"
3119
+ title="{{ t().deleteTable }}"
3103
3120
  variant="danger"
3104
3121
  (click)="deleteTable()"
3105
3122
  ></tiptap-button>
@@ -3115,19 +3132,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
3115
3132
  @if (config().addRowBefore !== false) {
3116
3133
  <tiptap-button
3117
3134
  icon="add_row_above"
3118
- title="{{ i18n.table().addRowBefore }}"
3135
+ title="{{ t().addRowBefore }}"
3119
3136
  (click)="addRowBefore()"
3120
3137
  ></tiptap-button>
3121
3138
  } @if (config().addRowAfter !== false) {
3122
3139
  <tiptap-button
3123
3140
  icon="add_row_below"
3124
- title="{{ i18n.table().addRowAfter }}"
3141
+ title="{{ t().addRowAfter }}"
3125
3142
  (click)="addRowAfter()"
3126
3143
  ></tiptap-button>
3127
3144
  } @if (config().deleteRow !== false) {
3128
3145
  <tiptap-button
3129
3146
  icon="delete"
3130
- title="{{ i18n.table().deleteRow }}"
3147
+ title="{{ t().deleteRow }}"
3131
3148
  variant="danger"
3132
3149
  (click)="deleteRow()"
3133
3150
  ></tiptap-button>
@@ -3139,19 +3156,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
3139
3156
  @if (config().addColumnBefore !== false) {
3140
3157
  <tiptap-button
3141
3158
  icon="add_column_left"
3142
- title="{{ i18n.table().addColumnBefore }}"
3159
+ title="{{ t().addColumnBefore }}"
3143
3160
  (click)="addColumnBefore()"
3144
3161
  ></tiptap-button>
3145
3162
  } @if (config().addColumnAfter !== false) {
3146
3163
  <tiptap-button
3147
3164
  icon="add_column_right"
3148
- title="{{ i18n.table().addColumnAfter }}"
3165
+ title="{{ t().addColumnAfter }}"
3149
3166
  (click)="addColumnAfter()"
3150
3167
  ></tiptap-button>
3151
3168
  } @if (config().deleteColumn !== false) {
3152
3169
  <tiptap-button
3153
3170
  icon="delete"
3154
- title="{{ i18n.table().deleteColumn }}"
3171
+ title="{{ t().deleteColumn }}"
3155
3172
  variant="danger"
3156
3173
  (click)="deleteColumn()"
3157
3174
  ></tiptap-button>
@@ -3163,13 +3180,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
3163
3180
  @if (config().toggleHeaderRow !== false) {
3164
3181
  <tiptap-button
3165
3182
  icon="toolbar"
3166
- title="{{ i18n.table().toggleHeaderRow }}"
3183
+ title="{{ t().toggleHeaderRow }}"
3167
3184
  (click)="toggleHeaderRow()"
3168
3185
  ></tiptap-button>
3169
3186
  } @if (config().toggleHeaderColumn !== false) {
3170
3187
  <tiptap-button
3171
3188
  icon="dock_to_right"
3172
- title="{{ i18n.table().toggleHeaderColumn }}"
3189
+ title="{{ t().toggleHeaderColumn }}"
3173
3190
  (click)="toggleHeaderColumn()"
3174
3191
  ></tiptap-button>
3175
3192
  } @if (config().separator !== false && config().deleteTable !== false) {
@@ -3180,7 +3197,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
3180
3197
  @if (config().deleteTable !== false) {
3181
3198
  <tiptap-button
3182
3199
  icon="delete_forever"
3183
- title="{{ i18n.table().deleteTable }}"
3200
+ title="{{ t().deleteTable }}"
3184
3201
  variant="danger"
3185
3202
  (click)="deleteTable()"
3186
3203
  ></tiptap-button>
@@ -3283,7 +3300,10 @@ class TiptapCellBubbleMenuComponent {
3283
3300
  content: menuElement,
3284
3301
  trigger: "manual",
3285
3302
  placement: "top-start",
3286
- appendTo: () => document.body,
3303
+ appendTo: (ref) => {
3304
+ const host = this.editor().options.element.closest("angular-tiptap-editor");
3305
+ return host || document.body;
3306
+ },
3287
3307
  interactive: true,
3288
3308
  arrow: false,
3289
3309
  offset: [0, 8],
@@ -3440,6 +3460,7 @@ class TiptapToolbarComponent {
3440
3460
  this.editorCommands = editorCommands;
3441
3461
  this.editor = input.required();
3442
3462
  this.config = input.required();
3463
+ this.imageUpload = input({});
3443
3464
  // Outputs pour les événements d'image
3444
3465
  this.imageUploaded = output();
3445
3466
  this.imageError = output();
@@ -3513,11 +3534,17 @@ class TiptapToolbarComponent {
3513
3534
  // Méthode pour insérer une image
3514
3535
  async insertImage() {
3515
3536
  try {
3516
- await this.imageService.selectAndUploadImage(this.editor());
3537
+ const config = this.imageUpload() || {};
3538
+ await this.imageService.selectAndUploadImage(this.editor(), {
3539
+ quality: config.quality,
3540
+ maxWidth: config.maxWidth,
3541
+ maxHeight: config.maxHeight,
3542
+ accept: config.allowedTypes?.join(',')
3543
+ });
3517
3544
  }
3518
3545
  catch (error) {
3519
- console.error("Erreur lors de l'upload d'image:", error);
3520
- this.imageError.emit("Erreur lors de l'upload d'image");
3546
+ console.error(this.i18nService.imageUpload().uploadError, error);
3547
+ this.imageError.emit(this.i18nService.imageUpload().uploadError);
3521
3548
  }
3522
3549
  }
3523
3550
  // Méthode pour vider le contenu
@@ -3532,7 +3559,7 @@ class TiptapToolbarComponent {
3532
3559
  this.imageError.emit(error);
3533
3560
  }
3534
3561
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: TiptapToolbarComponent, deps: [{ token: EditorCommandsService }], target: i0.ɵɵFactoryTarget.Component }); }
3535
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", type: TiptapToolbarComponent, isStandalone: true, selector: "tiptap-toolbar", inputs: { editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { imageUploaded: "imageUploaded", imageError: "imageError" }, ngImport: i0, template: `
3562
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", type: TiptapToolbarComponent, isStandalone: true, selector: "tiptap-toolbar", inputs: { editor: { classPropertyName: "editor", publicName: "editor", isSignal: true, isRequired: true, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, imageUpload: { classPropertyName: "imageUpload", publicName: "imageUpload", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { imageUploaded: "imageUploaded", imageError: "imageError" }, ngImport: i0, template: `
3536
3563
  <div class="tiptap-toolbar">
3537
3564
  @if (config().bold) {
3538
3565
  <tiptap-button
@@ -3607,7 +3634,6 @@ class TiptapToolbarComponent {
3607
3634
  <tiptap-button
3608
3635
  icon="format_h1"
3609
3636
  [title]="t().heading1"
3610
- variant="text"
3611
3637
  [active]="isActive('heading', { level: 1 })"
3612
3638
  (onClick)="toggleHeading(1)"
3613
3639
  />
@@ -3615,7 +3641,6 @@ class TiptapToolbarComponent {
3615
3641
  <tiptap-button
3616
3642
  icon="format_h2"
3617
3643
  [title]="t().heading2"
3618
- variant="text"
3619
3644
  [active]="isActive('heading', { level: 2 })"
3620
3645
  (onClick)="toggleHeading(2)"
3621
3646
  />
@@ -3623,7 +3648,6 @@ class TiptapToolbarComponent {
3623
3648
  <tiptap-button
3624
3649
  icon="format_h3"
3625
3650
  [title]="t().heading3"
3626
- variant="text"
3627
3651
  [active]="isActive('heading', { level: 3 })"
3628
3652
  (onClick)="toggleHeading(3)"
3629
3653
  />
@@ -3737,7 +3761,7 @@ class TiptapToolbarComponent {
3737
3761
  />
3738
3762
  }
3739
3763
  </div>
3740
- `, isInline: true, styles: [".tiptap-toolbar{display:flex;align-items:center;gap:4px;padding:4px 8px;background:#f8f9fa;border-bottom:1px solid #e2e8f0;flex-wrap:wrap;min-height:32px;position:relative}.toolbar-group{display:flex;align-items:center;gap:2px;padding:0 4px}.toolbar-separator{width:1px;height:24px;background:#e2e8f0;margin:0 4px}@media (max-width: 768px){.tiptap-toolbar{padding:6px 8px;gap:2px}.toolbar-group{gap:1px}}@keyframes toolbarSlideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.tiptap-toolbar{animation:toolbarSlideIn .3s cubic-bezier(.4,0,.2,1)}\n"], dependencies: [{ kind: "component", type: TiptapButtonComponent, selector: "tiptap-button", inputs: ["icon", "title", "active", "disabled", "color", "variant", "size", "iconSize"], outputs: ["onClick"] }, { kind: "component", type: TiptapSeparatorComponent, selector: "tiptap-separator", inputs: ["orientation", "size"] }, { kind: "component", type: TiptapTextColorPickerComponent, selector: "tiptap-text-color-picker", inputs: ["editor"], outputs: ["interactionChange", "requestUpdate"] }] }); }
3764
+ `, isInline: true, styles: [".tiptap-toolbar{display:flex;align-items:center;gap:4px;padding:4px 8px;background:var(--ate-toolbar-background);border-bottom:1px solid var(--ate-toolbar-border-color);flex-wrap:wrap;min-height:32px;position:relative;-webkit-backdrop-filter:blur(var(--ate-menu-blur, 16px));backdrop-filter:blur(var(--ate-menu-blur, 16px))}.toolbar-group{display:flex;align-items:center;gap:2px;padding:0 4px}.toolbar-separator{width:1px;height:24px;background:var(--ate-toolbar-border-color);margin:0 4px}@media (max-width: 768px){.tiptap-toolbar{padding:6px 8px;gap:2px}.toolbar-group{gap:1px}}@keyframes toolbarSlideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.tiptap-toolbar{animation:toolbarSlideIn .3s cubic-bezier(.4,0,.2,1)}\n"], dependencies: [{ kind: "component", type: TiptapButtonComponent, selector: "tiptap-button", inputs: ["icon", "title", "active", "disabled", "color", "variant", "size", "iconSize"], outputs: ["onClick"] }, { kind: "component", type: TiptapSeparatorComponent, selector: "tiptap-separator", inputs: ["orientation", "size"] }, { kind: "component", type: TiptapTextColorPickerComponent, selector: "tiptap-text-color-picker", inputs: ["editor"], outputs: ["interactionChange", "requestUpdate"] }] }); }
3741
3765
  }
3742
3766
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: TiptapToolbarComponent, decorators: [{
3743
3767
  type: Component,
@@ -3820,7 +3844,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
3820
3844
  <tiptap-button
3821
3845
  icon="format_h1"
3822
3846
  [title]="t().heading1"
3823
- variant="text"
3824
3847
  [active]="isActive('heading', { level: 1 })"
3825
3848
  (onClick)="toggleHeading(1)"
3826
3849
  />
@@ -3828,7 +3851,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
3828
3851
  <tiptap-button
3829
3852
  icon="format_h2"
3830
3853
  [title]="t().heading2"
3831
- variant="text"
3832
3854
  [active]="isActive('heading', { level: 2 })"
3833
3855
  (onClick)="toggleHeading(2)"
3834
3856
  />
@@ -3836,7 +3858,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
3836
3858
  <tiptap-button
3837
3859
  icon="format_h3"
3838
3860
  [title]="t().heading3"
3839
- variant="text"
3840
3861
  [active]="isActive('heading', { level: 3 })"
3841
3862
  (onClick)="toggleHeading(3)"
3842
3863
  />
@@ -3950,152 +3971,150 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
3950
3971
  />
3951
3972
  }
3952
3973
  </div>
3953
- `, styles: [".tiptap-toolbar{display:flex;align-items:center;gap:4px;padding:4px 8px;background:#f8f9fa;border-bottom:1px solid #e2e8f0;flex-wrap:wrap;min-height:32px;position:relative}.toolbar-group{display:flex;align-items:center;gap:2px;padding:0 4px}.toolbar-separator{width:1px;height:24px;background:#e2e8f0;margin:0 4px}@media (max-width: 768px){.tiptap-toolbar{padding:6px 8px;gap:2px}.toolbar-group{gap:1px}}@keyframes toolbarSlideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.tiptap-toolbar{animation:toolbarSlideIn .3s cubic-bezier(.4,0,.2,1)}\n"] }]
3974
+ `, styles: [".tiptap-toolbar{display:flex;align-items:center;gap:4px;padding:4px 8px;background:var(--ate-toolbar-background);border-bottom:1px solid var(--ate-toolbar-border-color);flex-wrap:wrap;min-height:32px;position:relative;-webkit-backdrop-filter:blur(var(--ate-menu-blur, 16px));backdrop-filter:blur(var(--ate-menu-blur, 16px))}.toolbar-group{display:flex;align-items:center;gap:2px;padding:0 4px}.toolbar-separator{width:1px;height:24px;background:var(--ate-toolbar-border-color);margin:0 4px}@media (max-width: 768px){.tiptap-toolbar{padding:6px 8px;gap:2px}.toolbar-group{gap:1px}}@keyframes toolbarSlideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.tiptap-toolbar{animation:toolbarSlideIn .3s cubic-bezier(.4,0,.2,1)}\n"] }]
3954
3975
  }], ctorParameters: () => [{ type: EditorCommandsService }] });
3955
3976
 
3956
- const DEFAULT_SLASH_COMMANDS$1 = [
3957
- {
3958
- title: "Titre 1",
3959
- description: "Grand titre de section",
3960
- icon: "format_h1",
3961
- keywords: ["heading", "h1", "titre", "title", "1"],
3962
- command: (editor) => editor.chain().focus().toggleHeading({ level: 1 }).run(),
3963
- },
3964
- {
3965
- title: "Titre 2",
3966
- description: "Titre de sous-section",
3967
- icon: "format_h2",
3968
- keywords: ["heading", "h2", "titre", "title", "2"],
3969
- command: (editor) => editor.chain().focus().toggleHeading({ level: 2 }).run(),
3970
- },
3971
- {
3972
- title: "Titre 3",
3973
- description: "Petit titre",
3974
- icon: "format_h3",
3975
- keywords: ["heading", "h3", "titre", "title", "3"],
3976
- command: (editor) => editor.chain().focus().toggleHeading({ level: 3 }).run(),
3977
- },
3978
- {
3979
- title: "Liste à puces",
3980
- description: "Créer une liste à puces",
3981
- icon: "format_list_bulleted",
3982
- keywords: ["bullet", "list", "liste", "puces", "ul"],
3983
- command: (editor) => editor.chain().focus().toggleBulletList().run(),
3984
- },
3985
- {
3986
- title: "Liste numérotée",
3987
- description: "Créer une liste numérotée",
3988
- icon: "format_list_numbered",
3989
- keywords: ["numbered", "list", "liste", "numérotée", "ol", "ordered"],
3990
- command: (editor) => editor.chain().focus().toggleOrderedList().run(),
3991
- },
3992
- {
3993
- title: "Citation",
3994
- description: "Ajouter une citation",
3995
- icon: "format_quote",
3996
- keywords: ["quote", "blockquote", "citation"],
3997
- command: (editor) => editor.chain().focus().toggleBlockquote().run(),
3998
- },
3999
- {
4000
- title: "Code",
4001
- description: "Bloc de code",
4002
- icon: "code",
4003
- keywords: ["code", "codeblock", "pre"],
4004
- command: (editor) => editor.chain().focus().toggleCodeBlock().run(),
4005
- },
4006
- {
4007
- title: "Image",
4008
- description: "Insérer une image",
4009
- icon: "image",
4010
- keywords: ["image", "photo", "picture", "img"],
4011
- command: (editor) => {
4012
- // Créer un input file temporaire pour sélectionner une image
4013
- const input = document.createElement("input");
4014
- input.type = "file";
4015
- input.accept = "image/*";
4016
- input.style.display = "none";
4017
- input.addEventListener("change", async (e) => {
4018
- const file = e.target.files?.[0];
4019
- if (file && file.type.startsWith("image/")) {
4020
- try {
4021
- // Utiliser la méthode de compression unifiée
4022
- const canvas = document.createElement("canvas");
4023
- const ctx = canvas.getContext("2d");
4024
- const img = new Image();
4025
- img.onload = () => {
4026
- // Vérifier les dimensions (max 1920x1080)
4027
- const maxWidth = 1920;
4028
- const maxHeight = 1080;
4029
- let { width, height } = img;
4030
- // Redimensionner si nécessaire
4031
- if (width > maxWidth || height > maxHeight) {
4032
- const ratio = Math.min(maxWidth / width, maxHeight / height);
4033
- width *= ratio;
4034
- height *= ratio;
4035
- }
4036
- canvas.width = width;
4037
- canvas.height = height;
4038
- // Dessiner l'image redimensionnée
4039
- ctx?.drawImage(img, 0, 0, width, height);
4040
- // Convertir en base64 avec compression
4041
- canvas.toBlob((blob) => {
4042
- if (blob) {
4043
- const reader = new FileReader();
4044
- reader.onload = (e) => {
4045
- const base64 = e.target?.result;
4046
- if (base64) {
4047
- // Utiliser setResizableImage avec toutes les propriétés
4048
- editor
4049
- .chain()
4050
- .focus()
4051
- .setResizableImage({
4052
- src: base64,
4053
- alt: file.name,
4054
- title: `${file.name} (${Math.round(width)}×${Math.round(height)})`,
4055
- width: Math.round(width),
4056
- height: Math.round(height),
4057
- })
4058
- .run();
4059
- }
4060
- };
4061
- reader.readAsDataURL(blob);
4062
- }
4063
- }, file.type, 0.8 // qualité de compression
4064
- );
4065
- };
4066
- img.onerror = () => {
4067
- console.error("Erreur lors du chargement de l'image");
4068
- };
4069
- img.src = URL.createObjectURL(file);
4070
- }
4071
- catch (error) {
4072
- console.error("Erreur lors de l'upload:", error);
4073
- }
4074
- }
4075
- document.body.removeChild(input);
4076
- });
4077
- document.body.appendChild(input);
4078
- input.click();
4079
- },
4080
- },
4081
- {
4082
- title: "Ligne horizontale",
4083
- description: "Ajouter une ligne de séparation",
4084
- icon: "horizontal_rule",
4085
- keywords: ["hr", "horizontal", "rule", "ligne", "séparation"],
4086
- command: (editor) => editor.chain().focus().setHorizontalRule().run(),
4087
- },
3977
+ /**
3978
+ * Clés des commandes natives dans l'ordre d'affichage
3979
+ */
3980
+ const SLASH_COMMAND_KEYS = [
3981
+ "heading1",
3982
+ "heading2",
3983
+ "heading3",
3984
+ "bulletList",
3985
+ "orderedList",
3986
+ "blockquote",
3987
+ "code",
3988
+ "image",
3989
+ "horizontalRule",
3990
+ "table",
4088
3991
  ];
3992
+ /**
3993
+ * Configuration par défaut : toutes les commandes natives sont activées
3994
+ */
3995
+ const DEFAULT_SLASH_COMMANDS_CONFIG = {
3996
+ heading1: true,
3997
+ heading2: true,
3998
+ heading3: true,
3999
+ bulletList: true,
4000
+ orderedList: true,
4001
+ blockquote: true,
4002
+ code: true,
4003
+ image: true,
4004
+ horizontalRule: true,
4005
+ table: true,
4006
+ };
4007
+ /**
4008
+ * Factory pour créer les commandes natives avec leurs traductions et leur logique d'exécution.
4009
+ * Utilise les services de l'éditeur pour garantir une cohérence de comportement.
4010
+ */
4011
+ function createDefaultSlashCommands(i18n, commands, images, imageOptions) {
4012
+ const t = i18n.slashCommands();
4013
+ return [
4014
+ {
4015
+ title: t.heading1.title,
4016
+ description: t.heading1.description,
4017
+ icon: "format_h1",
4018
+ keywords: t.heading1.keywords,
4019
+ command: (editor) => commands.toggleHeading(editor, 1),
4020
+ },
4021
+ {
4022
+ title: t.heading2.title,
4023
+ description: t.heading2.description,
4024
+ icon: "format_h2",
4025
+ keywords: t.heading2.keywords,
4026
+ command: (editor) => commands.toggleHeading(editor, 2),
4027
+ },
4028
+ {
4029
+ title: t.heading3.title,
4030
+ description: t.heading3.description,
4031
+ icon: "format_h3",
4032
+ keywords: t.heading3.keywords,
4033
+ command: (editor) => commands.toggleHeading(editor, 3),
4034
+ },
4035
+ {
4036
+ title: t.bulletList.title,
4037
+ description: t.bulletList.description,
4038
+ icon: "format_list_bulleted",
4039
+ keywords: t.bulletList.keywords,
4040
+ command: (editor) => commands.toggleBulletList(editor),
4041
+ },
4042
+ {
4043
+ title: t.orderedList.title,
4044
+ description: t.orderedList.description,
4045
+ icon: "format_list_numbered",
4046
+ keywords: t.orderedList.keywords,
4047
+ command: (editor) => commands.toggleOrderedList(editor),
4048
+ },
4049
+ {
4050
+ title: t.blockquote.title,
4051
+ description: t.blockquote.description,
4052
+ icon: "format_quote",
4053
+ keywords: t.blockquote.keywords,
4054
+ command: (editor) => commands.toggleBlockquote(editor),
4055
+ },
4056
+ {
4057
+ title: t.code.title,
4058
+ description: t.code.description,
4059
+ icon: "code",
4060
+ keywords: t.code.keywords,
4061
+ command: (editor) => commands.toggleCode(editor),
4062
+ },
4063
+ {
4064
+ title: t.image.title,
4065
+ description: t.image.description,
4066
+ icon: "image",
4067
+ keywords: t.image.keywords,
4068
+ command: (editor) => images.selectAndUploadImage(editor, {
4069
+ quality: imageOptions?.quality,
4070
+ maxWidth: imageOptions?.maxWidth,
4071
+ maxHeight: imageOptions?.maxHeight,
4072
+ accept: imageOptions?.allowedTypes?.join(',')
4073
+ }),
4074
+ },
4075
+ {
4076
+ title: t.horizontalRule.title,
4077
+ description: t.horizontalRule.description,
4078
+ icon: "horizontal_rule",
4079
+ keywords: t.horizontalRule.keywords,
4080
+ command: (editor) => commands.insertHorizontalRule(editor),
4081
+ },
4082
+ {
4083
+ title: t.table.title,
4084
+ description: t.table.description,
4085
+ icon: "table_view",
4086
+ keywords: t.table.keywords,
4087
+ command: (editor) => commands.insertTable(editor),
4088
+ },
4089
+ ];
4090
+ }
4091
+ /**
4092
+ * Filtre et assemble les commandes selon la configuration fournie.
4093
+ */
4094
+ function filterSlashCommands(config, i18n, commands, images, imageOptions) {
4095
+ const allNatives = createDefaultSlashCommands(i18n, commands, images, imageOptions);
4096
+ const activeConfig = { ...DEFAULT_SLASH_COMMANDS_CONFIG, ...config };
4097
+ const filtered = allNatives.filter((_, index) => {
4098
+ const key = SLASH_COMMAND_KEYS[index];
4099
+ return key && activeConfig[key] !== false;
4100
+ });
4101
+ if (config.custom && Array.isArray(config.custom)) {
4102
+ return [...filtered, ...config.custom];
4103
+ }
4104
+ return filtered;
4105
+ }
4106
+
4107
+ // La définition des commandes par défaut est maintenant centralisée dans src/lib/config/i18n-slash-commands.ts
4089
4108
  class TiptapSlashCommandsComponent {
4090
4109
  constructor() {
4110
+ this.i18nService = inject(TiptapI18nService);
4091
4111
  this.editor = input.required();
4092
- this.config = input({
4093
- commands: DEFAULT_SLASH_COMMANDS$1,
4094
- });
4112
+ this.config = input(undefined);
4095
4113
  // Output pour l'upload d'image
4096
4114
  this.imageUploadRequested = output();
4097
4115
  this.tippyInstance = null;
4098
4116
  this.imageService = inject(ImageService);
4117
+ this.editorCommands = inject(EditorCommandsService);
4099
4118
  // État local
4100
4119
  this.isActive = false;
4101
4120
  this.currentQuery = signal("");
@@ -4103,32 +4122,12 @@ class TiptapSlashCommandsComponent {
4103
4122
  // Signal pour l'index sélectionné
4104
4123
  this.selectedIndex = signal(0);
4105
4124
  this.commands = computed(() => {
4106
- const configCommands = this.config().commands || DEFAULT_SLASH_COMMANDS$1;
4107
- // Remplacer la commande image par une version qui utilise l'output
4108
- return configCommands.map((command) => {
4109
- if (command.icon === "image") {
4110
- return {
4111
- ...command,
4112
- command: (editor) => {
4113
- // Créer un input file temporaire
4114
- const input = document.createElement("input");
4115
- input.type = "file";
4116
- input.accept = "image/*";
4117
- input.style.display = "none";
4118
- input.addEventListener("change", (e) => {
4119
- const file = e.target.files?.[0];
4120
- if (file && file.type.startsWith("image/")) {
4121
- this.imageUploadRequested.emit(file);
4122
- }
4123
- document.body.removeChild(input);
4124
- });
4125
- document.body.appendChild(input);
4126
- input.click();
4127
- },
4128
- };
4129
- }
4130
- return command;
4131
- });
4125
+ const config = this.config();
4126
+ if (config && config.commands) {
4127
+ return config.commands;
4128
+ }
4129
+ // Fallback vers les commandes natives par défaut
4130
+ return createDefaultSlashCommands(this.i18nService, this.editorCommands, this.imageService);
4132
4131
  });
4133
4132
  this.filteredCommands = computed(() => {
4134
4133
  const query = this.currentQuery().toLowerCase();
@@ -4265,7 +4264,11 @@ class TiptapSlashCommandsComponent {
4265
4264
  content: menuElement,
4266
4265
  trigger: "manual",
4267
4266
  placement: "bottom-start",
4268
- appendTo: () => document.body,
4267
+ appendTo: (ref) => {
4268
+ // Toujours essayer de remonter jusqu'au host de l'éditeur pour hériter des variables CSS
4269
+ const host = this.editor().options.element.closest("angular-tiptap-editor");
4270
+ return host || document.body;
4271
+ },
4269
4272
  interactive: true,
4270
4273
  arrow: false,
4271
4274
  offset: [0, 8],
@@ -4304,7 +4307,7 @@ class TiptapSlashCommandsComponent {
4304
4307
  return new DOMRect(coords.left, coords.top, 0, coords.bottom - coords.top);
4305
4308
  }
4306
4309
  catch (error) {
4307
- console.warn("Erreur lors du calcul des coordonnées:", error);
4310
+ console.warn("Error calculating coordinates:", error);
4308
4311
  // Fallback sur window.getSelection
4309
4312
  const selection = window.getSelection();
4310
4313
  if (!selection || selection.rangeCount === 0) {
@@ -4337,14 +4340,18 @@ class TiptapSlashCommandsComponent {
4337
4340
  const ed = this.editor();
4338
4341
  if (!ed || !this.slashRange)
4339
4342
  return;
4340
- // Supprimer le texte slash
4343
+ // Supprimer le texte slash ("/")
4341
4344
  const { tr } = ed.state;
4342
4345
  tr.delete(this.slashRange.from, this.slashRange.to);
4343
4346
  ed.view.dispatch(tr);
4344
- // Cacher le menu
4347
+ // Cacher le menu immédiatement
4345
4348
  this.hideTippy();
4346
- // Exécuter la commande
4349
+ this.isActive = false;
4350
+ // Redonner le focus à l'éditeur et exécuter la commande
4351
+ // On utilise un micro-délai pour s'assurer que le DOM ProseMirror est stable
4352
+ // après la suppression du texte "/"
4347
4353
  setTimeout(() => {
4354
+ ed.commands.focus();
4348
4355
  command.command(ed);
4349
4356
  }, 10);
4350
4357
  }
@@ -4408,7 +4415,7 @@ class TiptapSlashCommandsComponent {
4408
4415
  <div
4409
4416
  class="slash-command-item"
4410
4417
  [class.selected]="$index === selectedIndex()"
4411
- (click)="executeCommand(command)"
4418
+ (mousedown)="executeCommand(command); $event.preventDefault(); $event.stopPropagation()"
4412
4419
  (mouseenter)="selectedIndex.set($index)"
4413
4420
  >
4414
4421
  <div class="slash-command-icon">
@@ -4421,7 +4428,7 @@ class TiptapSlashCommandsComponent {
4421
4428
  </div>
4422
4429
  }
4423
4430
  </div>
4424
- `, isInline: true, styles: [".slash-commands-menu{background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 12px #00000026;padding:8px;max-height:300px;overflow-y:auto;min-width:280px;outline:none}.slash-command-item{display:flex;align-items:center;gap:12px;padding:8px 12px;border-radius:6px;cursor:pointer;transition:all .2s ease;border:2px solid transparent;outline:none}.slash-command-item:hover{background:#f1f5f9;border-color:#e2e8f0}.slash-command-item.selected{background:#e6f3ff;border-color:#3182ce;box-shadow:0 0 0 1px #3182ce}.slash-command-item:focus{outline:2px solid #3182ce;outline-offset:2px}.slash-command-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;background:#f8f9fa;border-radius:6px;color:#3182ce;flex-shrink:0}.slash-command-icon .material-symbols-outlined{font-size:18px}.slash-command-content{flex:1;min-width:0}.slash-command-title{font-weight:600;color:#2d3748;font-size:14px;margin-bottom:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.slash-command-description{color:#718096;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }); }
4431
+ `, isInline: true, styles: [".slash-commands-menu{background:var(--ate-menu-bg);-webkit-backdrop-filter:blur(var(--ate-menu-blur, 16px));backdrop-filter:blur(var(--ate-menu-blur, 16px));border:1px solid var(--ate-menu-border);border-radius:var(--ate-border-radius, 12px);box-shadow:var(--ate-menu-shadow);padding:6px;max-height:320px;overflow-y:auto;min-width:280px;outline:none;animation:slashMenuFadeIn .2s cubic-bezier(0,0,.2,1);scrollbar-width:thin;scrollbar-color:var(--ate-scrollbar-thumb) var(--ate-scrollbar-track)}.slash-commands-menu::-webkit-scrollbar{width:var(--ate-scrollbar-width)}.slash-commands-menu::-webkit-scrollbar-track{background:var(--ate-scrollbar-track)}.slash-commands-menu::-webkit-scrollbar-thumb{background:var(--ate-scrollbar-thumb);border:3px solid transparent;background-clip:content-box;border-radius:10px}.slash-commands-menu::-webkit-scrollbar-thumb:hover{background:var(--ate-scrollbar-thumb-hover);background-clip:content-box}@keyframes slashMenuFadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.slash-command-item{display:flex;align-items:center;gap:12px;padding:8px 12px;border-radius:var(--ate-border-radius, 8px);cursor:pointer;transition:all .15s ease;border:var(--ate-border-width, 1px) solid transparent;outline:none;margin-bottom:2px}.slash-command-item:last-child{margin-bottom:0}.slash-command-item:hover{background:var(--ate-surface-secondary)}.slash-command-item.selected{background:var(--ate-primary-light);border-color:var(--ate-primary-light-alpha)}.slash-command-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;background:var(--ate-surface-tertiary);border-radius:var(--ate-border-radius, 8px);color:var(--ate-primary);flex-shrink:0;transition:all .15s ease}.slash-command-item.selected .slash-command-icon{background:var(--ate-primary);color:var(--ate-primary-contrast, #ffffff)}.slash-command-icon .material-symbols-outlined{font-size:18px}.slash-command-content{flex:1;min-width:0}.slash-command-title{font-weight:500;color:var(--ate-text);font-size:14px;margin-bottom:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.slash-command-description{color:var(--ate-text-secondary);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }); }
4425
4432
  }
4426
4433
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: TiptapSlashCommandsComponent, decorators: [{
4427
4434
  type: Component,
@@ -4431,7 +4438,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
4431
4438
  <div
4432
4439
  class="slash-command-item"
4433
4440
  [class.selected]="$index === selectedIndex()"
4434
- (click)="executeCommand(command)"
4441
+ (mousedown)="executeCommand(command); $event.preventDefault(); $event.stopPropagation()"
4435
4442
  (mouseenter)="selectedIndex.set($index)"
4436
4443
  >
4437
4444
  <div class="slash-command-icon">
@@ -4444,7 +4451,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
4444
4451
  </div>
4445
4452
  }
4446
4453
  </div>
4447
- `, styles: [".slash-commands-menu{background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 12px #00000026;padding:8px;max-height:300px;overflow-y:auto;min-width:280px;outline:none}.slash-command-item{display:flex;align-items:center;gap:12px;padding:8px 12px;border-radius:6px;cursor:pointer;transition:all .2s ease;border:2px solid transparent;outline:none}.slash-command-item:hover{background:#f1f5f9;border-color:#e2e8f0}.slash-command-item.selected{background:#e6f3ff;border-color:#3182ce;box-shadow:0 0 0 1px #3182ce}.slash-command-item:focus{outline:2px solid #3182ce;outline-offset:2px}.slash-command-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;background:#f8f9fa;border-radius:6px;color:#3182ce;flex-shrink:0}.slash-command-icon .material-symbols-outlined{font-size:18px}.slash-command-content{flex:1;min-width:0}.slash-command-title{font-weight:600;color:#2d3748;font-size:14px;margin-bottom:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.slash-command-description{color:#718096;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }]
4454
+ `, styles: [".slash-commands-menu{background:var(--ate-menu-bg);-webkit-backdrop-filter:blur(var(--ate-menu-blur, 16px));backdrop-filter:blur(var(--ate-menu-blur, 16px));border:1px solid var(--ate-menu-border);border-radius:var(--ate-border-radius, 12px);box-shadow:var(--ate-menu-shadow);padding:6px;max-height:320px;overflow-y:auto;min-width:280px;outline:none;animation:slashMenuFadeIn .2s cubic-bezier(0,0,.2,1);scrollbar-width:thin;scrollbar-color:var(--ate-scrollbar-thumb) var(--ate-scrollbar-track)}.slash-commands-menu::-webkit-scrollbar{width:var(--ate-scrollbar-width)}.slash-commands-menu::-webkit-scrollbar-track{background:var(--ate-scrollbar-track)}.slash-commands-menu::-webkit-scrollbar-thumb{background:var(--ate-scrollbar-thumb);border:3px solid transparent;background-clip:content-box;border-radius:10px}.slash-commands-menu::-webkit-scrollbar-thumb:hover{background:var(--ate-scrollbar-thumb-hover);background-clip:content-box}@keyframes slashMenuFadeIn{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.slash-command-item{display:flex;align-items:center;gap:12px;padding:8px 12px;border-radius:var(--ate-border-radius, 8px);cursor:pointer;transition:all .15s ease;border:var(--ate-border-width, 1px) solid transparent;outline:none;margin-bottom:2px}.slash-command-item:last-child{margin-bottom:0}.slash-command-item:hover{background:var(--ate-surface-secondary)}.slash-command-item.selected{background:var(--ate-primary-light);border-color:var(--ate-primary-light-alpha)}.slash-command-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;background:var(--ate-surface-tertiary);border-radius:var(--ate-border-radius, 8px);color:var(--ate-primary);flex-shrink:0;transition:all .15s ease}.slash-command-item.selected .slash-command-icon{background:var(--ate-primary);color:var(--ate-primary-contrast, #ffffff)}.slash-command-icon .material-symbols-outlined{font-size:18px}.slash-command-content{flex:1;min-width:0}.slash-command-title{font-weight:500;color:var(--ate-text);font-size:14px;margin-bottom:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.slash-command-description{color:var(--ate-text-secondary);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }]
4448
4455
  }], ctorParameters: () => [], propDecorators: { menuRef: [{
4449
4456
  type: ViewChild,
4450
4457
  args: ["menuRef", { static: false }]
@@ -4452,350 +4459,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
4452
4459
 
4453
4460
  // Main component
4454
4461
 
4455
- class TiptapImageUploadComponent {
4456
- constructor() {
4457
- // Inputs
4458
- this.config = input({
4459
- maxSize: 5, // 5MB par défaut
4460
- maxWidth: 1920, // largeur max par défaut
4461
- maxHeight: 1080, // hauteur max par défaut
4462
- allowedTypes: ["image/jpeg", "image/png", "image/gif", "image/webp"],
4463
- enableDragDrop: true,
4464
- showPreview: true,
4465
- multiple: false,
4466
- compressImages: true,
4467
- quality: 0.8,
4468
- });
4469
- // Outputs
4470
- this.imageSelected = output();
4471
- this.error = output();
4472
- // Signals internes
4473
- this.isDragOver = signal(false);
4474
- this.isUploading = signal(false);
4475
- this.uploadProgress = signal(0);
4476
- this.previewImage = signal(null);
4477
- this.previewInfo = signal("");
4478
- this.errorMessage = signal(null);
4479
- // Computed
4480
- this.acceptedTypes = computed(() => {
4481
- const types = this.config().allowedTypes || ["image/*"];
4482
- return types.join(",");
4483
- });
4484
- }
4485
- triggerFileInput() {
4486
- const input = document.querySelector('input[type="file"]');
4487
- if (input) {
4488
- input.click();
4489
- }
4490
- }
4491
- onFileSelected(event) {
4492
- const input = event.target;
4493
- const files = input.files;
4494
- if (files && files.length > 0) {
4495
- this.processFiles(Array.from(files));
4496
- }
4497
- // Reset input
4498
- input.value = "";
4499
- }
4500
- onDragOver(event) {
4501
- event.preventDefault();
4502
- event.stopPropagation();
4503
- this.isDragOver.set(true);
4504
- }
4505
- onDrop(event) {
4506
- event.preventDefault();
4507
- event.stopPropagation();
4508
- this.isDragOver.set(false);
4509
- const files = event.dataTransfer?.files;
4510
- if (files && files.length > 0) {
4511
- this.processFiles(Array.from(files));
4512
- }
4513
- }
4514
- onDragLeave(event) {
4515
- event.preventDefault();
4516
- event.stopPropagation();
4517
- this.isDragOver.set(false);
4518
- }
4519
- processFiles(files) {
4520
- const config = this.config();
4521
- const maxSize = (config.maxSize || 5) * 1024 * 1024; // Convertir en bytes
4522
- const allowedTypes = config.allowedTypes || ["image/*"];
4523
- // Vérifier le nombre de fichiers
4524
- if (!config.multiple && files.length > 1) {
4525
- this.showError("Veuillez sélectionner une seule image");
4526
- return;
4527
- }
4528
- // Traiter chaque fichier
4529
- files.forEach((file) => {
4530
- // Vérifier le type
4531
- if (!this.isValidFileType(file, allowedTypes)) {
4532
- this.showError(`Type de fichier non supporté: ${file.name}`);
4533
- return;
4534
- }
4535
- // Vérifier la taille
4536
- if (file.size > maxSize) {
4537
- this.showError(`Fichier trop volumineux: ${file.name} (max ${config.maxSize}MB)`);
4538
- return;
4539
- }
4540
- // Traiter l'image avec compression si nécessaire
4541
- this.processImage(file);
4542
- });
4543
- }
4544
- isValidFileType(file, allowedTypes) {
4545
- if (allowedTypes.includes("image/*")) {
4546
- return file.type.startsWith("image/");
4547
- }
4548
- return allowedTypes.includes(file.type);
4549
- }
4550
- processImage(file) {
4551
- this.isUploading.set(true);
4552
- this.uploadProgress.set(10);
4553
- const config = this.config();
4554
- const originalSize = file.size;
4555
- // Créer un canvas pour la compression
4556
- const canvas = document.createElement("canvas");
4557
- const ctx = canvas.getContext("2d");
4558
- const img = new Image();
4559
- img.onload = () => {
4560
- this.uploadProgress.set(30);
4561
- // Vérifier les dimensions
4562
- const maxWidth = config.maxWidth || 1920;
4563
- const maxHeight = config.maxHeight || 1080;
4564
- let { width, height } = img;
4565
- // Redimensionner si nécessaire
4566
- if (width > maxWidth || height > maxHeight) {
4567
- const ratio = Math.min(maxWidth / width, maxHeight / height);
4568
- width *= ratio;
4569
- height *= ratio;
4570
- }
4571
- canvas.width = width;
4572
- canvas.height = height;
4573
- // Dessiner l'image redimensionnée
4574
- ctx?.drawImage(img, 0, 0, width, height);
4575
- this.uploadProgress.set(70);
4576
- // Convertir en base64 avec compression
4577
- const quality = config.quality || 0.8;
4578
- const mimeType = file.type;
4579
- canvas.toBlob((blob) => {
4580
- this.uploadProgress.set(90);
4581
- if (blob) {
4582
- const reader = new FileReader();
4583
- reader.onload = (e) => {
4584
- const base64 = e.target?.result;
4585
- if (base64) {
4586
- const result = {
4587
- src: base64,
4588
- name: file.name,
4589
- size: blob.size,
4590
- type: file.type,
4591
- width: Math.round(width),
4592
- height: Math.round(height),
4593
- originalSize: originalSize,
4594
- };
4595
- // Afficher la prévisualisation si activée
4596
- if (config.showPreview) {
4597
- this.previewImage.set(base64);
4598
- this.previewInfo.set(`${result.width}×${result.height} • ${this.formatFileSize(blob.size)}`);
4599
- }
4600
- // Émettre l'événement
4601
- this.imageSelected.emit(result);
4602
- this.clearError();
4603
- }
4604
- this.uploadProgress.set(100);
4605
- setTimeout(() => {
4606
- this.isUploading.set(false);
4607
- this.uploadProgress.set(0);
4608
- }, 500);
4609
- };
4610
- reader.readAsDataURL(blob);
4611
- }
4612
- else {
4613
- this.showError("Erreur lors de la compression de l'image");
4614
- this.isUploading.set(false);
4615
- this.uploadProgress.set(0);
4616
- }
4617
- }, mimeType, quality);
4618
- };
4619
- img.onerror = () => {
4620
- this.showError("Erreur lors du chargement de l'image");
4621
- this.isUploading.set(false);
4622
- this.uploadProgress.set(0);
4623
- };
4624
- img.src = URL.createObjectURL(file);
4625
- }
4626
- formatFileSize(bytes) {
4627
- if (bytes === 0)
4628
- return "0 B";
4629
- const k = 1024;
4630
- const sizes = ["B", "KB", "MB", "GB"];
4631
- const i = Math.floor(Math.log(bytes) / Math.log(k));
4632
- return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
4633
- }
4634
- showError(message) {
4635
- this.errorMessage.set(message);
4636
- this.error.emit(message);
4637
- this.isUploading.set(false);
4638
- this.uploadProgress.set(0);
4639
- // Auto-clear après 5 secondes
4640
- setTimeout(() => {
4641
- this.clearError();
4642
- }, 5000);
4643
- }
4644
- clearError() {
4645
- this.errorMessage.set(null);
4646
- }
4647
- clearPreview() {
4648
- this.previewImage.set(null);
4649
- this.previewInfo.set("");
4650
- }
4651
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: TiptapImageUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4652
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", type: TiptapImageUploadComponent, isStandalone: true, selector: "tiptap-image-upload", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { imageSelected: "imageSelected", error: "error" }, ngImport: i0, template: `
4653
- <div class="image-upload-container">
4654
- <!-- Bouton d'upload -->
4655
- <tiptap-button
4656
- icon="image"
4657
- title="Ajouter une image"
4658
- [disabled]="isUploading()"
4659
- (onClick)="triggerFileInput()"
4660
- />
4661
-
4662
- <!-- Input file caché -->
4663
- <input
4664
- #fileInput
4665
- type="file"
4666
- [accept]="acceptedTypes()"
4667
- [multiple]="config().multiple"
4668
- (change)="onFileSelected($event)"
4669
- style="display: none;"
4670
- />
4671
-
4672
- <!-- Zone de drag & drop -->
4673
- @if (config().enableDragDrop && isDragOver()) {
4674
- <div
4675
- class="drag-overlay"
4676
- (dragover)="onDragOver($event)"
4677
- (drop)="onDrop($event)"
4678
- (dragleave)="onDragLeave($event)"
4679
- >
4680
- <div class="drag-content">
4681
- <span class="material-symbols-outlined">cloud_upload</span>
4682
- <p>Déposez votre image ici</p>
4683
- </div>
4684
- </div>
4685
- }
4686
-
4687
- <!-- Barre de progression -->
4688
- @if (isUploading() && uploadProgress() > 0) {
4689
- <div class="upload-progress">
4690
- <div class="progress-bar">
4691
- <div class="progress-fill" [style.width.%]="uploadProgress()"></div>
4692
- </div>
4693
- <div class="progress-text">{{ uploadProgress() }}%</div>
4694
- </div>
4695
- }
4696
-
4697
- <!-- Prévisualisation -->
4698
- @if (config().showPreview && previewImage()) {
4699
- <div class="image-preview">
4700
- <img [src]="previewImage()" alt="Prévisualisation" />
4701
- <div class="preview-info">
4702
- <span>{{ previewInfo() }}</span>
4703
- </div>
4704
- <button
4705
- class="preview-close"
4706
- (click)="clearPreview()"
4707
- title="Fermer la prévisualisation"
4708
- >
4709
- <span class="material-symbols-outlined">close</span>
4710
- </button>
4711
- </div>
4712
- }
4713
-
4714
- <!-- Messages d'erreur -->
4715
- @if (errorMessage()) {
4716
- <div class="error-message">
4717
- <span class="material-symbols-outlined">error</span>
4718
- {{ errorMessage() }}
4719
- </div>
4720
- }
4721
- </div>
4722
- `, isInline: true, styles: [".image-upload-container{position:relative;display:inline-block}.drag-overlay{position:fixed;inset:0;background:#3182ce1a;border:2px dashed #3182ce;border-radius:6px;display:flex;align-items:center;justify-content:center;z-index:1000;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.drag-content{text-align:center;color:#3182ce;font-weight:600}.drag-content .material-symbols-outlined{font-size:48px;margin-bottom:16px}.drag-content p{margin:0;font-size:18px}.upload-progress{position:absolute;top:100%;left:0;background:#fff;border:1px solid #e2e8f0;border-radius:8px;padding:12px;margin-top:8px;z-index:100;min-width:200px;box-shadow:0 4px 12px #00000026}.progress-bar{width:100%;height:6px;background:#e2e8f0;border-radius:3px;overflow:hidden;margin-bottom:8px}.progress-fill{height:100%;background:#3182ce;border-radius:3px;transition:width .3s ease}.progress-text{font-size:12px;color:#4a5568;text-align:center}.image-preview{position:absolute;top:100%;left:0;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 12px #00000026;padding:8px;margin-top:8px;z-index:100;min-width:200px}.image-preview img{max-width:200px;max-height:150px;border-radius:4px;display:block}.preview-info{margin-top:8px;font-size:11px;color:#718096;text-align:center}.preview-close{position:absolute;top:4px;right:4px;background:#000000b3;color:#fff;border:none;border-radius:50%;width:24px;height:24px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:14px}.preview-close:hover{background:#000000e6}.error-message{position:absolute;top:100%;left:0;background:#fed7d7;color:#c53030;border:1px solid #feb2b2;border-radius:6px;padding:8px 12px;margin-top:8px;font-size:12px;display:flex;align-items:center;gap:6px;z-index:100;min-width:200px}.error-message .material-symbols-outlined{font-size:16px}\n"], dependencies: [{ kind: "component", type: TiptapButtonComponent, selector: "tiptap-button", inputs: ["icon", "title", "active", "disabled", "color", "variant", "size", "iconSize"], outputs: ["onClick"] }] }); }
4723
- }
4724
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: TiptapImageUploadComponent, decorators: [{
4725
- type: Component,
4726
- args: [{ selector: "tiptap-image-upload", standalone: true, imports: [TiptapButtonComponent], template: `
4727
- <div class="image-upload-container">
4728
- <!-- Bouton d'upload -->
4729
- <tiptap-button
4730
- icon="image"
4731
- title="Ajouter une image"
4732
- [disabled]="isUploading()"
4733
- (onClick)="triggerFileInput()"
4734
- />
4735
-
4736
- <!-- Input file caché -->
4737
- <input
4738
- #fileInput
4739
- type="file"
4740
- [accept]="acceptedTypes()"
4741
- [multiple]="config().multiple"
4742
- (change)="onFileSelected($event)"
4743
- style="display: none;"
4744
- />
4745
-
4746
- <!-- Zone de drag & drop -->
4747
- @if (config().enableDragDrop && isDragOver()) {
4748
- <div
4749
- class="drag-overlay"
4750
- (dragover)="onDragOver($event)"
4751
- (drop)="onDrop($event)"
4752
- (dragleave)="onDragLeave($event)"
4753
- >
4754
- <div class="drag-content">
4755
- <span class="material-symbols-outlined">cloud_upload</span>
4756
- <p>Déposez votre image ici</p>
4757
- </div>
4758
- </div>
4759
- }
4760
-
4761
- <!-- Barre de progression -->
4762
- @if (isUploading() && uploadProgress() > 0) {
4763
- <div class="upload-progress">
4764
- <div class="progress-bar">
4765
- <div class="progress-fill" [style.width.%]="uploadProgress()"></div>
4766
- </div>
4767
- <div class="progress-text">{{ uploadProgress() }}%</div>
4768
- </div>
4769
- }
4770
-
4771
- <!-- Prévisualisation -->
4772
- @if (config().showPreview && previewImage()) {
4773
- <div class="image-preview">
4774
- <img [src]="previewImage()" alt="Prévisualisation" />
4775
- <div class="preview-info">
4776
- <span>{{ previewInfo() }}</span>
4777
- </div>
4778
- <button
4779
- class="preview-close"
4780
- (click)="clearPreview()"
4781
- title="Fermer la prévisualisation"
4782
- >
4783
- <span class="material-symbols-outlined">close</span>
4784
- </button>
4785
- </div>
4786
- }
4787
-
4788
- <!-- Messages d'erreur -->
4789
- @if (errorMessage()) {
4790
- <div class="error-message">
4791
- <span class="material-symbols-outlined">error</span>
4792
- {{ errorMessage() }}
4793
- </div>
4794
- }
4795
- </div>
4796
- `, styles: [".image-upload-container{position:relative;display:inline-block}.drag-overlay{position:fixed;inset:0;background:#3182ce1a;border:2px dashed #3182ce;border-radius:6px;display:flex;align-items:center;justify-content:center;z-index:1000;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.drag-content{text-align:center;color:#3182ce;font-weight:600}.drag-content .material-symbols-outlined{font-size:48px;margin-bottom:16px}.drag-content p{margin:0;font-size:18px}.upload-progress{position:absolute;top:100%;left:0;background:#fff;border:1px solid #e2e8f0;border-radius:8px;padding:12px;margin-top:8px;z-index:100;min-width:200px;box-shadow:0 4px 12px #00000026}.progress-bar{width:100%;height:6px;background:#e2e8f0;border-radius:3px;overflow:hidden;margin-bottom:8px}.progress-fill{height:100%;background:#3182ce;border-radius:3px;transition:width .3s ease}.progress-text{font-size:12px;color:#4a5568;text-align:center}.image-preview{position:absolute;top:100%;left:0;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 12px #00000026;padding:8px;margin-top:8px;z-index:100;min-width:200px}.image-preview img{max-width:200px;max-height:150px;border-radius:4px;display:block}.preview-info{margin-top:8px;font-size:11px;color:#718096;text-align:center}.preview-close{position:absolute;top:4px;right:4px;background:#000000b3;color:#fff;border:none;border-radius:50%;width:24px;height:24px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:14px}.preview-close:hover{background:#000000e6}.error-message{position:absolute;top:100%;left:0;background:#fed7d7;color:#c53030;border:1px solid #feb2b2;border-radius:6px;padding:8px 12px;margin-top:8px;font-size:12px;display:flex;align-items:center;gap:6px;z-index:100;min-width:200px}.error-message .material-symbols-outlined{font-size:16px}\n"] }]
4797
- }] });
4798
-
4799
4462
  class NoopValueAccessorDirective {
4800
4463
  writeValue(obj) { }
4801
4464
  registerOnChange(fn) { }
@@ -4823,178 +4486,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
4823
4486
  }]
4824
4487
  }] });
4825
4488
 
4826
- /**
4827
- * Clés des commandes dans l'ordre de création
4828
- */
4829
- const SLASH_COMMAND_KEYS = [
4830
- "heading1",
4831
- "heading2",
4832
- "heading3",
4833
- "bulletList",
4834
- "orderedList",
4835
- "blockquote",
4836
- "code",
4837
- "image",
4838
- "horizontalRule",
4839
- "table",
4840
- ];
4841
- /**
4842
- * Factory function pour créer les slash commands traduits
4843
- */
4844
- function createI18nSlashCommands(i18nService) {
4845
- const slashCommands = i18nService.slashCommands();
4846
- return [
4847
- {
4848
- title: slashCommands.heading1.title,
4849
- description: slashCommands.heading1.description,
4850
- icon: "format_h1",
4851
- keywords: slashCommands.heading1.keywords,
4852
- command: (editor) => editor.chain().focus().toggleHeading({ level: 1 }).run(),
4853
- },
4854
- {
4855
- title: slashCommands.heading2.title,
4856
- description: slashCommands.heading2.description,
4857
- icon: "format_h2",
4858
- keywords: slashCommands.heading2.keywords,
4859
- command: (editor) => editor.chain().focus().toggleHeading({ level: 2 }).run(),
4860
- },
4861
- {
4862
- title: slashCommands.heading3.title,
4863
- description: slashCommands.heading3.description,
4864
- icon: "format_h3",
4865
- keywords: slashCommands.heading3.keywords,
4866
- command: (editor) => editor.chain().focus().toggleHeading({ level: 3 }).run(),
4867
- },
4868
- {
4869
- title: slashCommands.bulletList.title,
4870
- description: slashCommands.bulletList.description,
4871
- icon: "format_list_bulleted",
4872
- keywords: slashCommands.bulletList.keywords,
4873
- command: (editor) => editor.chain().focus().toggleBulletList().run(),
4874
- },
4875
- {
4876
- title: slashCommands.orderedList.title,
4877
- description: slashCommands.orderedList.description,
4878
- icon: "format_list_numbered",
4879
- keywords: slashCommands.orderedList.keywords,
4880
- command: (editor) => editor.chain().focus().toggleOrderedList().run(),
4881
- },
4882
- {
4883
- title: slashCommands.blockquote.title,
4884
- description: slashCommands.blockquote.description,
4885
- icon: "format_quote",
4886
- keywords: slashCommands.blockquote.keywords,
4887
- command: (editor) => editor.chain().focus().toggleBlockquote().run(),
4888
- },
4889
- {
4890
- title: slashCommands.code.title,
4891
- description: slashCommands.code.description,
4892
- icon: "code",
4893
- keywords: slashCommands.code.keywords,
4894
- command: (editor) => editor.chain().focus().toggleCodeBlock().run(),
4895
- },
4896
- {
4897
- title: slashCommands.image.title,
4898
- description: slashCommands.image.description,
4899
- icon: "image",
4900
- keywords: slashCommands.image.keywords,
4901
- command: (editor) => {
4902
- // Créer un input file temporaire pour sélectionner une image
4903
- const input = document.createElement("input");
4904
- input.type = "file";
4905
- input.accept = "image/*";
4906
- input.style.display = "none";
4907
- input.addEventListener("change", async (e) => {
4908
- const file = e.target.files?.[0];
4909
- if (file && file.type.startsWith("image/")) {
4910
- try {
4911
- // Utiliser la méthode de compression unifiée
4912
- const canvas = document.createElement("canvas");
4913
- const ctx = canvas.getContext("2d");
4914
- const img = new Image();
4915
- img.onload = () => {
4916
- // Vérifier les dimensions (max 1920x1080)
4917
- const maxWidth = 1920;
4918
- const maxHeight = 1080;
4919
- let { width, height } = img;
4920
- // Redimensionner si nécessaire
4921
- if (width > maxWidth || height > maxHeight) {
4922
- const ratio = Math.min(maxWidth / width, maxHeight / height);
4923
- width *= ratio;
4924
- height *= ratio;
4925
- }
4926
- canvas.width = width;
4927
- canvas.height = height;
4928
- // Dessiner l'image redimensionnée
4929
- ctx?.drawImage(img, 0, 0, width, height);
4930
- // Convertir en base64 avec compression
4931
- canvas.toBlob((blob) => {
4932
- if (blob) {
4933
- const reader = new FileReader();
4934
- reader.onload = (e) => {
4935
- const base64 = e.target?.result;
4936
- if (base64) {
4937
- // Utiliser setResizableImage avec toutes les propriétés
4938
- editor
4939
- .chain()
4940
- .focus()
4941
- .setResizableImage({
4942
- src: base64,
4943
- alt: file.name,
4944
- title: `${file.name} (${Math.round(width)}×${Math.round(height)})`,
4945
- width: Math.round(width),
4946
- height: Math.round(height),
4947
- })
4948
- .run();
4949
- }
4950
- };
4951
- reader.readAsDataURL(blob);
4952
- }
4953
- }, file.type, 0.8 // qualité de compression
4954
- );
4955
- };
4956
- img.onerror = () => {
4957
- console.error(i18nService.editor().imageLoadError);
4958
- };
4959
- img.src = URL.createObjectURL(file);
4960
- }
4961
- catch (error) {
4962
- console.error("Error uploading image:", error);
4963
- }
4964
- }
4965
- document.body.removeChild(input);
4966
- });
4967
- document.body.appendChild(input);
4968
- input.click();
4969
- },
4970
- },
4971
- {
4972
- title: slashCommands.horizontalRule.title,
4973
- description: slashCommands.horizontalRule.description,
4974
- icon: "horizontal_rule",
4975
- keywords: slashCommands.horizontalRule.keywords,
4976
- command: (editor) => editor.chain().focus().setHorizontalRule().run(),
4977
- },
4978
- {
4979
- title: slashCommands.table.title,
4980
- description: slashCommands.table.description,
4981
- icon: "table_view",
4982
- keywords: slashCommands.table.keywords,
4983
- command: (editor) => editor.chain().focus().insertTable({ rows: 3, cols: 3 }).run(),
4984
- },
4985
- ];
4986
- }
4987
- /**
4988
- * Fonction utilitaire pour filtrer les slash commands selon les commandes actives
4989
- */
4990
- function filterSlashCommands(activeCommands, i18nService) {
4991
- const allCommands = createI18nSlashCommands(i18nService);
4992
- return allCommands.filter((command, index) => {
4993
- const commandKey = SLASH_COMMAND_KEYS[index];
4994
- return commandKey && activeCommands.has(commandKey);
4995
- });
4996
- }
4997
-
4998
4489
  // Configuration par défaut de la toolbar
4999
4490
  const DEFAULT_TOOLBAR_CONFIG = {
5000
4491
  bold: true,
@@ -5066,9 +4557,7 @@ const DEFAULT_CELL_MENU_CONFIG = {
5066
4557
  mergeCells: true,
5067
4558
  splitCell: true,
5068
4559
  };
5069
- const DEFAULT_SLASH_COMMANDS = {
5070
- commands: [], // Sera rempli par filterSlashCommands
5071
- };
4560
+ // La configuration des slash commands est gérée dynamiquement via slashCommandsConfigComputed
5072
4561
  class AngularTiptapEditorComponent {
5073
4562
  constructor() {
5074
4563
  this.content = input("");
@@ -5083,7 +4572,8 @@ class AngularTiptapEditorComponent {
5083
4572
  this.maxCharacters = input(undefined);
5084
4573
  this.enableOfficePaste = input(true);
5085
4574
  this.enableSlashCommands = input(true);
5086
- this.slashCommandsConfig = input(undefined);
4575
+ this.slashCommands = input({});
4576
+ this.customSlashCommands = input(undefined);
5087
4577
  this.locale = input(undefined);
5088
4578
  this.autofocus = input(false);
5089
4579
  this.tiptapExtensions = input([]);
@@ -5183,13 +4673,15 @@ class AngularTiptapEditorComponent {
5183
4673
  }));
5184
4674
  // Computed pour la configuration des slash commands
5185
4675
  this.slashCommandsConfigComputed = computed(() => {
5186
- const config = this.slashCommandsConfig();
5187
- if (config) {
5188
- return config;
4676
+ const customConfig = this.customSlashCommands();
4677
+ if (customConfig) {
4678
+ return customConfig;
5189
4679
  }
5190
- // Configuration par défaut avec toutes les commandes
5191
- const allCommands = filterSlashCommands(new Set(SLASH_COMMAND_KEYS), this.i18nService);
5192
- return { commands: allCommands };
4680
+ // Utilise l'utilitaire filterSlashCommands qui gère maintenant
4681
+ // les défauts, le filtrage et l'ajout de commandes personnalisées
4682
+ return {
4683
+ commands: filterSlashCommands(this.slashCommands(), this.i18nService, this.editorCommandsService, this.imageService, this.imageUploadConfig()),
4684
+ };
5193
4685
  });
5194
4686
  this._destroyRef = inject(DestroyRef);
5195
4687
  // NgControl pour gérer les FormControls
@@ -5386,22 +4878,6 @@ class AngularTiptapEditorComponent {
5386
4878
  this._wordCount.set(storage.words());
5387
4879
  }
5388
4880
  }
5389
- // Méthodes pour l'upload d'images
5390
- onImageUploaded(result) {
5391
- const currentEditor = this.editor();
5392
- if (currentEditor) {
5393
- this.imageService.insertImage(currentEditor, {
5394
- src: result.src,
5395
- alt: result.name,
5396
- title: `${result.name} (${result.width}×${result.height})`,
5397
- width: result.width,
5398
- height: result.height,
5399
- });
5400
- }
5401
- }
5402
- onImageUploadError(error) {
5403
- // Ici vous pourriez afficher une notification à l'utilisateur
5404
- }
5405
4881
  // Gestion de l'upload d'image depuis les slash commands
5406
4882
  async onSlashCommandImageUpload(file) {
5407
4883
  const currentEditor = this.editor();
@@ -5435,7 +4911,12 @@ class AngularTiptapEditorComponent {
5435
4911
  const currentEditor = this.editor();
5436
4912
  if (currentEditor) {
5437
4913
  try {
5438
- await this.imageService.uploadAndInsertImage(currentEditor, file);
4914
+ const config = this.imageUploadConfig();
4915
+ await this.imageService.uploadAndInsertImage(currentEditor, file, {
4916
+ quality: config.quality,
4917
+ maxWidth: config.maxWidth,
4918
+ maxHeight: config.maxHeight
4919
+ });
5439
4920
  }
5440
4921
  catch (error) {
5441
4922
  // Gérer l'erreur silencieusement ou afficher une notification
@@ -5520,19 +5001,15 @@ class AngularTiptapEditorComponent {
5520
5001
  }
5521
5002
  }
5522
5003
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AngularTiptapEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5523
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", type: AngularTiptapEditorComponent, isStandalone: true, selector: "angular-tiptap-editor", inputs: { 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 }, 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 }, showCharacterCount: { classPropertyName: "showCharacterCount", publicName: "showCharacterCount", 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 }, slashCommandsConfig: { classPropertyName: "slashCommandsConfig", publicName: "slashCommandsConfig", 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 }, tiptapExtensions: { classPropertyName: "tiptapExtensions", publicName: "tiptapExtensions", isSignal: true, isRequired: false, transformFunction: null }, tiptapOptions: { classPropertyName: "tiptapOptions", publicName: "tiptapOptions", 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 }, imageUpload: { classPropertyName: "imageUpload", publicName: "imageUpload", isSignal: true, isRequired: false, transformFunction: null }, imageUploadHandler: { classPropertyName: "imageUploadHandler", publicName: "imageUploadHandler", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { contentChange: "contentChange", editorCreated: "editorCreated", editorUpdate: "editorUpdate", editorFocus: "editorFocus", editorBlur: "editorBlur" }, host: { properties: { "class.fill-container": "fillContainer()" } }, viewQueries: [{ propertyName: "editorElement", first: true, predicate: ["editorElement"], descendants: true, isSignal: true }], hostDirectives: [{ directive: NoopValueAccessorDirective }], ngImport: i0, template: `
5004
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", type: AngularTiptapEditorComponent, isStandalone: true, selector: "angular-tiptap-editor", inputs: { 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 }, 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 }, showCharacterCount: { classPropertyName: "showCharacterCount", publicName: "showCharacterCount", 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 }, slashCommands: { classPropertyName: "slashCommands", publicName: "slashCommands", isSignal: true, isRequired: false, transformFunction: null }, customSlashCommands: { classPropertyName: "customSlashCommands", publicName: "customSlashCommands", 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 }, tiptapExtensions: { classPropertyName: "tiptapExtensions", publicName: "tiptapExtensions", isSignal: true, isRequired: false, transformFunction: null }, tiptapOptions: { classPropertyName: "tiptapOptions", publicName: "tiptapOptions", 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 }, imageUpload: { classPropertyName: "imageUpload", publicName: "imageUpload", isSignal: true, isRequired: false, transformFunction: null }, imageUploadHandler: { classPropertyName: "imageUploadHandler", publicName: "imageUploadHandler", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { contentChange: "contentChange", editorCreated: "editorCreated", editorUpdate: "editorUpdate", editorFocus: "editorFocus", editorBlur: "editorBlur" }, host: { properties: { "class.fill-container": "fillContainer()" } }, viewQueries: [{ propertyName: "editorElement", first: true, predicate: ["editorElement"], descendants: true, isSignal: true }], hostDirectives: [{ directive: NoopValueAccessorDirective }], ngImport: i0, template: `
5524
5005
  <div class="tiptap-editor" [class.fill-container]="fillContainer()">
5525
5006
  <!-- Toolbar -->
5526
5007
  @if (showToolbar() && editor()) {
5527
- <tiptap-toolbar [editor]="editor()!" [config]="toolbarConfig()">
5528
- <div image-upload class="image-upload-container">
5529
- <tiptap-image-upload
5530
- [config]="imageUploadConfig()"
5531
- (imageSelected)="onImageUploaded($event)"
5532
- (error)="onImageUploadError($event)"
5533
- />
5534
- </div>
5535
- </tiptap-toolbar>
5008
+ <tiptap-toolbar
5009
+ [editor]="editor()!"
5010
+ [config]="toolbarConfig()"
5011
+ [imageUpload]="imageUploadConfig()"
5012
+ />
5536
5013
  }
5537
5014
 
5538
5015
  <!-- Contenu de l'éditeur -->
@@ -5591,8 +5068,6 @@ class AngularTiptapEditorComponent {
5591
5068
  ></tiptap-cell-bubble-menu>
5592
5069
  }
5593
5070
 
5594
- <!-- Table Edit Button - Supprimé car remplacé par le menu bubble -->
5595
-
5596
5071
  <!-- Compteur de caractères -->
5597
5072
  @if (showCharacterCount()) {
5598
5073
  <div class="character-count">
@@ -5606,7 +5081,7 @@ class AngularTiptapEditorComponent {
5606
5081
  </div>
5607
5082
  }
5608
5083
  </div>
5609
- `, isInline: true, styles: [":host(.fill-container){display:block;height:100%}.tiptap-editor{border:2px solid #e2e8f0;border-radius:8px;background:#fff;overflow:hidden;transition:border-color .2s ease}.tiptap-editor.fill-container{display:flex;flex-direction:column;height:100%}.tiptap-editor.fill-container .tiptap-content{flex:1;min-height:0;overflow-y:auto}.tiptap-editor:focus-within{border-color:#3182ce}.tiptap-content{padding:16px;min-height:var(--editor-min-height, 200px);height:var(--editor-height, auto);max-height:var(--editor-max-height, none);overflow-y:var(--editor-overflow, visible);outline:none;position:relative}.tiptap-content.drag-over{background:#f0f8ff;border:2px dashed #3182ce}.character-count{padding:8px 16px;font-size:12px;color:#718096;text-align:right;border-top:1px solid #e2e8f0;background:#f8f9fa}.image-upload-container{position:relative;display:inline-block}:host ::ng-deep .ProseMirror{outline:none;line-height:1.6;color:#2d3748;min-height:100%;height:100%;word-wrap:break-word;overflow-wrap:break-word}:host ::ng-deep .ProseMirror h1{font-size:2em;font-weight:700;margin-top:0;margin-bottom:.5em}:host ::ng-deep .ProseMirror h2{font-size:1.5em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror h3{font-size:1.25em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror p{margin:.5em 0}:host ::ng-deep .ProseMirror ul,:host ::ng-deep .ProseMirror ol{padding-left:2em;margin:.5em 0}:host ::ng-deep .ProseMirror blockquote{border-left:4px solid #e2e8f0;margin:1em 0;font-style:italic;background:#f8f9fa;padding:.5em 1em;border-radius:0 4px 4px 0}:host ::ng-deep .ProseMirror code{background:#f1f5f9;padding:.2em .4em;border-radius:3px;font-family:Monaco,Consolas,monospace;font-size:.9em}:host ::ng-deep .ProseMirror pre{background:#1a202c;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;margin:1em 0}:host ::ng-deep .ProseMirror pre code{background:none;color:inherit;padding:0}:host ::ng-deep .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);color:#a0aec0;pointer-events:none;float:left;height:0}:host ::ng-deep .ProseMirror[contenteditable=false]{pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img{cursor:default;pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img:hover{transform:none;box-shadow:0 2px 8px #0000001a}:host ::ng-deep .ProseMirror[contenteditable=false] img.ProseMirror-selectednode{outline:none}:host ::ng-deep .ProseMirror img{position:relative;display:inline-block;max-width:100%;height:auto;cursor:pointer;transition:all .2s ease;border:2px solid transparent;border-radius:8px}:host ::ng-deep .ProseMirror img:hover{border-color:#e2e8f0;box-shadow:0 2px 4px #0000001a}:host ::ng-deep .ProseMirror img.ProseMirror-selectednode{border-color:#3182ce;box-shadow:0 0 0 3px #3182ce1a;transition:all .2s ease}:host ::ng-deep .ProseMirror .tiptap-image{max-width:100%;height:auto;border-radius:16px;box-shadow:0 4px 20px #00000014;margin:.5em 0;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);display:block;filter:brightness(1) contrast(1)}:host ::ng-deep .ProseMirror .tiptap-image:hover{box-shadow:0 8px 30px #0000001f;filter:brightness(1.02) contrast(1.02)}:host ::ng-deep .ProseMirror .tiptap-image.ProseMirror-selectednode{outline:2px solid #6366f1;outline-offset:2px;border-radius:16px;box-shadow:0 0 0 4px #6366f11a}:host ::ng-deep .image-container{margin:.5em 0;text-align:center;border-radius:16px;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1)}:host ::ng-deep .image-container.image-align-left{text-align:left}:host ::ng-deep .image-container.image-align-center{text-align:center}:host ::ng-deep .image-container.image-align-right{text-align:right}:host ::ng-deep .image-container img{display:inline-block;max-width:100%;height:auto;border-radius:16px}:host ::ng-deep .resizable-image-container{position:relative;display:inline-block;margin:.5em 0}:host ::ng-deep .resize-controls{position:absolute;inset:0;pointer-events:none;z-index:1000}:host ::ng-deep .resize-handle{position:absolute;width:12px;height:12px;background:#3b82f6;border:2px solid white;border-radius:50%;pointer-events:all;cursor:pointer;z-index:1001;transition:all .15s ease;box-shadow:0 2px 6px #0003}:host ::ng-deep .resize-handle:hover{background:#2563eb;box-shadow:0 3px 8px #0000004d}:host ::ng-deep .resize-handle:active{background:#1d4ed8}:host ::ng-deep .resize-handle-n:hover,:host ::ng-deep .resize-handle-s:hover{transform:translate(-50%) scale(1.2)}:host ::ng-deep .resize-handle-w:hover,:host ::ng-deep .resize-handle-e:hover{transform:translateY(-50%) scale(1.2)}:host ::ng-deep .resize-handle-n:active,:host ::ng-deep .resize-handle-s:active{transform:translate(-50%) scale(.9)}:host ::ng-deep .resize-handle-w:active,:host ::ng-deep .resize-handle-e:active{transform:translateY(-50%) scale(.9)}:host ::ng-deep .resize-handle-nw:hover,:host ::ng-deep .resize-handle-ne:hover,:host ::ng-deep .resize-handle-sw:hover,:host ::ng-deep .resize-handle-se:hover{transform:scale(1.2)}:host ::ng-deep .resize-handle-nw:active,:host ::ng-deep .resize-handle-ne:active,:host ::ng-deep .resize-handle-sw:active,:host ::ng-deep .resize-handle-se:active{transform:scale(.9)}:host ::ng-deep .resize-handle-nw{top:0;left:-6px;cursor:nw-resize}:host ::ng-deep .resize-handle-n{top:0;left:50%;transform:translate(-50%);cursor:n-resize}:host ::ng-deep .resize-handle-ne{top:0;right:-6px;cursor:ne-resize}:host ::ng-deep .resize-handle-w{top:50%;left:-6px;transform:translateY(-50%);cursor:w-resize}:host ::ng-deep .resize-handle-e{top:50%;right:-6px;transform:translateY(-50%);cursor:e-resize}:host ::ng-deep .resize-handle-sw{bottom:0;left:-6px;cursor:sw-resize}:host ::ng-deep .resize-handle-s{bottom:0;left:50%;transform:translate(-50%);cursor:s-resize}:host ::ng-deep .resize-handle-se{bottom:0;right:-6px;cursor:se-resize}:host ::ng-deep body.resizing{-webkit-user-select:none;user-select:none;cursor:crosshair}:host ::ng-deep body.resizing .ProseMirror{pointer-events:none}:host ::ng-deep body.resizing .ProseMirror .tiptap-image{pointer-events:none}:host ::ng-deep .image-size-info{position:absolute;bottom:-20px;left:50%;transform:translate(-50%);background:#000c;color:#fff;padding:2px 6px;border-radius:3px;font-size:11px;white-space:nowrap;opacity:0;transition:opacity .2s ease}:host ::ng-deep .image-container:hover .image-size-info{opacity:1}:host ::ng-deep .ProseMirror table{border-collapse:separate;border-spacing:0;margin:0;table-layout:fixed;width:100%;border-radius:8px;overflow:hidden}:host ::ng-deep .ProseMirror table td,:host ::ng-deep .ProseMirror table th{border:none;border-right:1px solid #e2e8f0;border-bottom:1px solid #e2e8f0;box-sizing:border-box;min-width:1em;padding:8px 12px;position:relative;vertical-align:top;background:#fff}:host ::ng-deep .ProseMirror table td:first-child,:host ::ng-deep .ProseMirror table th:first-child{border-left:1px solid #e2e8f0}:host ::ng-deep .ProseMirror table tr:first-child td,:host ::ng-deep .ProseMirror table tr:first-child th{border-top:1px solid #e2e8f0}:host ::ng-deep .ProseMirror table tr:first-child th:first-child{border-top-left-radius:8px}:host ::ng-deep .ProseMirror table tr:first-child th:last-child{border-top-right-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:first-child{border-bottom-left-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:last-child{border-bottom-right-radius:8px}:host ::ng-deep .ProseMirror table th{background:#f8f9fa;font-weight:600;color:#374151}:host ::ng-deep .ProseMirror table .selectedCell:after{background:#c8c8ff66;content:\"\";inset:0;pointer-events:none;position:absolute;z-index:2}:host ::ng-deep .ProseMirror table .column-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:4px;background-color:#6366f1;opacity:0;transition:opacity .2s ease}:host ::ng-deep .ProseMirror table:hover .column-resize-handle{opacity:1}:host ::ng-deep .ProseMirror table .column-resize-handle:hover{background-color:#4f46e5}:host ::ng-deep .ProseMirror .tableWrapper{overflow-x:auto;margin:1em 0;border-radius:8px}:host ::ng-deep .ProseMirror .tableWrapper table{margin:0;border-radius:8px;min-width:600px;overflow:hidden}:host ::ng-deep .ProseMirror table p{margin:0}:host ::ng-deep .ProseMirror table .selectedCell{background-color:#6366f11a}:host ::ng-deep .ProseMirror table th{background-color:#f8f9fa;font-weight:600;color:#374151;text-align:left}:host ::ng-deep .ProseMirror table tbody tr:nth-child(2n){background-color:#fafbfc}:host ::ng-deep .ProseMirror table tbody tr:hover{background-color:#f1f5f9}\n"], dependencies: [{ kind: "component", type: TiptapToolbarComponent, selector: "tiptap-toolbar", inputs: ["editor", "config"], outputs: ["imageUploaded", "imageError"] }, { kind: "component", type: TiptapImageUploadComponent, selector: "tiptap-image-upload", inputs: ["config"], outputs: ["imageSelected", "error"] }, { kind: "component", type: TiptapBubbleMenuComponent, selector: "tiptap-bubble-menu", inputs: ["editor", "config"] }, { kind: "component", type: TiptapImageBubbleMenuComponent, selector: "tiptap-image-bubble-menu", inputs: ["editor", "config"] }, { kind: "component", type: TiptapTableBubbleMenuComponent, selector: "tiptap-table-bubble-menu", inputs: ["editor", "config"] }, { kind: "component", type: TiptapCellBubbleMenuComponent, selector: "tiptap-cell-bubble-menu", inputs: ["editor", "config"] }, { kind: "component", type: TiptapSlashCommandsComponent, selector: "tiptap-slash-commands", inputs: ["editor", "config"], outputs: ["imageUploadRequested"] }] }); }
5084
+ `, isInline: true, styles: [":host{--ate-primary: #2563eb;--ate-primary-contrast: #ffffff;--ate-primary-light: color-mix(in srgb, var(--ate-primary), transparent 90%);--ate-primary-lighter: color-mix(in srgb, var(--ate-primary), transparent 95%);--ate-primary-light-alpha: color-mix(in srgb, var(--ate-primary), transparent 85%);--ate-surface: #ffffff;--ate-surface-secondary: #f8f9fa;--ate-surface-tertiary: #f1f5f9;--ate-text: #2d3748;--ate-text-secondary: #64748b;--ate-text-muted: #a0aec0;--ate-border: #e2e8f0;--ate-highlight-bg: #fef08a;--ate-highlight-color: #854d0e;--ate-button-hover: #f1f5f9;--ate-button-active: #e2e8f0;--ate-menu-bg: rgba(255, 255, 255, .98);--ate-menu-border: var(--ate-border);--ate-menu-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--ate-menu-blur: 16px;--ate-error-color: #c53030;--ate-error-bg: #fed7d7;--ate-error-border: #feb2b2;--ate-border-color: var(--ate-border);--ate-border-width: 2px;--ate-border-radius: 8px;--ate-focus-color: var(--ate-primary);--ate-background: var(--ate-surface);--ate-text-color: var(--ate-text);--ate-placeholder-color: var(--ate-text-muted);--ate-line-height: 1.6;--ate-content-padding: 16px;--ate-toolbar-background: var(--ate-surface-secondary);--ate-toolbar-border-color: var(--ate-border);--ate-toolbar-button-color: var(--ate-text-secondary);--ate-toolbar-button-hover-background: transparent;--ate-toolbar-button-active-background: var(--ate-primary-light);--ate-toolbar-button-active-color: var(--ate-primary);--ate-counter-color: var(--ate-text-secondary);--ate-counter-background: var(--ate-surface-secondary);--ate-counter-border-color: var(--ate-border);--ate-drag-background: #f0f8ff;--ate-drag-border-color: var(--ate-primary);--ate-blockquote-border-color: var(--ate-border);--ate-blockquote-background: var(--ate-surface-secondary);--ate-code-background: var(--ate-surface-secondary);--ate-code-color: var(--ate-text);--ate-code-block-background: #181825;--ate-code-block-color: #e2e8f0;--ate-code-border-color: var(--ate-border);--ate-code-block-border-color: var(--ate-border);--ate-image-border-radius: 8px;--ate-image-selected-color: var(--ate-primary);--ate-scrollbar-width: 10px;--ate-scrollbar-thumb: var(--ate-border);--ate-scrollbar-thumb-hover: var(--ate-text-muted);--ate-scrollbar-track: transparent;--ate-table-border-color: var(--ate-border);--ate-table-header-background: var(--ate-surface-secondary);--ate-table-header-color: var(--ate-text);--ate-table-cell-background: var(--ate-surface);--ate-table-cell-selected-background: var(--ate-primary-light);--ate-table-resize-handle-color: var(--ate-primary);--ate-table-row-hover-background: var(--ate-primary-lighter)}:host(.dark),:host([data-theme=\"dark\"]){--ate-primary: #3b82f6;--ate-primary-contrast: #ffffff;--ate-primary-light: color-mix(in srgb, var(--ate-primary), transparent 85%);--ate-primary-lighter: color-mix(in srgb, var(--ate-primary), transparent 92%);--ate-primary-light-alpha: color-mix(in srgb, var(--ate-primary), transparent 80%);--ate-surface: #020617;--ate-surface-secondary: #0f172a;--ate-surface-tertiary: #1e293b;--ate-text: #f8fafc;--ate-text-secondary: #94a3b8;--ate-text-muted: #64748b;--ate-border: #1e293b;--ate-highlight-bg: #854d0e;--ate-highlight-color: #fef08a;--ate-button-hover: #1e293b;--ate-button-active: #0f172a;--ate-menu-bg: rgba(15, 23, 42, .95);--ate-menu-border: rgba(255, 255, 255, .1);--ate-menu-shadow: 0 20px 25px -5px rgba(0, 0, 0, .3), 0 10px 10px -5px rgba(0, 0, 0, .2);--ate-menu-blur: 16px;--ate-error-color: #f87171;--ate-error-bg: #450a0a;--ate-error-border: #7f1d1d;--ate-drag-background: var(--ate-surface-tertiary);--ate-drag-border-color: var(--ate-primary);--ate-blockquote-border-color: var(--ate-primary);--ate-toolbar-button-active-background: var(--ate-primary-light);--ate-toolbar-button-active-color: var(--ate-primary);--ate-button-hover: var(--ate-surface-tertiary);--ate-button-active: var(--ate-surface-secondary);--ate-scrollbar-thumb: var(--ate-surface-tertiary);--ate-scrollbar-thumb-hover: var(--ate-text-muted)}:host(.fill-container){display:block;height:100%}.tiptap-editor{border:var(--ate-border-width) solid var(--ate-border-color);border-radius:var(--ate-border-radius);background:var(--ate-background);overflow:hidden;transition:border-color .2s ease}.tiptap-editor.fill-container{display:flex;flex-direction:column;height:100%}.tiptap-editor.fill-container .tiptap-content{flex:1;min-height:0;overflow-y:auto}.tiptap-editor:focus-within{border-color:var(--ate-focus-color)}.tiptap-content{padding:var(--ate-content-padding);min-height:var(--editor-min-height, 200px);height:var(--editor-height, auto);max-height:var(--editor-max-height, none);overflow-y:var(--editor-overflow, visible);outline:none;position:relative;scrollbar-width:thin;scrollbar-color:var(--ate-scrollbar-thumb) var(--ate-scrollbar-track)}.tiptap-content::-webkit-scrollbar{width:var(--ate-scrollbar-width);height:var(--ate-scrollbar-width)}.tiptap-content::-webkit-scrollbar-track{background:var(--ate-scrollbar-track)}.tiptap-content::-webkit-scrollbar-thumb{background:var(--ate-scrollbar-thumb);border:3px solid transparent;background-clip:content-box;border-radius:10px}.tiptap-content::-webkit-scrollbar-thumb:hover{background:var(--ate-scrollbar-thumb-hover);background-clip:content-box}.tiptap-content.drag-over{background:var(--ate-drag-background);border:2px dashed var(--ate-drag-border-color)}.character-count{padding:8px var(--ate-content-padding);font-size:12px;color:var(--ate-counter-color);text-align:right;border-top:1px solid var(--ate-counter-border-color);background:var(--ate-counter-background)}:host ::ng-deep .ProseMirror{outline:none;line-height:var(--ate-line-height);color:var(--ate-text-color);min-height:100%;height:100%;word-wrap:break-word;overflow-wrap:break-word}:host ::ng-deep .ProseMirror h1{font-size:2em;font-weight:700;margin-top:0;margin-bottom:.5em}:host ::ng-deep .ProseMirror h2{font-size:1.5em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror h3{font-size:1.25em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror p{margin:.5em 0}:host ::ng-deep .ProseMirror ul,:host ::ng-deep .ProseMirror ol{padding-left:2em;margin:.5em 0}:host ::ng-deep .ProseMirror blockquote{border-left:4px solid var(--ate-blockquote-border-color);margin:1em 0;font-style:italic;background:var(--ate-blockquote-background);padding:.5em 1em;border-radius:0 4px 4px 0}:host ::ng-deep .ProseMirror code{background:var(--ate-code-background);color:var(--ate-code-color);border:1px solid var(--ate-code-border-color);padding:.2em .4em;border-radius:3px;font-family:Monaco,Consolas,monospace;font-size:.9em}:host ::ng-deep .ProseMirror pre{background:var(--ate-code-block-background);color:var(--ate-code-block-color);border:1px solid var(--ate-code-block-border-color);padding:1em;border-radius:6px;overflow-x:auto;margin:1em 0}:host ::ng-deep .ProseMirror pre code{background:none;color:inherit;border:none;padding:0}:host ::ng-deep .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);color:var(--ate-placeholder-color);pointer-events:none;float:left;height:0}:host ::ng-deep .ProseMirror[contenteditable=false]{pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img{cursor:default;pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img:hover{transform:none;box-shadow:0 2px 8px #0000001a}:host ::ng-deep .ProseMirror[contenteditable=false] img.ProseMirror-selectednode{outline:none}:host ::ng-deep .ProseMirror img{position:relative;display:inline-block;max-width:100%;height:auto;cursor:pointer;transition:all .2s ease;border:2px solid transparent;border-radius:var(--ate-image-border-radius)}:host ::ng-deep .ProseMirror img:hover{border-color:var(--ate-border-color);box-shadow:0 2px 4px #0000001a}:host ::ng-deep .ProseMirror img.ProseMirror-selectednode{border-color:var(--ate-image-selected-color);box-shadow:0 0 0 3px var(--ate-primary-light-alpha);transition:all .2s ease}:host ::ng-deep .ProseMirror .tiptap-image{max-width:100%;height:auto;border-radius:16px;box-shadow:0 4px 20px #00000014;margin:.5em 0;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);display:block;filter:brightness(1) contrast(1)}:host ::ng-deep .ProseMirror .tiptap-image:hover{box-shadow:0 8px 30px #0000001f;filter:brightness(1.02) contrast(1.02)}:host ::ng-deep .ProseMirror .tiptap-image.ProseMirror-selectednode{outline:2px solid var(--ate-primary);outline-offset:2px;border-radius:16px;box-shadow:0 0 0 4px var(--ate-primary-light-alpha)}:host ::ng-deep .image-container{margin:.5em 0;text-align:center;border-radius:16px;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1)}:host ::ng-deep .image-container.image-align-left{text-align:left}:host ::ng-deep .image-container.image-align-center{text-align:center}:host ::ng-deep .image-container.image-align-right{text-align:right}:host ::ng-deep .image-container img{display:inline-block;max-width:100%;height:auto;border-radius:16px}:host ::ng-deep .resizable-image-container{position:relative;display:inline-block;margin:.5em 0}:host ::ng-deep .resize-controls{position:absolute;inset:0;pointer-events:none;z-index:1000}:host ::ng-deep .resize-handle{position:absolute;width:12px;height:12px;background:var(--ate-primary);border:2px solid var(--ate-surface);border-radius:50%;pointer-events:all;cursor:pointer;z-index:1001;transition:all .15s ease;box-shadow:0 2px 6px #0003}:host ::ng-deep .resize-handle:hover{background:var(--ate-primary);box-shadow:0 3px 8px #0000004d}:host ::ng-deep .resize-handle:active{background:var(--ate-primary)}:host ::ng-deep .resize-handle-n:hover,:host ::ng-deep .resize-handle-s:hover{transform:translate(-50%) scale(1.2)}:host ::ng-deep .resize-handle-w:hover,:host ::ng-deep .resize-handle-e:hover{transform:translateY(-50%) scale(1.2)}:host ::ng-deep .resize-handle-n:active,:host ::ng-deep .resize-handle-s:active{transform:translate(-50%) scale(.9)}:host ::ng-deep .resize-handle-w:active,:host ::ng-deep .resize-handle-e:active{transform:translateY(-50%) scale(.9)}:host ::ng-deep .resize-handle-nw:hover,:host ::ng-deep .resize-handle-ne:hover,:host ::ng-deep .resize-handle-sw:hover,:host ::ng-deep .resize-handle-se:hover{transform:scale(1.2)}:host ::ng-deep .resize-handle-nw:active,:host ::ng-deep .resize-handle-ne:active,:host ::ng-deep .resize-handle-sw:active,:host ::ng-deep .resize-handle-se:active{transform:scale(.9)}:host ::ng-deep .resize-handle-nw{top:0;left:-6px;cursor:nw-resize}:host ::ng-deep .resize-handle-n{top:0;left:50%;transform:translate(-50%);cursor:n-resize}:host ::ng-deep .resize-handle-ne{top:0;right:-6px;cursor:ne-resize}:host ::ng-deep .resize-handle-w{top:50%;left:-6px;transform:translateY(-50%);cursor:w-resize}:host ::ng-deep .resize-handle-e{top:50%;right:-6px;transform:translateY(-50%);cursor:e-resize}:host ::ng-deep .resize-handle-sw{bottom:0;left:-6px;cursor:sw-resize}:host ::ng-deep .resize-handle-s{bottom:0;left:50%;transform:translate(-50%);cursor:s-resize}:host ::ng-deep .resize-handle-se{bottom:0;right:-6px;cursor:se-resize}:host ::ng-deep body.resizing{-webkit-user-select:none;user-select:none;cursor:crosshair}:host ::ng-deep body.resizing .ProseMirror{pointer-events:none}:host ::ng-deep body.resizing .ProseMirror .tiptap-image{pointer-events:none}:host ::ng-deep .image-size-info{position:absolute;bottom:-20px;left:50%;transform:translate(-50%);background:#000c;color:#fff;padding:2px 6px;border-radius:3px;font-size:11px;white-space:nowrap;opacity:0;transition:opacity .2s ease}:host ::ng-deep .image-container:hover .image-size-info{opacity:1}:host ::ng-deep .ProseMirror table{border-collapse:separate;border-spacing:0;margin:0;table-layout:fixed;width:100%;border-radius:8px;overflow:hidden}:host ::ng-deep .ProseMirror table td,:host ::ng-deep .ProseMirror table th{border:none;border-right:1px solid var(--ate-table-border-color);border-bottom:1px solid var(--ate-table-border-color);box-sizing:border-box;min-width:1em;padding:8px 12px;position:relative;vertical-align:top;text-align:left}:host ::ng-deep .ProseMirror table td{background:var(--ate-table-cell-background)}:host ::ng-deep .ProseMirror table td:first-child,:host ::ng-deep .ProseMirror table th:first-child{border-left:1px solid var(--ate-table-border-color)}:host ::ng-deep .ProseMirror table tr:first-child td,:host ::ng-deep .ProseMirror table tr:first-child th{border-top:1px solid var(--ate-table-border-color)}:host ::ng-deep .ProseMirror table tr:first-child th:first-child{border-top-left-radius:8px}:host ::ng-deep .ProseMirror table tr:first-child th:last-child{border-top-right-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:first-child{border-bottom-left-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:last-child{border-bottom-right-radius:8px}:host ::ng-deep .ProseMirror table th{background:var(--ate-table-header-background);font-weight:600;color:var(--ate-table-header-color)}:host ::ng-deep .ProseMirror table .selectedCell:after{background:var(--ate-table-cell-selected-background);content:\"\";inset:0;pointer-events:none;position:absolute;z-index:2}:host ::ng-deep .ProseMirror table .column-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:4px;background-color:var(--ate-table-resize-handle-color);opacity:0;transition:opacity .2s ease}:host ::ng-deep .ProseMirror table:hover .column-resize-handle{opacity:1}:host ::ng-deep .ProseMirror table .column-resize-handle:hover{background-color:var(--ate-focus-color)}:host ::ng-deep .ProseMirror .tableWrapper{overflow-x:auto;margin:1em 0;border-radius:8px}:host ::ng-deep .ProseMirror .tableWrapper table{margin:0;border-radius:8px;min-width:600px;overflow:hidden}:host ::ng-deep .ProseMirror table p{margin:0}:host ::ng-deep .ProseMirror table tbody tr:hover td{background-color:var(--ate-table-row-hover-background)}\n"], dependencies: [{ kind: "component", type: TiptapToolbarComponent, selector: "tiptap-toolbar", inputs: ["editor", "config", "imageUpload"], outputs: ["imageUploaded", "imageError"] }, { kind: "component", type: TiptapBubbleMenuComponent, selector: "tiptap-bubble-menu", inputs: ["editor", "config"] }, { kind: "component", type: TiptapImageBubbleMenuComponent, selector: "tiptap-image-bubble-menu", inputs: ["editor", "config"] }, { kind: "component", type: TiptapTableBubbleMenuComponent, selector: "tiptap-table-bubble-menu", inputs: ["editor", "config"] }, { kind: "component", type: TiptapCellBubbleMenuComponent, selector: "tiptap-cell-bubble-menu", inputs: ["editor", "config"] }, { kind: "component", type: TiptapSlashCommandsComponent, selector: "tiptap-slash-commands", inputs: ["editor", "config"], outputs: ["imageUploadRequested"] }] }); }
5610
5085
  }
5611
5086
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AngularTiptapEditorComponent, decorators: [{
5612
5087
  type: Component,
@@ -5614,7 +5089,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
5614
5089
  '[class.fill-container]': 'fillContainer()',
5615
5090
  }, imports: [
5616
5091
  TiptapToolbarComponent,
5617
- TiptapImageUploadComponent,
5618
5092
  TiptapBubbleMenuComponent,
5619
5093
  TiptapImageBubbleMenuComponent,
5620
5094
  TiptapTableBubbleMenuComponent,
@@ -5624,15 +5098,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
5624
5098
  <div class="tiptap-editor" [class.fill-container]="fillContainer()">
5625
5099
  <!-- Toolbar -->
5626
5100
  @if (showToolbar() && editor()) {
5627
- <tiptap-toolbar [editor]="editor()!" [config]="toolbarConfig()">
5628
- <div image-upload class="image-upload-container">
5629
- <tiptap-image-upload
5630
- [config]="imageUploadConfig()"
5631
- (imageSelected)="onImageUploaded($event)"
5632
- (error)="onImageUploadError($event)"
5633
- />
5634
- </div>
5635
- </tiptap-toolbar>
5101
+ <tiptap-toolbar
5102
+ [editor]="editor()!"
5103
+ [config]="toolbarConfig()"
5104
+ [imageUpload]="imageUploadConfig()"
5105
+ />
5636
5106
  }
5637
5107
 
5638
5108
  <!-- Contenu de l'éditeur -->
@@ -5691,8 +5161,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
5691
5161
  ></tiptap-cell-bubble-menu>
5692
5162
  }
5693
5163
 
5694
- <!-- Table Edit Button - Supprimé car remplacé par le menu bubble -->
5695
-
5696
5164
  <!-- Compteur de caractères -->
5697
5165
  @if (showCharacterCount()) {
5698
5166
  <div class="character-count">
@@ -5706,7 +5174,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
5706
5174
  </div>
5707
5175
  }
5708
5176
  </div>
5709
- `, styles: [":host(.fill-container){display:block;height:100%}.tiptap-editor{border:2px solid #e2e8f0;border-radius:8px;background:#fff;overflow:hidden;transition:border-color .2s ease}.tiptap-editor.fill-container{display:flex;flex-direction:column;height:100%}.tiptap-editor.fill-container .tiptap-content{flex:1;min-height:0;overflow-y:auto}.tiptap-editor:focus-within{border-color:#3182ce}.tiptap-content{padding:16px;min-height:var(--editor-min-height, 200px);height:var(--editor-height, auto);max-height:var(--editor-max-height, none);overflow-y:var(--editor-overflow, visible);outline:none;position:relative}.tiptap-content.drag-over{background:#f0f8ff;border:2px dashed #3182ce}.character-count{padding:8px 16px;font-size:12px;color:#718096;text-align:right;border-top:1px solid #e2e8f0;background:#f8f9fa}.image-upload-container{position:relative;display:inline-block}:host ::ng-deep .ProseMirror{outline:none;line-height:1.6;color:#2d3748;min-height:100%;height:100%;word-wrap:break-word;overflow-wrap:break-word}:host ::ng-deep .ProseMirror h1{font-size:2em;font-weight:700;margin-top:0;margin-bottom:.5em}:host ::ng-deep .ProseMirror h2{font-size:1.5em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror h3{font-size:1.25em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror p{margin:.5em 0}:host ::ng-deep .ProseMirror ul,:host ::ng-deep .ProseMirror ol{padding-left:2em;margin:.5em 0}:host ::ng-deep .ProseMirror blockquote{border-left:4px solid #e2e8f0;margin:1em 0;font-style:italic;background:#f8f9fa;padding:.5em 1em;border-radius:0 4px 4px 0}:host ::ng-deep .ProseMirror code{background:#f1f5f9;padding:.2em .4em;border-radius:3px;font-family:Monaco,Consolas,monospace;font-size:.9em}:host ::ng-deep .ProseMirror pre{background:#1a202c;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;margin:1em 0}:host ::ng-deep .ProseMirror pre code{background:none;color:inherit;padding:0}:host ::ng-deep .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);color:#a0aec0;pointer-events:none;float:left;height:0}:host ::ng-deep .ProseMirror[contenteditable=false]{pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img{cursor:default;pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img:hover{transform:none;box-shadow:0 2px 8px #0000001a}:host ::ng-deep .ProseMirror[contenteditable=false] img.ProseMirror-selectednode{outline:none}:host ::ng-deep .ProseMirror img{position:relative;display:inline-block;max-width:100%;height:auto;cursor:pointer;transition:all .2s ease;border:2px solid transparent;border-radius:8px}:host ::ng-deep .ProseMirror img:hover{border-color:#e2e8f0;box-shadow:0 2px 4px #0000001a}:host ::ng-deep .ProseMirror img.ProseMirror-selectednode{border-color:#3182ce;box-shadow:0 0 0 3px #3182ce1a;transition:all .2s ease}:host ::ng-deep .ProseMirror .tiptap-image{max-width:100%;height:auto;border-radius:16px;box-shadow:0 4px 20px #00000014;margin:.5em 0;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);display:block;filter:brightness(1) contrast(1)}:host ::ng-deep .ProseMirror .tiptap-image:hover{box-shadow:0 8px 30px #0000001f;filter:brightness(1.02) contrast(1.02)}:host ::ng-deep .ProseMirror .tiptap-image.ProseMirror-selectednode{outline:2px solid #6366f1;outline-offset:2px;border-radius:16px;box-shadow:0 0 0 4px #6366f11a}:host ::ng-deep .image-container{margin:.5em 0;text-align:center;border-radius:16px;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1)}:host ::ng-deep .image-container.image-align-left{text-align:left}:host ::ng-deep .image-container.image-align-center{text-align:center}:host ::ng-deep .image-container.image-align-right{text-align:right}:host ::ng-deep .image-container img{display:inline-block;max-width:100%;height:auto;border-radius:16px}:host ::ng-deep .resizable-image-container{position:relative;display:inline-block;margin:.5em 0}:host ::ng-deep .resize-controls{position:absolute;inset:0;pointer-events:none;z-index:1000}:host ::ng-deep .resize-handle{position:absolute;width:12px;height:12px;background:#3b82f6;border:2px solid white;border-radius:50%;pointer-events:all;cursor:pointer;z-index:1001;transition:all .15s ease;box-shadow:0 2px 6px #0003}:host ::ng-deep .resize-handle:hover{background:#2563eb;box-shadow:0 3px 8px #0000004d}:host ::ng-deep .resize-handle:active{background:#1d4ed8}:host ::ng-deep .resize-handle-n:hover,:host ::ng-deep .resize-handle-s:hover{transform:translate(-50%) scale(1.2)}:host ::ng-deep .resize-handle-w:hover,:host ::ng-deep .resize-handle-e:hover{transform:translateY(-50%) scale(1.2)}:host ::ng-deep .resize-handle-n:active,:host ::ng-deep .resize-handle-s:active{transform:translate(-50%) scale(.9)}:host ::ng-deep .resize-handle-w:active,:host ::ng-deep .resize-handle-e:active{transform:translateY(-50%) scale(.9)}:host ::ng-deep .resize-handle-nw:hover,:host ::ng-deep .resize-handle-ne:hover,:host ::ng-deep .resize-handle-sw:hover,:host ::ng-deep .resize-handle-se:hover{transform:scale(1.2)}:host ::ng-deep .resize-handle-nw:active,:host ::ng-deep .resize-handle-ne:active,:host ::ng-deep .resize-handle-sw:active,:host ::ng-deep .resize-handle-se:active{transform:scale(.9)}:host ::ng-deep .resize-handle-nw{top:0;left:-6px;cursor:nw-resize}:host ::ng-deep .resize-handle-n{top:0;left:50%;transform:translate(-50%);cursor:n-resize}:host ::ng-deep .resize-handle-ne{top:0;right:-6px;cursor:ne-resize}:host ::ng-deep .resize-handle-w{top:50%;left:-6px;transform:translateY(-50%);cursor:w-resize}:host ::ng-deep .resize-handle-e{top:50%;right:-6px;transform:translateY(-50%);cursor:e-resize}:host ::ng-deep .resize-handle-sw{bottom:0;left:-6px;cursor:sw-resize}:host ::ng-deep .resize-handle-s{bottom:0;left:50%;transform:translate(-50%);cursor:s-resize}:host ::ng-deep .resize-handle-se{bottom:0;right:-6px;cursor:se-resize}:host ::ng-deep body.resizing{-webkit-user-select:none;user-select:none;cursor:crosshair}:host ::ng-deep body.resizing .ProseMirror{pointer-events:none}:host ::ng-deep body.resizing .ProseMirror .tiptap-image{pointer-events:none}:host ::ng-deep .image-size-info{position:absolute;bottom:-20px;left:50%;transform:translate(-50%);background:#000c;color:#fff;padding:2px 6px;border-radius:3px;font-size:11px;white-space:nowrap;opacity:0;transition:opacity .2s ease}:host ::ng-deep .image-container:hover .image-size-info{opacity:1}:host ::ng-deep .ProseMirror table{border-collapse:separate;border-spacing:0;margin:0;table-layout:fixed;width:100%;border-radius:8px;overflow:hidden}:host ::ng-deep .ProseMirror table td,:host ::ng-deep .ProseMirror table th{border:none;border-right:1px solid #e2e8f0;border-bottom:1px solid #e2e8f0;box-sizing:border-box;min-width:1em;padding:8px 12px;position:relative;vertical-align:top;background:#fff}:host ::ng-deep .ProseMirror table td:first-child,:host ::ng-deep .ProseMirror table th:first-child{border-left:1px solid #e2e8f0}:host ::ng-deep .ProseMirror table tr:first-child td,:host ::ng-deep .ProseMirror table tr:first-child th{border-top:1px solid #e2e8f0}:host ::ng-deep .ProseMirror table tr:first-child th:first-child{border-top-left-radius:8px}:host ::ng-deep .ProseMirror table tr:first-child th:last-child{border-top-right-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:first-child{border-bottom-left-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:last-child{border-bottom-right-radius:8px}:host ::ng-deep .ProseMirror table th{background:#f8f9fa;font-weight:600;color:#374151}:host ::ng-deep .ProseMirror table .selectedCell:after{background:#c8c8ff66;content:\"\";inset:0;pointer-events:none;position:absolute;z-index:2}:host ::ng-deep .ProseMirror table .column-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:4px;background-color:#6366f1;opacity:0;transition:opacity .2s ease}:host ::ng-deep .ProseMirror table:hover .column-resize-handle{opacity:1}:host ::ng-deep .ProseMirror table .column-resize-handle:hover{background-color:#4f46e5}:host ::ng-deep .ProseMirror .tableWrapper{overflow-x:auto;margin:1em 0;border-radius:8px}:host ::ng-deep .ProseMirror .tableWrapper table{margin:0;border-radius:8px;min-width:600px;overflow:hidden}:host ::ng-deep .ProseMirror table p{margin:0}:host ::ng-deep .ProseMirror table .selectedCell{background-color:#6366f11a}:host ::ng-deep .ProseMirror table th{background-color:#f8f9fa;font-weight:600;color:#374151;text-align:left}:host ::ng-deep .ProseMirror table tbody tr:nth-child(2n){background-color:#fafbfc}:host ::ng-deep .ProseMirror table tbody tr:hover{background-color:#f1f5f9}\n"] }]
5177
+ `, styles: [":host{--ate-primary: #2563eb;--ate-primary-contrast: #ffffff;--ate-primary-light: color-mix(in srgb, var(--ate-primary), transparent 90%);--ate-primary-lighter: color-mix(in srgb, var(--ate-primary), transparent 95%);--ate-primary-light-alpha: color-mix(in srgb, var(--ate-primary), transparent 85%);--ate-surface: #ffffff;--ate-surface-secondary: #f8f9fa;--ate-surface-tertiary: #f1f5f9;--ate-text: #2d3748;--ate-text-secondary: #64748b;--ate-text-muted: #a0aec0;--ate-border: #e2e8f0;--ate-highlight-bg: #fef08a;--ate-highlight-color: #854d0e;--ate-button-hover: #f1f5f9;--ate-button-active: #e2e8f0;--ate-menu-bg: rgba(255, 255, 255, .98);--ate-menu-border: var(--ate-border);--ate-menu-shadow: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--ate-menu-blur: 16px;--ate-error-color: #c53030;--ate-error-bg: #fed7d7;--ate-error-border: #feb2b2;--ate-border-color: var(--ate-border);--ate-border-width: 2px;--ate-border-radius: 8px;--ate-focus-color: var(--ate-primary);--ate-background: var(--ate-surface);--ate-text-color: var(--ate-text);--ate-placeholder-color: var(--ate-text-muted);--ate-line-height: 1.6;--ate-content-padding: 16px;--ate-toolbar-background: var(--ate-surface-secondary);--ate-toolbar-border-color: var(--ate-border);--ate-toolbar-button-color: var(--ate-text-secondary);--ate-toolbar-button-hover-background: transparent;--ate-toolbar-button-active-background: var(--ate-primary-light);--ate-toolbar-button-active-color: var(--ate-primary);--ate-counter-color: var(--ate-text-secondary);--ate-counter-background: var(--ate-surface-secondary);--ate-counter-border-color: var(--ate-border);--ate-drag-background: #f0f8ff;--ate-drag-border-color: var(--ate-primary);--ate-blockquote-border-color: var(--ate-border);--ate-blockquote-background: var(--ate-surface-secondary);--ate-code-background: var(--ate-surface-secondary);--ate-code-color: var(--ate-text);--ate-code-block-background: #181825;--ate-code-block-color: #e2e8f0;--ate-code-border-color: var(--ate-border);--ate-code-block-border-color: var(--ate-border);--ate-image-border-radius: 8px;--ate-image-selected-color: var(--ate-primary);--ate-scrollbar-width: 10px;--ate-scrollbar-thumb: var(--ate-border);--ate-scrollbar-thumb-hover: var(--ate-text-muted);--ate-scrollbar-track: transparent;--ate-table-border-color: var(--ate-border);--ate-table-header-background: var(--ate-surface-secondary);--ate-table-header-color: var(--ate-text);--ate-table-cell-background: var(--ate-surface);--ate-table-cell-selected-background: var(--ate-primary-light);--ate-table-resize-handle-color: var(--ate-primary);--ate-table-row-hover-background: var(--ate-primary-lighter)}:host(.dark),:host([data-theme=\"dark\"]){--ate-primary: #3b82f6;--ate-primary-contrast: #ffffff;--ate-primary-light: color-mix(in srgb, var(--ate-primary), transparent 85%);--ate-primary-lighter: color-mix(in srgb, var(--ate-primary), transparent 92%);--ate-primary-light-alpha: color-mix(in srgb, var(--ate-primary), transparent 80%);--ate-surface: #020617;--ate-surface-secondary: #0f172a;--ate-surface-tertiary: #1e293b;--ate-text: #f8fafc;--ate-text-secondary: #94a3b8;--ate-text-muted: #64748b;--ate-border: #1e293b;--ate-highlight-bg: #854d0e;--ate-highlight-color: #fef08a;--ate-button-hover: #1e293b;--ate-button-active: #0f172a;--ate-menu-bg: rgba(15, 23, 42, .95);--ate-menu-border: rgba(255, 255, 255, .1);--ate-menu-shadow: 0 20px 25px -5px rgba(0, 0, 0, .3), 0 10px 10px -5px rgba(0, 0, 0, .2);--ate-menu-blur: 16px;--ate-error-color: #f87171;--ate-error-bg: #450a0a;--ate-error-border: #7f1d1d;--ate-drag-background: var(--ate-surface-tertiary);--ate-drag-border-color: var(--ate-primary);--ate-blockquote-border-color: var(--ate-primary);--ate-toolbar-button-active-background: var(--ate-primary-light);--ate-toolbar-button-active-color: var(--ate-primary);--ate-button-hover: var(--ate-surface-tertiary);--ate-button-active: var(--ate-surface-secondary);--ate-scrollbar-thumb: var(--ate-surface-tertiary);--ate-scrollbar-thumb-hover: var(--ate-text-muted)}:host(.fill-container){display:block;height:100%}.tiptap-editor{border:var(--ate-border-width) solid var(--ate-border-color);border-radius:var(--ate-border-radius);background:var(--ate-background);overflow:hidden;transition:border-color .2s ease}.tiptap-editor.fill-container{display:flex;flex-direction:column;height:100%}.tiptap-editor.fill-container .tiptap-content{flex:1;min-height:0;overflow-y:auto}.tiptap-editor:focus-within{border-color:var(--ate-focus-color)}.tiptap-content{padding:var(--ate-content-padding);min-height:var(--editor-min-height, 200px);height:var(--editor-height, auto);max-height:var(--editor-max-height, none);overflow-y:var(--editor-overflow, visible);outline:none;position:relative;scrollbar-width:thin;scrollbar-color:var(--ate-scrollbar-thumb) var(--ate-scrollbar-track)}.tiptap-content::-webkit-scrollbar{width:var(--ate-scrollbar-width);height:var(--ate-scrollbar-width)}.tiptap-content::-webkit-scrollbar-track{background:var(--ate-scrollbar-track)}.tiptap-content::-webkit-scrollbar-thumb{background:var(--ate-scrollbar-thumb);border:3px solid transparent;background-clip:content-box;border-radius:10px}.tiptap-content::-webkit-scrollbar-thumb:hover{background:var(--ate-scrollbar-thumb-hover);background-clip:content-box}.tiptap-content.drag-over{background:var(--ate-drag-background);border:2px dashed var(--ate-drag-border-color)}.character-count{padding:8px var(--ate-content-padding);font-size:12px;color:var(--ate-counter-color);text-align:right;border-top:1px solid var(--ate-counter-border-color);background:var(--ate-counter-background)}:host ::ng-deep .ProseMirror{outline:none;line-height:var(--ate-line-height);color:var(--ate-text-color);min-height:100%;height:100%;word-wrap:break-word;overflow-wrap:break-word}:host ::ng-deep .ProseMirror h1{font-size:2em;font-weight:700;margin-top:0;margin-bottom:.5em}:host ::ng-deep .ProseMirror h2{font-size:1.5em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror h3{font-size:1.25em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror p{margin:.5em 0}:host ::ng-deep .ProseMirror ul,:host ::ng-deep .ProseMirror ol{padding-left:2em;margin:.5em 0}:host ::ng-deep .ProseMirror blockquote{border-left:4px solid var(--ate-blockquote-border-color);margin:1em 0;font-style:italic;background:var(--ate-blockquote-background);padding:.5em 1em;border-radius:0 4px 4px 0}:host ::ng-deep .ProseMirror code{background:var(--ate-code-background);color:var(--ate-code-color);border:1px solid var(--ate-code-border-color);padding:.2em .4em;border-radius:3px;font-family:Monaco,Consolas,monospace;font-size:.9em}:host ::ng-deep .ProseMirror pre{background:var(--ate-code-block-background);color:var(--ate-code-block-color);border:1px solid var(--ate-code-block-border-color);padding:1em;border-radius:6px;overflow-x:auto;margin:1em 0}:host ::ng-deep .ProseMirror pre code{background:none;color:inherit;border:none;padding:0}:host ::ng-deep .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);color:var(--ate-placeholder-color);pointer-events:none;float:left;height:0}:host ::ng-deep .ProseMirror[contenteditable=false]{pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img{cursor:default;pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img:hover{transform:none;box-shadow:0 2px 8px #0000001a}:host ::ng-deep .ProseMirror[contenteditable=false] img.ProseMirror-selectednode{outline:none}:host ::ng-deep .ProseMirror img{position:relative;display:inline-block;max-width:100%;height:auto;cursor:pointer;transition:all .2s ease;border:2px solid transparent;border-radius:var(--ate-image-border-radius)}:host ::ng-deep .ProseMirror img:hover{border-color:var(--ate-border-color);box-shadow:0 2px 4px #0000001a}:host ::ng-deep .ProseMirror img.ProseMirror-selectednode{border-color:var(--ate-image-selected-color);box-shadow:0 0 0 3px var(--ate-primary-light-alpha);transition:all .2s ease}:host ::ng-deep .ProseMirror .tiptap-image{max-width:100%;height:auto;border-radius:16px;box-shadow:0 4px 20px #00000014;margin:.5em 0;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);display:block;filter:brightness(1) contrast(1)}:host ::ng-deep .ProseMirror .tiptap-image:hover{box-shadow:0 8px 30px #0000001f;filter:brightness(1.02) contrast(1.02)}:host ::ng-deep .ProseMirror .tiptap-image.ProseMirror-selectednode{outline:2px solid var(--ate-primary);outline-offset:2px;border-radius:16px;box-shadow:0 0 0 4px var(--ate-primary-light-alpha)}:host ::ng-deep .image-container{margin:.5em 0;text-align:center;border-radius:16px;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1)}:host ::ng-deep .image-container.image-align-left{text-align:left}:host ::ng-deep .image-container.image-align-center{text-align:center}:host ::ng-deep .image-container.image-align-right{text-align:right}:host ::ng-deep .image-container img{display:inline-block;max-width:100%;height:auto;border-radius:16px}:host ::ng-deep .resizable-image-container{position:relative;display:inline-block;margin:.5em 0}:host ::ng-deep .resize-controls{position:absolute;inset:0;pointer-events:none;z-index:1000}:host ::ng-deep .resize-handle{position:absolute;width:12px;height:12px;background:var(--ate-primary);border:2px solid var(--ate-surface);border-radius:50%;pointer-events:all;cursor:pointer;z-index:1001;transition:all .15s ease;box-shadow:0 2px 6px #0003}:host ::ng-deep .resize-handle:hover{background:var(--ate-primary);box-shadow:0 3px 8px #0000004d}:host ::ng-deep .resize-handle:active{background:var(--ate-primary)}:host ::ng-deep .resize-handle-n:hover,:host ::ng-deep .resize-handle-s:hover{transform:translate(-50%) scale(1.2)}:host ::ng-deep .resize-handle-w:hover,:host ::ng-deep .resize-handle-e:hover{transform:translateY(-50%) scale(1.2)}:host ::ng-deep .resize-handle-n:active,:host ::ng-deep .resize-handle-s:active{transform:translate(-50%) scale(.9)}:host ::ng-deep .resize-handle-w:active,:host ::ng-deep .resize-handle-e:active{transform:translateY(-50%) scale(.9)}:host ::ng-deep .resize-handle-nw:hover,:host ::ng-deep .resize-handle-ne:hover,:host ::ng-deep .resize-handle-sw:hover,:host ::ng-deep .resize-handle-se:hover{transform:scale(1.2)}:host ::ng-deep .resize-handle-nw:active,:host ::ng-deep .resize-handle-ne:active,:host ::ng-deep .resize-handle-sw:active,:host ::ng-deep .resize-handle-se:active{transform:scale(.9)}:host ::ng-deep .resize-handle-nw{top:0;left:-6px;cursor:nw-resize}:host ::ng-deep .resize-handle-n{top:0;left:50%;transform:translate(-50%);cursor:n-resize}:host ::ng-deep .resize-handle-ne{top:0;right:-6px;cursor:ne-resize}:host ::ng-deep .resize-handle-w{top:50%;left:-6px;transform:translateY(-50%);cursor:w-resize}:host ::ng-deep .resize-handle-e{top:50%;right:-6px;transform:translateY(-50%);cursor:e-resize}:host ::ng-deep .resize-handle-sw{bottom:0;left:-6px;cursor:sw-resize}:host ::ng-deep .resize-handle-s{bottom:0;left:50%;transform:translate(-50%);cursor:s-resize}:host ::ng-deep .resize-handle-se{bottom:0;right:-6px;cursor:se-resize}:host ::ng-deep body.resizing{-webkit-user-select:none;user-select:none;cursor:crosshair}:host ::ng-deep body.resizing .ProseMirror{pointer-events:none}:host ::ng-deep body.resizing .ProseMirror .tiptap-image{pointer-events:none}:host ::ng-deep .image-size-info{position:absolute;bottom:-20px;left:50%;transform:translate(-50%);background:#000c;color:#fff;padding:2px 6px;border-radius:3px;font-size:11px;white-space:nowrap;opacity:0;transition:opacity .2s ease}:host ::ng-deep .image-container:hover .image-size-info{opacity:1}:host ::ng-deep .ProseMirror table{border-collapse:separate;border-spacing:0;margin:0;table-layout:fixed;width:100%;border-radius:8px;overflow:hidden}:host ::ng-deep .ProseMirror table td,:host ::ng-deep .ProseMirror table th{border:none;border-right:1px solid var(--ate-table-border-color);border-bottom:1px solid var(--ate-table-border-color);box-sizing:border-box;min-width:1em;padding:8px 12px;position:relative;vertical-align:top;text-align:left}:host ::ng-deep .ProseMirror table td{background:var(--ate-table-cell-background)}:host ::ng-deep .ProseMirror table td:first-child,:host ::ng-deep .ProseMirror table th:first-child{border-left:1px solid var(--ate-table-border-color)}:host ::ng-deep .ProseMirror table tr:first-child td,:host ::ng-deep .ProseMirror table tr:first-child th{border-top:1px solid var(--ate-table-border-color)}:host ::ng-deep .ProseMirror table tr:first-child th:first-child{border-top-left-radius:8px}:host ::ng-deep .ProseMirror table tr:first-child th:last-child{border-top-right-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:first-child{border-bottom-left-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:last-child{border-bottom-right-radius:8px}:host ::ng-deep .ProseMirror table th{background:var(--ate-table-header-background);font-weight:600;color:var(--ate-table-header-color)}:host ::ng-deep .ProseMirror table .selectedCell:after{background:var(--ate-table-cell-selected-background);content:\"\";inset:0;pointer-events:none;position:absolute;z-index:2}:host ::ng-deep .ProseMirror table .column-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:4px;background-color:var(--ate-table-resize-handle-color);opacity:0;transition:opacity .2s ease}:host ::ng-deep .ProseMirror table:hover .column-resize-handle{opacity:1}:host ::ng-deep .ProseMirror table .column-resize-handle:hover{background-color:var(--ate-focus-color)}:host ::ng-deep .ProseMirror .tableWrapper{overflow-x:auto;margin:1em 0;border-radius:8px}:host ::ng-deep .ProseMirror .tableWrapper table{margin:0;border-radius:8px;min-width:600px;overflow:hidden}:host ::ng-deep .ProseMirror table p{margin:0}:host ::ng-deep .ProseMirror table tbody tr:hover td{background-color:var(--ate-table-row-hover-background)}\n"] }]
5710
5178
  }], ctorParameters: () => [] });
5711
5179
 
5712
5180
  /*
@@ -5718,5 +5186,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
5718
5186
  * Generated bundle index. Do not edit.
5719
5187
  */
5720
5188
 
5721
- export { AngularTiptapEditorComponent, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_CELL_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS$1 as DEFAULT_SLASH_COMMANDS, DEFAULT_TABLE_MENU_CONFIG, DEFAULT_TOOLBAR_CONFIG, EditorCommandsService, ImageService, NoopValueAccessorDirective, SLASH_COMMAND_KEYS, TiptapI18nService, createI18nSlashCommands, filterSlashCommands };
5189
+ export { AngularTiptapEditorComponent, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_CELL_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS_CONFIG, DEFAULT_TABLE_MENU_CONFIG, DEFAULT_TOOLBAR_CONFIG, EditorCommandsService, ImageService, NoopValueAccessorDirective, SLASH_COMMAND_KEYS, TiptapI18nService, createDefaultSlashCommands, filterSlashCommands };
5722
5190
  //# sourceMappingURL=flogeez-angular-tiptap-editor.mjs.map