@malva-ui/editor 0.1.15 → 0.2.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.
@@ -1,7 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, signal, Injectable, inject, computed, ElementRef, DestroyRef, Directive, input, viewChild, effect, ChangeDetectionStrategy, ViewEncapsulation, Component, untracked, Injector, viewChildren, TemplateRef, model, PLATFORM_ID, output, contentChildren, forwardRef } from '@angular/core';
2
+ import { InjectionToken, signal, Injectable, computed, inject, DestroyRef, ElementRef, Directive, input, viewChild, effect, ChangeDetectionStrategy, ViewEncapsulation, Component, TemplateRef, ViewContainerRef, PLATFORM_ID, afterNextRender, untracked, afterRenderEffect, Injector, viewChildren, model, output, contentChildren, forwardRef } from '@angular/core';
3
3
  import { LiveAnnouncer } from '@angular/cdk/a11y';
4
4
  import { MLV_EDITOR_I18N, MlvI18nResolverService, MLV_FILE_UPLOAD_I18N } from '@malva-ui/i18n';
5
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
+ import { Subscription, fromEvent, merge, take, delay } from 'rxjs';
5
7
  import { closeHistory } from '@tiptap/pm/history';
6
8
  import { Node as Node$1, Slice, Fragment } from '@tiptap/pm/model';
7
9
  import { PluginKey, Plugin, TextSelection, NodeSelection } from '@tiptap/pm/state';
@@ -16,12 +18,14 @@ import { MlvMenuTrigger, MlvMenu, MlvMenuItem } from '@malva-ui/core/menu';
16
18
  import { MlvPopup, MlvPopupContainer, MlvPopupContent, MlvPopupTrigger } from '@malva-ui/core/popup';
17
19
  import { MlvRadio, MlvRadioGroup } from '@malva-ui/core/radio';
18
20
  import { MlvTooltip } from '@malva-ui/core/tooltip';
19
- import { getExtensionField, flattenExtensions, findDuplicates, Extension, generateHTML, getMarkRange, Editor } from '@tiptap/core';
20
- import { MlvRtlService, MlvResizeObserverService, mlvNextId, MlvFade, MlvSpacer } from '@malva-ui/cdk/utils';
21
+ import { getExtensionField, flattenExtensions, findDuplicates, posToDOMRect, Extension, generateHTML, getMarkRange, Editor } from '@tiptap/core';
22
+ import { MlvRtlService, MlvResizeObserverService, mlvMirrorInlineOffsets, mlvNextId, MlvFade, MlvSpacer } from '@malva-ui/cdk/utils';
21
23
  import { MlvSelectionService, MlvSignalFormControlBase, MlvFormControlWrapper, MlvFormControlWrapperControl, MlvHint, MlvLabel, MlvMessage, MLV_FORM_CONTROL } from '@malva-ui/core/form-utils';
22
24
  import { MlvToolbar } from '@malva-ui/core/toolbar';
23
25
  import { MlvDivider } from '@malva-ui/core/divider';
24
26
  import { Markdown } from '@tiptap/markdown';
27
+ import { Overlay } from '@angular/cdk/overlay';
28
+ import { TemplatePortal } from '@angular/cdk/portal';
25
29
  import { FileHandlePlugin } from '@tiptap/extension-file-handler';
26
30
  import Image from '@tiptap/extension-image';
27
31
  import { TaskList, TaskItem } from '@tiptap/extension-list';
@@ -37,7 +41,6 @@ import { MlvDialogRef, MlvDialog, MlvDialogBody, MlvDialogFooter, MlvDialogHeade
37
41
  import { MlvProgress } from '@malva-ui/core/progress';
38
42
  import { MlvFileUpload } from '@malva-ui/core/file-upload';
39
43
  import { DOWN_ARROW, UP_ARROW, RIGHT_ARROW, LEFT_ARROW } from '@angular/cdk/keycodes';
40
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
41
44
  import { MlvDropdownPanel } from '@malva-ui/core/dropdown';
42
45
 
43
46
  /** Optional dependency-injection token for a host-provided image uploader. */
@@ -173,10 +176,10 @@ class MlvEditorToolbarRevision {
173
176
  /** @internal Increments after every Tiptap transaction or selection update. */
174
177
  revision = signal(0, /* @ts-ignore */
175
178
  ...(ngDevMode ? [{ debugName: "revision" }] : /* istanbul ignore next */ []));
176
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarRevision, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
177
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarRevision });
179
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRevision, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
180
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRevision });
178
181
  }
179
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarRevision, decorators: [{
182
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRevision, decorators: [{
180
183
  type: Injectable
181
184
  }] });
182
185
  /** @internal Supplies the built-in toolbar's per-editor invalidation signal. */
@@ -212,15 +215,41 @@ class MlvEditorToolbarRovingRegistry {
212
215
  this._disconnectObserver();
213
216
  };
214
217
  }
215
- /** @internal Resolves the single effective tab stop, even during DOM mutations. */
218
+ /**
219
+ * @internal Resolves the single effective tab stop, even during DOM mutations.
220
+ *
221
+ * Called once per registered widget on every invalidation, so it does no DOM
222
+ * work of its own: the enabled set and its order come from
223
+ * `_enabledWidgets()`, which is derived once per generation and carries the
224
+ * `_domStateRevision` dependency that keeps this reactive.
225
+ */
216
226
  isActive(widget) {
217
- this._domStateRevision();
227
+ const enabled = this._enabledWidgets();
218
228
  const active = this._active();
219
- const effectiveActive = active && this._enabled(active)
220
- ? active
221
- : (this._enabledWidgets()[0] ?? null);
222
- return (!this._disabled() && effectiveActive === widget && this._enabled(widget));
229
+ const effectiveActive = active && enabled.includes(active) ? active : (enabled[0] ?? null);
230
+ return (!this._disabled() &&
231
+ effectiveActive === widget &&
232
+ enabled.includes(widget));
223
233
  }
234
+ /**
235
+ * @internal Promotes a widget to the tab stop, gated on a **live** DOM read
236
+ * rather than on `_enabledWidgets()`.
237
+ *
238
+ * Deliberate, and the only place the two readings differ. `activate()` runs
239
+ * from a `focus` / `pointerdown` handler, so the question it has to answer is
240
+ * "is this widget interactive at this instant" — the live read is that
241
+ * question, and it is never wrong. The memo can only lag the DOM inside the
242
+ * window between a DOM write and the observer's microtask, and no input event
243
+ * is dispatched in that window, so the two agree today; keeping the live read
244
+ * means they still agree if some future code writes an attribute and calls
245
+ * `activate()` in the same turn, where the memo would be a generation behind
246
+ * and would refuse a widget the user can see is enabled.
247
+ *
248
+ * `move()` and `focusBoundary()` take the memo instead because they need the
249
+ * *order* of the enabled set, and an order assembled from N independent live
250
+ * reads is exactly what the memo exists to prevent. Do not "harmonise" the
251
+ * two: they are asking different questions.
252
+ */
224
253
  activate(widget) {
225
254
  if (this._widgets().includes(widget) && this._enabled(widget)) {
226
255
  this._hasInteracted = true;
@@ -273,14 +302,42 @@ class MlvEditorToolbarRovingRegistry {
273
302
  !widget.hasAttribute('disabled') &&
274
303
  widget.getAttribute('aria-disabled') !== 'true');
275
304
  }
276
- _enabledWidgets() {
305
+ /**
306
+ * @private Registered widgets that are currently interactive, in DOM order.
307
+ *
308
+ * Derived once per invalidation generation rather than once per widget.
309
+ * `isActive()` runs for every registered widget — 21 on the built-in
310
+ * toolbar — and Angular settles one `disabled` transition in about two
311
+ * change-detection passes, so the enabled order was assembled roughly
312
+ * `2N + 2` times, each assembly walking the DOM with `closest('[hidden]')`
313
+ * once per widget. The shape is therefore `(≈2N + c) derivations × N walks`,
314
+ * not the plain `N^2` an earlier draft of this comment claimed — `N^2` at
315
+ * N=21 is 441, and the measured cost is about twice that.
316
+ *
317
+ * Measured on the built-in toolbar at N=21, over `disabled` false → true:
318
+ * the code this replaced took 39 derivations / 841 walks; this file with only
319
+ * the `computed()` removed takes 44 / 924; as written it takes 1 / 21. The
320
+ * two pre-fix figures differ because the old `isActive()` short-circuited on
321
+ * a still-enabled `_active` and paid single `_enabled()` reads outside the
322
+ * derivations. 924 is the one the spec's guard is written against, since that
323
+ * ablation isolates this `computed()` and changes nothing else.
324
+ *
325
+ * `_domStateRevision` is what makes the memo correct. The enabled set is a
326
+ * DOM read, and the `MutationObserver` that bumps that signal is the only
327
+ * thing that tells the graph the DOM moved — the same dependency `isActive()`
328
+ * already relied on. Every consumer now shares one snapshot per generation
329
+ * instead of each re-reading the DOM at a different point in the same pass.
330
+ */
331
+ _enabledWidgets = computed(() => {
332
+ this._domStateRevision();
277
333
  return this._widgets()
278
334
  .filter((widget) => this._enabled(widget))
279
335
  .sort((left, right) => {
280
336
  const position = left.compareDocumentPosition(right);
281
337
  return position & 4 ? -1 : position & 2 ? 1 : 0;
282
338
  });
283
- }
339
+ }, /* @ts-ignore */
340
+ ...(ngDevMode ? [{ debugName: "_enabledWidgets" }] : /* istanbul ignore next */ []));
284
341
  _observeStateChanges() {
285
342
  if (this._observer ||
286
343
  typeof MutationObserver === 'undefined' ||
@@ -289,6 +346,21 @@ class MlvEditorToolbarRovingRegistry {
289
346
  !(this._root instanceof Node)) {
290
347
  return;
291
348
  }
349
+ // No debounce, deliberately, and the durable reason is the memo below, not
350
+ // the batching: after it, a redundant invalidation costs exactly one
351
+ // derivation — 21 `closest('[hidden]')` walks on the built-in toolbar — so
352
+ // even a coalescer that did merge something would be buying ~nothing, and
353
+ // would pay for it by leaving the tab stop a turn stale.
354
+ //
355
+ // A `queueMicrotask` coalescer also merges nothing here, in either
356
+ // direction. Mutations written in one turn are already one callback:
357
+ // `MutationObserver` delivers once per microtask checkpoint carrying every
358
+ // record accumulated since the last delivery, so disabling the editor
359
+ // writes 22 attributes and lands here once ("coalesces a whole batch of
360
+ // observed mutations into one invalidation" pins that). Mutations spread
361
+ // across separate microtasks are not merged either — each already lands in
362
+ // its own turn, which is precisely where a per-turn debounce flushes.
363
+ // The cost that was worth removing was the fan-out below.
292
364
  this._observer = new MutationObserver(() => {
293
365
  this._domStateRevision.update((revision) => revision + 1);
294
366
  this._synchronizeActiveWidget();
@@ -300,9 +372,23 @@ class MlvEditorToolbarRovingRegistry {
300
372
  subtree: true,
301
373
  });
302
374
  }
375
+ /**
376
+ * @private Re-resolves `_active` when the widget holding the tab stop is no
377
+ * longer part of the enabled set.
378
+ *
379
+ * Reactive-graph note: this reads `_enabledWidgets()`, where it used to read
380
+ * a non-reactive `_enabled(active)`, so `MlvEditorToolbarRoot`'s effect —
381
+ * which reaches here through `setDisabled()` — now tracks `_widgets` and
382
+ * `_domStateRevision` on *both* branches, not just on the fall-through into
383
+ * `_focusFirstWidget()`. The effect therefore re-runs on every widget
384
+ * registration and every observed mutation. That is deliberate and cheap
385
+ * post-memo (a re-run costs one cached memo read), and it does not loop:
386
+ * `_active` converges in at most two runs, and the effect already both read
387
+ * and wrote `_active` before this change.
388
+ */
303
389
  _synchronizeActiveWidget() {
304
390
  const active = this._active();
305
- if (active && this._enabled(active))
391
+ if (active && this._enabledWidgets().includes(active))
306
392
  return;
307
393
  this._focusFirstWidget();
308
394
  }
@@ -313,14 +399,28 @@ class MlvEditorToolbarRovingRegistry {
313
399
  this._observer?.disconnect();
314
400
  this._observer = undefined;
315
401
  }
316
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarRovingRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
317
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarRovingRegistry });
402
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRovingRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
403
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRovingRegistry });
318
404
  }
319
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarRovingRegistry, decorators: [{
405
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRovingRegistry, decorators: [{
320
406
  type: Injectable
321
407
  }] });
322
408
  /** @internal Resolves the nearest editor's projected-toolbar roving registry. */
323
409
  const MLV_EDITOR_TOOLBAR_ROVING = new InjectionToken('MLV_EDITOR_TOOLBAR_ROVING');
410
+ /**
411
+ * @internal Focuses the editor content with its selection. `view.focus()`
412
+ * alone moves no DOM focus while the view is not editable (`readonly`), so the
413
+ * contenteditable root is focused first; ProseMirror then writes the selection
414
+ * back to the DOM.
415
+ */
416
+ function mlvEditorFocusContent(editor) {
417
+ if (editor.isDestroyed)
418
+ return;
419
+ if (!editor.view.editable) {
420
+ editor.view.dom.focus({ preventScroll: true });
421
+ }
422
+ editor.view.focus();
423
+ }
324
424
  /**
325
425
  * Internal per-editor registry for CDK overlay roots owned by toolbar controls.
326
426
  * It is intentionally excluded from the public barrel: public controls use it
@@ -330,6 +430,14 @@ class MlvEditorOverlayRegistry {
330
430
  _entries = new Map();
331
431
  _onFocusIn;
332
432
  _onFocusOut;
433
+ /**
434
+ * @private Net for the whole registry. Each entry is released the moment its
435
+ * overlay is removed — a destroy-scoped lifetime alone would hold every
436
+ * overlay ever opened — but this guarantees release even if a consumer never
437
+ * calls `destroy()` or `closeAll()`. The registry is provided by the editor
438
+ * component, so this ref is the editor's.
439
+ */
440
+ _destroyRef = inject(DestroyRef);
333
441
  /** @internal Wires the nearest editor's composite focus handlers. */
334
442
  setFocusHandlers(onFocusIn, onFocusOut) {
335
443
  this._onFocusIn = onFocusIn;
@@ -342,12 +450,17 @@ class MlvEditorOverlayRegistry {
342
450
  existing.close = close;
343
451
  return () => this._remove(root);
344
452
  }
345
- const onFocusIn = (event) => this._onFocusIn?.(event);
346
- const onFocusOut = (event) => this._onFocusOut?.(event);
347
- const entry = { root, close, onFocusIn, onFocusOut };
348
- this._entries.set(root, entry);
349
- root.addEventListener('focusin', onFocusIn, true);
350
- root.addEventListener('focusout', onFocusOut, true);
453
+ // Capture phase, matching the editor host's own boundary listeners: an
454
+ // overlay's content may stop focus events on its way back up, and the
455
+ // composite has to see the crossing regardless.
456
+ const listeners = new Subscription();
457
+ listeners.add(fromEvent(root, 'focusin', { capture: true })
458
+ .pipe(takeUntilDestroyed(this._destroyRef))
459
+ .subscribe((event) => this._onFocusIn?.(event)));
460
+ listeners.add(fromEvent(root, 'focusout', { capture: true })
461
+ .pipe(takeUntilDestroyed(this._destroyRef))
462
+ .subscribe((event) => this._onFocusOut?.(event)));
463
+ this._entries.set(root, { root, close, listeners });
351
464
  return () => this._remove(root);
352
465
  }
353
466
  /** @internal Whether a node is rendered in an editor-owned overlay. */
@@ -370,6 +483,45 @@ class MlvEditorOverlayRegistry {
370
483
  }
371
484
  }
372
485
  }
486
+ /**
487
+ * @internal Asks every owned overlay except `keep` to close and leaves each
488
+ * entry for its owner to release, unlike `closeAll()`: an entry with nothing
489
+ * to close (the AI review bar) keeps its focus listeners.
490
+ */
491
+ closeOthers(keep) {
492
+ for (const entry of [...this._entries.values()]) {
493
+ if (entry.root === keep)
494
+ continue;
495
+ try {
496
+ entry.close();
497
+ }
498
+ catch {
499
+ // A third-party overlay must not keep the others open.
500
+ }
501
+ }
502
+ }
503
+ /**
504
+ * @internal A toolbar surface is going away because the editor turned
505
+ * `readonly` (#498): the docked band, which the next render removes, or the
506
+ * selection bubble's pane, which it hides. Asks every owned overlay except
507
+ * `surface` to close, like `closeOthers()`, and keeps focus in the editor:
508
+ * focus in `surface`, or in a popup (a registered root portaled into an
509
+ * overlay pane that `content` does not share; inside a dialog it does), moves
510
+ * to the content through `focusContent` first, while both still exist, so a
511
+ * closing popup finds focus outside its pane and hands nothing back to a
512
+ * trigger in `surface`. Focus anywhere else (the content, the status row,
513
+ * the AI review bar) stays where it is.
514
+ */
515
+ closeForReadonly(surface, content, focusContent) {
516
+ const active = surface.ownerDocument.activeElement;
517
+ if (active &&
518
+ (surface.contains(active) ||
519
+ (this.contains(active) &&
520
+ !(active.closest('.cdk-overlay-pane')?.contains(content) ?? true)))) {
521
+ focusContent();
522
+ }
523
+ this.closeOthers(surface);
524
+ }
373
525
  /** @internal Removes listener resources without invoking overlay close callbacks. */
374
526
  destroy() {
375
527
  for (const entry of [...this._entries.values()])
@@ -380,13 +532,12 @@ class MlvEditorOverlayRegistry {
380
532
  if (!entry)
381
533
  return;
382
534
  this._entries.delete(root);
383
- entry.root.removeEventListener('focusin', entry.onFocusIn, true);
384
- entry.root.removeEventListener('focusout', entry.onFocusOut, true);
535
+ entry.listeners.unsubscribe();
385
536
  }
386
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorOverlayRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
387
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorOverlayRegistry });
537
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorOverlayRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
538
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorOverlayRegistry });
388
539
  }
389
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorOverlayRegistry, decorators: [{
540
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorOverlayRegistry, decorators: [{
390
541
  type: Injectable
391
542
  }] });
392
543
  /** @internal Injection token for editor-owned overlay registration. */
@@ -414,10 +565,10 @@ class MlvEditorUploadAbortRegistry {
414
565
  }
415
566
  }
416
567
  }
417
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorUploadAbortRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
418
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorUploadAbortRegistry });
568
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorUploadAbortRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
569
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorUploadAbortRegistry });
419
570
  }
420
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorUploadAbortRegistry, decorators: [{
571
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorUploadAbortRegistry, decorators: [{
421
572
  type: Injectable
422
573
  }] });
423
574
  /** @internal Injection token for the editor-owned upload abort registry. */
@@ -2472,10 +2623,10 @@ class MlvEditorAiContext {
2472
2623
  const text = editor.getText();
2473
2624
  return text.length > 0 ? text : null;
2474
2625
  }
2475
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAiContext, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2476
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAiContext });
2626
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiContext, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2627
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiContext });
2477
2628
  }
2478
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAiContext, decorators: [{
2629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiContext, decorators: [{
2479
2630
  type: Injectable
2480
2631
  }] });
2481
2632
  /** Injects the AI command state belonging to the nearest `mlv-editor`. */
@@ -2504,10 +2655,10 @@ class MlvEditorToolbarWidget {
2504
2655
  _activateOnPointerDown() {
2505
2656
  this._registry.activate(this._element);
2506
2657
  }
2507
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarWidget, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2508
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.7", type: MlvEditorToolbarWidget, isStandalone: true, selector: "[mlvEditorToolbarWidget]", host: { listeners: { "focus": "_activate()", "pointerdown": "_activateOnPointerDown()" }, properties: { "attr.tabindex": "_tabIndex()" } }, ngImport: i0 });
2658
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarWidget, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2659
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorToolbarWidget, isStandalone: true, selector: "[mlvEditorToolbarWidget]", host: { listeners: { "focus": "_activate()", "pointerdown": "_activateOnPointerDown()" }, properties: { "attr.tabindex": "_tabIndex()" } }, ngImport: i0 });
2509
2660
  }
2510
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarWidget, decorators: [{
2661
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarWidget, decorators: [{
2511
2662
  type: Directive,
2512
2663
  args: [{
2513
2664
  selector: '[mlvEditorToolbarWidget]',
@@ -2540,6 +2691,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2540
2691
  * resolves no provider (presence), and its actions disable — without hiding —
2541
2692
  * while the editor is readonly or disabled or a transform is already running
2542
2693
  * (executability), matching the toolbar's presence-vs-executability rule.
2694
+ * Projected into `mlv-editor`'s toolbar it is not rendered at all while the
2695
+ * editor is `readonly`, which renders no toolbar (#498).
2543
2696
  */
2544
2697
  class MlvEditorAiMenu {
2545
2698
  /**
@@ -2759,10 +2912,10 @@ class MlvEditorAiMenu {
2759
2912
  this._unregisterMenu?.();
2760
2913
  this._unregisterMenu = undefined;
2761
2914
  }
2762
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAiMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
2763
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorAiMenu, isStandalone: true, selector: "mlv-editor-ai-menu", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, showCustomPrompt: { classPropertyName: "showCustomPrompt", publicName: "showCustomPrompt", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!_hasProvider()" }, classAttribute: "mlv-editor-ai-menu" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_menuTrigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }, { propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_triggerElement", first: true, predicate: ["_triggerElement"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_instructionInput", first: true, predicate: ["_instructionInput"], descendants: true, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["_panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- The trigger is declared once and rendered by whichever branch applies:\n `mlv-popup-container` requires a popup content child, so it cannot wrap the\n trigger when the custom prompt is dropped. -->\n<ng-template #_trigger>\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled() || _running()\"\n [attr.aria-label]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n [mlvTooltip]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n (click)=\"_onTriggerClick()\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n @if (_running()) {\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n } @else {\n <svg mlvButtonIcon lucideSparkles aria-hidden=\"true\" />\n }\n </button>\n</ng-template>\n\n@if (showCustomPrompt()) {\n<mlv-popup-container class=\"mlv-editor-ai-menu__anchor\">\n <ng-container [ngTemplateOutlet]=\"_trigger\" />\n\n <mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [position]=\"['bottom-start', 'top-start', 'bottom-end', 'top-end']\"\n [ariaLabel]=\"_copy().aiCustom\"\n (afterOpened)=\"_onPromptOpened()\"\n (afterClosed)=\"_onPromptClosed()\"\n >\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-ai-menu__panel\">\n <mlv-input\n #_instructionInput\n [label]=\"_copy().aiCustom\"\n [placeholder]=\"_copy().aiPromptPlaceholder\"\n [disabled]=\"_disabled()\"\n [value]=\"_instruction()\"\n (valueChange)=\"_instruction.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-radio-group\n class=\"mlv-editor-ai-menu__output\"\n [label]=\"_copy().aiOutputMode\"\n [value]=\"_output()\"\n (valueChange)=\"_onOutputChange($event)\"\n >\n <mlv-radio value=\"replace-selection\" [disabled]=\"_disabled()\">\n {{ _copy().aiReplaceSelection }}\n </mlv-radio>\n <mlv-radio value=\"insert-below\" [disabled]=\"_disabled()\">\n {{ _copy().aiInsertBelow }}\n </mlv-radio>\n <mlv-radio value=\"review\" [disabled]=\"_disabled()\">\n {{ _copy().aiReviewChanges }}\n </mlv-radio>\n </mlv-radio-group>\n <div class=\"mlv-editor-ai-menu__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_applyDisabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().aiApply }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n (click)=\"_closePrompt()\"\n >\n {{ _copy().aiCancel }}\n </button>\n </div>\n </div>\n </ng-template>\n </mlv-popup>\n</mlv-popup-container>\n} @else {\n<ng-container [ngTemplateOutlet]=\"_trigger\" />\n}\n\n<mlv-menu #_menu [label]=\"_copy().aiMenu\">\n <!-- Tracked by index: a host-authored list may repeat a kind, so the kind\n is not an identity. -->\n @for (action of _actions(); track $index) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_runAction(action)\"\n >\n {{ action.label }}\n </mlv-list-item>\n } @if (showCustomPrompt()) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_openPrompt()\"\n >\n {{ _copy().aiCustom }}\n </mlv-list-item>\n }\n</mlv-menu>\n", styles: ["@layer mlv.components{.mlv-editor-ai-menu{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-menu *{box-sizing:border-box}.mlv-editor-ai-menu,.mlv-editor-ai-menu__anchor{display:inline-flex}.mlv-editor-ai-menu__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-ai-menu__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideCircleStop, selector: "svg[lucideCircleStop], svg[lucideStopCircle]" }, { kind: "component", type: LucideSparkles, selector: "svg[lucideSparkles], svg[lucideStars]" }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "component", type: MlvPopupContainer, selector: "mlv-popup-container" }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "component", type: MlvRadio, selector: "mlv-radio", inputs: ["ariaLabel", "ariaLabelledBy", "value", "disabled"] }, { kind: "component", type: MlvRadioGroup, selector: "mlv-radio-group", inputs: ["value", "name"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2915
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
2916
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorAiMenu, isStandalone: true, selector: "mlv-editor-ai-menu", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, showCustomPrompt: { classPropertyName: "showCustomPrompt", publicName: "showCustomPrompt", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!_hasProvider()" }, classAttribute: "mlv-editor-ai-menu" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_menuTrigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }, { propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_triggerElement", first: true, predicate: ["_triggerElement"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_instructionInput", first: true, predicate: ["_instructionInput"], descendants: true, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["_panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- The trigger is declared once and rendered by whichever branch applies:\n `mlv-popup-container` requires a popup content child, so it cannot wrap the\n trigger when the custom prompt is dropped. -->\n<ng-template #_trigger>\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled() || _running()\"\n [attr.aria-label]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n [mlvTooltip]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n (click)=\"_onTriggerClick()\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n @if (_running()) {\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n } @else {\n <svg mlvButtonIcon lucideSparkles aria-hidden=\"true\" />\n }\n </button>\n</ng-template>\n\n@if (showCustomPrompt()) {\n <mlv-popup-container class=\"mlv-editor-ai-menu__anchor\">\n <ng-container [ngTemplateOutlet]=\"_trigger\" />\n\n <mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [position]=\"['bottom-start', 'top-start', 'bottom-end', 'top-end']\"\n [ariaLabel]=\"_copy().aiCustom\"\n (afterOpened)=\"_onPromptOpened()\"\n (afterClosed)=\"_onPromptClosed()\"\n >\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-ai-menu__panel\">\n <mlv-input\n #_instructionInput\n [label]=\"_copy().aiCustom\"\n [placeholder]=\"_copy().aiPromptPlaceholder\"\n [disabled]=\"_disabled()\"\n [value]=\"_instruction()\"\n (valueChange)=\"_instruction.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-radio-group\n class=\"mlv-editor-ai-menu__output\"\n [label]=\"_copy().aiOutputMode\"\n [value]=\"_output()\"\n (valueChange)=\"_onOutputChange($event)\"\n >\n <mlv-radio value=\"replace-selection\" [disabled]=\"_disabled()\">\n {{ _copy().aiReplaceSelection }}\n </mlv-radio>\n <mlv-radio value=\"insert-below\" [disabled]=\"_disabled()\">\n {{ _copy().aiInsertBelow }}\n </mlv-radio>\n <mlv-radio value=\"review\" [disabled]=\"_disabled()\">\n {{ _copy().aiReviewChanges }}\n </mlv-radio>\n </mlv-radio-group>\n <div class=\"mlv-editor-ai-menu__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_applyDisabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().aiApply }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n (click)=\"_closePrompt()\"\n >\n {{ _copy().aiCancel }}\n </button>\n </div>\n </div>\n </ng-template>\n </mlv-popup>\n </mlv-popup-container>\n} @else {\n <ng-container [ngTemplateOutlet]=\"_trigger\" />\n}\n\n<mlv-menu #_menu [label]=\"_copy().aiMenu\">\n <!-- Tracked by index: a host-authored list may repeat a kind, so the kind\n is not an identity. -->\n @for (action of _actions(); track $index) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_runAction(action)\"\n >\n {{ action.label }}\n </mlv-list-item>\n }\n @if (showCustomPrompt()) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_openPrompt()\"\n >\n {{ _copy().aiCustom }}\n </mlv-list-item>\n }\n</mlv-menu>\n", styles: ["@layer mlv.components{.mlv-editor-ai-menu{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-menu *{box-sizing:border-box}.mlv-editor-ai-menu,.mlv-editor-ai-menu__anchor{display:inline-flex}.mlv-editor-ai-menu__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-ai-menu__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideCircleStop, selector: "svg[lucideCircleStop], svg[lucideStopCircle]" }, { kind: "component", type: LucideSparkles, selector: "svg[lucideSparkles], svg[lucideStars]" }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "component", type: MlvPopupContainer, selector: "mlv-popup-container" }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "component", type: MlvRadio, selector: "mlv-radio", inputs: ["ariaLabel", "ariaLabelledBy", "value", "disabled"] }, { kind: "component", type: MlvRadioGroup, selector: "mlv-radio-group", inputs: ["value", "name"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2764
2917
  }
2765
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAiMenu, decorators: [{
2918
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiMenu, decorators: [{
2766
2919
  type: Component,
2767
2920
  args: [{ selector: 'mlv-editor-ai-menu', imports: [
2768
2921
  NgTemplateOutlet,
@@ -2785,7 +2938,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
2785
2938
  ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
2786
2939
  class: 'mlv-editor-ai-menu',
2787
2940
  '[hidden]': '!_hasProvider()',
2788
- }, template: "<!-- The trigger is declared once and rendered by whichever branch applies:\n `mlv-popup-container` requires a popup content child, so it cannot wrap the\n trigger when the custom prompt is dropped. -->\n<ng-template #_trigger>\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled() || _running()\"\n [attr.aria-label]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n [mlvTooltip]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n (click)=\"_onTriggerClick()\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n @if (_running()) {\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n } @else {\n <svg mlvButtonIcon lucideSparkles aria-hidden=\"true\" />\n }\n </button>\n</ng-template>\n\n@if (showCustomPrompt()) {\n<mlv-popup-container class=\"mlv-editor-ai-menu__anchor\">\n <ng-container [ngTemplateOutlet]=\"_trigger\" />\n\n <mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [position]=\"['bottom-start', 'top-start', 'bottom-end', 'top-end']\"\n [ariaLabel]=\"_copy().aiCustom\"\n (afterOpened)=\"_onPromptOpened()\"\n (afterClosed)=\"_onPromptClosed()\"\n >\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-ai-menu__panel\">\n <mlv-input\n #_instructionInput\n [label]=\"_copy().aiCustom\"\n [placeholder]=\"_copy().aiPromptPlaceholder\"\n [disabled]=\"_disabled()\"\n [value]=\"_instruction()\"\n (valueChange)=\"_instruction.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-radio-group\n class=\"mlv-editor-ai-menu__output\"\n [label]=\"_copy().aiOutputMode\"\n [value]=\"_output()\"\n (valueChange)=\"_onOutputChange($event)\"\n >\n <mlv-radio value=\"replace-selection\" [disabled]=\"_disabled()\">\n {{ _copy().aiReplaceSelection }}\n </mlv-radio>\n <mlv-radio value=\"insert-below\" [disabled]=\"_disabled()\">\n {{ _copy().aiInsertBelow }}\n </mlv-radio>\n <mlv-radio value=\"review\" [disabled]=\"_disabled()\">\n {{ _copy().aiReviewChanges }}\n </mlv-radio>\n </mlv-radio-group>\n <div class=\"mlv-editor-ai-menu__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_applyDisabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().aiApply }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n (click)=\"_closePrompt()\"\n >\n {{ _copy().aiCancel }}\n </button>\n </div>\n </div>\n </ng-template>\n </mlv-popup>\n</mlv-popup-container>\n} @else {\n<ng-container [ngTemplateOutlet]=\"_trigger\" />\n}\n\n<mlv-menu #_menu [label]=\"_copy().aiMenu\">\n <!-- Tracked by index: a host-authored list may repeat a kind, so the kind\n is not an identity. -->\n @for (action of _actions(); track $index) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_runAction(action)\"\n >\n {{ action.label }}\n </mlv-list-item>\n } @if (showCustomPrompt()) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_openPrompt()\"\n >\n {{ _copy().aiCustom }}\n </mlv-list-item>\n }\n</mlv-menu>\n", styles: ["@layer mlv.components{.mlv-editor-ai-menu{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-menu *{box-sizing:border-box}.mlv-editor-ai-menu,.mlv-editor-ai-menu__anchor{display:inline-flex}.mlv-editor-ai-menu__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-ai-menu__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"] }]
2941
+ }, template: "<!-- The trigger is declared once and rendered by whichever branch applies:\n `mlv-popup-container` requires a popup content child, so it cannot wrap the\n trigger when the custom prompt is dropped. -->\n<ng-template #_trigger>\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled() || _running()\"\n [attr.aria-label]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n [mlvTooltip]=\"_running() ? _copy().aiCancel : _copy().aiMenu\"\n (click)=\"_onTriggerClick()\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n @if (_running()) {\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n } @else {\n <svg mlvButtonIcon lucideSparkles aria-hidden=\"true\" />\n }\n </button>\n</ng-template>\n\n@if (showCustomPrompt()) {\n <mlv-popup-container class=\"mlv-editor-ai-menu__anchor\">\n <ng-container [ngTemplateOutlet]=\"_trigger\" />\n\n <mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [position]=\"['bottom-start', 'top-start', 'bottom-end', 'top-end']\"\n [ariaLabel]=\"_copy().aiCustom\"\n (afterOpened)=\"_onPromptOpened()\"\n (afterClosed)=\"_onPromptClosed()\"\n >\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-ai-menu__panel\">\n <mlv-input\n #_instructionInput\n [label]=\"_copy().aiCustom\"\n [placeholder]=\"_copy().aiPromptPlaceholder\"\n [disabled]=\"_disabled()\"\n [value]=\"_instruction()\"\n (valueChange)=\"_instruction.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-radio-group\n class=\"mlv-editor-ai-menu__output\"\n [label]=\"_copy().aiOutputMode\"\n [value]=\"_output()\"\n (valueChange)=\"_onOutputChange($event)\"\n >\n <mlv-radio value=\"replace-selection\" [disabled]=\"_disabled()\">\n {{ _copy().aiReplaceSelection }}\n </mlv-radio>\n <mlv-radio value=\"insert-below\" [disabled]=\"_disabled()\">\n {{ _copy().aiInsertBelow }}\n </mlv-radio>\n <mlv-radio value=\"review\" [disabled]=\"_disabled()\">\n {{ _copy().aiReviewChanges }}\n </mlv-radio>\n </mlv-radio-group>\n <div class=\"mlv-editor-ai-menu__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_applyDisabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().aiApply }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n (click)=\"_closePrompt()\"\n >\n {{ _copy().aiCancel }}\n </button>\n </div>\n </div>\n </ng-template>\n </mlv-popup>\n </mlv-popup-container>\n} @else {\n <ng-container [ngTemplateOutlet]=\"_trigger\" />\n}\n\n<mlv-menu #_menu [label]=\"_copy().aiMenu\">\n <!-- Tracked by index: a host-authored list may repeat a kind, so the kind\n is not an identity. -->\n @for (action of _actions(); track $index) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_runAction(action)\"\n >\n {{ action.label }}\n </mlv-list-item>\n }\n @if (showCustomPrompt()) {\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_itemsDisabled()\"\n (itemClick)=\"_openPrompt()\"\n >\n {{ _copy().aiCustom }}\n </mlv-list-item>\n }\n</mlv-menu>\n", styles: ["@layer mlv.components{.mlv-editor-ai-menu{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-menu *{box-sizing:border-box}.mlv-editor-ai-menu,.mlv-editor-ai-menu__anchor{display:inline-flex}.mlv-editor-ai-menu__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-ai-menu__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"] }]
2789
2942
  }], ctorParameters: () => [], propDecorators: { actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], showCustomPrompt: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCustomPrompt", required: false }] }], _menu: [{ type: i0.ViewChild, args: ['_menu', { isSignal: true }] }], _menuTrigger: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MlvMenuTrigger), { isSignal: true }] }], _popup: [{ type: i0.ViewChild, args: ['_popup', { isSignal: true }] }], _triggerElement: [{ type: i0.ViewChild, args: ['_triggerElement', { ...{
2790
2943
  read: ElementRef,
2791
2944
  }, isSignal: true }] }], _instructionInput: [{ type: i0.ViewChild, args: ['_instructionInput', { isSignal: true }] }], _panel: [{ type: i0.ViewChild, args: ['_panel', { isSignal: true }] }] } });
@@ -2983,10 +3136,10 @@ class MlvEditorAiReviewBar {
2983
3136
  _rejectAll() {
2984
3137
  this._ai?.rejectAll();
2985
3138
  }
2986
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAiReviewBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
2987
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorAiReviewBar, isStandalone: true, selector: "mlv-editor-ai-review-bar", host: { attributes: { "role": "group" }, properties: { "attr.aria-label": "_copy().aiReviewBar", "hidden": "!_visible()" }, classAttribute: "mlv-editor-ai-review-bar" }, ngImport: i0, template: "@if (_running()) {\n<button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n class=\"mlv-editor-ai-review-bar__stop\"\n [disabled]=\"_disabled()\"\n [mlvTooltip]=\"_copy().aiStopGeneration\"\n (click)=\"_stop()\"\n>\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n {{ _copy().aiStopGeneration }}\n</button>\n} @if (_reviewing()) {\n<span class=\"mlv-editor-ai-review-bar__count\">{{ _countText() }}</span>\n<!-- Screen-reader-only description of the current suggestion: the removed\n text exists only in the aria-hidden strikethrough widget, so the\n accept/reject buttons point here via aria-describedby. -->\n<span\n class=\"mlv-editor-ai-review-bar__current-description\"\n [id]=\"_descriptionId\"\n >{{ _currentDescription() }}</span\n>\n<div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiPreviousSuggestion\"\n [mlvTooltip]=\"_copy().aiPreviousSuggestion\"\n (click)=\"_previous()\"\n >\n <svg mlvButtonIcon lucideChevronLeft aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiNextSuggestion\"\n [mlvTooltip]=\"_copy().aiNextSuggestion\"\n (click)=\"_next()\"\n >\n <svg mlvButtonIcon lucideChevronRight aria-hidden=\"true\" />\n </button>\n</div>\n<div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiAcceptSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiAcceptSuggestion\"\n (click)=\"_acceptCurrent()\"\n >\n <svg mlvButtonIcon lucideCheck aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiRejectSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiRejectSuggestion\"\n (click)=\"_rejectCurrent()\"\n >\n <svg mlvButtonIcon lucideX aria-hidden=\"true\" />\n </button>\n</div>\n<div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_acceptAll()\"\n >\n {{ _copy().aiAcceptAll }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_rejectAll()\"\n >\n {{ _copy().aiRejectAll }}\n </button>\n</div>\n}\n", styles: ["@layer mlv.components{.mlv-editor-ai-review-bar{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-review-bar *{box-sizing:border-box}.mlv-editor-ai-review-bar{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--mlv-spacing-2);font-size:var(--mlv-typography-body-s-size)}.mlv-editor-ai-review-bar[hidden]{display:none}.mlv-editor-ai-review-bar__count{color:var(--mlv-text-secondary);white-space:nowrap}.mlv-editor-ai-review-bar__group{display:inline-flex;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-ai-review-bar__stop{white-space:nowrap}.mlv-editor-ai-review-bar__current-description{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;padding:0;margin:-1px}}\n"], dependencies: [{ kind: "component", type: LucideCheck, selector: "svg[lucideCheck]" }, { kind: "component", type: LucideChevronLeft, selector: "svg[lucideChevronLeft]" }, { kind: "component", type: LucideChevronRight, selector: "svg[lucideChevronRight]" }, { kind: "component", type: LucideCircleStop, selector: "svg[lucideCircleStop], svg[lucideStopCircle]" }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }, { kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3139
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiReviewBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
3140
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorAiReviewBar, isStandalone: true, selector: "mlv-editor-ai-review-bar", host: { attributes: { "role": "group" }, properties: { "attr.aria-label": "_copy().aiReviewBar", "hidden": "!_visible()" }, classAttribute: "mlv-editor-ai-review-bar" }, ngImport: i0, template: "@if (_running()) {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n class=\"mlv-editor-ai-review-bar__stop\"\n [disabled]=\"_disabled()\"\n (click)=\"_stop()\"\n >\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n {{ _copy().aiStopGeneration }}\n </button>\n}\n@if (_reviewing()) {\n <span class=\"mlv-editor-ai-review-bar__count\">{{ _countText() }}</span>\n <!-- Screen-reader-only description of the current suggestion: the removed\n text exists only in the aria-hidden strikethrough widget, so the\n accept/reject buttons point here via aria-describedby. -->\n <span\n class=\"mlv-editor-ai-review-bar__current-description\"\n [id]=\"_descriptionId\"\n >{{ _currentDescription() }}</span\n >\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiPreviousSuggestion\"\n [mlvTooltip]=\"_copy().aiPreviousSuggestion\"\n (click)=\"_previous()\"\n >\n <svg mlvButtonIcon lucideChevronLeft aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiNextSuggestion\"\n [mlvTooltip]=\"_copy().aiNextSuggestion\"\n (click)=\"_next()\"\n >\n <svg mlvButtonIcon lucideChevronRight aria-hidden=\"true\" />\n </button>\n </div>\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiAcceptSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiAcceptSuggestion\"\n (click)=\"_acceptCurrent()\"\n >\n <svg mlvButtonIcon lucideCheck aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiRejectSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiRejectSuggestion\"\n (click)=\"_rejectCurrent()\"\n >\n <svg mlvButtonIcon lucideX aria-hidden=\"true\" />\n </button>\n </div>\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_acceptAll()\"\n >\n {{ _copy().aiAcceptAll }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_rejectAll()\"\n >\n {{ _copy().aiRejectAll }}\n </button>\n </div>\n}\n", styles: ["@layer mlv.components{.mlv-editor-ai-review-bar{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-review-bar *{box-sizing:border-box}.mlv-editor-ai-review-bar{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--mlv-spacing-2);font-size:var(--mlv-typography-body-s-size)}.mlv-editor-ai-review-bar[hidden]{display:none}.mlv-editor-ai-review-bar__count{color:var(--mlv-text-secondary);white-space:nowrap}.mlv-editor-ai-review-bar__group{display:inline-flex;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-ai-review-bar__stop{white-space:nowrap}.mlv-editor-ai-review-bar__current-description{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;padding:0;margin:-1px}}\n"], dependencies: [{ kind: "component", type: LucideCheck, selector: "svg[lucideCheck]" }, { kind: "component", type: LucideChevronLeft, selector: "svg[lucideChevronLeft]" }, { kind: "component", type: LucideChevronRight, selector: "svg[lucideChevronRight]" }, { kind: "component", type: LucideCircleStop, selector: "svg[lucideCircleStop], svg[lucideStopCircle]" }, { kind: "component", type: LucideX, selector: "svg[lucideX]" }, { kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2988
3141
  }
2989
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAiReviewBar, decorators: [{
3142
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAiReviewBar, decorators: [{
2990
3143
  type: Component,
2991
3144
  args: [{ selector: 'mlv-editor-ai-review-bar', imports: [
2992
3145
  LucideCheck,
@@ -3002,7 +3155,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
3002
3155
  role: 'group',
3003
3156
  '[attr.aria-label]': '_copy().aiReviewBar',
3004
3157
  '[hidden]': '!_visible()',
3005
- }, template: "@if (_running()) {\n<button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n class=\"mlv-editor-ai-review-bar__stop\"\n [disabled]=\"_disabled()\"\n [mlvTooltip]=\"_copy().aiStopGeneration\"\n (click)=\"_stop()\"\n>\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n {{ _copy().aiStopGeneration }}\n</button>\n} @if (_reviewing()) {\n<span class=\"mlv-editor-ai-review-bar__count\">{{ _countText() }}</span>\n<!-- Screen-reader-only description of the current suggestion: the removed\n text exists only in the aria-hidden strikethrough widget, so the\n accept/reject buttons point here via aria-describedby. -->\n<span\n class=\"mlv-editor-ai-review-bar__current-description\"\n [id]=\"_descriptionId\"\n >{{ _currentDescription() }}</span\n>\n<div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiPreviousSuggestion\"\n [mlvTooltip]=\"_copy().aiPreviousSuggestion\"\n (click)=\"_previous()\"\n >\n <svg mlvButtonIcon lucideChevronLeft aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiNextSuggestion\"\n [mlvTooltip]=\"_copy().aiNextSuggestion\"\n (click)=\"_next()\"\n >\n <svg mlvButtonIcon lucideChevronRight aria-hidden=\"true\" />\n </button>\n</div>\n<div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiAcceptSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiAcceptSuggestion\"\n (click)=\"_acceptCurrent()\"\n >\n <svg mlvButtonIcon lucideCheck aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiRejectSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiRejectSuggestion\"\n (click)=\"_rejectCurrent()\"\n >\n <svg mlvButtonIcon lucideX aria-hidden=\"true\" />\n </button>\n</div>\n<div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_acceptAll()\"\n >\n {{ _copy().aiAcceptAll }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_rejectAll()\"\n >\n {{ _copy().aiRejectAll }}\n </button>\n</div>\n}\n", styles: ["@layer mlv.components{.mlv-editor-ai-review-bar{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-review-bar *{box-sizing:border-box}.mlv-editor-ai-review-bar{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--mlv-spacing-2);font-size:var(--mlv-typography-body-s-size)}.mlv-editor-ai-review-bar[hidden]{display:none}.mlv-editor-ai-review-bar__count{color:var(--mlv-text-secondary);white-space:nowrap}.mlv-editor-ai-review-bar__group{display:inline-flex;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-ai-review-bar__stop{white-space:nowrap}.mlv-editor-ai-review-bar__current-description{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;padding:0;margin:-1px}}\n"] }]
3158
+ }, template: "@if (_running()) {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n class=\"mlv-editor-ai-review-bar__stop\"\n [disabled]=\"_disabled()\"\n (click)=\"_stop()\"\n >\n <svg mlvButtonIcon lucideCircleStop aria-hidden=\"true\" />\n {{ _copy().aiStopGeneration }}\n </button>\n}\n@if (_reviewing()) {\n <span class=\"mlv-editor-ai-review-bar__count\">{{ _countText() }}</span>\n <!-- Screen-reader-only description of the current suggestion: the removed\n text exists only in the aria-hidden strikethrough widget, so the\n accept/reject buttons point here via aria-describedby. -->\n <span\n class=\"mlv-editor-ai-review-bar__current-description\"\n [id]=\"_descriptionId\"\n >{{ _currentDescription() }}</span\n >\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiPreviousSuggestion\"\n [mlvTooltip]=\"_copy().aiPreviousSuggestion\"\n (click)=\"_previous()\"\n >\n <svg mlvButtonIcon lucideChevronLeft aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiNextSuggestion\"\n [mlvTooltip]=\"_copy().aiNextSuggestion\"\n (click)=\"_next()\"\n >\n <svg mlvButtonIcon lucideChevronRight aria-hidden=\"true\" />\n </button>\n </div>\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiAcceptSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiAcceptSuggestion\"\n (click)=\"_acceptCurrent()\"\n >\n <svg mlvButtonIcon lucideCheck aria-hidden=\"true\" />\n </button>\n <button\n mlvButton\n type=\"button\"\n shape=\"square\"\n variant=\"transparent\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().aiRejectSuggestion\"\n [attr.aria-describedby]=\"_descriptionId\"\n [mlvTooltip]=\"_copy().aiRejectSuggestion\"\n (click)=\"_rejectCurrent()\"\n >\n <svg mlvButtonIcon lucideX aria-hidden=\"true\" />\n </button>\n </div>\n <div class=\"mlv-editor-ai-review-bar__group\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_acceptAll()\"\n >\n {{ _copy().aiAcceptAll }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n mlvDensity=\"tight\"\n [disabled]=\"_disabled()\"\n (click)=\"_rejectAll()\"\n >\n {{ _copy().aiRejectAll }}\n </button>\n </div>\n}\n", styles: ["@layer mlv.components{.mlv-editor-ai-review-bar{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-ai-review-bar *{box-sizing:border-box}.mlv-editor-ai-review-bar{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--mlv-spacing-2);font-size:var(--mlv-typography-body-s-size)}.mlv-editor-ai-review-bar[hidden]{display:none}.mlv-editor-ai-review-bar__count{color:var(--mlv-text-secondary);white-space:nowrap}.mlv-editor-ai-review-bar__group{display:inline-flex;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-ai-review-bar__stop{white-space:nowrap}.mlv-editor-ai-review-bar__current-description{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;padding:0;margin:-1px}}\n"] }]
3006
3159
  }], ctorParameters: () => [] });
3007
3160
 
3008
3161
  /**
@@ -3238,18 +3391,793 @@ function preflightMlvEditorExtensions(extensions, initialFormat) {
3238
3391
  }
3239
3392
  }
3240
3393
 
3394
+ /**
3395
+ * @internal Resolves an `MlvEditor` sizing input to the value its custom
3396
+ * property receives, or `null` to write nothing.
3397
+ *
3398
+ * A finite number is px, matching `mlv-popup`. A string passes through
3399
+ * verbatim once trimmed, so any CSS length, `calc()`, `min()` or viewport unit
3400
+ * works. `undefined`, `null`, a blank string and a non-finite number all
3401
+ * resolve to `null`, so the host binding removes the property and the
3402
+ * stylesheet's `var()` fallback applies.
3403
+ */
3404
+ function mlvEditorCssLength(value) {
3405
+ if (typeof value === 'number') {
3406
+ return Number.isFinite(value) ? `${value}px` : null;
3407
+ }
3408
+ if (typeof value === 'string') {
3409
+ const trimmed = value.trim();
3410
+ return trimmed === '' ? null : trimmed;
3411
+ }
3412
+ return null;
3413
+ }
3414
+ /** @internal ProseMirror's own default `scrollMargin`, in px (prosemirror-view `scrollRectIntoView`). */
3415
+ const MLV_EDITOR_SCROLL_MARGIN = 5;
3416
+ /**
3417
+ * @internal ProseMirror `scrollMargin` / `scrollThreshold` for a toolbar that
3418
+ * obscures `obscured` px on its own side (WCAG 2.2 SC 2.4.11). The threshold is
3419
+ * the obscured extent: ProseMirror scrolls only once the caret enters it. The
3420
+ * margin leaves the default 5px beyond it. Every other side keeps
3421
+ * ProseMirror's defaults.
3422
+ */
3423
+ function mlvEditorScrollSides(kind, position, obscured) {
3424
+ const base = kind === 'margin' ? MLV_EDITOR_SCROLL_MARGIN : 0;
3425
+ const edge = base + Math.max(0, Math.ceil(obscured));
3426
+ return {
3427
+ top: position === 'top' ? edge : base,
3428
+ right: base,
3429
+ bottom: position === 'bottom' ? edge : base,
3430
+ left: base,
3431
+ };
3432
+ }
3433
+ /**
3434
+ * @internal A scroll-prop object whose sides re-resolve on every read.
3435
+ * ProseMirror reads `value[side]` lazily each time it scrolls a rect into view
3436
+ * (`getSide()` in prosemirror-view), so one stable object follows density,
3437
+ * configuration and consumer CSS offsets without `setProps` churn. Only the
3438
+ * toolbar's side measures anything.
3439
+ */
3440
+ function createMlvEditorLiveScrollSides(kind, position, obscured) {
3441
+ const base = kind === 'margin' ? MLV_EDITOR_SCROLL_MARGIN : 0;
3442
+ const side = (which) => position() === which
3443
+ ? mlvEditorScrollSides(kind, which, obscured())[which]
3444
+ : base;
3445
+ return {
3446
+ get top() {
3447
+ return side('top');
3448
+ },
3449
+ get right() {
3450
+ return base;
3451
+ },
3452
+ get bottom() {
3453
+ return side('bottom');
3454
+ },
3455
+ get left() {
3456
+ return base;
3457
+ },
3458
+ };
3459
+ }
3460
+ /**
3461
+ * @internal The part of `rect` inside `boundary`, or `null` once none of it
3462
+ * is. Edges count as inside, so a zero-size caret lying on the boundary is
3463
+ * still visible.
3464
+ *
3465
+ * The selection bubble anchors on this: while a capped viewport scrolls, it
3466
+ * follows the visible part of the selection, and when the selection has left
3467
+ * the viewport (or the window) it hides instead of floating over content the
3468
+ * selection is no longer in.
3469
+ */
3470
+ function mlvEditorVisibleRect(rect, boundary) {
3471
+ const top = Math.max(rect.top, boundary.top);
3472
+ const bottom = Math.min(rect.bottom, boundary.bottom);
3473
+ const left = Math.max(rect.left, boundary.left);
3474
+ const right = Math.min(rect.right, boundary.right);
3475
+ return top <= bottom && left <= right ? { top, bottom, left, right } : null;
3476
+ }
3477
+ /**
3478
+ * @internal Which side of `anchor` the selection bubble opens on: above,
3479
+ * unless a bubble `height` px tall, `gap` px from the anchor, would cross the
3480
+ * boundary's top edge (the window, or a capped scroller's edge).
3481
+ *
3482
+ * CDK's own fallback flips only on the window's edges; a capped viewport's top
3483
+ * edge is not one it knows about, so the preference is decided here and CDK
3484
+ * keeps the other side as its fallback.
3485
+ */
3486
+ function mlvEditorBubblePlacement(anchor, boundary, height, gap) {
3487
+ return anchor.top - gap - height >= boundary.top ? 'above' : 'below';
3488
+ }
3489
+
3490
+ /** @internal Gap between the selection and the bubble, in px. */
3491
+ const MLV_EDITOR_BUBBLE_GAP = 8;
3492
+ /** @internal Closest the bubble comes to a window edge, in px. */
3493
+ const MLV_EDITOR_BUBBLE_VIEWPORT_MARGIN = 8;
3494
+ /** @internal Pane class; the bubble's BEM block. */
3495
+ const MLV_EDITOR_BUBBLE_CLASS = 'mlv-editor-bubble';
3496
+ /** @internal Pane modifier while the bubble is off screen. */
3497
+ const MLV_EDITOR_BUBBLE_HIDDEN_CLASS = 'mlv-editor-bubble--hidden';
3498
+ /** @internal Pane modifier while CDK resolved the below position. */
3499
+ const MLV_EDITOR_BUBBLE_BELOW_CLASS = 'mlv-editor-bubble--below';
3500
+ /** @internal Centred above the selection. */
3501
+ const MLV_EDITOR_BUBBLE_ABOVE = {
3502
+ originX: 'center',
3503
+ originY: 'top',
3504
+ overlayX: 'center',
3505
+ overlayY: 'bottom',
3506
+ offsetY: -MLV_EDITOR_BUBBLE_GAP,
3507
+ };
3508
+ /** @internal Centred below the selection. */
3509
+ const MLV_EDITOR_BUBBLE_BELOW = {
3510
+ originX: 'center',
3511
+ originY: 'bottom',
3512
+ overlayX: 'center',
3513
+ overlayY: 'top',
3514
+ offsetY: MLV_EDITOR_BUBBLE_GAP,
3515
+ };
3516
+ /** @internal Above first; CDK falls back below on a window edge. */
3517
+ const MLV_EDITOR_BUBBLE_ABOVE_FIRST = [
3518
+ MLV_EDITOR_BUBBLE_ABOVE,
3519
+ MLV_EDITOR_BUBBLE_BELOW,
3520
+ ];
3521
+ /** @internal Below first, once above would cross the boundary. */
3522
+ const MLV_EDITOR_BUBBLE_BELOW_FIRST = [
3523
+ MLV_EDITOR_BUBBLE_BELOW,
3524
+ MLV_EDITOR_BUBBLE_ABOVE,
3525
+ ];
3526
+ /** @internal Elements a press may focus; a press anywhere else keeps the caret. */
3527
+ const MLV_EDITOR_BUBBLE_FOCUSABLE = 'button, a[href], input, select, textarea, [tabindex], [contenteditable="true"]';
3528
+ /**
3529
+ * @internal Renders its template in a CDK overlay that floats over the
3530
+ * nearest `mlv-editor`'s selection: `toolbarAppearance="floating"`.
3531
+ *
3532
+ * The contents are whatever the template holds, so the same host can carry a
3533
+ * different set of controls later; `MlvEditor` stamps its toolbar template.
3534
+ * The overlay is created once and kept attached, hidden by a pane modifier,
3535
+ * so the controls keep their state and their narrow-mode measurement between
3536
+ * appearances instead of being re-created per selection. The one exception is
3537
+ * a `readonly` flip: `MlvEditor` stamps no toolbar into the template while
3538
+ * `readonly` (#498), so the pane is empty then and the controls are
3539
+ * re-created on the way back.
3540
+ *
3541
+ * Shown only while all of these hold: focus is in the editor composite, the
3542
+ * selection is non-empty (or Alt+F10 summoned it at the caret), no pointer is
3543
+ * still selecting, no drag or IME composition is in progress, the editor is
3544
+ * enabled and not `readonly`, and the selection is visible inside the window
3545
+ * and, for a capped editor, inside its scrolling viewport. It prefers above
3546
+ * the selection and flips below only when above would cross that boundary's
3547
+ * top edge. Turning `readonly` on while it is shown hides it and closes every
3548
+ * popup the editor owns; focus inside either returns to the content.
3549
+ *
3550
+ * Keyboard: Alt+F10 in the content shows it and moves focus to its first
3551
+ * control through the toolbar's roving registry; Escape dismisses it and
3552
+ * returns focus to the content with the selection intact; Tab leaves it back
3553
+ * into the content rather than for the end of the document, where the portal
3554
+ * sits. Appearing never moves focus.
3555
+ *
3556
+ * Escape and Alt+F10 in the content go through a ProseMirror plugin, not a DOM
3557
+ * listener: ProseMirror cancels every Escape it sees, so a listener behind it
3558
+ * would find each one already `defaultPrevented`, and one in front of it
3559
+ * would take Escape from a content popup. The plugin offers both keys to
3560
+ * every plugin registered after it and acts only when none claims them, so a
3561
+ * consumer's `@tiptap/suggestion` list or an AI stream still gets Escape
3562
+ * first. A dismissing Escape stops propagating, so a dialog or drawer around
3563
+ * the editor stays open; the next one reaches it. While `readonly` the bubble
3564
+ * claims neither key.
3565
+ */
3566
+ class MlvEditorBubble {
3567
+ /** The editor's content viewport: the listener scope and the capped boundary. */
3568
+ viewport = input.required(/* @ts-ignore */
3569
+ ...(ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []));
3570
+ /** Whether the viewport is the editor's scroll container, bounding where the selection counts as visible. */
3571
+ capped = input(false, { ...(ngDevMode ? { debugName: "capped" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
3572
+ /** @private Template rendered into the overlay pane. */
3573
+ _templateRef = inject(TemplateRef);
3574
+ /** @private Container the template view is created in, so it keeps the editor's injector. */
3575
+ _viewContainerRef = inject(ViewContainerRef);
3576
+ /** @private CDK overlay factory. */
3577
+ _overlay = inject(Overlay);
3578
+ /** @private Injected document; never the ambient global. */
3579
+ _document = inject(DOCUMENT);
3580
+ /** @private Directive lifetime; owns every listener below. */
3581
+ _destroyRef = inject(DestroyRef);
3582
+ /** @private Editor state: instance, focus, disabled and zoom. */
3583
+ _context = inject(MLV_EDITOR_TOOLBAR_CONTEXT);
3584
+ /** @private Bumped after every Tiptap transaction or selection update. */
3585
+ _revision = inject(MLV_EDITOR_TOOLBAR_REVISION, {
3586
+ optional: true,
3587
+ });
3588
+ /** @private The toolbar's roving focus; Alt+F10 enters through it. */
3589
+ _roving = inject(MLV_EDITOR_TOOLBAR_ROVING);
3590
+ /** @private Makes the pane part of the editor's composite focus while shown. */
3591
+ _overlays = inject(MLV_EDITOR_OVERLAY_REGISTRY);
3592
+ /** @private Scoped direction for the pane. */
3593
+ _rtl = inject(MlvRtlService);
3594
+ /** @private Pane size changes reposition the bubble. */
3595
+ _resizeObserver = inject(MlvResizeObserverService);
3596
+ /** @private Invalidates the render effect from DOM events. */
3597
+ _tick = signal(0, /* @ts-ignore */
3598
+ ...(ngDevMode ? [{ debugName: "_tick" }] : /* istanbul ignore next */ []));
3599
+ /** @private Identifies this instance's content-key plugin in the editor state. */
3600
+ _keyPluginKey = new PluginKey('mlvEditorBubbleKeys');
3601
+ /** @private The overlay, created after the first render in the browser. */
3602
+ _overlayRef = null;
3603
+ /** @private The overlay's position strategy, re-pointed at the selection. */
3604
+ _strategy = null;
3605
+ /** @private The position list last handed to the strategy. */
3606
+ _positions = null;
3607
+ /** @private Whether the bubble is on screen. */
3608
+ _shown = false;
3609
+ /** @private Releases the pane's composite-focus registration. */
3610
+ _unregister = null;
3611
+ /** @private Alt+F10 showed it at a bare caret; cleared when focus returns to the content. */
3612
+ _summoned = false;
3613
+ /** @private Escape dismissed it for this selection; any other selection clears it. */
3614
+ _dismissed = null;
3615
+ /** @private A primary pointer is down in the content, still selecting. */
3616
+ _pointerSelecting = false;
3617
+ /** @private A pointer is down on the bubble; covers browsers that blur on a button press. */
3618
+ _pressing = false;
3619
+ /** @private A native drag (block handle or text) is in progress. */
3620
+ _dragging = false;
3621
+ /** @private An IME composition is in progress. */
3622
+ _composing = false;
3623
+ /** @private Per-gesture listeners, replaced by the next gesture of the same kind. */
3624
+ _gestures = { select: null, press: null, drag: null };
3625
+ constructor() {
3626
+ if (!isPlatformBrowser(inject(PLATFORM_ID)))
3627
+ return;
3628
+ afterNextRender(() => this._create());
3629
+ // Registered once per editor instance and removed with this directive,
3630
+ // e.g. when `toolbarAppearance` returns to `'bar'`: left behind, it would
3631
+ // keep claiming Escape and Alt+F10 for a bubble that no longer exists.
3632
+ effect((onCleanup) => {
3633
+ const editor = this._context.editor();
3634
+ if (!editor || editor.isDestroyed)
3635
+ return;
3636
+ untracked(() => editor.registerPlugin(this._createKeyPlugin()));
3637
+ onCleanup(() => {
3638
+ if (!editor.isDestroyed)
3639
+ editor.unregisterPlugin(this._keyPluginKey);
3640
+ });
3641
+ });
3642
+ // Before the render effect below would hide the bubble, so focus in a
3643
+ // popup moves to the content while that popup still exists.
3644
+ effect(() => {
3645
+ if (!this._context.readonly())
3646
+ return;
3647
+ untracked(() => this._closeForReadonly());
3648
+ });
3649
+ afterRenderEffect(() => {
3650
+ this._tick();
3651
+ this._context.editor();
3652
+ this._context.disabled();
3653
+ this._context.readonly();
3654
+ this._context.focused();
3655
+ this._context.zoom();
3656
+ this._revision?.();
3657
+ this.capped();
3658
+ untracked(() => this._sync());
3659
+ });
3660
+ this._destroyRef.onDestroy(() => {
3661
+ for (const gesture of Object.values(this._gestures)) {
3662
+ gesture?.unsubscribe();
3663
+ }
3664
+ // Nothing is on screen once the overlay is gone.
3665
+ this._shown = false;
3666
+ this._unregister?.();
3667
+ this._unregister = null;
3668
+ this._overlayRef?.dispose();
3669
+ this._overlayRef = null;
3670
+ });
3671
+ }
3672
+ /** @private Creates the overlay and binds every listener, once. */
3673
+ _create() {
3674
+ const viewport = this.viewport();
3675
+ const direction = this._rtl.resolveDirection(viewport);
3676
+ const strategy = this._overlay
3677
+ .position()
3678
+ .flexibleConnectedTo({ x: 0, y: 0 })
3679
+ .withPositions(mlvMirrorInlineOffsets(MLV_EDITOR_BUBBLE_ABOVE_FIRST, direction))
3680
+ .withFlexibleDimensions(false)
3681
+ .withPush(true)
3682
+ .withViewportMargin(MLV_EDITOR_BUBBLE_VIEWPORT_MARGIN);
3683
+ const ref = this._overlay.create({
3684
+ positionStrategy: strategy,
3685
+ // Repositioned by `_sync()` from a document-level scroll listener, which
3686
+ // also sees the capped viewport's own scrolling; CDK's reposition
3687
+ // strategy only knows registered `cdkScrollable`s.
3688
+ scrollStrategy: this._overlay.scrollStrategies.noop(),
3689
+ direction,
3690
+ panelClass: [MLV_EDITOR_BUBBLE_CLASS, MLV_EDITOR_BUBBLE_HIDDEN_CLASS],
3691
+ hasBackdrop: false,
3692
+ });
3693
+ ref.attach(new TemplatePortal(this._templateRef, this._viewContainerRef));
3694
+ this._overlayRef = ref;
3695
+ this._strategy = strategy;
3696
+ this._positions = MLV_EDITOR_BUBBLE_ABOVE_FIRST;
3697
+ const pane = ref.overlayElement;
3698
+ strategy.positionChanges
3699
+ .pipe(takeUntilDestroyed(this._destroyRef))
3700
+ .subscribe((change) => pane.classList.toggle(MLV_EDITOR_BUBBLE_BELOW_CLASS, change.connectionPair.overlayY === 'top'));
3701
+ // Nothing re-parents an open pane, so a `[dir]` flip above the editor
3702
+ // re-mirrors it here.
3703
+ this._destroyRef.onDestroy(this._rtl.watchDirection(viewport, (next) => {
3704
+ ref.setDirection(next);
3705
+ this._positions = null;
3706
+ this._invalidate();
3707
+ }));
3708
+ this._resizeObserver
3709
+ .observe(pane)
3710
+ .pipe(takeUntilDestroyed(this._destroyRef))
3711
+ .subscribe(() => this._invalidate());
3712
+ this._bindViewportListeners(viewport);
3713
+ this._bindPaneListeners(pane);
3714
+ const window = this._document.defaultView;
3715
+ // Passive and capture: every scroll container's `scroll` reaches the
3716
+ // document in the capture phase, the capped viewport's included.
3717
+ fromEvent(this._document, 'scroll', { capture: true, passive: true })
3718
+ .pipe(takeUntilDestroyed(this._destroyRef))
3719
+ .subscribe(() => this._invalidateOnLayout());
3720
+ if (window) {
3721
+ fromEvent(window, 'resize', { passive: true })
3722
+ .pipe(takeUntilDestroyed(this._destroyRef))
3723
+ .subscribe(() => this._invalidateOnLayout());
3724
+ }
3725
+ this._sync();
3726
+ }
3727
+ /** @private Content-side gestures and keys. */
3728
+ _bindViewportListeners(viewport) {
3729
+ // Fallback behind the key plugin for ProseMirror's composition window: it
3730
+ // skips every key handler while it still counts as composing, and in
3731
+ // Safari for 500ms after `compositionend`, by when the bubble may already
3732
+ // be back. It cancels every key it does handle, so only a key it never
3733
+ // processed arrives here uncancelled. (A read-only view also skips them,
3734
+ // but the bubble claims no key there.)
3735
+ fromEvent(viewport, 'keydown')
3736
+ .pipe(takeUntilDestroyed(this._destroyRef))
3737
+ .subscribe((event) => {
3738
+ if (event.defaultPrevented)
3739
+ return;
3740
+ if (this._onContentKey(event))
3741
+ event.preventDefault();
3742
+ });
3743
+ fromEvent(viewport, 'focusin')
3744
+ .pipe(takeUntilDestroyed(this._destroyRef))
3745
+ .subscribe((event) => {
3746
+ if (!this._inContent(event.target))
3747
+ return;
3748
+ this._summoned = false;
3749
+ this._invalidate();
3750
+ });
3751
+ fromEvent(viewport, 'pointerdown')
3752
+ .pipe(takeUntilDestroyed(this._destroyRef))
3753
+ .subscribe((event) => {
3754
+ if (event.button !== 0 ||
3755
+ event.isPrimary === false ||
3756
+ !this._inContent(event.target)) {
3757
+ return;
3758
+ }
3759
+ this._pointerSelecting = true;
3760
+ this._summoned = false;
3761
+ this._invalidate();
3762
+ this._gesture('select', merge(fromEvent(this._document, 'pointerup', { capture: true }), fromEvent(this._document, 'pointercancel', { capture: true })).pipe(take(1)), () => {
3763
+ this._pointerSelecting = false;
3764
+ this._invalidate();
3765
+ });
3766
+ });
3767
+ fromEvent(viewport, 'compositionstart')
3768
+ .pipe(takeUntilDestroyed(this._destroyRef))
3769
+ .subscribe(() => {
3770
+ this._composing = true;
3771
+ this._invalidate();
3772
+ });
3773
+ fromEvent(viewport, 'compositionend')
3774
+ .pipe(takeUntilDestroyed(this._destroyRef))
3775
+ .subscribe(() => {
3776
+ this._composing = false;
3777
+ this._invalidate();
3778
+ });
3779
+ // Capture, so a drag source that stops `dragstart` still counts.
3780
+ fromEvent(viewport, 'dragstart', { capture: true })
3781
+ .pipe(takeUntilDestroyed(this._destroyRef))
3782
+ .subscribe(() => {
3783
+ this._dragging = true;
3784
+ this._invalidate();
3785
+ // `dragend` never reaches the document when the source left the DOM
3786
+ // mid-drag; the next press ends the wait instead.
3787
+ this._gesture('drag', merge(fromEvent(this._document, 'dragend', { capture: true }), fromEvent(this._document, 'drop', { capture: true }), fromEvent(this._document, 'pointerdown', { capture: true })).pipe(take(1)), () => {
3788
+ this._dragging = false;
3789
+ this._invalidate();
3790
+ });
3791
+ });
3792
+ }
3793
+ /** @private Bubble-side keys and presses. */
3794
+ _bindPaneListeners(pane) {
3795
+ fromEvent(pane, 'keydown')
3796
+ .pipe(takeUntilDestroyed(this._destroyRef))
3797
+ .subscribe((event) => {
3798
+ if (event.defaultPrevented)
3799
+ return;
3800
+ if (event.key === 'Escape') {
3801
+ event.preventDefault();
3802
+ // Kept from the page and any dialog around the editor: this Escape
3803
+ // belongs to the bubble.
3804
+ event.stopPropagation();
3805
+ this._dismiss();
3806
+ }
3807
+ else if (event.key === 'Tab') {
3808
+ // The pane is portaled to the end of the document; the next tab
3809
+ // stop after it is not the editor's.
3810
+ event.preventDefault();
3811
+ this._context.editor()?.view.focus();
3812
+ }
3813
+ });
3814
+ fromEvent(pane, 'pointerdown', { capture: true })
3815
+ .pipe(takeUntilDestroyed(this._destroyRef))
3816
+ .subscribe(() => {
3817
+ this._pressing = true;
3818
+ // Released a task after `pointerup`, so the `click` that follows
3819
+ // still finds the bubble on screen.
3820
+ this._gesture('press', merge(fromEvent(this._document, 'pointerup', { capture: true }), fromEvent(this._document, 'pointercancel', { capture: true })).pipe(take(1), delay(0)), () => {
3821
+ this._pressing = false;
3822
+ this._invalidate();
3823
+ });
3824
+ });
3825
+ // A press on the bubble's own padding keeps the caret and the selection
3826
+ // in the content; controls still take focus as usual.
3827
+ fromEvent(pane, 'mousedown')
3828
+ .pipe(takeUntilDestroyed(this._destroyRef))
3829
+ .subscribe((event) => {
3830
+ const target = event.target;
3831
+ if (target instanceof Element &&
3832
+ target.closest(MLV_EDITOR_BUBBLE_FOCUSABLE)) {
3833
+ return;
3834
+ }
3835
+ event.preventDefault();
3836
+ });
3837
+ }
3838
+ /**
3839
+ * @private The plugin carrying content-side Escape and Alt+F10. The loop is
3840
+ * what makes it the lowest-priority key handler, not its place in the list:
3841
+ * plugins before it run first anyway, and it offers each of those keys to
3842
+ * every plugin after it (an AI stream session registers its Escape claim
3843
+ * later) before acting. Once it has offered the key it acts and returns
3844
+ * `true` unconditionally, even if a declining later handler changed the
3845
+ * bubble's state meanwhile; returning `false` there would make ProseMirror
3846
+ * run those handlers a second time. Returning `true` makes ProseMirror
3847
+ * cancel the event.
3848
+ */
3849
+ _createKeyPlugin() {
3850
+ const key = this._keyPluginKey;
3851
+ return new Plugin({
3852
+ key,
3853
+ props: {
3854
+ handleKeyDown: (view, event) => {
3855
+ if (!this._ownsContentKey(event))
3856
+ return false;
3857
+ const plugins = view.state.plugins;
3858
+ const own = key.get(view.state);
3859
+ for (let index = own ? plugins.indexOf(own) + 1 : plugins.length; index < plugins.length; index++) {
3860
+ const plugin = plugins[index];
3861
+ if (plugin.props.handleKeyDown?.call(plugin, view, event)) {
3862
+ return true;
3863
+ }
3864
+ }
3865
+ this._act(event);
3866
+ return true;
3867
+ },
3868
+ },
3869
+ });
3870
+ }
3871
+ /**
3872
+ * @private Whether `event` is a key the bubble acts on right now: never
3873
+ * while disabled or `readonly`, where it cannot show.
3874
+ */
3875
+ _ownsContentKey(event) {
3876
+ if (this._context.disabled() || this._context.readonly())
3877
+ return false;
3878
+ return ((event.key === 'Escape' && this._shown) ||
3879
+ (event.key === 'F10' &&
3880
+ event.altKey &&
3881
+ !event.ctrlKey &&
3882
+ !event.metaKey &&
3883
+ !event.shiftKey));
3884
+ }
3885
+ /**
3886
+ * @private The DOM fallback's entry: acts only on a key the bubble owns.
3887
+ * Returns whether it acted; the caller cancels the event.
3888
+ */
3889
+ _onContentKey(event) {
3890
+ if (!this._ownsContentKey(event))
3891
+ return false;
3892
+ this._act(event);
3893
+ return true;
3894
+ }
3895
+ /**
3896
+ * @private Alt+F10 summons; Escape dismisses and stops there, so a dialog or
3897
+ * drawer around the editor does not close with it. Unchecked: callers
3898
+ * establish that the bubble owns the key.
3899
+ */
3900
+ _act(event) {
3901
+ if (event.key === 'Escape') {
3902
+ event.stopPropagation();
3903
+ this._dismiss();
3904
+ }
3905
+ else {
3906
+ this._summon();
3907
+ }
3908
+ }
3909
+ /** @private Shows the bubble at the caret or selection and focuses its first control. */
3910
+ _summon() {
3911
+ const ref = this._overlayRef;
3912
+ if (!ref || this._context.disabled() || !this._context.editor())
3913
+ return;
3914
+ this._summoned = true;
3915
+ this._dismissed = null;
3916
+ this._sync();
3917
+ if (!this._shown) {
3918
+ this._summoned = false;
3919
+ return;
3920
+ }
3921
+ this._roving.focusBoundary('start');
3922
+ if (!ref.overlayElement.contains(this._document.activeElement)) {
3923
+ this._summoned = false;
3924
+ this._sync();
3925
+ }
3926
+ }
3927
+ /** @private Hides the bubble for the current selection; focus returns to the content. */
3928
+ _dismiss() {
3929
+ this._dismissed = this._context.editor()?.state.selection ?? null;
3930
+ this._summoned = false;
3931
+ this._sync();
3932
+ }
3933
+ /** @private Shows, repositions or hides the bubble for the current state. */
3934
+ _sync() {
3935
+ const ref = this._overlayRef;
3936
+ const editor = this._context.editor();
3937
+ if (!ref || !editor || editor.isDestroyed || !this._wanted(editor)) {
3938
+ this._hide();
3939
+ return;
3940
+ }
3941
+ const windowRect = this._windowRect();
3942
+ const boundary = this.capped()
3943
+ ? mlvEditorVisibleRect(this.viewport().getBoundingClientRect(), windowRect)
3944
+ : windowRect;
3945
+ const selection = this._selectionRect(editor);
3946
+ const visible = boundary ? mlvEditorVisibleRect(selection, boundary) : null;
3947
+ // Focus in the bubble (or a popup it opened) keeps it on screen when the
3948
+ // selection scrolls away, pushed to the window edge; hiding it would drop
3949
+ // that focus.
3950
+ const pinned = this._summoned || !editor.view.hasFocus();
3951
+ const anchor = visible ?? (pinned ? selection : null);
3952
+ if (!anchor) {
3953
+ this._hide();
3954
+ return;
3955
+ }
3956
+ this._show(ref, anchor, boundary ?? windowRect);
3957
+ }
3958
+ /** @private Whether every visibility condition except the anchor holds. */
3959
+ _wanted(editor) {
3960
+ if (this._context.disabled() || this._context.readonly())
3961
+ return false;
3962
+ if (!this._context.focused() && !this._pressing)
3963
+ return false;
3964
+ if (this._pointerSelecting || this._dragging || this._composing) {
3965
+ return false;
3966
+ }
3967
+ const selection = editor.state.selection;
3968
+ if (this._dismissed) {
3969
+ if (selection.eq(this._dismissed))
3970
+ return false;
3971
+ this._dismissed = null;
3972
+ }
3973
+ return this._summoned || !selection.empty;
3974
+ }
3975
+ /** @private Places and reveals the bubble at `anchor`. */
3976
+ _show(ref, anchor, boundary) {
3977
+ const strategy = this._strategy;
3978
+ if (!strategy)
3979
+ return;
3980
+ const pane = ref.overlayElement;
3981
+ const positions = mlvEditorBubblePlacement(anchor, boundary, pane.getBoundingClientRect().height, MLV_EDITOR_BUBBLE_GAP) === 'above'
3982
+ ? MLV_EDITOR_BUBBLE_ABOVE_FIRST
3983
+ : MLV_EDITOR_BUBBLE_BELOW_FIRST;
3984
+ if (positions !== this._positions) {
3985
+ this._positions = positions;
3986
+ strategy.withPositions(mlvMirrorInlineOffsets(positions, ref.getDirection()));
3987
+ }
3988
+ strategy.setOrigin({
3989
+ x: anchor.left,
3990
+ y: anchor.top,
3991
+ width: anchor.right - anchor.left,
3992
+ height: anchor.bottom - anchor.top,
3993
+ });
3994
+ if (!this._shown) {
3995
+ this._shown = true;
3996
+ pane.classList.remove(MLV_EDITOR_BUBBLE_HIDDEN_CLASS);
3997
+ this._unregister = this._overlays.register(pane, () => {
3998
+ // The registry drops the entry itself after calling this.
3999
+ this._unregister = null;
4000
+ this._hide();
4001
+ });
4002
+ }
4003
+ ref.updatePosition();
4004
+ }
4005
+ /** @private Takes the bubble off screen, handing focus back to the content first. */
4006
+ _hide() {
4007
+ const ref = this._overlayRef;
4008
+ if (!ref || !this._shown)
4009
+ return;
4010
+ this._shown = false;
4011
+ const pane = ref.overlayElement;
4012
+ if (pane.contains(this._document.activeElement))
4013
+ this._focusContent();
4014
+ pane.classList.add(MLV_EDITOR_BUBBLE_HIDDEN_CLASS);
4015
+ this._unregister?.();
4016
+ this._unregister = null;
4017
+ }
4018
+ /**
4019
+ * @private `readonly` just turned on. While the bubble is shown, every popup
4020
+ * the editor owns closes (their controls no longer apply, and the ones the
4021
+ * bubble opened would float beside a hidden pane), and focus in the bubble
4022
+ * or in such a popup returns to the content before either goes away. The
4023
+ * docked bar goes through the same registry call (#498).
4024
+ */
4025
+ _closeForReadonly() {
4026
+ const ref = this._overlayRef;
4027
+ const editor = this._context.editor();
4028
+ if (!ref || !this._shown || !editor || editor.isDestroyed)
4029
+ return;
4030
+ this._overlays.closeForReadonly(ref.overlayElement, editor.view.dom, () => this._focusContent());
4031
+ this._sync();
4032
+ }
4033
+ /** @private Focuses the content with its selection, unless disabled. */
4034
+ _focusContent() {
4035
+ const editor = this._context.editor();
4036
+ if (!editor || editor.isDestroyed || this._context.disabled())
4037
+ return;
4038
+ mlvEditorFocusContent(editor);
4039
+ }
4040
+ /** @private The selection's client rect; a caret is zero-width. */
4041
+ _selectionRect(editor) {
4042
+ const { from, to } = editor.state.selection;
4043
+ try {
4044
+ const { top, bottom, left, right } = posToDOMRect(editor.view, from, to);
4045
+ return { top, bottom, left, right };
4046
+ }
4047
+ catch {
4048
+ // `coordsAtPos` throws where nothing is laid out; fall back to the
4049
+ // content's top edge.
4050
+ const rect = editor.view.dom.getBoundingClientRect();
4051
+ return {
4052
+ top: rect.top,
4053
+ bottom: rect.top,
4054
+ left: rect.left,
4055
+ right: rect.right,
4056
+ };
4057
+ }
4058
+ }
4059
+ /** @private The window's client rect. */
4060
+ _windowRect() {
4061
+ const window = this._document.defaultView;
4062
+ return {
4063
+ top: 0,
4064
+ left: 0,
4065
+ bottom: window?.innerHeight ?? 0,
4066
+ right: window?.innerWidth ?? 0,
4067
+ };
4068
+ }
4069
+ /** @private Whether `target` is inside the ProseMirror content. */
4070
+ _inContent(target) {
4071
+ const dom = this._context.editor()?.view.dom;
4072
+ return !!dom && target instanceof Node && dom.contains(target);
4073
+ }
4074
+ /**
4075
+ * @private Runs `done` on the first emission of `end`, replacing any wait
4076
+ * of the same kind still open. Per gesture, so released here and at
4077
+ * destroy rather than by `takeUntilDestroyed`, which would keep every
4078
+ * earlier generation alive.
4079
+ */
4080
+ _gesture(kind, end, done) {
4081
+ this._gestures[kind]?.unsubscribe();
4082
+ this._gestures[kind] = end.subscribe(() => {
4083
+ this._gestures[kind] = null;
4084
+ done();
4085
+ });
4086
+ }
4087
+ /** @private Schedules `_sync()` for the next render. */
4088
+ _invalidate() {
4089
+ this._tick.update((tick) => tick + 1);
4090
+ }
4091
+ /**
4092
+ * @private Scroll and resize only matter while the bubble is shown or could
4093
+ * show: summoned, or a non-empty selection that may scroll back into view.
4094
+ * A focused bare caret, and a disabled or `readonly` editor, skip the render
4095
+ * these events would schedule.
4096
+ */
4097
+ _invalidateOnLayout() {
4098
+ if (this._shown) {
4099
+ this._invalidate();
4100
+ return;
4101
+ }
4102
+ if (!untracked(this._context.focused) ||
4103
+ untracked(this._context.disabled) ||
4104
+ untracked(this._context.readonly)) {
4105
+ return;
4106
+ }
4107
+ const editor = untracked(this._context.editor);
4108
+ if (this._summoned ||
4109
+ (editor && !editor.isDestroyed && !editor.state.selection.empty)) {
4110
+ this._invalidate();
4111
+ }
4112
+ }
4113
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorBubble, deps: [], target: i0.ɵɵFactoryTarget.Directive });
4114
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: MlvEditorBubble, isStandalone: true, selector: "ng-template[mlvEditorBubble]", inputs: { viewport: { classPropertyName: "viewport", publicName: "viewport", isSignal: true, isRequired: true, transformFunction: null }, capped: { classPropertyName: "capped", publicName: "capped", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
4115
+ }
4116
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorBubble, decorators: [{
4117
+ type: Directive,
4118
+ args: [{
4119
+ selector: 'ng-template[mlvEditorBubble]',
4120
+ }]
4121
+ }], ctorParameters: () => [], propDecorators: { viewport: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewport", required: true }] }], capped: [{ type: i0.Input, args: [{ isSignal: true, alias: "capped", required: false }] }] } });
4122
+
3241
4123
  /** @internal Class of the floating drop indicator. */
3242
4124
  const MLV_EDITOR_DROP_INDICATOR_CLASS = 'mlv-editor__drop-indicator';
3243
4125
  /** @internal Class of the off-screen wrapper handed to `setDragImage`. */
3244
4126
  const MLV_EDITOR_DRAG_GHOST_CLASS = 'mlv-editor__drag-ghost';
3245
4127
  /**
3246
- * @internal Marks the editor DOM while a block drag is in flight, so the
3247
- * stylesheet — not this file — owns the partition's transition and its
3248
- * reduced-motion fallback. JavaScript writes only the `transform` itself.
4128
+ * @internal Marks the block currently on the cursor, dimmed in place. Applied
4129
+ * as a node decoration, never written onto the element — see
4130
+ * `MLV_EDITOR_DRAG_SOURCE_KEY`.
3249
4131
  */
3250
- const MLV_EDITOR_DRAGGING_CLASS = 'ProseMirror--block-dragging';
3251
- /** @internal Marks the block currently on the cursor, dimmed in place. */
3252
4132
  const MLV_EDITOR_DRAG_SOURCE_CLASS = 'mlv-editor__block--dragging';
4133
+ /**
4134
+ * @internal Plugin state holding the dimmed source's node decoration: empty
4135
+ * while no block drag is in flight, one `Decoration.node` while one is.
4136
+ *
4137
+ * The dim has to travel through ProseMirror's own render path because nothing
4138
+ * written directly onto a block's element survives a drag. ProseMirror's
4139
+ * `DOMObserver` treats an attribute mutation on any node it rendered — a class,
4140
+ * an inline style — as an external DOM change, marks that node dirty and
4141
+ * redraws it, **replacing** the element. Measured in Chromium, 22 of 25
4142
+ * top-level elements were replaced by the first `dragover`: the dim class and
4143
+ * the old partition's transforms all ended up on detached nodes, so none of it
4144
+ * was ever visible. A decoration is part of the view description instead, so a
4145
+ * redraw re-applies it rather than discarding it.
4146
+ */
4147
+ const MLV_EDITOR_DRAG_SOURCE_KEY = new PluginKey('mlvEditorBlockDragSource');
4148
+ /**
4149
+ * @internal Clears a source dim a freshly built plugin view inherited rather
4150
+ * than published.
4151
+ *
4152
+ * Plugin state outlives the plugin view. `EditorState.reconfigure` — what
4153
+ * Tiptap's `registerPlugin` / `unregisterPlugin` call, e.g. when an AI stream
4154
+ * or suggestion session settles — keeps every state field but destroys and
4155
+ * rebuilds every plugin view. A reconfigure mid-drag therefore takes the view
4156
+ * that owned the drag, and its `dragend` listener, while the dim stays in
4157
+ * state; the replacing view never saw the drag, so without this the block
4158
+ * would stay dimmed until the next document change.
4159
+ *
4160
+ * Deferred to a microtask because this runs from the view factory, inside
4161
+ * ProseMirror's `updatePluginViews`, where a dispatch would re-enter
4162
+ * `updateState` while the views are still being built. The old view's
4163
+ * `destroy` cannot clear it either: it runs inside that same update, or inside
4164
+ * `EditorView.destroy()` before `isDestroyed` turns true. Every other teardown
4165
+ * discards the state (a destroyed editor) or the field (the plugin removed)
4166
+ * along with the dim. The clear only drops the exact set inherited, so it can
4167
+ * never undo a newer drag's dim.
4168
+ */
4169
+ function clearInheritedDragSource(view) {
4170
+ const inherited = MLV_EDITOR_DRAG_SOURCE_KEY.getState(view.state);
4171
+ if (inherited === undefined || inherited === DecorationSet.empty)
4172
+ return;
4173
+ queueMicrotask(() => {
4174
+ if (view.isDestroyed)
4175
+ return;
4176
+ if (MLV_EDITOR_DRAG_SOURCE_KEY.getState(view.state) !== inherited)
4177
+ return;
4178
+ view.dispatch(view.state.tr.setMeta(MLV_EDITOR_DRAG_SOURCE_KEY, DecorationSet.empty));
4179
+ });
4180
+ }
3253
4181
  /** @internal Duration of the post-drop settle, matching `--mlv-duration-normal`. */
3254
4182
  const MLV_EDITOR_SETTLE_DURATION = 200;
3255
4183
  /** @internal Sub-pixel movement not worth animating after a drop. */
@@ -3470,19 +4398,20 @@ function dropTargetIndex(hit, clientY, from) {
3470
4398
  /**
3471
4399
  * @internal Measures every top-level block once, in the mount's unscaled space.
3472
4400
  *
3473
- * Taken at `dragstart` and read by everything downstream, which is a
3474
- * correctness requirement rather than an optimisation. The partition applies
3475
- * `transform` to blocks, and a transform *is* reflected in
3476
- * `getBoundingClientRect()`. Resolving the target from live rects after moving
3477
- * them would feed this plugin's own output back into its input: the gap opens,
3478
- * the pointer is now over a different block, the target changes, the gap moves.
3479
- * A snapshot taken before any transform breaks that loop.
4401
+ * Taken at `dragstart` and read by everything downstream — target resolution,
4402
+ * the indicator's `top` and the settle's "before" — so no pointer event during
4403
+ * the drag reads a block's rect. Mount-relative rather than viewport-relative
4404
+ * so drag-autoscroll cannot invalidate it: only the mount's own rect is re-read
4405
+ * per event, one layout read instead of one per block. Unscaled for the same
4406
+ * reason the handle's `top` is: a CSS length on a child of the zoom layer
4407
+ * applies in that layer's space, while `getBoundingClientRect()` reports scaled
4408
+ * screen pixels.
3480
4409
  *
3481
- * Mount-relative rather than viewport-relative so drag-autoscroll cannot
3482
- * invalidate it — only the mount's own rect is re-read per event, one layout
3483
- * read instead of one per block. Unscaled for the same reason the handle's
3484
- * `top` is: a CSS length on a child of the zoom layer applies in that layer's
3485
- * space, while `getBoundingClientRect()` reports scaled screen pixels.
4410
+ * Boxes only, deliberately **no element**. ProseMirror replaces top-level
4411
+ * elements during a drag (see `MLV_EDITOR_DRAG_SOURCE_KEY`), so an element
4412
+ * captured here is detached by the first `dragover`, and anything written to it
4413
+ * afterwards is written to nothing. Everything after `dragstart` that needs a
4414
+ * live element resolves it from a document position instead.
3486
4415
  */
3487
4416
  function snapshotBlocks(view, mount) {
3488
4417
  const { top: mountTop, scale } = mountFrame(mount);
@@ -3491,10 +4420,10 @@ function snapshotBlocks(view, mount) {
3491
4420
  for (let i = 0; i < children.length; i += 1) {
3492
4421
  const element = children[i];
3493
4422
  // Same category check the hover path uses: a widget decoration owns no
3494
- // top-level node and must not occupy a slot, or the partition would shift
3495
- // it as though it were a block. Mapping every child once here is what the
4423
+ // top-level node and must not occupy a slot, or its sliver of a box would
4424
+ // decide a neighbour's drop side. Mapping every child once here is what the
3496
4425
  // drag pays instead of the hover path's deferred single mapping — the
3497
- // partition and the settle need a slot per block regardless.
4426
+ // settle needs a box per block regardless.
3498
4427
  const index = topLevelIndexOfDom(view, element);
3499
4428
  if (index === null)
3500
4429
  continue;
@@ -3503,7 +4432,6 @@ function snapshotBlocks(view, mount) {
3503
4432
  index,
3504
4433
  top: (rect.top - mountTop) / scale,
3505
4434
  bottom: (rect.bottom - mountTop) / scale,
3506
- element: element,
3507
4435
  });
3508
4436
  }
3509
4437
  return slots;
@@ -3541,9 +4469,12 @@ function slotAt(slots, y) {
3541
4469
  * @internal Mount-space y of the boundary the given gap sits on, where `gap`
3542
4470
  * counts the `slots.length + 1` positions a block can be inserted at.
3543
4471
  *
3544
- * Deliberately the *pre-partition* boundary: the indicator is offset into the
3545
- * opened space by half the gap in CSS, so the gap's size stays a single token
3546
- * and this never has to resolve a rem into pixels.
4472
+ * An interior gap is the middle of the **natural** space between two blocks —
4473
+ * the midpoint of the previous block's bottom and the next block's top — since
4474
+ * nothing parts the blocks to preview a drop. The outer two sit on the first
4475
+ * block's top and the last block's bottom. The stylesheet centres the line on
4476
+ * this y (`translateY(-50%)`), so its own thickness never has to be resolved
4477
+ * here.
3547
4478
  */
3548
4479
  function gapOffset(slots, gap) {
3549
4480
  if (gap <= 0)
@@ -3561,9 +4492,9 @@ function gapOffset(slots, gap) {
3561
4492
  * an ancestor class chain that would have to track whichever selectors happen
3562
4493
  * to exist. Adapted from `@tiptap/extension-drag-handle`'s `cloneElement`.
3563
4494
  *
3564
- * Note what this cannot carry: computed styles are used values, and an
3565
- * ancestor `transform: scale()` never reaches them. The editor's zoom is
3566
- * applied to the wrapper instead — see `createGhostElement`.
4495
+ * Note what this cannot carry: computed styles are the element's own values
4496
+ * and never carry an ancestor's `zoom`. The editor's zoom is applied to the
4497
+ * wrapper instead — see `createGhostElement`.
3567
4498
  */
3568
4499
  function cloneWithComputedStyles(source) {
3569
4500
  const clone = source.cloneNode(true);
@@ -3598,7 +4529,8 @@ function cloneWithComputedStyles(source) {
3598
4529
  * requires — without ever being seen. `zoom` rather than `transform` carries
3599
4530
  * the editor's scale because it affects layout, so the wrapper's own box
3600
4531
  * reflects it before the browser rasterizes; a `transform` is honoured
3601
- * inconsistently across engines for drag images.
4532
+ * inconsistently across engines for drag images, and the source's computed
4533
+ * styles carry no ancestor `zoom`.
3602
4534
  */
3603
4535
  function createGhostElement(source, scale) {
3604
4536
  const wrapper = document.createElement('div');
@@ -3744,7 +4676,26 @@ const MlvEditorBlockHandle = Extension.create({
3744
4676
  const editor = this.editor;
3745
4677
  return [
3746
4678
  new Plugin({
4679
+ key: MLV_EDITOR_DRAG_SOURCE_KEY,
4680
+ state: {
4681
+ init: () => DecorationSet.empty,
4682
+ // Set and cleared only through a meta, from the plugin view (and
4683
+ // `clearInheritedDragSource`). A document change also ends the
4684
+ // drag (see `update` below), so the decoration is dropped in that
4685
+ // same transaction rather than mapped onto a block that may no
4686
+ // longer be the one on the pointer. That transaction therefore
4687
+ // never renders a set built against the previous document, and
4688
+ // `update`'s `endDrag` finds nothing left to dispatch.
4689
+ apply: (tr, decorations) => tr.getMeta(MLV_EDITOR_DRAG_SOURCE_KEY) ??
4690
+ (tr.docChanged ? DecorationSet.empty : decorations),
4691
+ },
4692
+ props: {
4693
+ decorations: (state) => MLV_EDITOR_DRAG_SOURCE_KEY.getState(state),
4694
+ },
3747
4695
  view: (view) => {
4696
+ // First, before the mount check: a rebuilt view with no mount
4697
+ // still inherits whatever dim its predecessor left in state.
4698
+ clearInheritedDragSource(view);
3748
4699
  const mount = options.mount();
3749
4700
  if (!mount)
3750
4701
  return { destroy: () => undefined };
@@ -3789,9 +4740,9 @@ const MlvEditorBlockHandle = Extension.create({
3789
4740
  const hit = blockAtPoint(view, event.clientY);
3790
4741
  if (!hit)
3791
4742
  return hide();
3792
- // `getBoundingClientRect()` reports scaled screen pixels, but a
3793
- // CSS `top` on a child of the scaled layer is applied in the
3794
- // layer's own unscaled space. Convert once, off one rect read of
4743
+ // `getBoundingClientRect()` reports zoomed screen pixels, but a
4744
+ // CSS `top` on a child of the zoomed layer is applied in the
4745
+ // layer's own unzoomed space. Convert once, off one rect read of
3795
4746
  // the mount.
3796
4747
  const { top: mountTop, scale } = mountFrame(mount);
3797
4748
  const top = `${(hit.top - mountTop) / scale}px`;
@@ -3811,13 +4762,11 @@ const MlvEditorBlockHandle = Extension.create({
3811
4762
  };
3812
4763
  /** Top-level index being dragged, or null when no drag is active. */
3813
4764
  let source = null;
3814
- /** The dimmed element the drag started from. */
3815
- let sourceElement = null;
3816
4765
  /** Geometry measured once at `dragstart`; empty while no drag runs. */
3817
4766
  let slots = [];
3818
4767
  /** The off-screen element handed to `setDragImage`. */
3819
4768
  let ghost = null;
3820
- /** Gap the partition is currently opened at, or null when closed. */
4769
+ /** Gap the indicator currently marks, or null while it is retracted. */
3821
4770
  let gap = null;
3822
4771
  /**
3823
4772
  * Converts a viewport y into the mount's own unscaled space, off
@@ -3831,45 +4780,61 @@ const MlvEditorBlockHandle = Extension.create({
3831
4780
  * Gap a resolved target index sits on. `dropTargetIndex` folds the
3832
4781
  * `slots.length + 1` gaps onto `moveBlock`'s pre-move convention,
3833
4782
  * where `to` names the block the moved one ends up after when
3834
- * moving down; this is that fold inverted, so the previewed space
4783
+ * moving down; this is that fold inverted, so the previewed line
3835
4784
  * and the committed move come from one resolved index.
3836
4785
  */
3837
4786
  const gapForTarget = (to, from) => to <= from ? to : to + 1;
3838
- /** Opens the partition at `next`, or closes it when null. */
4787
+ /**
4788
+ * Moves the indicator to `next`, or retracts it when null. Only
4789
+ * the mount-owned line is written: nothing parts the blocks, so no
4790
+ * element ProseMirror rendered is touched during a drag.
4791
+ */
3839
4792
  const setGap = (next) => {
3840
4793
  if (gap === next)
3841
4794
  return;
3842
4795
  gap = next;
3843
- for (const slot of slots) {
3844
- // The gap is a token rather than a number here, so the size
3845
- // stays owned by the stylesheet.
3846
- slot.element.style.transform =
3847
- next !== null && slot.index >= next
3848
- ? 'translateY(var(--mlv-editor-drop-gap))'
3849
- : '';
3850
- }
3851
4796
  if (next === null) {
3852
4797
  indicator.setAttribute('data-visible', 'false');
3853
4798
  return;
3854
4799
  }
3855
- // The *pre-partition* boundary: the stylesheet offsets the line
3856
- // into the opened space by half the gap, so this never has to
3857
- // resolve the token into pixels.
3858
4800
  indicator.style.top = `${gapOffset(slots, next)}px`;
3859
4801
  indicator.setAttribute('data-visible', 'true');
3860
4802
  };
4803
+ /**
4804
+ * Publishes the source's dim, or clears it with `null`, through
4805
+ * plugin state. Skipped when nothing would change, so the several
4806
+ * paths that end one drag (`drop`, then `dragend`) dispatch once.
4807
+ */
4808
+ const setSourceDecoration = (from) => {
4809
+ const current = MLV_EDITOR_DRAG_SOURCE_KEY.getState(view.state);
4810
+ if (from === null && current === DecorationSet.empty)
4811
+ return;
4812
+ const node = from === null ? null : view.state.doc.nodeAt(from);
4813
+ const next = from === null || node === null
4814
+ ? DecorationSet.empty
4815
+ : DecorationSet.create(view.state.doc, [
4816
+ Decoration.node(from, from + node.nodeSize, {
4817
+ class: MLV_EDITOR_DRAG_SOURCE_CLASS,
4818
+ }),
4819
+ ]);
4820
+ // A meta-only transaction: no step, so nothing for history to
4821
+ // record (prosemirror-history returns its state unchanged for a
4822
+ // step-less transaction, so no `addToHistory` flag is needed)
4823
+ // and no model emission. `update` below compares documents by
4824
+ // identity, so this does not read as a document change and end
4825
+ // the drag.
4826
+ view.dispatch(view.state.tr.setMeta(MLV_EDITOR_DRAG_SOURCE_KEY, next));
4827
+ };
3861
4828
  const endDrag = () => {
3862
- for (const slot of slots)
3863
- slot.element.style.transform = '';
3864
4829
  indicator.setAttribute('data-visible', 'false');
3865
- view.dom.classList.remove(MLV_EDITOR_DRAGGING_CLASS);
3866
- sourceElement?.classList.remove(MLV_EDITOR_DRAG_SOURCE_CLASS);
3867
4830
  ghost?.remove();
3868
4831
  slots = [];
3869
4832
  gap = null;
3870
4833
  source = null;
3871
- sourceElement = null;
3872
4834
  ghost = null;
4835
+ // Last, once the closure no longer records a drag: the dispatch
4836
+ // re-enters `update`, which must not see one still in flight.
4837
+ setSourceDecoration(null);
3873
4838
  };
3874
4839
  /** Target index for the pointer, or null when the drop is a no-op. */
3875
4840
  const targetAt = (clientY) => {
@@ -3894,11 +4859,15 @@ const MlvEditorBlockHandle = Extension.create({
3894
4859
  *
3895
4860
  * Only the span between the two indices moved, so the walk is
3896
4861
  * bounded by the distance dragged rather than by document length.
4862
+ * Each element is resolved live through `nodeDOM` after the move,
4863
+ * never taken from the snapshot: ProseMirror has replaced most of
4864
+ * the elements measured at `dragstart` by now.
3897
4865
  *
3898
- * Uses the Web Animations API rather than a transition: nothing has
3899
- * to force a reflow between setting and clearing the offset, the
3900
- * animation cleans itself up, and it never collides with the inline
3901
- * `transform` the partition writes.
4866
+ * Uses the Web Animations API rather than a transition or an inline
4867
+ * `transform`: nothing has to force a reflow between setting and
4868
+ * clearing the offset, the animation cleans itself up, and it
4869
+ * writes no attribute — an inline style is a DOM mutation
4870
+ * ProseMirror would answer by redrawing the block mid-animation.
3902
4871
  */
3903
4872
  const settle = (tops, from, to) => {
3904
4873
  if (prefersReducedMotion())
@@ -3945,11 +4914,18 @@ const MlvEditorBlockHandle = Extension.create({
3945
4914
  return;
3946
4915
  }
3947
4916
  const measured = snapshotBlocks(view, mount);
3948
- const slot = measured.find((candidate) => candidate.index === index);
4917
+ const from = index < view.state.doc.childCount
4918
+ ? childStart(view.state.doc, index)
4919
+ : null;
4920
+ // Resolved now, from the document position, and used only in
4921
+ // this handler — see `snapshotBlocks` for why no element is kept.
4922
+ const element = from === null ? null : view.nodeDOM(from);
3949
4923
  // The hovered index outliving its block means the snapshot and
3950
4924
  // `data-index` disagree; refusing is the same answer as a drag
3951
4925
  // that never hovered.
3952
- if (!slot) {
4926
+ if (from === null ||
4927
+ !(element instanceof HTMLElement) ||
4928
+ !measured.some((candidate) => candidate.index === index)) {
3953
4929
  event.preventDefault();
3954
4930
  return;
3955
4931
  }
@@ -3970,13 +4946,17 @@ const MlvEditorBlockHandle = Extension.create({
3970
4946
  // Cloned *before* the source is dimmed: the clone carries
3971
4947
  // resolved computed styles, so dimming first would bake the
3972
4948
  // reduced opacity into the drag image.
3973
- ghost = createGhostElement(slot.element, mountFrame(mount).scale);
4949
+ ghost = createGhostElement(element, mountFrame(mount).scale);
4950
+ // The pointer rides the image's inline-start edge: its right
4951
+ // edge in RTL. The offset is in CSS pixels against the image as
4952
+ // painted, and the wrapper carries the editor zoom as CSS
4953
+ // `zoom`, which `offsetWidth` leaves out (measured 475 against
4954
+ // 594 at 125%): only the rendered width puts the pointer on
4955
+ // that edge.
3974
4956
  event.dataTransfer?.setDragImage(ghost, getComputedStyle(view.dom).direction === 'rtl'
3975
- ? ghost.offsetWidth
4957
+ ? Math.round(ghost.getBoundingClientRect().width)
3976
4958
  : 0, 0);
3977
- sourceElement = slot.element;
3978
- sourceElement.classList.add(MLV_EDITOR_DRAG_SOURCE_CLASS);
3979
- view.dom.classList.add(MLV_EDITOR_DRAGGING_CLASS);
4959
+ setSourceDecoration(from);
3980
4960
  };
3981
4961
  const onDragOver = (event) => {
3982
4962
  if (source === null)
@@ -4014,8 +4994,8 @@ const MlvEditorBlockHandle = Extension.create({
4014
4994
  event.clientY > rect.bottom);
4015
4995
  };
4016
4996
  const onDragLeave = (event) => {
4017
- // Only the preview goes — the partition closes and the line
4018
- // retracts. The drag itself is still in flight, and re-entering
4997
+ // Only the line retracts; the source stays dimmed. The drag
4998
+ // itself is still in flight, and re-entering
4019
4999
  // the editor must resolve a target again rather than leave the
4020
5000
  // user dragging something that can no longer be dropped.
4021
5001
  if (source !== null && leavesMount(event))
@@ -4030,10 +5010,9 @@ const MlvEditorBlockHandle = Extension.create({
4030
5010
  // Resolved from the drop's own coordinates rather than the last
4031
5011
  // `dragover`, so the release position always wins.
4032
5012
  const to = targetAt(event.clientY);
4033
- // Captured before `endDrag` discards the snapshot. These are the
4034
- // pre-partition tops, which is exactly the FLIP's "before": the
4035
- // partition's transforms are cleared by `endDrag` in the same
4036
- // turn, so nothing the user saw shifted is measured as movement.
5013
+ // Captured before `endDrag` discards the snapshot. Nothing moves
5014
+ // a block during the drag, so the `dragstart` tops are exactly
5015
+ // the FLIP's "before".
4037
5016
  const tops = slots.map((slot) => slot.top);
4038
5017
  endDrag();
4039
5018
  if (to === null)
@@ -4045,6 +5024,27 @@ const MlvEditorBlockHandle = Extension.create({
4045
5024
  if (event.key === 'Escape' && source !== null)
4046
5025
  endDrag();
4047
5026
  };
5027
+ // These eight stay raw `addEventListener`s rather than becoming
5028
+ // `fromEvent` + `takeUntilDestroyed`, deliberately and for two
5029
+ // reasons.
5030
+ //
5031
+ // There is no `DestroyRef` to take. This file is a plain
5032
+ // ProseMirror plugin with no Angular import at all, constructed by
5033
+ // Tiptap outside any injection context, so `inject()` would throw.
5034
+ // The lifetime that matters here is not the editor component's
5035
+ // either: it is this plugin *view's*, which is torn down and
5036
+ // rebuilt whenever the editor is recreated (a format switch, an
5037
+ // extension-set change). `destroy()` below is that boundary, and it
5038
+ // unbinds all eight with the capture flags they were bound with.
5039
+ //
5040
+ // And the three capture-phase drag listeners are protocol-sensitive.
5041
+ // ProseMirror installs its own `dragover`/`drop` handlers on
5042
+ // `view.dom`, a descendant of `mount`; capturing on the ancestor is
5043
+ // what lets `onDrop` claim the drop and `stopPropagation()` before
5044
+ // ProseMirror inserts the dragged slice itself. Nothing in the unit
5045
+ // suite can drive a native HTML5 drag, so that ordering cannot be
5046
+ // pinned by a test here — and an unprovable reordering of block
5047
+ // drag-and-drop is not worth a stylistic conversion.
4048
5048
  mount.addEventListener('mousemove', onMouseMove);
4049
5049
  mount.addEventListener('mouseleave', hide);
4050
5050
  handle.addEventListener('dragstart', onDragStart);
@@ -4081,6 +5081,8 @@ const MlvEditorBlockHandle = Extension.create({
4081
5081
  // selection-only transaction carries the same reference — so
4082
5082
  // this is exactly `docChanged` at O(1), where a structural
4083
5083
  // compare would walk the whole document on every keystroke.
5084
+ // The same change has already emptied the source decoration in
5085
+ // plugin state (`apply`), so this `endDrag` dispatches nothing.
4084
5086
  if (previous.doc !== updated.state.doc && source !== null) {
4085
5087
  endDrag();
4086
5088
  }
@@ -4091,7 +5093,9 @@ const MlvEditorBlockHandle = Extension.create({
4091
5093
  // releasing the button after the revocation cannot reorder
4092
5094
  // anything. Previously the line went through `decorations`,
4093
5095
  // which read `enabled()` itself; a mount-owned element has to
4094
- // be told.
5096
+ // be told. Clearing the dim dispatches from inside `update`;
5097
+ // that re-entry finds no drag recorded and returns, so it runs
5098
+ // once.
4095
5099
  if (source !== null)
4096
5100
  endDrag();
4097
5101
  },
@@ -4109,6 +5113,8 @@ const MlvEditorBlockHandle = Extension.create({
4109
5113
  // A drag interrupted by teardown leaves its drag image on
4110
5114
  // `document.body`, outside everything else this removes.
4111
5115
  ghost?.remove();
5116
+ // The dim stays in plugin state, where this cannot dispatch;
5117
+ // see `clearInheritedDragSource`.
4112
5118
  },
4113
5119
  };
4114
5120
  },
@@ -5055,8 +6061,16 @@ class EditorImageUploadCoordinator extends MlvEditorImageUploadControl {
5055
6061
  catch {
5056
6062
  return undefined;
5057
6063
  }
5058
- const alt = typeof value['alt'] === 'string' ? value['alt'] : state.alt;
5059
- const title = typeof value['title'] === 'string' ? value['title'] : state.title;
6064
+ // The author's metadata states intent and wins; the adapter's only fills
6065
+ // what the author left out. An author `alt: ''` marks the image
6066
+ // decorative: the alt stays empty (WCAG 1.1.1) and no title is kept,
6067
+ // the author's or the adapter's, because an `alt=""` image with a title
6068
+ // is exposed as an unnamed image described by it (WCAG H67). Otherwise
6069
+ // an empty author title means no title, so the adapter's may fill it.
6070
+ const adapterAlt = typeof value['alt'] === 'string' ? value['alt'] : undefined;
6071
+ const adapterTitle = typeof value['title'] === 'string' ? value['title'] : undefined;
6072
+ const alt = state.alt ?? adapterAlt;
6073
+ const title = state.alt === '' ? undefined : state.title || adapterTitle;
5060
6074
  return {
5061
6075
  src,
5062
6076
  ...(alt === undefined ? {} : { alt }),
@@ -5317,8 +6331,8 @@ class MlvEditorAlignment {
5317
6331
  this._unregister?.();
5318
6332
  this._unregister = undefined;
5319
6333
  }
5320
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAlignment, deps: [], target: i0.ɵɵFactoryTarget.Component });
5321
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorAlignment, isStandalone: true, selector: "mlv-editor-alignment", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-alignment" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
6334
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAlignment, deps: [], target: i0.ɵɵFactoryTarget.Component });
6335
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorAlignment, isStandalone: true, selector: "mlv-editor-alignment", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-alignment" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
5322
6336
  <button
5323
6337
  mlvButton
5324
6338
  mlvEditorToolbarWidget
@@ -5350,7 +6364,7 @@ class MlvEditorAlignment {
5350
6364
  </mlv-menu>
5351
6365
  `, isInline: true, dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideAlignLeft, selector: "svg[lucideTextAlignStart], svg[lucideText], svg[lucideAlignLeft]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5352
6366
  }
5353
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAlignment, decorators: [{
6367
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorAlignment, decorators: [{
5354
6368
  type: Component,
5355
6369
  args: [{
5356
6370
  selector: 'mlv-editor-alignment',
@@ -5460,8 +6474,8 @@ class MlvEditorCommandButton {
5460
6474
  return;
5461
6475
  this._context.run(this.command());
5462
6476
  }
5463
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorCommandButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
5464
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.7", type: MlvEditorCommandButton, isStandalone: true, selector: "mlv-editor-command-button", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, supports: { classPropertyName: "supports", publicName: "supports", isSignal: true, isRequired: true, transformFunction: null }, command: { classPropertyName: "command", publicName: "command", isSignal: true, isRequired: true, transformFunction: null }, canCommand: { classPropertyName: "canCommand", publicName: "canCommand", isSignal: true, isRequired: false, transformFunction: null }, pressed: { classPropertyName: "pressed", publicName: "pressed", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!supported()" }, classAttribute: "mlv-editor-command-button" }, ngImport: i0, template: `
6477
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorCommandButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
6478
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.7", type: MlvEditorCommandButton, isStandalone: true, selector: "mlv-editor-command-button", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, supports: { classPropertyName: "supports", publicName: "supports", isSignal: true, isRequired: true, transformFunction: null }, command: { classPropertyName: "command", publicName: "command", isSignal: true, isRequired: true, transformFunction: null }, canCommand: { classPropertyName: "canCommand", publicName: "canCommand", isSignal: true, isRequired: false, transformFunction: null }, pressed: { classPropertyName: "pressed", publicName: "pressed", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!supported()" }, classAttribute: "mlv-editor-command-button" }, ngImport: i0, template: `
5465
6479
  <button
5466
6480
  mlvButton
5467
6481
  mlvEditorToolbarWidget
@@ -5481,7 +6495,7 @@ class MlvEditorCommandButton {
5481
6495
  </button>
5482
6496
  `, isInline: true, dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5483
6497
  }
5484
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorCommandButton, decorators: [{
6498
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorCommandButton, decorators: [{
5485
6499
  type: Component,
5486
6500
  args: [{
5487
6501
  selector: 'mlv-editor-command-button',
@@ -5539,8 +6553,8 @@ class MlvEditorBlockInsert {
5539
6553
  this._revision();
5540
6554
  return this._context.isActive(name);
5541
6555
  };
5542
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorBlockInsert, deps: [], target: i0.ɵɵFactoryTarget.Component });
5543
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.7", type: MlvEditorBlockInsert, isStandalone: true, selector: "mlv-editor-block-insert", host: { attributes: { "role": "group" }, classAttribute: "mlv-editor-block-insert" }, ngImport: i0, template: `
6556
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorBlockInsert, deps: [], target: i0.ɵɵFactoryTarget.Component });
6557
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorBlockInsert, isStandalone: true, selector: "mlv-editor-block-insert", host: { attributes: { "role": "group" }, classAttribute: "mlv-editor-block-insert" }, ngImport: i0, template: `
5544
6558
  <mlv-editor-command-button
5545
6559
  [label]="_copy().blockquote"
5546
6560
  [pressed]="_isActive('blockquote')"
@@ -5566,7 +6580,7 @@ class MlvEditorBlockInsert {
5566
6580
  /></mlv-editor-command-button>
5567
6581
  `, isInline: true, dependencies: [{ kind: "component", type: MlvEditorCommandButton, selector: "mlv-editor-command-button", inputs: ["label", "supports", "command", "canCommand", "pressed"] }, { kind: "component", type: LucideCode2, selector: "svg[lucideCodeXml], svg[lucideCode2]" }, { kind: "component", type: LucideMinus, selector: "svg[lucideMinus]" }, { kind: "component", type: LucideTextQuote, selector: "svg[lucideTextQuote]" }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5568
6582
  }
5569
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorBlockInsert, decorators: [{
6583
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorBlockInsert, decorators: [{
5570
6584
  type: Component,
5571
6585
  args: [{
5572
6586
  selector: 'mlv-editor-block-insert',
@@ -5763,8 +6777,8 @@ class MlvEditorHeading {
5763
6777
  this._unregister?.();
5764
6778
  this._unregister = undefined;
5765
6779
  }
5766
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorHeading, deps: [], target: i0.ɵɵFactoryTarget.Component });
5767
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorHeading, isStandalone: true, selector: "mlv-editor-heading", inputs: { levels: { classPropertyName: "levels", publicName: "levels", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-heading" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
6780
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorHeading, deps: [], target: i0.ɵɵFactoryTarget.Component });
6781
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorHeading, isStandalone: true, selector: "mlv-editor-heading", inputs: { levels: { classPropertyName: "levels", publicName: "levels", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-heading" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
5768
6782
  <button
5769
6783
  mlvButton
5770
6784
  mlvEditorToolbarWidget
@@ -5833,7 +6847,7 @@ class MlvEditorHeading {
5833
6847
  </mlv-menu>
5834
6848
  `, isInline: true, dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideHeading, selector: "svg[lucideHeading]" }, { kind: "component", type: LucideHeading1, selector: "svg[lucideHeading1]" }, { kind: "component", type: LucideHeading2, selector: "svg[lucideHeading2]" }, { kind: "component", type: LucideHeading3, selector: "svg[lucideHeading3]" }, { kind: "component", type: LucideHeading4, selector: "svg[lucideHeading4]" }, { kind: "component", type: LucideHeading5, selector: "svg[lucideHeading5]" }, { kind: "component", type: LucideHeading6, selector: "svg[lucideHeading6]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5835
6849
  }
5836
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorHeading, decorators: [{
6850
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorHeading, decorators: [{
5837
6851
  type: Component,
5838
6852
  args: [{
5839
6853
  selector: 'mlv-editor-heading',
@@ -5953,14 +6967,18 @@ class MlvEditorColorControl {
5953
6967
  const trigger = this._picker().triggerElement();
5954
6968
  if (!trigger)
5955
6969
  return;
6970
+ // Released from `onCleanup`, not `takeUntilDestroyed`: this effect
6971
+ // re-runs whenever the picker publishes a different trigger element, and
6972
+ // a destroy-scoped teardown would leave every superseded generation
6973
+ // subscribed for the rest of the control's life.
6974
+ const listeners = new Subscription();
5956
6975
  const activate = () => this._roving.activate(trigger);
5957
- trigger.addEventListener('focus', activate);
5958
- trigger.addEventListener('pointerdown', activate);
6976
+ listeners.add(fromEvent(trigger, 'focus').subscribe(activate));
6977
+ listeners.add(fromEvent(trigger, 'pointerdown').subscribe(activate));
5959
6978
  const unregister = untracked(() => this._roving.register(trigger));
5960
6979
  onCleanup(() => {
5961
6980
  unregister();
5962
- trigger.removeEventListener('focus', activate);
5963
- trigger.removeEventListener('pointerdown', activate);
6981
+ listeners.unsubscribe();
5964
6982
  });
5965
6983
  });
5966
6984
  effect(() => {
@@ -5992,10 +7010,10 @@ class MlvEditorColorControl {
5992
7010
  return;
5993
7011
  this._context.run((editor) => this._run(editor, selection, color));
5994
7012
  }
5995
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorColorControl, deps: [], target: i0.ɵɵFactoryTarget.Directive });
5996
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.7", type: MlvEditorColorControl, isStandalone: true, viewQueries: [{ propertyName: "_picker", first: true, predicate: ["picker"], descendants: true, isSignal: true }], ngImport: i0 });
7013
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorColorControl, deps: [], target: i0.ɵɵFactoryTarget.Directive });
7014
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.1.7", type: MlvEditorColorControl, isStandalone: true, viewQueries: [{ propertyName: "_picker", first: true, predicate: ["picker"], descendants: true, isSignal: true }], ngImport: i0 });
5997
7015
  }
5998
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorColorControl, decorators: [{
7016
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorColorControl, decorators: [{
5999
7017
  type: Directive
6000
7018
  }], ctorParameters: () => [], propDecorators: { _picker: [{ type: i0.ViewChild, args: ['picker', { isSignal: true }] }] } });
6001
7019
 
@@ -6028,8 +7046,8 @@ class MlvEditorHighlight extends MlvEditorColorControl {
6028
7046
  ? chain.toggleHighlight({ color }).run()
6029
7047
  : chain.unsetHighlight().run();
6030
7048
  }
6031
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorHighlight, deps: null, target: i0.ɵɵFactoryTarget.Component });
6032
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.7", type: MlvEditorHighlight, isStandalone: true, selector: "mlv-editor-highlight", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-highlight" }, usesInheritance: true, ngImport: i0, template: `
7049
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorHighlight, deps: null, target: i0.ɵɵFactoryTarget.Component });
7050
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorHighlight, isStandalone: true, selector: "mlv-editor-highlight", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-highlight" }, usesInheritance: true, ngImport: i0, template: `
6033
7051
  <mlv-color-picker-popup
6034
7052
  #picker
6035
7053
  presentation="icon"
@@ -6046,7 +7064,7 @@ class MlvEditorHighlight extends MlvEditorColorControl {
6046
7064
  </mlv-color-picker-popup>
6047
7065
  `, isInline: true, dependencies: [{ kind: "component", type: MlvColorPickerPopup, selector: "mlv-color-picker-popup", inputs: ["value", "presentation", "opened", "showOpacity", "defaultMode", "supportedFormats", "live"], outputs: ["valueChange", "openedChange", "afterOpened", "afterClosed", "colorChange"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "component", type: LucideHighlighter, selector: "svg[lucideHighlighter]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
6048
7066
  }
6049
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorHighlight, decorators: [{
7067
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorHighlight, decorators: [{
6050
7068
  type: Component,
6051
7069
  args: [{
6052
7070
  selector: 'mlv-editor-highlight',
@@ -6105,8 +7123,8 @@ class MlvEditorInlineMarks {
6105
7123
  /** @protected Italic command. */ _italic = (editor) => editor.chain().focus().toggleItalic().run();
6106
7124
  /** @protected Strike command. */ _strike = (editor) => editor.chain().focus().toggleStrike().run();
6107
7125
  /** @protected Underline command. */ _underline = (editor) => editor.chain().focus().toggleUnderline().run();
6108
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorInlineMarks, deps: [], target: i0.ɵɵFactoryTarget.Component });
6109
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.7", type: MlvEditorInlineMarks, isStandalone: true, selector: "mlv-editor-inline-marks", host: { attributes: { "role": "group" }, classAttribute: "mlv-editor-inline-marks" }, ngImport: i0, template: `
7126
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorInlineMarks, deps: [], target: i0.ɵɵFactoryTarget.Component });
7127
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorInlineMarks, isStandalone: true, selector: "mlv-editor-inline-marks", host: { attributes: { "role": "group" }, classAttribute: "mlv-editor-inline-marks" }, ngImport: i0, template: `
6110
7128
  <mlv-editor-command-button
6111
7129
  [label]="_copy().bold"
6112
7130
  [pressed]="_isActive('bold')"
@@ -6141,7 +7159,7 @@ class MlvEditorInlineMarks {
6141
7159
  /></mlv-editor-command-button>
6142
7160
  `, isInline: true, dependencies: [{ kind: "component", type: MlvEditorCommandButton, selector: "mlv-editor-command-button", inputs: ["label", "supports", "command", "canCommand", "pressed"] }, { kind: "component", type: LucideBold, selector: "svg[lucideBold]" }, { kind: "component", type: LucideItalic, selector: "svg[lucideItalic]" }, { kind: "component", type: LucideStrikethrough, selector: "svg[lucideStrikethrough]" }, { kind: "component", type: LucideUnderline, selector: "svg[lucideUnderline]" }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
6143
7161
  }
6144
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorInlineMarks, decorators: [{
7162
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorInlineMarks, decorators: [{
6145
7163
  type: Component,
6146
7164
  args: [{
6147
7165
  selector: 'mlv-editor-inline-marks',
@@ -6303,8 +7321,8 @@ class MlvEditorList {
6303
7321
  this._unregister?.();
6304
7322
  this._unregister = undefined;
6305
7323
  }
6306
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorList, deps: [], target: i0.ɵɵFactoryTarget.Component });
6307
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorList, isStandalone: true, selector: "mlv-editor-list", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-list" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
7324
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorList, deps: [], target: i0.ɵɵFactoryTarget.Component });
7325
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorList, isStandalone: true, selector: "mlv-editor-list", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-list" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
6308
7326
  <button
6309
7327
  mlvButton
6310
7328
  mlvEditorToolbarWidget
@@ -6356,7 +7374,7 @@ class MlvEditorList {
6356
7374
  </mlv-menu>
6357
7375
  `, isInline: true, dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideList, selector: "svg[lucideList]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
6358
7376
  }
6359
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorList, decorators: [{
7377
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorList, decorators: [{
6360
7378
  type: Component,
6361
7379
  args: [{
6362
7380
  selector: 'mlv-editor-list',
@@ -6659,15 +7677,14 @@ class MlvEditorLink {
6659
7677
  .map((protocol) => protocol.trim().toLowerCase().replace(/:+$/, ''))
6660
7678
  .filter(Boolean));
6661
7679
  }
6662
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorLink, deps: [], target: i0.ɵɵFactoryTarget.Component });
6663
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorLink, isStandalone: true, selector: "mlv-editor-link", inputs: { allowedProtocols: { classPropertyName: "allowedProtocols", publicName: "allowedProtocols", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-link" }, viewQueries: [{ propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, isSignal: true }, { propertyName: "_triggerElement", first: true, predicate: ["_triggerElement"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_urlInput", first: true, predicate: ["_urlInput"], descendants: true, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["_panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().link\"\n [attr.aria-pressed]=\"_active()\"\n [mlvTooltip]=\"_copy().link\"\n>\n <svg mlvButtonIcon lucideLink aria-hidden=\"true\" />\n</button>\n\n<mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().link\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n>\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-link__panel\">\n <mlv-input\n #_urlInput\n type=\"url\"\n [label]=\"_copy().linkUrl\"\n [value]=\"_urlDraft()\"\n [disabled]=\"_disabled()\"\n [state]=\"_invalid() ? 'error' : 'default'\"\n [message]=\"_invalid() ? _copy().invalidLink : ''\"\n (valueChange)=\"_onUrlChange($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-input\n [label]=\"_copy().linkText\"\n [value]=\"_textDraft()\"\n [disabled]=\"_disabled()\"\n (valueChange)=\"_textDraft.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-switch [label]=\"_copy().openInNewTab\" [(checked)]=\"_newTab\"\n >{{_copy().openInNewTab}}</mlv-switch\n >\n <div class=\"mlv-editor-link__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_disabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().applyLink }}\n </button>\n @if (_canRemove()) {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _copy().removeLink }}\n </button>\n }\n </div>\n </div>\n </ng-template>\n</mlv-popup>\n", styles: ["@layer mlv.components{.mlv-editor-link{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-link *{box-sizing:border-box}.mlv-editor-link{display:inline-flex}.mlv-editor-link__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-link__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideLink, selector: "svg[lucideLink]" }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "component", type: MlvSwitch, selector: "mlv-switch", inputs: ["ariaLabelledBy", "checked"], outputs: ["checkedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
7680
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorLink, deps: [], target: i0.ɵɵFactoryTarget.Component });
7681
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorLink, isStandalone: true, selector: "mlv-editor-link", inputs: { allowedProtocols: { classPropertyName: "allowedProtocols", publicName: "allowedProtocols", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-link" }, viewQueries: [{ propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, isSignal: true }, { propertyName: "_triggerElement", first: true, predicate: ["_triggerElement"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_urlInput", first: true, predicate: ["_urlInput"], descendants: true, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["_panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().link\"\n [attr.aria-pressed]=\"_active()\"\n [mlvTooltip]=\"_copy().link\"\n>\n <svg mlvButtonIcon lucideLink aria-hidden=\"true\" />\n</button>\n\n<mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().link\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n>\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-link__panel\">\n <mlv-input\n #_urlInput\n type=\"url\"\n [label]=\"_copy().linkUrl\"\n [value]=\"_urlDraft()\"\n [disabled]=\"_disabled()\"\n [state]=\"_invalid() ? 'error' : 'default'\"\n [message]=\"_invalid() ? _copy().invalidLink : ''\"\n (valueChange)=\"_onUrlChange($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-input\n [label]=\"_copy().linkText\"\n [value]=\"_textDraft()\"\n [disabled]=\"_disabled()\"\n (valueChange)=\"_textDraft.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-switch [label]=\"_copy().openInNewTab\" [(checked)]=\"_newTab\">{{\n _copy().openInNewTab\n }}</mlv-switch>\n <div class=\"mlv-editor-link__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_disabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().applyLink }}\n </button>\n @if (_canRemove()) {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _copy().removeLink }}\n </button>\n }\n </div>\n </div>\n </ng-template>\n</mlv-popup>\n", styles: ["@layer mlv.components{.mlv-editor-link{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-link *{box-sizing:border-box}.mlv-editor-link{display:inline-flex}.mlv-editor-link__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-link__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideLink, selector: "svg[lucideLink]" }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "component", type: MlvSwitch, selector: "mlv-switch", inputs: ["ariaLabelledBy", "checked"], outputs: ["checkedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
6664
7682
  }
6665
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorLink, decorators: [{
7683
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorLink, decorators: [{
6666
7684
  type: Component,
6667
7685
  args: [{ selector: 'mlv-editor-link', imports: [
6668
7686
  MlvButton,
6669
7687
  MlvButtonIcon,
6670
- MlvButtonToggle,
6671
7688
  MlvTooltip,
6672
7689
  LucideLink,
6673
7690
  MlvEditorToolbarWidget,
@@ -6679,7 +7696,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
6679
7696
  ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
6680
7697
  class: 'mlv-editor-link',
6681
7698
  '[hidden]': '!_supported()',
6682
- }, template: "<button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().link\"\n [attr.aria-pressed]=\"_active()\"\n [mlvTooltip]=\"_copy().link\"\n>\n <svg mlvButtonIcon lucideLink aria-hidden=\"true\" />\n</button>\n\n<mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().link\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n>\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-link__panel\">\n <mlv-input\n #_urlInput\n type=\"url\"\n [label]=\"_copy().linkUrl\"\n [value]=\"_urlDraft()\"\n [disabled]=\"_disabled()\"\n [state]=\"_invalid() ? 'error' : 'default'\"\n [message]=\"_invalid() ? _copy().invalidLink : ''\"\n (valueChange)=\"_onUrlChange($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-input\n [label]=\"_copy().linkText\"\n [value]=\"_textDraft()\"\n [disabled]=\"_disabled()\"\n (valueChange)=\"_textDraft.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-switch [label]=\"_copy().openInNewTab\" [(checked)]=\"_newTab\"\n >{{_copy().openInNewTab}}</mlv-switch\n >\n <div class=\"mlv-editor-link__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_disabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().applyLink }}\n </button>\n @if (_canRemove()) {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _copy().removeLink }}\n </button>\n }\n </div>\n </div>\n </ng-template>\n</mlv-popup>\n", styles: ["@layer mlv.components{.mlv-editor-link{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-link *{box-sizing:border-box}.mlv-editor-link{display:inline-flex}.mlv-editor-link__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-link__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"] }]
7699
+ }, template: "<button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().link\"\n [attr.aria-pressed]=\"_active()\"\n [mlvTooltip]=\"_copy().link\"\n>\n <svg mlvButtonIcon lucideLink aria-hidden=\"true\" />\n</button>\n\n<mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().link\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n>\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-link__panel\">\n <mlv-input\n #_urlInput\n type=\"url\"\n [label]=\"_copy().linkUrl\"\n [value]=\"_urlDraft()\"\n [disabled]=\"_disabled()\"\n [state]=\"_invalid() ? 'error' : 'default'\"\n [message]=\"_invalid() ? _copy().invalidLink : ''\"\n (valueChange)=\"_onUrlChange($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-input\n [label]=\"_copy().linkText\"\n [value]=\"_textDraft()\"\n [disabled]=\"_disabled()\"\n (valueChange)=\"_textDraft.set($event)\"\n (keydown.enter)=\"_apply()\"\n />\n <mlv-switch [label]=\"_copy().openInNewTab\" [(checked)]=\"_newTab\">{{\n _copy().openInNewTab\n }}</mlv-switch>\n <div class=\"mlv-editor-link__actions\">\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [disabled]=\"_disabled()\"\n (click)=\"_apply()\"\n >\n {{ _copy().applyLink }}\n </button>\n @if (_canRemove()) {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _copy().removeLink }}\n </button>\n }\n </div>\n </div>\n </ng-template>\n</mlv-popup>\n", styles: ["@layer mlv.components{.mlv-editor-link{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-link *{box-sizing:border-box}.mlv-editor-link{display:inline-flex}.mlv-editor-link__panel{display:grid;width:min(22rem,100vw - 3.5rem);gap:var(--mlv-spacing-1-5);padding:var(--mlv-spacing-2)}.mlv-editor-link__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-3)}}\n"] }]
6683
7700
  }], ctorParameters: () => [], propDecorators: { allowedProtocols: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowedProtocols", required: false }] }], _popup: [{ type: i0.ViewChild, args: ['_popup', { isSignal: true }] }], _trigger: [{ type: i0.ViewChild, args: ['_trigger', { isSignal: true }] }], _triggerElement: [{ type: i0.ViewChild, args: ['_triggerElement', { ...{
6684
7701
  read: ElementRef,
6685
7702
  }, isSignal: true }] }], _urlInput: [{ type: i0.ViewChild, args: ['_urlInput', { isSignal: true }] }], _panel: [{ type: i0.ViewChild, args: ['_panel', { isSignal: true }] }] } });
@@ -6832,6 +7849,8 @@ class MlvEditorImageUploadDialog {
6832
7849
  this._dialogRef.close();
6833
7850
  return;
6834
7851
  }
7852
+ // A removed failure: its "failed" text must not outlive it.
7853
+ this._announcement.set('');
6835
7854
  this._reset();
6836
7855
  });
6837
7856
  this._dialogRef.beforeClose().subscribe(() => {
@@ -6905,6 +7924,16 @@ class MlvEditorImageUploadDialog {
6905
7924
  this._progressBucket = -1;
6906
7925
  this._coordinator.retry(id);
6907
7926
  }
7927
+ /**
7928
+ * @protected Toggles Decorative. Turning it on clears the title draft: a
7929
+ * decorative image carries an empty alt and no title (WCAG H67), and the
7930
+ * title field stays disabled until it is turned off.
7931
+ */
7932
+ _onDecorativeChange(decorative) {
7933
+ this._decorative.set(decorative);
7934
+ if (decorative)
7935
+ this._title.set('');
7936
+ }
6908
7937
  /** @protected Removes the failed item and clears the draft. */
6909
7938
  _remove() {
6910
7939
  const id = this._activeId();
@@ -6943,10 +7972,10 @@ class MlvEditorImageUploadDialog {
6943
7972
  if (url)
6944
7973
  URL.revokeObjectURL(url);
6945
7974
  }
6946
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorImageUploadDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
6947
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorImageUploadDialog, isStandalone: true, selector: "mlv-editor-image-upload-dialog", host: { classAttribute: "mlv-editor-image-upload-dialog" }, ngImport: i0, template: "<mlv-dialog>\n <mlv-dialog-header />\n <mlv-dialog-body>\n <div class=\"mlv-editor-image-upload-dialog__body\">\n <mlv-file-upload\n [accept]=\"_accept()\"\n [maxSize]=\"_maxSize()\"\n [multiple]=\"false\"\n [disabled]=\"_disabled() || !!_activeId()\"\n [value]=\"_files()\"\n (filesChange)=\"_onFilesChange($event)\"\n />\n\n @if (_file(); as selected) { @if (selected.previewUrl) {\n <img\n class=\"mlv-editor-image-upload-dialog__preview\"\n [src]=\"selected.previewUrl\"\n [alt]=\"_previewAlt()\"\n />\n } @if (_item(); as item) { @if (item.status === 'uploading') {\n <mlv-progress\n [value]=\"item.progress\"\n [showPercentage]=\"true\"\n [ariaLabel]=\"_progressLabel(item.progress)\"\n />\n } @else {\n <p\n class=\"mlv-editor-image-upload-dialog__error\"\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {{ _copy().failed }}\n </p>\n } } }\n\n <mlv-input\n [label]=\"_copy().alt\"\n [ariaLabel]=\"_copy().alt\"\n [value]=\"_alt()\"\n [disabled]=\"_disabled() || !!_activeId() || _decorative()\"\n [state]=\"_altInvalid() ? 'error' : 'default'\"\n [message]=\"_altInvalid() ? _copy().altRequired : ''\"\n (valueChange)=\"_alt.set($event)\"\n />\n <mlv-input\n [label]=\"_copy().imageTitle\"\n [ariaLabel]=\"_copy().imageTitle\"\n [value]=\"_title()\"\n [disabled]=\"_disabled() || !!_activeId()\"\n (valueChange)=\"_title.set($event)\"\n />\n <mlv-switch\n [ariaLabel]=\"_copy().decorative\"\n [checked]=\"_decorative()\"\n [disabled]=\"_disabled() || !!_activeId()\"\n (checkedChange)=\"_decorative.set($event)\"\n >\n {{ _copy().decorative }}\n </mlv-switch>\n\n <span\n class=\"mlv-editor-image-upload-dialog__live\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >{{ _announcement() }}</span\n >\n </div>\n </mlv-dialog-body>\n\n <mlv-dialog-footer>\n @if (_item()?.status === 'uploading') {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_copy().cancel\"\n (click)=\"_cancel()\"\n >\n {{ _copy().cancel }}\n </button>\n } @else if (_item()?.status === 'failed') {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().retry\"\n [disabled]=\"_disabled()\"\n (click)=\"_retry()\"\n >\n {{ _copy().retry }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_removeLabel()\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _removeLabel() }}\n </button>\n } @else {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().title\"\n [disabled]=\"_submitDisabled()\"\n (click)=\"_submit()\"\n >\n {{ _copy().title }}\n </button>\n }\n </mlv-dialog-footer>\n</mlv-dialog>\n", styles: ["@layer mlv.components{.mlv-editor-image-upload-dialog{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-dialog *{box-sizing:border-box}.mlv-editor-image-upload-dialog__body{display:grid;gap:var(--mlv-spacing-2)}.mlv-editor-image-upload-dialog__preview{display:block;max-block-size:12rem;max-inline-size:100%;margin-inline:auto;border-radius:var(--mlv-radius-card);object-fit:contain}.mlv-editor-image-upload-dialog__error{margin:0;color:var(--mlv-text-negative)}.mlv-editor-image-upload-dialog__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.mlv-editor-image-upload-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-status *{box-sizing:border-box}.mlv-editor-image-upload-status{display:block;padding:0 var(--mlv-spacing-4) var(--mlv-spacing-2)}.mlv-editor-image-upload-status__list{display:grid;gap:var(--mlv-spacing-1-5);padding:0;margin:0;list-style:none}.mlv-editor-image-upload-status__item{display:grid;grid-template-columns:minmax(0,1fr) minmax(8rem,2fr) auto auto;align-items:center;gap:var(--mlv-spacing-3)}.mlv-editor-image-upload-status__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-editor-image-upload-status__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "component", type: MlvDialog, selector: "mlv-dialog" }, { kind: "component", type: MlvDialogBody, selector: "mlv-dialog-body, [mlvDialogBody]" }, { kind: "component", type: MlvDialogFooter, selector: "mlv-dialog-footer, [mlvDialogFooter]", inputs: ["align"] }, { kind: "component", type: MlvDialogHeader, selector: "mlv-dialog-header, [mlvDialogHeader]", inputs: ["title", "closable"] }, { kind: "component", type: MlvFileUpload, selector: "mlv-file-upload", inputs: ["value", "accept", "maxSize", "multiple", "title", "subtitle", "actionLabel", "compact", "previewMode"], outputs: ["valueChange", "filesChange"] }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvProgress, selector: "mlv-progress", inputs: ["shape", "value", "size", "tone", "showPercentage", "ariaLabel"] }, { kind: "component", type: MlvSwitch, selector: "mlv-switch", inputs: ["ariaLabelledBy", "checked"], outputs: ["checkedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
7975
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUploadDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
7976
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorImageUploadDialog, isStandalone: true, selector: "mlv-editor-image-upload-dialog", host: { classAttribute: "mlv-editor-image-upload-dialog" }, ngImport: i0, template: "<mlv-dialog>\n <mlv-dialog-header />\n <mlv-dialog-body>\n <div class=\"mlv-editor-image-upload-dialog__body\">\n <mlv-file-upload\n [accept]=\"_accept()\"\n [maxSize]=\"_maxSize()\"\n [multiple]=\"false\"\n [disabled]=\"_disabled() || !!_activeId()\"\n [value]=\"_files()\"\n (filesChange)=\"_onFilesChange($event)\"\n />\n\n @if (_file(); as selected) {\n @if (selected.previewUrl) {\n <img\n class=\"mlv-editor-image-upload-dialog__preview\"\n [src]=\"selected.previewUrl\"\n [alt]=\"_previewAlt()\"\n />\n }\n @if (_item(); as item) {\n @if (item.status === 'uploading') {\n <mlv-progress\n [value]=\"item.progress\"\n [showPercentage]=\"true\"\n [ariaLabel]=\"_progressLabel(item.progress)\"\n />\n } @else {\n <p\n class=\"mlv-editor-image-upload-dialog__error\"\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {{ _copy().failed }}\n </p>\n }\n }\n }\n\n <mlv-input\n [label]=\"_copy().alt\"\n [ariaLabel]=\"_copy().alt\"\n [value]=\"_alt()\"\n [disabled]=\"_disabled() || !!_activeId() || _decorative()\"\n [state]=\"_altInvalid() ? 'error' : 'default'\"\n [message]=\"_altInvalid() ? _copy().altRequired : ''\"\n (valueChange)=\"_alt.set($event)\"\n />\n <mlv-input\n [label]=\"_copy().imageTitle\"\n [ariaLabel]=\"_copy().imageTitle\"\n [value]=\"_title()\"\n [disabled]=\"_disabled() || !!_activeId() || _decorative()\"\n (valueChange)=\"_title.set($event)\"\n />\n <mlv-switch\n [ariaLabel]=\"_copy().decorative\"\n [checked]=\"_decorative()\"\n [disabled]=\"_disabled() || !!_activeId()\"\n (checkedChange)=\"_onDecorativeChange($event)\"\n >\n {{ _copy().decorative }}\n </mlv-switch>\n\n <span\n class=\"mlv-editor-image-upload-dialog__live\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >{{ _announcement() }}</span\n >\n </div>\n </mlv-dialog-body>\n\n <mlv-dialog-footer>\n @if (_item()?.status === 'uploading') {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_copy().cancel\"\n (click)=\"_cancel()\"\n >\n {{ _copy().cancel }}\n </button>\n } @else if (_item()?.status === 'failed') {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().retry\"\n [disabled]=\"_disabled()\"\n (click)=\"_retry()\"\n >\n {{ _copy().retry }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_removeLabel()\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _removeLabel() }}\n </button>\n } @else {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().title\"\n [disabled]=\"_submitDisabled()\"\n (click)=\"_submit()\"\n >\n {{ _copy().title }}\n </button>\n }\n </mlv-dialog-footer>\n</mlv-dialog>\n", styles: ["@layer mlv.components{.mlv-editor-image-upload-dialog{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-dialog *{box-sizing:border-box}.mlv-editor-image-upload-dialog__body{display:grid;gap:var(--mlv-spacing-2)}.mlv-editor-image-upload-dialog__preview{display:block;max-block-size:12rem;max-inline-size:100%;margin-inline:auto;border-radius:var(--mlv-radius-card);object-fit:contain}.mlv-editor-image-upload-dialog__error{margin:0;color:var(--mlv-text-negative)}.mlv-editor-image-upload-dialog__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.mlv-editor-image-upload-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-status *{box-sizing:border-box}.mlv-editor-image-upload-status{display:block;padding:0 var(--mlv-spacing-4) var(--mlv-spacing-2)}.mlv-editor-image-upload-status__list{display:grid;gap:var(--mlv-spacing-1-5);padding:0;margin:0;list-style:none}.mlv-editor-image-upload-status__item{display:grid;grid-template-columns:minmax(0,1fr) minmax(8rem,2fr) auto auto;align-items:center;gap:var(--mlv-spacing-3)}.mlv-editor-image-upload-status__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-editor-image-upload-status__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "component", type: MlvDialog, selector: "mlv-dialog" }, { kind: "component", type: MlvDialogBody, selector: "mlv-dialog-body, [mlvDialogBody]" }, { kind: "component", type: MlvDialogFooter, selector: "mlv-dialog-footer, [mlvDialogFooter]", inputs: ["align"] }, { kind: "component", type: MlvDialogHeader, selector: "mlv-dialog-header, [mlvDialogHeader]", inputs: ["title", "closable"] }, { kind: "component", type: MlvFileUpload, selector: "mlv-file-upload", inputs: ["value", "accept", "maxSize", "multiple", "title", "subtitle", "actionLabel", "compact", "previewMode"], outputs: ["valueChange", "filesChange"] }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvProgress, selector: "mlv-progress", inputs: ["shape", "value", "size", "tone", "showPercentage", "ariaLabel"] }, { kind: "component", type: MlvSwitch, selector: "mlv-switch", inputs: ["ariaLabelledBy", "checked"], outputs: ["checkedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
6948
7977
  }
6949
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorImageUploadDialog, decorators: [{
7978
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUploadDialog, decorators: [{
6950
7979
  type: Component,
6951
7980
  args: [{ selector: 'mlv-editor-image-upload-dialog', imports: [
6952
7981
  MlvButton,
@@ -6958,7 +7987,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
6958
7987
  MlvInput,
6959
7988
  MlvProgress,
6960
7989
  MlvSwitch,
6961
- ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'mlv-editor-image-upload-dialog' }, template: "<mlv-dialog>\n <mlv-dialog-header />\n <mlv-dialog-body>\n <div class=\"mlv-editor-image-upload-dialog__body\">\n <mlv-file-upload\n [accept]=\"_accept()\"\n [maxSize]=\"_maxSize()\"\n [multiple]=\"false\"\n [disabled]=\"_disabled() || !!_activeId()\"\n [value]=\"_files()\"\n (filesChange)=\"_onFilesChange($event)\"\n />\n\n @if (_file(); as selected) { @if (selected.previewUrl) {\n <img\n class=\"mlv-editor-image-upload-dialog__preview\"\n [src]=\"selected.previewUrl\"\n [alt]=\"_previewAlt()\"\n />\n } @if (_item(); as item) { @if (item.status === 'uploading') {\n <mlv-progress\n [value]=\"item.progress\"\n [showPercentage]=\"true\"\n [ariaLabel]=\"_progressLabel(item.progress)\"\n />\n } @else {\n <p\n class=\"mlv-editor-image-upload-dialog__error\"\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {{ _copy().failed }}\n </p>\n } } }\n\n <mlv-input\n [label]=\"_copy().alt\"\n [ariaLabel]=\"_copy().alt\"\n [value]=\"_alt()\"\n [disabled]=\"_disabled() || !!_activeId() || _decorative()\"\n [state]=\"_altInvalid() ? 'error' : 'default'\"\n [message]=\"_altInvalid() ? _copy().altRequired : ''\"\n (valueChange)=\"_alt.set($event)\"\n />\n <mlv-input\n [label]=\"_copy().imageTitle\"\n [ariaLabel]=\"_copy().imageTitle\"\n [value]=\"_title()\"\n [disabled]=\"_disabled() || !!_activeId()\"\n (valueChange)=\"_title.set($event)\"\n />\n <mlv-switch\n [ariaLabel]=\"_copy().decorative\"\n [checked]=\"_decorative()\"\n [disabled]=\"_disabled() || !!_activeId()\"\n (checkedChange)=\"_decorative.set($event)\"\n >\n {{ _copy().decorative }}\n </mlv-switch>\n\n <span\n class=\"mlv-editor-image-upload-dialog__live\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >{{ _announcement() }}</span\n >\n </div>\n </mlv-dialog-body>\n\n <mlv-dialog-footer>\n @if (_item()?.status === 'uploading') {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_copy().cancel\"\n (click)=\"_cancel()\"\n >\n {{ _copy().cancel }}\n </button>\n } @else if (_item()?.status === 'failed') {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().retry\"\n [disabled]=\"_disabled()\"\n (click)=\"_retry()\"\n >\n {{ _copy().retry }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_removeLabel()\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _removeLabel() }}\n </button>\n } @else {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().title\"\n [disabled]=\"_submitDisabled()\"\n (click)=\"_submit()\"\n >\n {{ _copy().title }}\n </button>\n }\n </mlv-dialog-footer>\n</mlv-dialog>\n", styles: ["@layer mlv.components{.mlv-editor-image-upload-dialog{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-dialog *{box-sizing:border-box}.mlv-editor-image-upload-dialog__body{display:grid;gap:var(--mlv-spacing-2)}.mlv-editor-image-upload-dialog__preview{display:block;max-block-size:12rem;max-inline-size:100%;margin-inline:auto;border-radius:var(--mlv-radius-card);object-fit:contain}.mlv-editor-image-upload-dialog__error{margin:0;color:var(--mlv-text-negative)}.mlv-editor-image-upload-dialog__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.mlv-editor-image-upload-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-status *{box-sizing:border-box}.mlv-editor-image-upload-status{display:block;padding:0 var(--mlv-spacing-4) var(--mlv-spacing-2)}.mlv-editor-image-upload-status__list{display:grid;gap:var(--mlv-spacing-1-5);padding:0;margin:0;list-style:none}.mlv-editor-image-upload-status__item{display:grid;grid-template-columns:minmax(0,1fr) minmax(8rem,2fr) auto auto;align-items:center;gap:var(--mlv-spacing-3)}.mlv-editor-image-upload-status__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-editor-image-upload-status__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}}\n"] }]
7990
+ ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'mlv-editor-image-upload-dialog' }, template: "<mlv-dialog>\n <mlv-dialog-header />\n <mlv-dialog-body>\n <div class=\"mlv-editor-image-upload-dialog__body\">\n <mlv-file-upload\n [accept]=\"_accept()\"\n [maxSize]=\"_maxSize()\"\n [multiple]=\"false\"\n [disabled]=\"_disabled() || !!_activeId()\"\n [value]=\"_files()\"\n (filesChange)=\"_onFilesChange($event)\"\n />\n\n @if (_file(); as selected) {\n @if (selected.previewUrl) {\n <img\n class=\"mlv-editor-image-upload-dialog__preview\"\n [src]=\"selected.previewUrl\"\n [alt]=\"_previewAlt()\"\n />\n }\n @if (_item(); as item) {\n @if (item.status === 'uploading') {\n <mlv-progress\n [value]=\"item.progress\"\n [showPercentage]=\"true\"\n [ariaLabel]=\"_progressLabel(item.progress)\"\n />\n } @else {\n <p\n class=\"mlv-editor-image-upload-dialog__error\"\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {{ _copy().failed }}\n </p>\n }\n }\n }\n\n <mlv-input\n [label]=\"_copy().alt\"\n [ariaLabel]=\"_copy().alt\"\n [value]=\"_alt()\"\n [disabled]=\"_disabled() || !!_activeId() || _decorative()\"\n [state]=\"_altInvalid() ? 'error' : 'default'\"\n [message]=\"_altInvalid() ? _copy().altRequired : ''\"\n (valueChange)=\"_alt.set($event)\"\n />\n <mlv-input\n [label]=\"_copy().imageTitle\"\n [ariaLabel]=\"_copy().imageTitle\"\n [value]=\"_title()\"\n [disabled]=\"_disabled() || !!_activeId() || _decorative()\"\n (valueChange)=\"_title.set($event)\"\n />\n <mlv-switch\n [ariaLabel]=\"_copy().decorative\"\n [checked]=\"_decorative()\"\n [disabled]=\"_disabled() || !!_activeId()\"\n (checkedChange)=\"_onDecorativeChange($event)\"\n >\n {{ _copy().decorative }}\n </mlv-switch>\n\n <span\n class=\"mlv-editor-image-upload-dialog__live\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >{{ _announcement() }}</span\n >\n </div>\n </mlv-dialog-body>\n\n <mlv-dialog-footer>\n @if (_item()?.status === 'uploading') {\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_copy().cancel\"\n (click)=\"_cancel()\"\n >\n {{ _copy().cancel }}\n </button>\n } @else if (_item()?.status === 'failed') {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().retry\"\n [disabled]=\"_disabled()\"\n (click)=\"_retry()\"\n >\n {{ _copy().retry }}\n </button>\n <button\n mlvButton\n type=\"button\"\n variant=\"secondary\"\n [attr.aria-label]=\"_removeLabel()\"\n [disabled]=\"_disabled()\"\n (click)=\"_remove()\"\n >\n {{ _removeLabel() }}\n </button>\n } @else {\n <button\n mlvButton\n type=\"button\"\n variant=\"primary\"\n [attr.aria-label]=\"_copy().title\"\n [disabled]=\"_submitDisabled()\"\n (click)=\"_submit()\"\n >\n {{ _copy().title }}\n </button>\n }\n </mlv-dialog-footer>\n</mlv-dialog>\n", styles: ["@layer mlv.components{.mlv-editor-image-upload-dialog{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-dialog *{box-sizing:border-box}.mlv-editor-image-upload-dialog__body{display:grid;gap:var(--mlv-spacing-2)}.mlv-editor-image-upload-dialog__preview{display:block;max-block-size:12rem;max-inline-size:100%;margin-inline:auto;border-radius:var(--mlv-radius-card);object-fit:contain}.mlv-editor-image-upload-dialog__error{margin:0;color:var(--mlv-text-negative)}.mlv-editor-image-upload-dialog__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.mlv-editor-image-upload-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-status *{box-sizing:border-box}.mlv-editor-image-upload-status{display:block;padding:0 var(--mlv-spacing-4) var(--mlv-spacing-2)}.mlv-editor-image-upload-status__list{display:grid;gap:var(--mlv-spacing-1-5);padding:0;margin:0;list-style:none}.mlv-editor-image-upload-status__item{display:grid;grid-template-columns:minmax(0,1fr) minmax(8rem,2fr) auto auto;align-items:center;gap:var(--mlv-spacing-3)}.mlv-editor-image-upload-status__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-editor-image-upload-status__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}}\n"] }]
6962
7991
  }], ctorParameters: () => [] });
6963
7992
 
6964
7993
  /** Public toolbar entry point for the editor image-upload dialog. */
@@ -7052,10 +8081,10 @@ class MlvEditorImageUpload {
7052
8081
  }
7053
8082
  });
7054
8083
  }
7055
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorImageUpload, deps: [], target: i0.ɵɵFactoryTarget.Component });
7056
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.7", type: MlvEditorImageUpload, isStandalone: true, selector: "mlv-editor-image-upload", host: { properties: { "hidden": "!_visible()" }, classAttribute: "mlv-editor-image-upload" }, viewQueries: [{ propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<button\n #_trigger\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy()\"\n [attr.aria-pressed]=\"false\"\n [mlvTooltip]=\"_copy()\"\n (click)=\"_open()\"\n>\n <svg mlvButtonIcon lucideImage aria-hidden=\"true\" />\n</button>\n", dependencies: [{ kind: "component", type: LucideImage, selector: "svg[lucideImage]" }, { kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
8084
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUpload, deps: [], target: i0.ɵɵFactoryTarget.Component });
8085
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.7", type: MlvEditorImageUpload, isStandalone: true, selector: "mlv-editor-image-upload", host: { properties: { "hidden": "!_visible()" }, classAttribute: "mlv-editor-image-upload" }, viewQueries: [{ propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<button\n #_trigger\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy()\"\n [attr.aria-pressed]=\"false\"\n [mlvTooltip]=\"_copy()\"\n (click)=\"_open()\"\n>\n <svg mlvButtonIcon lucideImage aria-hidden=\"true\" />\n</button>\n", dependencies: [{ kind: "component", type: LucideImage, selector: "svg[lucideImage]" }, { kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
7057
8086
  }
7058
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorImageUpload, decorators: [{
8087
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUpload, decorators: [{
7059
8088
  type: Component,
7060
8089
  args: [{ selector: 'mlv-editor-image-upload', imports: [
7061
8090
  LucideImage,
@@ -7087,6 +8116,11 @@ class MlvEditorImageUploadStatus {
7087
8116
  ...(ngDevMode ? [{ debugName: "_announcement" }] : /* istanbul ignore next */ []));
7088
8117
  /** @private Last announced progress bucket for each active item. */
7089
8118
  _buckets = new Map();
8119
+ /**
8120
+ * @private Upload whose progress or failure the live region currently
8121
+ * reads, or `null` when it reads a terminal announcement (or nothing).
8122
+ */
8123
+ _announcedId = null;
7090
8124
  /** @protected Whether status actions are inert. */
7091
8125
  _disabled = computed(() => this._context.disabled() || this._context.readonly(), /* @ts-ignore */
7092
8126
  ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
@@ -7106,7 +8140,7 @@ class MlvEditorImageUploadStatus {
7106
8140
  const pending = this._pending();
7107
8141
  for (const item of pending) {
7108
8142
  if (item.status === 'failed') {
7109
- this._announcement.set(this._copy().failed);
8143
+ this._announce(this._copy().failed, item.id);
7110
8144
  this._buckets.delete(item.id);
7111
8145
  continue;
7112
8146
  }
@@ -7114,16 +8148,22 @@ class MlvEditorImageUploadStatus {
7114
8148
  if (this._buckets.get(item.id) === bucket)
7115
8149
  continue;
7116
8150
  this._buckets.set(item.id, bucket);
7117
- this._announcement.set(this._progressLabel(bucket));
8151
+ this._announce(this._progressLabel(bucket), item.id);
7118
8152
  }
7119
8153
  });
7120
8154
  const stopTerminalEvents = this._coordinator.onTerminal(({ id, reason }) => {
7121
8155
  this._buckets.delete(id);
7122
8156
  if (reason === 'success' && !this._coordinator.hasDialogOwner(id)) {
7123
- this._announcement.set(this._fileI18n().uploadComplete);
8157
+ this._announce(this._fileI18n().uploadComplete, null);
7124
8158
  }
7125
8159
  else if (reason === 'cancelled') {
7126
- this._announcement.set(this._copy().cancel);
8160
+ this._announce(this._copy().cancel, null);
8161
+ }
8162
+ else if (this._announcedId === id) {
8163
+ // A dialog-owned success (the dialog announces it), a lifecycle
8164
+ // abort or a removed failure: the ended upload's progress or
8165
+ // failure text must not outlive it. Another upload's text stays.
8166
+ this._announce('', null);
7127
8167
  }
7128
8168
  });
7129
8169
  inject(DestroyRef).onDestroy(stopTerminalEvents);
@@ -7132,6 +8172,11 @@ class MlvEditorImageUploadStatus {
7132
8172
  _progressLabel(progress) {
7133
8173
  return this._copy().progress.replace('{progress}', String(Math.floor(progress / 10) * 10));
7134
8174
  }
8175
+ /** @private Writes the live region and records which upload it describes. */
8176
+ _announce(text, id) {
8177
+ this._announcement.set(text);
8178
+ this._announcedId = id;
8179
+ }
7135
8180
  /** @protected Labels a remove action without exposing a bare icon. */
7136
8181
  _removeLabel(name) {
7137
8182
  return this._fileI18n().removeFile.replace('{name}', name);
@@ -7140,8 +8185,8 @@ class MlvEditorImageUploadStatus {
7140
8185
  _cancel(id) {
7141
8186
  this._coordinator.cancel(id);
7142
8187
  }
7143
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorImageUploadStatus, deps: [], target: i0.ɵɵFactoryTarget.Component });
7144
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorImageUploadStatus, isStandalone: true, selector: "mlv-editor-image-upload-status", host: { classAttribute: "mlv-editor-image-upload-status" }, ngImport: i0, template: `
8188
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUploadStatus, deps: [], target: i0.ɵɵFactoryTarget.Component });
8189
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorImageUploadStatus, isStandalone: true, selector: "mlv-editor-image-upload-status", host: { classAttribute: "mlv-editor-image-upload-status" }, ngImport: i0, template: `
7145
8190
  @if (_pending().length) {
7146
8191
  <ul class="mlv-editor-image-upload-status__list">
7147
8192
  @for (item of _pending(); track item.id) {
@@ -7201,7 +8246,7 @@ class MlvEditorImageUploadStatus {
7201
8246
  >
7202
8247
  `, isInline: true, styles: ["@layer mlv.components{.mlv-editor-image-upload-dialog{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-dialog *{box-sizing:border-box}.mlv-editor-image-upload-dialog__body{display:grid;gap:var(--mlv-spacing-2)}.mlv-editor-image-upload-dialog__preview{display:block;max-block-size:12rem;max-inline-size:100%;margin-inline:auto;border-radius:var(--mlv-radius-card);object-fit:contain}.mlv-editor-image-upload-dialog__error{margin:0;color:var(--mlv-text-negative)}.mlv-editor-image-upload-dialog__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.mlv-editor-image-upload-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-image-upload-status *{box-sizing:border-box}.mlv-editor-image-upload-status{display:block;padding:0 var(--mlv-spacing-4) var(--mlv-spacing-2)}.mlv-editor-image-upload-status__list{display:grid;gap:var(--mlv-spacing-1-5);padding:0;margin:0;list-style:none}.mlv-editor-image-upload-status__item{display:grid;grid-template-columns:minmax(0,1fr) minmax(8rem,2fr) auto auto;align-items:center;gap:var(--mlv-spacing-3)}.mlv-editor-image-upload-status__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-editor-image-upload-status__live{position:absolute;width:.0625rem;height:.0625rem;overflow:hidden;clip-path:inset(50%);white-space:nowrap}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "component", type: MlvProgress, selector: "mlv-progress", inputs: ["shape", "value", "size", "tone", "showPercentage", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
7203
8248
  }
7204
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorImageUploadStatus, decorators: [{
8249
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorImageUploadStatus, decorators: [{
7205
8250
  type: Component,
7206
8251
  args: [{ selector: 'mlv-editor-image-upload-status', imports: [MlvButton, MlvProgress], template: `
7207
8252
  @if (_pending().length) {
@@ -7271,6 +8316,18 @@ class MlvEditorTable {
7271
8316
  _context = inject(MLV_EDITOR_TOOLBAR_CONTEXT);
7272
8317
  /** @private Normalizes horizontal grid movement for RTL. */
7273
8318
  _rtlService = inject(MlvRtlService);
8319
+ /**
8320
+ * @private Host element; the scope horizontal arrow keys resolve their
8321
+ * direction against. The grid renders in a popup pane portaled out of this
8322
+ * subtree, so the handler speaks for the host, never for `event.target`.
8323
+ */
8324
+ _elementRef = inject((ElementRef));
8325
+ /**
8326
+ * @private Direction applying to this control, resolved once and cached
8327
+ * behind the shared `dir` observer rather than re-walked on every arrow
8328
+ * keypress.
8329
+ */
8330
+ _direction = this._rtlService.elementDirection(this._elementRef);
7274
8331
  /** @private Transaction and selection invalidation bridge. */
7275
8332
  _revision = inject(MLV_EDITOR_TOOLBAR_REVISION, {
7276
8333
  optional: true,
@@ -7524,7 +8581,11 @@ class MlvEditorTable {
7524
8581
  _onGridKeydown(event) {
7525
8582
  const current = this._selectedSize();
7526
8583
  let next = current;
7527
- switch (this._rtlService.normalizeArrowKey(event) ?? event.key) {
8584
+ // Columns run along the inline axis, resolved against this control's own
8585
+ // host rather than the document, so a scoped `dir` subtree — or a CDK
8586
+ // overlay pane, which carries its own `dir` — mirrors the pair correctly.
8587
+ switch (this._rtlService.normalizeArrowKey(event, this._direction()) ??
8588
+ event.key) {
7528
8589
  case LEFT_ARROW:
7529
8590
  next = { ...current, cols: Math.max(1, current.cols - 1) };
7530
8591
  break;
@@ -7567,10 +8628,10 @@ class MlvEditorTable {
7567
8628
  const index = (selected.rows - 1) * TABLE_GRID_BOUNDARY + (selected.cols - 1);
7568
8629
  this._gridCells()[index]?.nativeElement.focus();
7569
8630
  }
7570
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
7571
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorTable, isStandalone: true, selector: "mlv-editor-table", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-table" }, viewQueries: [{ propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, isSignal: true }, { propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_menuTrigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }, { propertyName: "_triggerElement", first: true, predicate: ["_triggerElement"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["_panel"], descendants: true, isSignal: true }, { propertyName: "_firstAction", first: true, predicate: ["_firstAction"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_gridCells", predicate: ["_gridCell"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "@if (_active()) {\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n selected\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().table\"\n [mlvTooltip]=\"_copy().table\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n <svg mlvButtonIcon lucideTable2 aria-hidden=\"true\" />\n </button>\n\n <mlv-menu #_menu [label]=\"_copy().table\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddRowBefore)\"\n (itemClick)=\"_run(_addRowBefore)\"\n >\n {{ _copy().addRowBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddRowAfter)\"\n (itemClick)=\"_run(_addRowAfter)\"\n >\n {{ _copy().addRowAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteRow)\"\n (itemClick)=\"_run(_deleteRow)\"\n >\n {{ _copy().deleteRow }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddColumnBefore)\"\n (itemClick)=\"_run(_addColumnBefore)\"\n >\n {{ _copy().addColumnBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddColumnAfter)\"\n (itemClick)=\"_run(_addColumnAfter)\"\n >\n {{ _copy().addColumnAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteColumn)\"\n (itemClick)=\"_run(_deleteColumn)\"\n >\n {{ _copy().deleteColumn }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canMergeCells)\"\n (itemClick)=\"_run(_mergeCells)\"\n >\n {{ _copy().mergeCells }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canSplitCell)\"\n (itemClick)=\"_run(_splitCell)\"\n >\n {{ _copy().splitCell }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderRow)\"\n (itemClick)=\"_run(_toggleHeaderRow)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderColumn)\"\n (itemClick)=\"_run(_toggleHeaderColumn)\"\n >\n {{ _copy().toggleHeaderColumn }}\n </mlv-list-item>\n <!-- `aria-current` marks the state the caret's cell is already in; the row\n paints itself from the shared selected pair. -->\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderCell)\"\n [attr.aria-current]=\"_isHeaderCell() ? 'true' : null\"\n (itemClick)=\"_run(_toggleHeaderCell)\"\n >\n {{ _copy().toggleHeaderCell }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteTable)\"\n (itemClick)=\"_run(_deleteTable, true)\"\n >\n {{ _copy().deleteTable }}\n </mlv-list-item>\n </mlv-menu>\n} @else {\n <button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().table\"\n [mlvTooltip]=\"_copy().table\"\n >\n <svg mlvButtonIcon lucideTable2 aria-hidden=\"true\" />\n </button>\n\n <mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().table\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n >\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-table__panel\">\n <div\n class=\"mlv-editor-table__grid\"\n role=\"grid\"\n tabindex=\"-1\"\n [attr.aria-label]=\"_copy().insertTable\"\n (keydown)=\"_onGridKeydown($event)\"\n >\n @for (row of _gridRows; track $index) {\n <div class=\"mlv-editor-table__grid-row\" role=\"row\">\n @for (size of row; track size.cols) {\n <button\n #_gridCell\n class=\"mlv-editor-table__grid-cell\"\n type=\"button\"\n role=\"gridcell\"\n [class.mlv-editor-table__grid-cell--selected]=\"\n _sizeSelected(size)\n \"\n [attr.aria-label]=\"_sizeLabel(size)\"\n [attr.aria-selected]=\"_sizeSelected(size)\"\n [attr.tabindex]=\"\n size.rows === _selectedSize().rows &&\n size.cols === _selectedSize().cols\n ? 0\n : -1\n \"\n [disabled]=\"_disabled()\"\n (focus)=\"_selectSize(size)\"\n (mouseenter)=\"_selectSize(size)\"\n (click)=\"_insertSize(size)\"\n ></button>\n }\n </div>\n }\n </div>\n\n <div class=\"mlv-editor-table__footer\">\n <span class=\"mlv-editor-table__size\" aria-hidden=\"true\">\n {{ _selectedSize().rows }} \u00D7 {{ _selectedSize().cols }}\n </span>\n <mlv-button-toggle\n [pressed]=\"_withHeaderRow()\"\n [disabled]=\"_disabled()\"\n (pressedChange)=\"_withHeaderRow.set($event)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-button-toggle>\n </div>\n </div>\n </ng-template>\n </mlv-popup>\n}\n", styles: ["@layer mlv.components{.mlv-editor-table{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-table *{box-sizing:border-box}.mlv-editor-table{display:inline-flex}.mlv-editor-table__panel{display:grid;width:max-content;max-width:calc(100vw - 3.5rem);padding:var(--mlv-popover-inset);gap:var(--mlv-spacing-2)}.mlv-editor-table__actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));width:min(24rem,100vw - 4rem);gap:var(--mlv-spacing-2)}.mlv-editor-table__grid{display:grid;grid-template-columns:1fr;gap:.125rem}.mlv-editor-table__grid-row{display:grid;grid-template-columns:repeat(10,1.25rem);gap:.125rem}.mlv-editor-table__grid-cell{appearance:none;box-sizing:border-box;inline-size:1.25rem;block-size:1.25rem;margin:0;padding:0;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-xs);background:var(--mlv-background-base);cursor:pointer;transition:background-color var(--mlv-duration-fast) var(--mlv-ease-default),border-color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor-table__grid-cell--selected{border-color:var(--mlv-border-focus);background:var(--mlv-background-selected)}.mlv-editor-table__grid-cell:disabled{cursor:default;opacity:var(--mlv-disabled-opacity)}.mlv-editor-table__grid-cell:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor-table__footer{display:flex;align-items:center;justify-content:space-between;gap:var(--mlv-spacing-3)}.mlv-editor-table__size{color:var(--mlv-text-secondary);font-size:var(--mlv-typography-body-s-size);font-variant-numeric:tabular-nums;white-space:nowrap}@media(prefers-reduced-motion:reduce){.mlv-editor-table,.mlv-editor-table *,.mlv-editor-table:before,.mlv-editor-table:after,[class^=mlv-editor-table__],[class*=\" mlv-editor-table__\"],[class^=mlv-editor-table--],[class*=\" mlv-editor-table--\"]{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:var(--mlv-duration-instant)!important}}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "component", type: MlvButtonToggle, selector: "mlv-button-toggle", inputs: ["pressed", "disabled", "variant", "shape", "mlvDensity", "ariaLabel", "ariaLabelledBy"], outputs: ["pressedChange"] }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideTable2, selector: "svg[lucideTable2]" }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
8631
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
8632
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorTable, isStandalone: true, selector: "mlv-editor-table", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-table" }, viewQueries: [{ propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, isSignal: true }, { propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_menuTrigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }, { propertyName: "_triggerElement", first: true, predicate: ["_triggerElement"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_panel", first: true, predicate: ["_panel"], descendants: true, isSignal: true }, { propertyName: "_firstAction", first: true, predicate: ["_firstAction"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "_gridCells", predicate: ["_gridCell"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "@if (_active()) {\n <button\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n selected\n [disabled]=\"_disabled()\"\n [mlvMenuTrigger]=\"_menu\"\n [menuTriggerDisabled]=\"_disabled()\"\n [attr.aria-label]=\"_copy().table\"\n [mlvTooltip]=\"_copy().table\"\n (menuOpened)=\"_registerMenuOverlay()\"\n (menuClosed)=\"_unregisterMenuOverlay()\"\n >\n <svg mlvButtonIcon lucideTable2 aria-hidden=\"true\" />\n </button>\n\n <mlv-menu #_menu [label]=\"_copy().table\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddRowBefore)\"\n (itemClick)=\"_run(_addRowBefore)\"\n >\n {{ _copy().addRowBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddRowAfter)\"\n (itemClick)=\"_run(_addRowAfter)\"\n >\n {{ _copy().addRowAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteRow)\"\n (itemClick)=\"_run(_deleteRow)\"\n >\n {{ _copy().deleteRow }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddColumnBefore)\"\n (itemClick)=\"_run(_addColumnBefore)\"\n >\n {{ _copy().addColumnBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canAddColumnAfter)\"\n (itemClick)=\"_run(_addColumnAfter)\"\n >\n {{ _copy().addColumnAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteColumn)\"\n (itemClick)=\"_run(_deleteColumn)\"\n >\n {{ _copy().deleteColumn }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canMergeCells)\"\n (itemClick)=\"_run(_mergeCells)\"\n >\n {{ _copy().mergeCells }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canSplitCell)\"\n (itemClick)=\"_run(_splitCell)\"\n >\n {{ _copy().splitCell }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderRow)\"\n (itemClick)=\"_run(_toggleHeaderRow)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderColumn)\"\n (itemClick)=\"_run(_toggleHeaderColumn)\"\n >\n {{ _copy().toggleHeaderColumn }}\n </mlv-list-item>\n <!-- `aria-current` marks the state the caret's cell is already in; the row\n paints itself from the shared selected pair. -->\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canToggleHeaderCell)\"\n [attr.aria-current]=\"_isHeaderCell() ? 'true' : null\"\n (itemClick)=\"_run(_toggleHeaderCell)\"\n >\n {{ _copy().toggleHeaderCell }}\n </mlv-list-item>\n\n <mlv-divider muted />\n\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_commandDisabled(_canDeleteTable)\"\n (itemClick)=\"_run(_deleteTable, true)\"\n >\n {{ _copy().deleteTable }}\n </mlv-list-item>\n </mlv-menu>\n} @else {\n <button\n #_trigger=\"mlvPopupTrigger\"\n #_triggerElement\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().table\"\n [mlvTooltip]=\"_copy().table\"\n >\n <svg mlvButtonIcon lucideTable2 aria-hidden=\"true\" />\n </button>\n\n <mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().table\"\n (afterOpened)=\"_onOpened()\"\n (afterClosed)=\"_onClosed()\"\n >\n <ng-template mlvPopupContent>\n <div #_panel class=\"mlv-editor-table__panel\">\n <div\n class=\"mlv-editor-table__grid\"\n role=\"grid\"\n tabindex=\"-1\"\n [attr.aria-label]=\"_copy().insertTable\"\n (keydown)=\"_onGridKeydown($event)\"\n >\n @for (row of _gridRows; track $index) {\n <div class=\"mlv-editor-table__grid-row\" role=\"row\">\n @for (size of row; track size.cols) {\n <button\n #_gridCell\n class=\"mlv-editor-table__grid-cell\"\n type=\"button\"\n role=\"gridcell\"\n [class.mlv-editor-table__grid-cell--selected]=\"\n _sizeSelected(size)\n \"\n [attr.aria-label]=\"_sizeLabel(size)\"\n [attr.aria-selected]=\"_sizeSelected(size)\"\n [attr.tabindex]=\"\n size.rows === _selectedSize().rows &&\n size.cols === _selectedSize().cols\n ? 0\n : -1\n \"\n [disabled]=\"_disabled()\"\n (focus)=\"_selectSize(size)\"\n (mouseenter)=\"_selectSize(size)\"\n (click)=\"_insertSize(size)\"\n ></button>\n }\n </div>\n }\n </div>\n\n <div class=\"mlv-editor-table__footer\">\n <span class=\"mlv-editor-table__size\" aria-hidden=\"true\">\n {{ _selectedSize().rows }} \u00D7 {{ _selectedSize().cols }}\n </span>\n <mlv-button-toggle\n [pressed]=\"_withHeaderRow()\"\n [disabled]=\"_disabled()\"\n (pressedChange)=\"_withHeaderRow.set($event)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-button-toggle>\n </div>\n </div>\n </ng-template>\n </mlv-popup>\n}\n", styles: ["@layer mlv.components{.mlv-editor-table{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-table *{box-sizing:border-box}.mlv-editor-table{display:inline-flex}.mlv-editor-table__panel{display:grid;width:max-content;max-width:calc(100vw - 3.5rem);padding:var(--mlv-popover-inset);gap:var(--mlv-spacing-2)}.mlv-editor-table__actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));width:min(24rem,100vw - 4rem);gap:var(--mlv-spacing-2)}.mlv-editor-table__grid{display:grid;grid-template-columns:1fr;gap:.125rem}.mlv-editor-table__grid-row{display:grid;grid-template-columns:repeat(10,1.25rem);gap:.125rem}.mlv-editor-table__grid-cell{appearance:none;box-sizing:border-box;inline-size:1.25rem;block-size:1.25rem;margin:0;padding:0;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-xs);background:var(--mlv-background-base);cursor:pointer;transition:background-color var(--mlv-duration-fast) var(--mlv-ease-default),border-color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor-table__grid-cell--selected{border-color:var(--mlv-border-focus);background:var(--mlv-background-selected)}.mlv-editor-table__grid-cell:disabled{cursor:default;opacity:var(--mlv-disabled-opacity)}.mlv-editor-table__grid-cell:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor-table__footer{display:flex;align-items:center;justify-content:space-between;gap:var(--mlv-spacing-3)}.mlv-editor-table__size{color:var(--mlv-text-secondary);font-size:var(--mlv-typography-body-s-size);font-variant-numeric:tabular-nums;white-space:nowrap}@media(prefers-reduced-motion:reduce){.mlv-editor-table,.mlv-editor-table *,.mlv-editor-table:before,.mlv-editor-table:after,[class^=mlv-editor-table__],[class*=\" mlv-editor-table__\"],[class^=mlv-editor-table--],[class*=\" mlv-editor-table--\"]{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:var(--mlv-duration-instant)!important}}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "component", type: MlvButtonToggle, selector: "mlv-button-toggle", inputs: ["pressed", "disabled", "variant", "shape", "mlvDensity", "ariaLabel", "ariaLabelledBy"], outputs: ["pressedChange"] }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideTable2, selector: "svg[lucideTable2]" }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
7572
8633
  }
7573
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorTable, decorators: [{
8634
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTable, decorators: [{
7574
8635
  type: Component,
7575
8636
  args: [{ selector: 'mlv-editor-table', imports: [
7576
8637
  MlvButton,
@@ -7753,21 +8814,24 @@ class MlvEditorTableControls {
7753
8814
  ...(ngDevMode ? [{ debugName: "_enabled" }] : /* istanbul ignore next */ []));
7754
8815
  constructor() {
7755
8816
  const destroyRef = inject(DestroyRef);
7756
- const onPointerMove = (event) => this._track(event);
7757
- const onPointerLeave = () => this._release();
7758
- // Registered on the document rather than on the editor DOM: the ProseMirror
8817
+ // Subscribed on the document rather than on the editor DOM: the ProseMirror
7759
8818
  // element is replaced whenever the editor is recreated (a format switch, an
7760
8819
  // extension-set change), and a listener bound to the old node would go
7761
- // quiet without ever erroring. Capture is not needed — the events bubble.
7762
- document.addEventListener('pointermove', onPointerMove, { passive: true });
7763
- document.addEventListener('pointerleave', onPointerLeave, {
7764
- passive: true,
7765
- });
7766
- destroyRef.onDestroy(() => {
7767
- document.removeEventListener('pointermove', onPointerMove);
7768
- document.removeEventListener('pointerleave', onPointerLeave);
7769
- this._unregisterOverlay();
7770
- });
8820
+ // quiet without ever erroring. Capture is not needed — the events bubble,
8821
+ // and `passive` is carried through because neither handler calls
8822
+ // `preventDefault()`.
8823
+ //
8824
+ // The *injected* document, never the ambient global: this component is
8825
+ // constructed during server rendering too, where the two are different
8826
+ // objects, so the bare global would bind a per-render component to a
8827
+ // process-wide document that no teardown reaches.
8828
+ fromEvent(this._document, 'pointermove', { passive: true })
8829
+ .pipe(takeUntilDestroyed(destroyRef))
8830
+ .subscribe((event) => this._track(event));
8831
+ fromEvent(this._document, 'pointerleave', { passive: true })
8832
+ .pipe(takeUntilDestroyed(destroyRef))
8833
+ .subscribe(() => this._release());
8834
+ destroyRef.onDestroy(() => this._unregisterOverlay());
7771
8835
  }
7772
8836
  /** @protected Inline placement for one grip box. */
7773
8837
  _style(box) {
@@ -7940,10 +9004,10 @@ class MlvEditorTableControls {
7940
9004
  gripSize: GRIP_SIZE,
7941
9005
  }));
7942
9006
  }
7943
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorTableControls, deps: [], target: i0.ɵɵFactoryTarget.Component });
7944
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorTableControls, isStandalone: true, selector: "mlv-editor-table-controls", inputs: { layer: { classPropertyName: "layer", publicName: "layer", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "aria-hidden": "true" }, classAttribute: "mlv-editor-table-controls" }, ngImport: i0, template: "@if (_geometry(); as geometry) {\n <button\n class=\"mlv-editor-table-controls__grip mlv-editor-table-controls__grip--corner\"\n type=\"button\"\n tabindex=\"-1\"\n [style]=\"_style(geometry.corner)\"\n [mlvMenuTrigger]=\"_cornerMenu\"\n [attr.aria-label]=\"_copy().tableActions\"\n (menuOpened)=\"_openMenu(_cornerMenu)\"\n (menuClosed)=\"_closeMenu()\"\n >\n <svg lucideEllipsis [size]=\"10\" aria-hidden=\"true\" />\n </button>\n <mlv-menu #_cornerMenu [label]=\"_copy().tableActions\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderRow)\"\n (itemClick)=\"_run(_toggleHeaderRow)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderColumn)\"\n (itemClick)=\"_run(_toggleHeaderColumn)\"\n >\n {{ _copy().toggleHeaderColumn }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderCell)\"\n (itemClick)=\"_run(_toggleHeaderCell)\"\n >\n {{ _copy().toggleHeaderCell }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canMergeCells)\"\n (itemClick)=\"_run(_mergeCells)\"\n >\n {{ _copy().mergeCells }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canSplitCell)\"\n (itemClick)=\"_run(_splitCell)\"\n >\n {{ _copy().splitCell }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canDeleteTable)\"\n (itemClick)=\"_run(_deleteTable)\"\n >\n {{ _copy().deleteTable }}\n </mlv-list-item>\n </mlv-menu>\n\n <button\n class=\"mlv-editor-table-controls__grip mlv-editor-table-controls__grip--column\"\n type=\"button\"\n tabindex=\"-1\"\n [style]=\"_style(geometry.column)\"\n [mlvMenuTrigger]=\"_columnMenu\"\n [attr.aria-label]=\"_copy().columnActions\"\n (menuOpened)=\"_openMenu(_columnMenu)\"\n (menuClosed)=\"_closeMenu()\"\n >\n <svg lucideGripHorizontal [size]=\"12\" aria-hidden=\"true\" />\n </button>\n <mlv-menu #_columnMenu [label]=\"_copy().columnActions\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddColumnBefore)\"\n (itemClick)=\"_run(_addColumnBefore)\"\n >\n {{ _copy().addColumnBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddColumnAfter)\"\n (itemClick)=\"_run(_addColumnAfter)\"\n >\n {{ _copy().addColumnAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderColumn)\"\n (itemClick)=\"_run(_toggleHeaderColumn)\"\n >\n {{ _copy().toggleHeaderColumn }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canDeleteColumn)\"\n (itemClick)=\"_run(_deleteColumn)\"\n >\n {{ _copy().deleteColumn }}\n </mlv-list-item>\n </mlv-menu>\n\n <button\n class=\"mlv-editor-table-controls__grip mlv-editor-table-controls__grip--row\"\n type=\"button\"\n tabindex=\"-1\"\n [style]=\"_style(geometry.row)\"\n [mlvMenuTrigger]=\"_rowMenu\"\n [attr.aria-label]=\"_copy().rowActions\"\n (menuOpened)=\"_openMenu(_rowMenu)\"\n (menuClosed)=\"_closeMenu()\"\n >\n <svg lucideGripVertical [size]=\"12\" aria-hidden=\"true\" />\n </button>\n <mlv-menu #_rowMenu [label]=\"_copy().rowActions\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddRowBefore)\"\n (itemClick)=\"_run(_addRowBefore)\"\n >\n {{ _copy().addRowBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddRowAfter)\"\n (itemClick)=\"_run(_addRowAfter)\"\n >\n {{ _copy().addRowAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderRow)\"\n (itemClick)=\"_run(_toggleHeaderRow)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canDeleteRow)\"\n (itemClick)=\"_run(_deleteRow)\"\n >\n {{ _copy().deleteRow }}\n </mlv-list-item>\n </mlv-menu>\n}\n", styles: ["@layer mlv.components{.mlv-editor-table-controls{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-table-controls *{box-sizing:border-box}.mlv-editor-table-controls{display:contents;--mlv-editor-table-grip-size: 14px}.mlv-editor-table-controls__grip{position:absolute;display:flex;align-items:center;justify-content:center;padding:0;border:var(--mlv-stroke-width) solid var(--mlv-border-subtle);border-radius:var(--mlv-radius-xs);background:var(--mlv-background-neutral-1);color:var(--mlv-text-secondary);cursor:pointer;z-index:1;transition:background-color var(--mlv-duration-fast) var(--mlv-ease-default),color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor-table-controls__grip:hover{background:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-primary)}.mlv-editor-table-controls__grip[aria-expanded=true]{background:var(--mlv-background-selected);color:var(--mlv-text-on-selected)}.mlv-editor-table-controls__grip--corner svg{opacity:.75}@media(prefers-reduced-motion:reduce){.mlv-editor-table-controls,.mlv-editor-table-controls *,.mlv-editor-table-controls:before,.mlv-editor-table-controls:after,[class^=mlv-editor-table-controls__],[class*=\" mlv-editor-table-controls__\"],[class^=mlv-editor-table-controls--],[class*=\" mlv-editor-table-controls--\"]{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:var(--mlv-duration-instant)!important}}}\n"], dependencies: [{ kind: "component", type: LucideEllipsis, selector: "svg[lucideEllipsis], svg[lucideMoreHorizontal]" }, { kind: "component", type: LucideGripHorizontal, selector: "svg[lucideGripHorizontal]" }, { kind: "component", type: LucideGripVertical, selector: "svg[lucideGripVertical]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
9007
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTableControls, deps: [], target: i0.ɵɵFactoryTarget.Component });
9008
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorTableControls, isStandalone: true, selector: "mlv-editor-table-controls", inputs: { layer: { classPropertyName: "layer", publicName: "layer", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "aria-hidden": "true" }, classAttribute: "mlv-editor-table-controls" }, ngImport: i0, template: "@if (_geometry(); as geometry) {\n <button\n class=\"mlv-editor-table-controls__grip mlv-editor-table-controls__grip--corner\"\n type=\"button\"\n tabindex=\"-1\"\n [style]=\"_style(geometry.corner)\"\n [mlvMenuTrigger]=\"_cornerMenu\"\n [attr.aria-label]=\"_copy().tableActions\"\n (menuOpened)=\"_openMenu(_cornerMenu)\"\n (menuClosed)=\"_closeMenu()\"\n >\n <svg lucideEllipsis [size]=\"10\" aria-hidden=\"true\" />\n </button>\n <mlv-menu #_cornerMenu [label]=\"_copy().tableActions\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderRow)\"\n (itemClick)=\"_run(_toggleHeaderRow)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderColumn)\"\n (itemClick)=\"_run(_toggleHeaderColumn)\"\n >\n {{ _copy().toggleHeaderColumn }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderCell)\"\n (itemClick)=\"_run(_toggleHeaderCell)\"\n >\n {{ _copy().toggleHeaderCell }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canMergeCells)\"\n (itemClick)=\"_run(_mergeCells)\"\n >\n {{ _copy().mergeCells }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canSplitCell)\"\n (itemClick)=\"_run(_splitCell)\"\n >\n {{ _copy().splitCell }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canDeleteTable)\"\n (itemClick)=\"_run(_deleteTable)\"\n >\n {{ _copy().deleteTable }}\n </mlv-list-item>\n </mlv-menu>\n\n <button\n class=\"mlv-editor-table-controls__grip mlv-editor-table-controls__grip--column\"\n type=\"button\"\n tabindex=\"-1\"\n [style]=\"_style(geometry.column)\"\n [mlvMenuTrigger]=\"_columnMenu\"\n [attr.aria-label]=\"_copy().columnActions\"\n (menuOpened)=\"_openMenu(_columnMenu)\"\n (menuClosed)=\"_closeMenu()\"\n >\n <svg lucideGripHorizontal [size]=\"12\" aria-hidden=\"true\" />\n </button>\n <mlv-menu #_columnMenu [label]=\"_copy().columnActions\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddColumnBefore)\"\n (itemClick)=\"_run(_addColumnBefore)\"\n >\n {{ _copy().addColumnBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddColumnAfter)\"\n (itemClick)=\"_run(_addColumnAfter)\"\n >\n {{ _copy().addColumnAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderColumn)\"\n (itemClick)=\"_run(_toggleHeaderColumn)\"\n >\n {{ _copy().toggleHeaderColumn }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canDeleteColumn)\"\n (itemClick)=\"_run(_deleteColumn)\"\n >\n {{ _copy().deleteColumn }}\n </mlv-list-item>\n </mlv-menu>\n\n <button\n class=\"mlv-editor-table-controls__grip mlv-editor-table-controls__grip--row\"\n type=\"button\"\n tabindex=\"-1\"\n [style]=\"_style(geometry.row)\"\n [mlvMenuTrigger]=\"_rowMenu\"\n [attr.aria-label]=\"_copy().rowActions\"\n (menuOpened)=\"_openMenu(_rowMenu)\"\n (menuClosed)=\"_closeMenu()\"\n >\n <svg lucideGripVertical [size]=\"12\" aria-hidden=\"true\" />\n </button>\n <mlv-menu #_rowMenu [label]=\"_copy().rowActions\">\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddRowBefore)\"\n (itemClick)=\"_run(_addRowBefore)\"\n >\n {{ _copy().addRowBefore }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canAddRowAfter)\"\n (itemClick)=\"_run(_addRowAfter)\"\n >\n {{ _copy().addRowAfter }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canToggleHeaderRow)\"\n (itemClick)=\"_run(_toggleHeaderRow)\"\n >\n {{ _copy().toggleHeaderRow }}\n </mlv-list-item>\n <mlv-list-item\n mlvMenuItem\n [disabled]=\"_disabled(_canDeleteRow)\"\n (itemClick)=\"_run(_deleteRow)\"\n >\n {{ _copy().deleteRow }}\n </mlv-list-item>\n </mlv-menu>\n}\n", styles: ["@layer mlv.components{.mlv-editor-table-controls{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-table-controls *{box-sizing:border-box}.mlv-editor-table-controls{display:contents;--mlv-editor-table-grip-size: 14px}.mlv-editor-table-controls__grip{position:absolute;display:flex;align-items:center;justify-content:center;padding:0;border:var(--mlv-stroke-width) solid var(--mlv-border-subtle);border-radius:var(--mlv-radius-xs);background:var(--mlv-background-neutral-1);color:var(--mlv-text-secondary);cursor:pointer;z-index:1;transition:background-color var(--mlv-duration-fast) var(--mlv-ease-default),color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor-table-controls__grip:hover{background:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-primary)}.mlv-editor-table-controls__grip[aria-expanded=true]{background:var(--mlv-background-selected);color:var(--mlv-text-on-selected)}.mlv-editor-table-controls__grip--corner svg{opacity:.75}@media(prefers-reduced-motion:reduce){.mlv-editor-table-controls,.mlv-editor-table-controls *,.mlv-editor-table-controls:before,.mlv-editor-table-controls:after,[class^=mlv-editor-table-controls__],[class*=\" mlv-editor-table-controls__\"],[class^=mlv-editor-table-controls--],[class*=\" mlv-editor-table-controls--\"]{scroll-behavior:auto!important;animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:var(--mlv-duration-instant)!important}}}\n"], dependencies: [{ kind: "component", type: LucideEllipsis, selector: "svg[lucideEllipsis], svg[lucideMoreHorizontal]" }, { kind: "component", type: LucideGripHorizontal, selector: "svg[lucideGripHorizontal]" }, { kind: "component", type: LucideGripVertical, selector: "svg[lucideGripVertical]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
7945
9009
  }
7946
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorTableControls, decorators: [{
9010
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTableControls, decorators: [{
7947
9011
  type: Component,
7948
9012
  args: [{ selector: 'mlv-editor-table-controls', imports: [
7949
9013
  LucideEllipsis,
@@ -7969,10 +9033,10 @@ class MlvEditorToolbarDef {
7969
9033
  static ngTemplateContextGuard(_dir, _context) {
7970
9034
  return true;
7971
9035
  }
7972
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
7973
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.7", type: MlvEditorToolbarDef, isStandalone: true, selector: "[mlvEditorToolbar]", ngImport: i0 });
9036
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
9037
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorToolbarDef, isStandalone: true, selector: "[mlvEditorToolbar]", ngImport: i0 });
7974
9038
  }
7975
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarDef, decorators: [{
9039
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarDef, decorators: [{
7976
9040
  type: Directive,
7977
9041
  args: [{
7978
9042
  selector: '[mlvEditorToolbar]',
@@ -7988,10 +9052,10 @@ class MlvEditorToolbarStartDef {
7988
9052
  static ngTemplateContextGuard(_dir, _context) {
7989
9053
  return true;
7990
9054
  }
7991
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarStartDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
7992
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.7", type: MlvEditorToolbarStartDef, isStandalone: true, selector: "[mlvEditorToolbarStart]", ngImport: i0 });
9055
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarStartDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
9056
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorToolbarStartDef, isStandalone: true, selector: "[mlvEditorToolbarStart]", ngImport: i0 });
7993
9057
  }
7994
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarStartDef, decorators: [{
9058
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarStartDef, decorators: [{
7995
9059
  type: Directive,
7996
9060
  args: [{
7997
9061
  selector: '[mlvEditorToolbarStart]',
@@ -8007,10 +9071,10 @@ class MlvEditorToolbarEndDef {
8007
9071
  static ngTemplateContextGuard(_dir, _context) {
8008
9072
  return true;
8009
9073
  }
8010
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarEndDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
8011
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.7", type: MlvEditorToolbarEndDef, isStandalone: true, selector: "[mlvEditorToolbarEnd]", ngImport: i0 });
9074
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarEndDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
9075
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorToolbarEndDef, isStandalone: true, selector: "[mlvEditorToolbarEnd]", ngImport: i0 });
8012
9076
  }
8013
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarEndDef, decorators: [{
9077
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarEndDef, decorators: [{
8014
9078
  type: Directive,
8015
9079
  args: [{
8016
9080
  selector: '[mlvEditorToolbarEnd]',
@@ -8039,8 +9103,8 @@ class MlvEditorUndoRedo {
8039
9103
  _undo = (editor) => editor.chain().focus().undo().run();
8040
9104
  /** @protected Selection-restoring redo command. */
8041
9105
  _redo = (editor) => editor.chain().focus().redo().run();
8042
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorUndoRedo, deps: [], target: i0.ɵɵFactoryTarget.Component });
8043
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.7", type: MlvEditorUndoRedo, isStandalone: true, selector: "mlv-editor-undo-redo", host: { attributes: { "role": "group" }, classAttribute: "mlv-editor-undo-redo" }, ngImport: i0, template: `
9106
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorUndoRedo, deps: [], target: i0.ɵɵFactoryTarget.Component });
9107
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorUndoRedo, isStandalone: true, selector: "mlv-editor-undo-redo", host: { attributes: { "role": "group" }, classAttribute: "mlv-editor-undo-redo" }, ngImport: i0, template: `
8044
9108
  <mlv-editor-command-button
8045
9109
  [label]="_copy().undo"
8046
9110
  [supports]="_canUndo"
@@ -8057,7 +9121,7 @@ class MlvEditorUndoRedo {
8057
9121
  /></mlv-editor-command-button>
8058
9122
  `, isInline: true, dependencies: [{ kind: "component", type: MlvEditorCommandButton, selector: "mlv-editor-command-button", inputs: ["label", "supports", "command", "canCommand", "pressed"] }, { kind: "component", type: LucideRedo2, selector: "svg[lucideRedo2]" }, { kind: "component", type: LucideUndo2, selector: "svg[lucideUndo2]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
8059
9123
  }
8060
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorUndoRedo, decorators: [{
9124
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorUndoRedo, decorators: [{
8061
9125
  type: Component,
8062
9126
  args: [{
8063
9127
  selector: 'mlv-editor-undo-redo',
@@ -8428,10 +9492,10 @@ class MlvEditorZoom {
8428
9492
  const finite = Number.isFinite(value) ? value : DEFAULT_ZOOM;
8429
9493
  return Math.min(Math.max(finite, min), max);
8430
9494
  }
8431
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorZoom, deps: [], target: i0.ɵɵFactoryTarget.Component });
8432
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.7", type: MlvEditorZoom, isStandalone: true, selector: "mlv-editor-zoom", inputs: { zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, zoomLevels: { classPropertyName: "zoomLevels", publicName: "zoomLevels", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, fitToContainer: { classPropertyName: "fitToContainer", publicName: "fitToContainer", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zoom: "zoomChange" }, host: { classAttribute: "mlv-editor-zoom" }, providers: [MlvSelectionService], viewQueries: [{ propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, isSignal: true }, { propertyName: "_input", first: true, predicate: ["_input"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<button\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_zoomOutDisabled()\"\n [attr.aria-label]=\"_copy().zoomOut\"\n [mlvTooltip]=\"_copy().zoomOut\"\n (click)=\"_zoomOut()\"\n>\n <svg mlvButtonIcon lucideZoomOut aria-hidden=\"true\" />\n</button>\n\n<button\n #_trigger=\"mlvPopupTrigger\"\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().zoom\"\n [mlvTooltip]=\"_copy().zoom\"\n>\n {{ zoom() }}%\n</button>\n\n<button\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_zoomInDisabled()\"\n [attr.aria-label]=\"_copy().zoomIn\"\n [mlvTooltip]=\"_copy().zoomIn\"\n (click)=\"_zoomIn()\"\n>\n <svg mlvButtonIcon lucideZoomIn aria-hidden=\"true\" />\n</button>\n\n<mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().zoom\"\n (afterOpened)=\"_onPopupOpened()\"\n (afterClosed)=\"_onPopupClosed()\"\n>\n <ng-template mlvPopupContent>\n <div [id]=\"_panelId\" class=\"mlv-editor-zoom__panel\">\n <div class=\"mlv-editor-zoom__field\">\n <mlv-input\n #_input\n type=\"number\"\n inputMode=\"numeric\"\n [ariaLabel]=\"_copy().zoom\"\n [min]=\"_bounds().min\"\n [max]=\"_bounds().max\"\n [disabled]=\"_disabled()\"\n [value]=\"_draft()\"\n (valueChange)=\"_draft.set($event)\"\n (keydown.enter)=\"_applyDraft($event)\"\n (inputBlur)=\"_applyDraft()\"\n />\n </div>\n <mlv-dropdown-panel\n class=\"mlv-editor-zoom__levels\"\n [options]=\"_levelOptions()\"\n [selectedValues]=\"_selectedLevels()\"\n [ariaLabel]=\"_copy().zoom\"\n [maxHeight]=\"_PANEL_MAX_HEIGHT\"\n (valueChange)=\"_selectLevel($event)\"\n />\n </div>\n </ng-template>\n</mlv-popup>\n", styles: ["@layer mlv.components{.mlv-editor-zoom{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size)}.mlv-editor-zoom *{box-sizing:border-box}.mlv-editor-zoom{display:inline-flex;align-items:center;gap:var(--mlv-spacing-1);white-space:nowrap}.mlv-editor-zoom__panel{display:grid;inline-size:11rem;padding:var(--mlv-popover-inset);gap:var(--mlv-spacing-1)}.mlv-editor-zoom__field,.mlv-editor-zoom__levels{display:block}.mlv-editor-zoom__levels.mlv-dropdown-panel .mlv-dropdown-panel__listbox{--mlv-list-padding-block: 0}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideZoomIn, selector: "svg[lucideZoomIn]" }, { kind: "component", type: LucideZoomOut, selector: "svg[lucideZoomOut]" }, { kind: "component", type: MlvDropdownPanel, selector: "mlv-dropdown-panel", inputs: ["scrollMode", "minHeight", "maxHeight", "options", "multiple", "selectedValues", "itemTemplate", "focusMode", "activeIndex", "listboxId", "ariaLabel", "highlightQuery", "loading", "loadingText", "loadingMore", "hasMore", "infiniteScrollThreshold"], outputs: ["loadMore", "valueChange"] }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
9495
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorZoom, deps: [], target: i0.ɵɵFactoryTarget.Component });
9496
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.7", type: MlvEditorZoom, isStandalone: true, selector: "mlv-editor-zoom", inputs: { zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, zoomLevels: { classPropertyName: "zoomLevels", publicName: "zoomLevels", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, fitToContainer: { classPropertyName: "fitToContainer", publicName: "fitToContainer", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zoom: "zoomChange" }, host: { classAttribute: "mlv-editor-zoom" }, providers: [MlvSelectionService], viewQueries: [{ propertyName: "_popup", first: true, predicate: ["_popup"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: ["_trigger"], descendants: true, isSignal: true }, { propertyName: "_input", first: true, predicate: ["_input"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<button\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_zoomOutDisabled()\"\n [attr.aria-label]=\"_copy().zoomOut\"\n [mlvTooltip]=\"_copy().zoomOut\"\n (click)=\"_zoomOut()\"\n>\n <svg mlvButtonIcon lucideZoomOut aria-hidden=\"true\" />\n</button>\n\n<button\n #_trigger=\"mlvPopupTrigger\"\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_disabled()\"\n [mlvPopupTrigger]=\"_popup\"\n [ariaHasPopup]=\"'dialog'\"\n [attr.aria-label]=\"_copy().zoom\"\n [mlvTooltip]=\"_copy().zoom\"\n>\n {{ zoom() }}%\n</button>\n\n<button\n mlvButton\n mlvEditorToolbarWidget\n type=\"button\"\n shape=\"square\"\n mlvDensity=\"tight\"\n variant=\"transparent\"\n [disabled]=\"_zoomInDisabled()\"\n [attr.aria-label]=\"_copy().zoomIn\"\n [mlvTooltip]=\"_copy().zoomIn\"\n (click)=\"_zoomIn()\"\n>\n <svg mlvButtonIcon lucideZoomIn aria-hidden=\"true\" />\n</button>\n\n<mlv-popup\n #_popup\n panelRole=\"dialog\"\n [modal]=\"true\"\n [ariaLabel]=\"_copy().zoom\"\n (afterOpened)=\"_onPopupOpened()\"\n (afterClosed)=\"_onPopupClosed()\"\n>\n <ng-template mlvPopupContent>\n <div [id]=\"_panelId\" class=\"mlv-editor-zoom__panel\">\n <div class=\"mlv-editor-zoom__field\">\n <mlv-input\n #_input\n type=\"number\"\n inputMode=\"numeric\"\n [ariaLabel]=\"_copy().zoom\"\n [min]=\"_bounds().min\"\n [max]=\"_bounds().max\"\n [disabled]=\"_disabled()\"\n [value]=\"_draft()\"\n (valueChange)=\"_draft.set($event)\"\n (keydown.enter)=\"_applyDraft($event)\"\n (inputBlur)=\"_applyDraft()\"\n />\n </div>\n <mlv-dropdown-panel\n class=\"mlv-editor-zoom__levels\"\n [options]=\"_levelOptions()\"\n [selectedValues]=\"_selectedLevels()\"\n [ariaLabel]=\"_copy().zoom\"\n [maxHeight]=\"_PANEL_MAX_HEIGHT\"\n (valueChange)=\"_selectLevel($event)\"\n />\n </div>\n </ng-template>\n</mlv-popup>\n", styles: ["@layer mlv.components{.mlv-editor-zoom{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size)}.mlv-editor-zoom *{box-sizing:border-box}.mlv-editor-zoom{display:inline-flex;align-items:center;gap:var(--mlv-spacing-1);white-space:nowrap}.mlv-editor-zoom__panel{display:grid;inline-size:11rem;padding:var(--mlv-popover-inset);gap:var(--mlv-spacing-1)}.mlv-editor-zoom__field,.mlv-editor-zoom__levels{display:block}.mlv-editor-zoom__levels.mlv-dropdown-panel .mlv-dropdown-panel__listbox{--mlv-list-padding-block: 0}}\n"], dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideZoomIn, selector: "svg[lucideZoomIn]" }, { kind: "component", type: LucideZoomOut, selector: "svg[lucideZoomOut]" }, { kind: "component", type: MlvDropdownPanel, selector: "mlv-dropdown-panel", inputs: ["scrollMode", "minHeight", "maxHeight", "options", "multiple", "selectedValues", "compareWith", "itemTemplate", "focusMode", "activeIndex", "listboxId", "ariaLabel", "highlightQuery", "loading", "loadingText", "loadingMore", "hasMore", "infiniteScrollThreshold"], outputs: ["loadMore", "valueChange"] }, { kind: "component", type: MlvInput, selector: "mlv-input", inputs: ["type", "placeholder", "autocomplete", "inputMode", "maxLength", "minLength", "pattern", "name", "autofocus", "min", "max", "step", "bare", "projectControl", "role", "ariaExpanded", "ariaHasPopup", "ariaAutocomplete", "ariaControls", "ariaActiveDescendant", "ariaDescribedBy", "value"], outputs: ["valueChange", "inputFocus", "inputBlur"] }, { kind: "component", type: MlvPopup, selector: "mlv-popup", inputs: ["position", "positions", "width", "height", "minWidth", "minHeight", "maxWidth", "maxHeight", "hasArrow", "hasShadow", "class", "mlvDensity", "flexibleDimensions", "scrollStrategy", "hasBackdrop", "restrictPosition", "panelRole", "modal", "ariaLabel", "dismissExcludeElements", "mobileMode", "mobileBreakpoint", "mobileTitle", "mobileCloseLabel", "opened"], outputs: ["openedChange", "afterOpened", "afterClosed"] }, { kind: "directive", type: MlvPopupContent, selector: "[mlvPopupContent]" }, { kind: "directive", type: MlvPopupTrigger, selector: "[mlvPopupTrigger]", inputs: ["mlvPopupTrigger", "triggerOn", "ariaHasPopup"], exportAs: ["mlvPopupTrigger"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
8433
9497
  }
8434
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorZoom, decorators: [{
9498
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorZoom, decorators: [{
8435
9499
  type: Component,
8436
9500
  args: [{ selector: 'mlv-editor-zoom', imports: [
8437
9501
  MlvButton,
@@ -8477,8 +9541,8 @@ class MlvEditorTextColor extends MlvEditorColorControl {
8477
9541
  const chain = editor.chain().setTextSelection(selection);
8478
9542
  return color ? chain.setColor(color).run() : chain.unsetColor().run();
8479
9543
  }
8480
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorTextColor, deps: null, target: i0.ɵɵFactoryTarget.Component });
8481
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.7", type: MlvEditorTextColor, isStandalone: true, selector: "mlv-editor-text-color", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-text-color" }, usesInheritance: true, ngImport: i0, template: `
9544
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTextColor, deps: null, target: i0.ɵɵFactoryTarget.Component });
9545
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.7", type: MlvEditorTextColor, isStandalone: true, selector: "mlv-editor-text-color", host: { properties: { "hidden": "!_supported()" }, classAttribute: "mlv-editor-text-color" }, usesInheritance: true, ngImport: i0, template: `
8482
9546
  <mlv-color-picker-popup
8483
9547
  #picker
8484
9548
  presentation="icon"
@@ -8495,7 +9559,7 @@ class MlvEditorTextColor extends MlvEditorColorControl {
8495
9559
  </mlv-color-picker-popup>
8496
9560
  `, isInline: true, dependencies: [{ kind: "component", type: MlvColorPickerPopup, selector: "mlv-color-picker-popup", inputs: ["value", "presentation", "opened", "showOpacity", "defaultMode", "supportedFormats", "live"], outputs: ["valueChange", "openedChange", "afterOpened", "afterClosed", "colorChange"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "component", type: LucideType, selector: "svg[lucideType]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
8497
9561
  }
8498
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorTextColor, decorators: [{
9562
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorTextColor, decorators: [{
8499
9563
  type: Component,
8500
9564
  args: [{
8501
9565
  selector: 'mlv-editor-text-color',
@@ -8540,6 +9604,16 @@ class MlvEditorToolbarRoot {
8540
9604
  ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
8541
9605
  /** Whether arrow navigation wraps at the boundaries. */
8542
9606
  wrap = input(true, { ...(ngDevMode ? { debugName: "wrap" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
9607
+ /**
9608
+ * @internal Element whose width decides narrow mode, instead of this root's
9609
+ * own. `MlvEditor` passes its `.mlv-editor__surface`, for the docked bar
9610
+ * and the selection bubble alike. The bubble hugs its controls, so measuring
9611
+ * itself would be circular: hiding groups shrinks the bubble, which
9612
+ * un-narrows it. `undefined` observes this root, which the standalone
9613
+ * `MlvEditorToolbar` shell keeps doing.
9614
+ */
9615
+ measureTarget = input(undefined, /* @ts-ignore */
9616
+ ...(ngDevMode ? [{ debugName: "measureTarget" }] : /* istanbul ignore next */ []));
8543
9617
  /** @internal Editor-scoped focus registry. */
8544
9618
  _registry = inject(MLV_EDITOR_TOOLBAR_ROVING);
8545
9619
  /** @internal Transaction state that re-evaluates disabled toolbar widgets. */
@@ -8548,7 +9622,10 @@ class MlvEditorToolbarRoot {
8548
9622
  });
8549
9623
  /** @internal Optional localized defaults. */
8550
9624
  _i18n = inject(MLV_EDITOR_I18N, { optional: true });
8551
- /** @internal Actual composite-root element. */
9625
+ /**
9626
+ * @internal Actual composite-root element. Also the scope the horizontal
9627
+ * roving arrows resolve their direction against.
9628
+ */
8552
9629
  _element = inject(ElementRef);
8553
9630
  /** @internal Root teardown lifecycle. */
8554
9631
  _destroyRef = inject(DestroyRef);
@@ -8558,6 +9635,12 @@ class MlvEditorToolbarRoot {
8558
9635
  _resizeObserver = inject(MlvResizeObserverService);
8559
9636
  /** @internal Normalizes horizontal arrow meaning for RTL toolbars. */
8560
9637
  _rtlService = inject(MlvRtlService);
9638
+ /**
9639
+ * @internal Direction applying to this toolbar, resolved once and cached
9640
+ * behind the shared `dir` observer rather than re-walked on every arrow
9641
+ * keypress.
9642
+ */
9643
+ _direction = this._rtlService.elementDirection(this._element);
8561
9644
  constructor() {
8562
9645
  effect(() => {
8563
9646
  this._revision?.();
@@ -8565,15 +9648,23 @@ class MlvEditorToolbarRoot {
8565
9648
  });
8566
9649
  if (this._isBrowser) {
8567
9650
  this._destroyRef.onDestroy(this._registry.connect(this._element.nativeElement));
8568
- this._resizeObserver
8569
- .observe(this._element)
8570
- .pipe(takeUntilDestroyed(this._destroyRef))
9651
+ }
9652
+ effect((onCleanup) => {
9653
+ if (!this._isBrowser)
9654
+ return;
9655
+ const target = this.measureTarget() ?? this._element.nativeElement;
9656
+ // Re-created whenever the target changes, so it is released by this
9657
+ // effect's cleanup; `takeUntilDestroyed` would fire only at destroy and
9658
+ // leak every earlier generation (best-practices, "DOM Listeners").
9659
+ const subscription = this._resizeObserver
9660
+ .observe(target)
8571
9661
  .subscribe((entries) => {
8572
9662
  const width = entries[entries.length - 1]?.contentRect.width;
8573
9663
  if (width !== undefined && width > 0)
8574
9664
  this.narrow.set(width < 640);
8575
9665
  });
8576
- }
9666
+ onCleanup(() => subscription.unsubscribe());
9667
+ });
8577
9668
  }
8578
9669
  /** @internal Resolves the user or localized toolbar label. */
8579
9670
  resolvedAriaLabel() {
@@ -8591,7 +9682,10 @@ class MlvEditorToolbarRoot {
8591
9682
  this._registry.focusBoundary(event.key === 'Home' ? 'start' : 'end');
8592
9683
  return;
8593
9684
  }
8594
- const key = this._rtlService.normalizeArrowKey(event);
9685
+ // Resolved against the toolbar root, not the document: direction is scoped,
9686
+ // so a toolbar inside a `dir` subtree — or inside a CDK overlay pane, which
9687
+ // is stamped with its own `dir` — must mirror on its own reading.
9688
+ const key = this._rtlService.normalizeArrowKey(event, this._direction());
8595
9689
  const direction = this.orientation() === 'vertical'
8596
9690
  ? key === UP_ARROW
8597
9691
  ? -1
@@ -8608,10 +9702,10 @@ class MlvEditorToolbarRoot {
8608
9702
  event.preventDefault();
8609
9703
  this._registry.move(target, direction, this.wrap());
8610
9704
  }
8611
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive });
8612
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: MlvEditorToolbarRoot, isStandalone: true, selector: "[mlvEditorToolbarRoot]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "wrap", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "toolbar" }, listeners: { "keydown": "handleKeydown($event)" }, properties: { "class.mlv-editor-toolbar--narrow": "narrow()", "attr.aria-label": "resolvedAriaLabel()", "attr.aria-orientation": "orientation()", "attr.aria-disabled": "disabled() || null" }, classAttribute: "mlv-editor-toolbar" }, exportAs: ["mlvEditorToolbarRoot"], ngImport: i0 });
9705
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive });
9706
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: MlvEditorToolbarRoot, isStandalone: true, selector: "[mlvEditorToolbarRoot]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, wrap: { classPropertyName: "wrap", publicName: "wrap", isSignal: true, isRequired: false, transformFunction: null }, measureTarget: { classPropertyName: "measureTarget", publicName: "measureTarget", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "toolbar" }, listeners: { "keydown": "handleKeydown($event)" }, properties: { "class.mlv-editor-toolbar--narrow": "narrow()", "attr.aria-label": "resolvedAriaLabel()", "attr.aria-orientation": "orientation()", "attr.aria-disabled": "disabled() || null" }, classAttribute: "mlv-editor-toolbar" }, exportAs: ["mlvEditorToolbarRoot"], ngImport: i0 });
8613
9707
  }
8614
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarRoot, decorators: [{
9708
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarRoot, decorators: [{
8615
9709
  type: Directive,
8616
9710
  args: [{
8617
9711
  selector: '[mlvEditorToolbarRoot]',
@@ -8626,7 +9720,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
8626
9720
  '(keydown)': 'handleKeydown($event)',
8627
9721
  },
8628
9722
  }]
8629
- }], ctorParameters: () => [], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrap", required: false }] }] } });
9723
+ }], ctorParameters: () => [], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], wrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrap", required: false }] }], measureTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "measureTarget", required: false }] }] } });
8630
9724
  /** @internal Responsive menu containing commands removed from the primary row. */
8631
9725
  class MlvEditorToolbarOverflow {
8632
9726
  /** @protected Responsive state owned by the enclosing toolbar root. */
@@ -8777,8 +9871,8 @@ class MlvEditorToolbarOverflow {
8777
9871
  this._unregister?.();
8778
9872
  this._unregister = undefined;
8779
9873
  }
8780
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarOverflow, deps: [], target: i0.ɵɵFactoryTarget.Component });
8781
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorToolbarOverflow, isStandalone: true, selector: "mlv-editor-toolbar-overflow", host: { properties: { "hidden": "!_root.narrow()" }, classAttribute: "mlv-editor-toolbar__overflow" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
9874
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarOverflow, deps: [], target: i0.ɵɵFactoryTarget.Component });
9875
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorToolbarOverflow, isStandalone: true, selector: "mlv-editor-toolbar-overflow", host: { properties: { "hidden": "!_root.narrow()" }, classAttribute: "mlv-editor-toolbar__overflow" }, viewQueries: [{ propertyName: "_menu", first: true, predicate: ["_menu"], descendants: true, isSignal: true }, { propertyName: "_trigger", first: true, predicate: MlvMenuTrigger, descendants: true, isSignal: true }], ngImport: i0, template: `
8782
9876
  <button
8783
9877
  mlvButton
8784
9878
  mlvEditorToolbarWidget
@@ -8876,7 +9970,7 @@ class MlvEditorToolbarOverflow {
8876
9970
  </mlv-menu>
8877
9971
  `, isInline: true, dependencies: [{ kind: "component", type: MlvButton, selector: "button[mlvButton], a[mlvButton]", inputs: ["variant", "shape", "disabled", "loading", "selected"] }, { kind: "directive", type: MlvButtonIcon, selector: "[mlvButtonIcon]" }, { kind: "directive", type: MlvTooltip, selector: "[mlvTooltip]", inputs: ["mlvTooltip", "tooltipPlacement", "tooltipTone", "tooltipDelay", "tooltipDisabled", "tooltipArrow"] }, { kind: "component", type: LucideEllipsis, selector: "svg[lucideEllipsis], svg[lucideMoreHorizontal]" }, { kind: "component", type: MlvListItem, selector: "mlv-list-item", inputs: ["itemRole", "unread", "accent"] }, { kind: "component", type: MlvMenu, selector: "mlv-menu", inputs: ["label", "mlvDensity", "dataSource"], outputs: ["closed"] }, { kind: "directive", type: MlvMenuItem, selector: "mlv-list-item[mlvMenuItem]", inputs: ["disabled"], outputs: ["itemClick"] }, { kind: "directive", type: MlvMenuTrigger, selector: "[mlvMenuTrigger]", inputs: ["mlvMenuTrigger", "isSubmenuTrigger", "menuTriggerDisabled"], outputs: ["menuOpened", "menuClosed"] }, { kind: "directive", type: MlvEditorToolbarWidget, selector: "[mlvEditorToolbarWidget]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
8878
9972
  }
8879
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarOverflow, decorators: [{
9973
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarOverflow, decorators: [{
8880
9974
  type: Component,
8881
9975
  args: [{
8882
9976
  selector: 'mlv-editor-toolbar-overflow',
@@ -9047,10 +10141,10 @@ class MlvEditorToolbarContextProxy {
9047
10141
  reportError(error) {
9048
10142
  this._toolbar.context().reportError(error);
9049
10143
  }
9050
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarContextProxy, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
9051
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarContextProxy });
10144
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarContextProxy, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
10145
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarContextProxy });
9052
10146
  }
9053
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbarContextProxy, decorators: [{
10147
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbarContextProxy, decorators: [{
9054
10148
  type: Injectable
9055
10149
  }] });
9056
10150
  /** Public compatibility toolbar shell for standalone consumer use. */
@@ -9086,8 +10180,8 @@ class MlvEditorToolbar {
9086
10180
  });
9087
10181
  });
9088
10182
  }
9089
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
9090
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorToolbar, isStandalone: true, selector: "mlv-editor-toolbar", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, startTemplate: { classPropertyName: "startTemplate", publicName: "startTemplate", isSignal: true, isRequired: false, transformFunction: null }, endTemplate: { classPropertyName: "endTemplate", publicName: "endTemplate", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.mlv-editor-toolbar--disabled": "disabled() || context().disabled()" }, classAttribute: "mlv-editor-toolbar" }, providers: [
10183
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
10184
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorToolbar, isStandalone: true, selector: "mlv-editor-toolbar", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, startTemplate: { classPropertyName: "startTemplate", publicName: "startTemplate", isSignal: true, isRequired: false, transformFunction: null }, endTemplate: { classPropertyName: "endTemplate", publicName: "endTemplate", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.mlv-editor-toolbar--disabled": "disabled() || context().disabled()" }, classAttribute: "mlv-editor-toolbar" }, providers: [
9091
10185
  MlvEditorOverlayRegistry,
9092
10186
  MlvEditorToolbarRevision,
9093
10187
  MlvEditorToolbarRovingRegistry,
@@ -9192,9 +10286,9 @@ class MlvEditorToolbar {
9192
10286
  />
9193
10287
  }
9194
10288
  <ng-content select="[mlvEditorToolbarEnd]" /><ng-content />
9195
- </div>`, isInline: true, styles: ["@layer mlv.components{.mlv-editor-toolbar{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size)}.mlv-editor-toolbar *{box-sizing:border-box}.mlv-editor-toolbar{display:flex;align-items:center;min-width:0;overflow:hidden}.mlv-editor-toolbar [hidden]{display:none!important}.mlv-editor-toolbar__scroll{flex:1 1 auto;min-width:0}.mlv-editor-toolbar__scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor-toolbar__separator{flex:0 0 auto;align-self:stretch;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor-toolbar__overflow,.mlv-editor-toolbar .mlv-editor-undo-redo,.mlv-editor-toolbar .mlv-editor-inline-marks,.mlv-editor-toolbar .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-toolbar mlv-editor-zoom,.mlv-editor-toolbar mlv-editor-heading,.mlv-editor-toolbar mlv-editor-list,.mlv-editor-toolbar mlv-editor-text-color,.mlv-editor-toolbar mlv-editor-highlight,.mlv-editor-toolbar mlv-editor-alignment,.mlv-editor-toolbar mlv-editor-link,.mlv-editor-toolbar mlv-editor-table,.mlv-editor-toolbar mlv-editor-image-upload{flex:0 0 auto}.mlv-editor-toolbar--disabled{opacity:var(--mlv-disabled-opacity)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvFade, selector: "[mlvFade]", inputs: ["mlvFadeHeight", "mlvFadeSize", "mlvFadeOffset", "mlvFade"] }, { kind: "component", type: MlvToolbar, selector: "mlv-toolbar", inputs: ["gap", "equalSize"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "directive", type: MlvEditorToolbarRoot, selector: "[mlvEditorToolbarRoot]", inputs: ["ariaLabel", "disabled", "orientation", "wrap"], exportAs: ["mlvEditorToolbarRoot"] }, { kind: "component", type: MlvEditorUndoRedo, selector: "mlv-editor-undo-redo" }, { kind: "component", type: MlvEditorZoom, selector: "mlv-editor-zoom", inputs: ["zoom", "zoomLevels", "min", "max", "fitToContainer"], outputs: ["zoomChange"] }, { kind: "component", type: MlvEditorHeading, selector: "mlv-editor-heading", inputs: ["levels"] }, { kind: "component", type: MlvEditorList, selector: "mlv-editor-list" }, { kind: "component", type: MlvEditorInlineMarks, selector: "mlv-editor-inline-marks" }, { kind: "component", type: MlvEditorTextColor, selector: "mlv-editor-text-color" }, { kind: "component", type: MlvEditorHighlight, selector: "mlv-editor-highlight" }, { kind: "component", type: MlvEditorLink, selector: "mlv-editor-link", inputs: ["allowedProtocols"] }, { kind: "component", type: MlvEditorImageUpload, selector: "mlv-editor-image-upload" }, { kind: "component", type: MlvEditorTable, selector: "mlv-editor-table" }, { kind: "component", type: MlvEditorAlignment, selector: "mlv-editor-alignment" }, { kind: "component", type: MlvEditorBlockInsert, selector: "mlv-editor-block-insert" }, { kind: "component", type: MlvEditorToolbarOverflow, selector: "mlv-editor-toolbar-overflow" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
10289
+ </div>`, isInline: true, styles: ["@layer mlv.components{.mlv-editor-toolbar{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size)}.mlv-editor-toolbar *{box-sizing:border-box}.mlv-editor-toolbar{display:flex;align-items:center;min-width:0;overflow:hidden}.mlv-editor-toolbar [hidden]{display:none!important}.mlv-editor-toolbar__scroll{flex:1 1 auto;min-width:0}.mlv-editor-toolbar__scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor-toolbar__separator{flex:0 0 auto;align-self:stretch;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor-toolbar__overflow,.mlv-editor-toolbar .mlv-editor-undo-redo,.mlv-editor-toolbar .mlv-editor-inline-marks,.mlv-editor-toolbar .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-toolbar mlv-editor-zoom,.mlv-editor-toolbar mlv-editor-heading,.mlv-editor-toolbar mlv-editor-list,.mlv-editor-toolbar mlv-editor-text-color,.mlv-editor-toolbar mlv-editor-highlight,.mlv-editor-toolbar mlv-editor-alignment,.mlv-editor-toolbar mlv-editor-link,.mlv-editor-toolbar mlv-editor-table,.mlv-editor-toolbar mlv-editor-image-upload{flex:0 0 auto}.mlv-editor-toolbar--disabled{opacity:var(--mlv-disabled-opacity)}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvFade, selector: "[mlvFade]", inputs: ["mlvFadeHeight", "mlvFadeSize", "mlvFadeOffset", "mlvFade"] }, { kind: "component", type: MlvToolbar, selector: "mlv-toolbar", inputs: ["gap", "equalSize"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "directive", type: MlvEditorToolbarRoot, selector: "[mlvEditorToolbarRoot]", inputs: ["ariaLabel", "disabled", "orientation", "wrap", "measureTarget"], exportAs: ["mlvEditorToolbarRoot"] }, { kind: "component", type: MlvEditorUndoRedo, selector: "mlv-editor-undo-redo" }, { kind: "component", type: MlvEditorZoom, selector: "mlv-editor-zoom", inputs: ["zoom", "zoomLevels", "min", "max", "fitToContainer"], outputs: ["zoomChange"] }, { kind: "component", type: MlvEditorHeading, selector: "mlv-editor-heading", inputs: ["levels"] }, { kind: "component", type: MlvEditorList, selector: "mlv-editor-list" }, { kind: "component", type: MlvEditorInlineMarks, selector: "mlv-editor-inline-marks" }, { kind: "component", type: MlvEditorTextColor, selector: "mlv-editor-text-color" }, { kind: "component", type: MlvEditorHighlight, selector: "mlv-editor-highlight" }, { kind: "component", type: MlvEditorLink, selector: "mlv-editor-link", inputs: ["allowedProtocols"] }, { kind: "component", type: MlvEditorImageUpload, selector: "mlv-editor-image-upload" }, { kind: "component", type: MlvEditorTable, selector: "mlv-editor-table" }, { kind: "component", type: MlvEditorAlignment, selector: "mlv-editor-alignment" }, { kind: "component", type: MlvEditorBlockInsert, selector: "mlv-editor-block-insert" }, { kind: "component", type: MlvEditorToolbarOverflow, selector: "mlv-editor-toolbar-overflow" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
9196
10290
  }
9197
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorToolbar, decorators: [{
10291
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorToolbar, decorators: [{
9198
10292
  type: Component,
9199
10293
  args: [{ selector: 'mlv-editor-toolbar', host: {
9200
10294
  class: 'mlv-editor-toolbar',
@@ -9375,15 +10469,15 @@ class MlvEditorStatus {
9375
10469
  return typeof storage?.characters === 'function' ? storage : null;
9376
10470
  }, /* @ts-ignore */
9377
10471
  ...(ngDevMode ? [{ debugName: "_storage" }] : /* istanbul ignore next */ []));
9378
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorStatus, deps: [], target: i0.ɵɵFactoryTarget.Component });
9379
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditorStatus, isStandalone: true, selector: "mlv-editor-status", inputs: { characterLimit: { classPropertyName: "characterLimit", publicName: "characterLimit", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!available()" }, classAttribute: "mlv-editor-status" }, ngImport: i0, template: "@if (available()) {\n<span\n class=\"mlv-editor-status__summary\"\n role=\"status\"\n aria-live=\"polite\"\n [attr.aria-label]=\"_ariaLabel()\"\n>\n @if (_limit(); as limit) {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }}/{{ limit }} characters</span\n >\n } @else {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }} characters</span\n >\n }\n <span aria-hidden=\"true\"> \u00B7 </span>\n <span class=\"mlv-editor-status__words\">{{ words() }} words</span>\n</span>\n}\n", styles: ["@layer mlv.components{.mlv-editor-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-status *{box-sizing:border-box}.mlv-editor-status{display:inline-flex;color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}.mlv-editor-status__summary{display:inline-flex;gap:var(--mlv-spacing-1)}.mlv-editor-status__summary *{color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
10472
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorStatus, deps: [], target: i0.ɵɵFactoryTarget.Component });
10473
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditorStatus, isStandalone: true, selector: "mlv-editor-status", inputs: { characterLimit: { classPropertyName: "characterLimit", publicName: "characterLimit", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "hidden": "!available()" }, classAttribute: "mlv-editor-status" }, ngImport: i0, template: "@if (available()) {\n <span\n class=\"mlv-editor-status__summary\"\n role=\"status\"\n aria-live=\"polite\"\n [attr.aria-label]=\"_ariaLabel()\"\n >\n @if (_limit(); as limit) {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }}/{{ limit }} characters</span\n >\n } @else {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }} characters</span\n >\n }\n <span aria-hidden=\"true\"> \u00B7 </span>\n <span class=\"mlv-editor-status__words\">{{ words() }} words</span>\n </span>\n}\n", styles: ["@layer mlv.components{.mlv-editor-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-status *{box-sizing:border-box}.mlv-editor-status{display:inline-flex;color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}.mlv-editor-status__summary{display:inline-flex;gap:var(--mlv-spacing-1)}.mlv-editor-status__summary *{color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
9380
10474
  }
9381
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorStatus, decorators: [{
10475
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditorStatus, decorators: [{
9382
10476
  type: Component,
9383
10477
  args: [{ selector: 'mlv-editor-status', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
9384
10478
  class: 'mlv-editor-status',
9385
10479
  '[hidden]': '!available()',
9386
- }, template: "@if (available()) {\n<span\n class=\"mlv-editor-status__summary\"\n role=\"status\"\n aria-live=\"polite\"\n [attr.aria-label]=\"_ariaLabel()\"\n>\n @if (_limit(); as limit) {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }}/{{ limit }} characters</span\n >\n } @else {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }} characters</span\n >\n }\n <span aria-hidden=\"true\"> \u00B7 </span>\n <span class=\"mlv-editor-status__words\">{{ words() }} words</span>\n</span>\n}\n", styles: ["@layer mlv.components{.mlv-editor-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-status *{box-sizing:border-box}.mlv-editor-status{display:inline-flex;color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}.mlv-editor-status__summary{display:inline-flex;gap:var(--mlv-spacing-1)}.mlv-editor-status__summary *{color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}}\n"] }]
10480
+ }, template: "@if (available()) {\n <span\n class=\"mlv-editor-status__summary\"\n role=\"status\"\n aria-live=\"polite\"\n [attr.aria-label]=\"_ariaLabel()\"\n >\n @if (_limit(); as limit) {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }}/{{ limit }} characters</span\n >\n } @else {\n <span class=\"mlv-editor-status__characters\"\n >{{ characters() }} characters</span\n >\n }\n <span aria-hidden=\"true\"> \u00B7 </span>\n <span class=\"mlv-editor-status__words\">{{ words() }} words</span>\n </span>\n}\n", styles: ["@layer mlv.components{.mlv-editor-status{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-status *{box-sizing:border-box}.mlv-editor-status{display:inline-flex;color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}.mlv-editor-status__summary{display:inline-flex;gap:var(--mlv-spacing-1)}.mlv-editor-status__summary *{color:var(--mlv-text-secondary);font-size:var(--mlv-typography-caption-size);line-height:var(--mlv-typography-caption-line-height)}}\n"] }]
9387
10481
  }], propDecorators: { characterLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "characterLimit", required: false }] }] } });
9388
10482
 
9389
10483
  /**
@@ -9406,6 +10500,87 @@ class MlvEditor extends MlvSignalFormControlBase {
9406
10500
  /** Width of the centred content column; a gutter is reserved at every value. */
9407
10501
  contentWidth = input('default', /* @ts-ignore */
9408
10502
  ...(ngDevMode ? [{ debugName: "contentWidth" }] : /* istanbul ignore next */ []));
10503
+ /**
10504
+ * Fixed block size of the editor surface (toolbar plus content viewport).
10505
+ * A number is px; a string is any CSS length, passed through verbatim.
10506
+ *
10507
+ * Setting it **caps** the editor: `.mlv-editor__viewport` becomes the only
10508
+ * scroll container. With neither `height` nor `maxHeight`, the editor grows
10509
+ * with its content, never shows a scrollbar, and never stops a scroll meant
10510
+ * for the page. Writing `--mlv-editor-height` in CSS alone does not cap.
10511
+ */
10512
+ height = input(undefined, /* @ts-ignore */
10513
+ ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
10514
+ /**
10515
+ * Minimum block size of the editable content area; the viewport adds its
10516
+ * 1rem padding around it. A number is px; a string is any CSS length. `undefined` keeps the `8rem` floor. Setting only this input
10517
+ * leaves the editor in auto mode. `--mlv-editor-min-height` may also be set
10518
+ * in CSS.
10519
+ *
10520
+ * Under a cap (`height` / `maxHeight`) the cap wins, even over a larger
10521
+ * `minHeight`: the viewport shrinks to fit the surface, and the floor moves
10522
+ * to the content inside the scroller, at every zoom level. A `minHeight`
10523
+ * above what the cap leaves the content (the cap minus the toolbar band and
10524
+ * the viewport padding) scrolls an empty document; for a fixed size set
10525
+ * `height` rather than `minHeight` equal to `maxHeight`.
10526
+ */
10527
+ minHeight = input(undefined, /* @ts-ignore */
10528
+ ...(ngDevMode ? [{ debugName: "minHeight" }] : /* istanbul ignore next */ []));
10529
+ /**
10530
+ * Largest block size of the editor surface. The surface grows with its
10531
+ * content up to this cap, and then the viewport scrolls. A number is px; a
10532
+ * string is any CSS length. Like `height`, this caps the editor; writing
10533
+ * `--mlv-editor-max-height` in CSS alone does not.
10534
+ */
10535
+ maxHeight = input(undefined, /* @ts-ignore */
10536
+ ...(ngDevMode ? [{ debugName: "maxHeight" }] : /* istanbul ignore next */ []));
10537
+ /**
10538
+ * Docked-bar placement. The DOM order follows it, so the Tab order matches
10539
+ * the visual order (WCAG 2.4.3). Changing it re-creates the toolbar view,
10540
+ * which closes any open toolbar popup. It does not move the floating
10541
+ * selection bubble, which always prefers above the selection. While
10542
+ * `readonly` no bar renders in either position (see `toolbarAppearance`).
10543
+ */
10544
+ toolbarPosition = input('top', /* @ts-ignore */
10545
+ ...(ngDevMode ? [{ debugName: "toolbarPosition" }] : /* istanbul ignore next */ []));
10546
+ /**
10547
+ * Toolbar drawing: `'bar'` is the docked row with a hairline; `'floating'` is
10548
+ * a selection bubble. The bubble is a CDK overlay (`.mlv-editor-bubble`)
10549
+ * shown only while focus is in the editor, the selection is non-empty and
10550
+ * the editor is neither disabled nor `readonly`; it prefers above the
10551
+ * selection and flips below at the window's or a capped viewport's edge.
10552
+ * Alt+F10 in the content summons it at the caret and focuses its first
10553
+ * control (the content carries `aria-keyshortcuts="Alt+F10"` while floating
10554
+ * and editable); Escape dismisses it and returns focus to the content with
10555
+ * the selection intact. A plugin that claims Escape itself (a consumer's
10556
+ * `@tiptap/suggestion` list, an AI stream) gets it first, and the Escape
10557
+ * that dismisses the bubble goes no further, so a dialog or drawer around
10558
+ * the editor closes only on the next one. Turning `readonly` on while the
10559
+ * bubble is shown hides it and closes its popups.
10560
+ *
10561
+ * A `readonly` editor renders **no toolbar in either appearance** (#498):
10562
+ * no `.mlv-editor__toolbar-band`, and no space kept for one. Turning
10563
+ * `readonly` on tears the bar down, closes every popup the editor owns and
10564
+ * returns focus from the bar or such a popup to the content, with the
10565
+ * selection intact; turning it off stamps the bar again in its position.
10566
+ * A consumer toolbar (`[mlvEditorToolbar]`, `[mlvEditorToolbarStart]`,
10567
+ * `[mlvEditorToolbarEnd]`) is stamped in the same band and goes with it.
10568
+ */
10569
+ toolbarAppearance = input('bar', /* @ts-ignore */
10570
+ ...(ngDevMode ? [{ debugName: "toolbarAppearance" }] : /* istanbul ignore next */ []));
10571
+ /**
10572
+ * Keeps the docked bar `position: sticky` against the page (or the nearest
10573
+ * consumer scroll container) while the surface is on screen, offset by
10574
+ * `--mlv-editor-toolbar-sticky-offset` (default `0`).
10575
+ *
10576
+ * It applies only to an **uncapped** **bar**. A capped editor
10577
+ * (`height` / `maxHeight`) scrolls inside its own viewport, and the bar
10578
+ * sits outside that scroller, so there is nothing for it to stick against;
10579
+ * the floating bubble follows the selection instead; and a `readonly`
10580
+ * editor renders no bar at all (#498). In each case the input is ignored
10581
+ * and `.mlv-editor--toolbar-sticky` is not stamped.
10582
+ */
10583
+ toolbarSticky = input(false, { ...(ngDevMode ? { debugName: "toolbarSticky" } : /* istanbul ignore next */ {}), transform: coerceBooleanProperty });
9409
10584
  /** Complete Tiptap extension replacement; omit it for Malva's fresh preset. */
9410
10585
  extensions = input(undefined, /* @ts-ignore */
9411
10586
  ...(ngDevMode ? [{ debugName: "extensions" }] : /* istanbul ignore next */ []));
@@ -9443,6 +10618,57 @@ class MlvEditor extends MlvSignalFormControlBase {
9443
10618
  /** View-only zoom percentage shared with the nearest toolbar. */
9444
10619
  zoom = signal(100, /* @ts-ignore */
9445
10620
  ...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
10621
+ /** @protected Resolved `height`, written into `--mlv-editor-height`; `null` writes nothing. */
10622
+ _heightStyle = computed(() => mlvEditorCssLength(this.height()), /* @ts-ignore */
10623
+ ...(ngDevMode ? [{ debugName: "_heightStyle" }] : /* istanbul ignore next */ []));
10624
+ /** @protected Resolved `minHeight`, written into `--mlv-editor-min-height`; `null` writes nothing. */
10625
+ _minHeightStyle = computed(() => mlvEditorCssLength(this.minHeight()), /* @ts-ignore */
10626
+ ...(ngDevMode ? [{ debugName: "_minHeightStyle" }] : /* istanbul ignore next */ []));
10627
+ /** @protected Resolved `maxHeight`, written into `--mlv-editor-max-height`; `null` writes nothing. */
10628
+ _maxHeightStyle = computed(() => mlvEditorCssLength(this.maxHeight()), /* @ts-ignore */
10629
+ ...(ngDevMode ? [{ debugName: "_maxHeightStyle" }] : /* istanbul ignore next */ []));
10630
+ /**
10631
+ * @protected Whether the editor is capped. Only `height` or `maxHeight`
10632
+ * switches it on; `minHeight` alone stays in auto mode. It is the one input
10633
+ * that turns `.mlv-editor__viewport` into a scroll container.
10634
+ */
10635
+ _capped = computed(() => this._heightStyle() !== null || this._maxHeightStyle() !== null, /* @ts-ignore */
10636
+ ...(ngDevMode ? [{ debugName: "_capped" }] : /* istanbul ignore next */ []));
10637
+ /**
10638
+ * @protected Whether the editor stamps its toolbar at all, in either
10639
+ * appearance: never while `readonly` (#498 owner ruling, "a readonly editor
10640
+ * renders no toolbar"). The docked bar is torn down and the selection
10641
+ * bubble's pane stays empty; both come back when `readonly` turns off.
10642
+ *
10643
+ * This is the one switch, and it covers a consumer toolbar too: a complete
10644
+ * `[mlvEditorToolbar]` replacement and the `[mlvEditorToolbarStart]` /
10645
+ * `[mlvEditorToolbarEnd]` slots are stamped inside the same band. That is
10646
+ * an assumption the owner may reverse. Keeping a complete replacement while
10647
+ * `readonly` is `!this.readonly() || this._hasToolbarDef()` here for the
10648
+ * bar, and the default groups inside the band then need their own readonly
10649
+ * hiding; `_closeBarForReadonly()`'s `_barRendered()` early return must
10650
+ * then key on the removed region, not the band, or focus on a removed
10651
+ * default control drops to `<body>`. The bubble also gates itself on
10652
+ * `readonly` in `MlvEditorBubble._wanted()` / `_ownsContentKey()`, and
10653
+ * `_editorAttributes()` drops `aria-keyshortcuts`.
10654
+ */
10655
+ _toolbarRendered = computed(() => !this.readonly(), /* @ts-ignore */
10656
+ ...(ngDevMode ? [{ debugName: "_toolbarRendered" }] : /* istanbul ignore next */ []));
10657
+ /**
10658
+ * @protected Whether the docked bar is stamped before or after the
10659
+ * viewport: the `'bar'` appearance while `_toolbarRendered()`. With it
10660
+ * false no band exists, so nothing reserves space for one and no sticky
10661
+ * extent is measured.
10662
+ */
10663
+ _barRendered = computed(() => this.toolbarAppearance() === 'bar' && this._toolbarRendered(), /* @ts-ignore */
10664
+ ...(ngDevMode ? [{ debugName: "_barRendered" }] : /* istanbul ignore next */ []));
10665
+ /**
10666
+ * @protected Whether the docked bar is actually sticky: `toolbarSticky` on an
10667
+ * uncapped bar that renders (not while `readonly`). Drives
10668
+ * `.mlv-editor--toolbar-sticky` and the caret scroll margin.
10669
+ */
10670
+ _stickyToolbar = computed(() => this.toolbarSticky() && this._barRendered() && !this._capped(), /* @ts-ignore */
10671
+ ...(ngDevMode ? [{ debugName: "_stickyToolbar" }] : /* istanbul ignore next */ []));
9446
10672
  /** Whether the mounted editor currently accepts document mutations. */
9447
10673
  editable = computed(() => this._editor() !== null && !this.computedDisabled() && !this.readonly(), /* @ts-ignore */
9448
10674
  ...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
@@ -9475,7 +10701,7 @@ class MlvEditor extends MlvSignalFormControlBase {
9475
10701
  _content = viewChild.required('content', /* @ts-ignore */
9476
10702
  ...(ngDevMode ? [{ debugName: "_content" }] : /* istanbul ignore next */ []));
9477
10703
  /**
9478
- * @private Zoom-transformed layer the floating block handle is mounted into.
10704
+ * @private Zoomed layer (CSS `zoom`) the floating block handle is mounted into.
9479
10705
  *
9480
10706
  * Deliberately optional rather than `viewChild.required`: the block-handle
9481
10707
  * `mount` capability is typed `() => HTMLElement | null`, and the extension
@@ -9485,6 +10711,12 @@ class MlvEditor extends MlvSignalFormControlBase {
9485
10711
  */
9486
10712
  _view = viewChild('view', /* @ts-ignore */
9487
10713
  ...(ngDevMode ? [{ debugName: "_view" }] : /* istanbul ignore next */ []));
10714
+ /**
10715
+ * @private Toolbar band currently stamped before or after the viewport.
10716
+ * Queried by declaration, so it follows the band across position changes.
10717
+ */
10718
+ _toolbarBand = viewChild('toolbarBand', /* @ts-ignore */
10719
+ ...(ngDevMode ? [{ debugName: "_toolbarBand" }] : /* istanbul ignore next */ []));
9488
10720
  /** @protected Complete projected toolbar replacement, if the consumer provides one. */
9489
10721
  _toolbarDefs = contentChildren(MlvEditorToolbarDef, /* @ts-ignore */
9490
10722
  ...(ngDevMode ? [{ debugName: "_toolbarDefs" }] : /* istanbul ignore next */ []));
@@ -9513,6 +10745,16 @@ class MlvEditor extends MlvSignalFormControlBase {
9513
10745
  _uploadAbortRegistry = inject(MlvEditorUploadAbortRegistry);
9514
10746
  /** @private Physical host used to determine the composite focus boundary. */
9515
10747
  _host = inject(ElementRef);
10748
+ /**
10749
+ * @private Live ProseMirror `scrollMargin` (WCAG 2.2 SC 2.4.11). One stable
10750
+ * object passed on every `editorProps` write; its sides re-measure per read.
10751
+ * Read untracked: ProseMirror scrolls synchronously inside `updateState`,
10752
+ * which can run within `_synchronizeEditor`'s effect, and that effect must
10753
+ * not start depending on the toolbar inputs.
10754
+ */
10755
+ _scrollMargin = createMlvEditorLiveScrollSides('margin', () => untracked(this.toolbarPosition), () => untracked(() => this._obscuredToolbarExtent()));
10756
+ /** @private Live ProseMirror `scrollThreshold`, paired with `_scrollMargin`. */
10757
+ _scrollThreshold = createMlvEditorLiveScrollSides('threshold', () => untracked(this.toolbarPosition), () => untracked(() => this._obscuredToolbarExtent()));
9516
10758
  /** @private Optional translated accessible defaults. */
9517
10759
  _i18n = inject(MLV_EDITOR_I18N, { optional: true });
9518
10760
  /** @private Announces block moves politely; the editor content itself is not a live region. */
@@ -9539,13 +10781,7 @@ class MlvEditor extends MlvSignalFormControlBase {
9539
10781
  _blurCheckQueued = false;
9540
10782
  /** @private The last boundary event, retained until the microtask resolves focus ownership. */
9541
10783
  _pendingBlurEvent;
9542
- /** @private Stable capture listener for focus entering the physical host. */
9543
- _onHostFocusIn = (event) => this._handleCompositeFocusIn(event);
9544
- /** @private Stable capture listener for focus leaving the physical host. */
9545
- _onHostFocusOut = (event) => this._handleCompositeFocusOut(event);
9546
- /** @private Stable capture listener that blocks disabled projected controls. */
9547
- _onDisabledPointerOrClick = (event) => this._blockDisabledInteraction(event);
9548
- /** @private Stable capture listener that blocks disabled keyboard activation. */
10784
+ /** @private Blocks disabled keyboard activation; the other three subscribe to their handler directly. */
9549
10785
  _onDisabledKeydown = (event) => {
9550
10786
  if (event.key !== 'Enter' &&
9551
10787
  event.key !== ' ' &&
@@ -9588,6 +10824,10 @@ class MlvEditor extends MlvSignalFormControlBase {
9588
10824
  this._wasDisabled = disabled;
9589
10825
  if (readonly && !this._wasReadonly) {
9590
10826
  this._abortInFlightUploads();
10827
+ // Constructor effect: registered on the parent view, so it runs
10828
+ // before this component's template (and the wrapper-stamped band
10829
+ // `@if`) refreshes; focus in the band or its popups can still move.
10830
+ untracked(() => this._closeBarForReadonly());
9591
10831
  }
9592
10832
  this._wasReadonly = readonly;
9593
10833
  if (!editor)
@@ -9681,7 +10921,11 @@ class MlvEditor extends MlvSignalFormControlBase {
9681
10921
  extensions: selectedExtensions,
9682
10922
  editable: !this.computedDisabled() && !this.readonly(),
9683
10923
  enableContentCheck: true,
9684
- editorProps: { attributes: this._editorAttributes() },
10924
+ editorProps: {
10925
+ attributes: this._editorAttributes(),
10926
+ scrollMargin: this._scrollMargin,
10927
+ scrollThreshold: this._scrollThreshold,
10928
+ },
9685
10929
  onTransaction: ({ editor: changedEditor, transaction }) => {
9686
10930
  if (this._destroyed)
9687
10931
  return;
@@ -9725,6 +10969,23 @@ class MlvEditor extends MlvSignalFormControlBase {
9725
10969
  });
9726
10970
  }
9727
10971
  }
10972
+ /**
10973
+ * @protected Focuses the editable content when the control's own
10974
+ * `<mlv-label>` is clicked.
10975
+ *
10976
+ * ProseMirror's contenteditable root is focusable but not HTML-labelable, so
10977
+ * `<label for>` can never name it and the native click-to-focus a field
10978
+ * label owes its control never ran — the visible label was inert (#216), the
10979
+ * same gap the three date/time pickers close. `readonly` still focuses (a
10980
+ * read-only `<textarea>` does), `disabled` returns early (the host is
10981
+ * `inert` anyway), and Tiptap's `focus` command restores the stored
10982
+ * selection rather than dropping the caret at the document start.
10983
+ */
10984
+ _onLabelClick() {
10985
+ if (this.computedDisabled())
10986
+ return;
10987
+ this._editor()?.commands.focus();
10988
+ }
9728
10989
  /** Clears the document through Tiptap when the shared form wrapper is clearable. */
9729
10990
  clearValue() {
9730
10991
  if (this.computedDisabled() || this.readonly())
@@ -9811,7 +11072,15 @@ class MlvEditor extends MlvSignalFormControlBase {
9811
11072
  return;
9812
11073
  const requestedFormat = this.format();
9813
11074
  editor.setOptions({
9814
- editorProps: { attributes: this._editorAttributes() },
11075
+ // `setOptions` replaces Tiptap's stored `editorProps` wholesale. The
11076
+ // live view keeps its props (ProseMirror's `setProps` merges), but a
11077
+ // later `mount()` rebuilds the view from the stored copy, so the scroll
11078
+ // props travel with every call.
11079
+ editorProps: {
11080
+ attributes: this._editorAttributes(),
11081
+ scrollMargin: this._scrollMargin,
11082
+ scrollThreshold: this._scrollThreshold,
11083
+ },
9815
11084
  });
9816
11085
  this._synchronizeContentSurfaceState(editor);
9817
11086
  const rawIncoming = this.value();
@@ -10020,6 +11289,39 @@ class MlvEditor extends MlvSignalFormControlBase {
10020
11289
  recoverable: true,
10021
11290
  });
10022
11291
  }
11292
+ /**
11293
+ * @private Block extent the toolbar hides on its own side, in px.
11294
+ *
11295
+ * Only a sticky bar hides anything: its height plus its offset, at its
11296
+ * nearest scroll container. A docked bar sits outside the content, and the
11297
+ * floating bubble is an overlay that follows the selection, clear of it by
11298
+ * construction.
11299
+ * Sticky applies only to an uncapped bar (`_stickyToolbar`), so the margin
11300
+ * never lands on a capped viewport's own edge.
11301
+ *
11302
+ * Read by ProseMirror at scroll time, never for layout. ProseMirror applies
11303
+ * one margin at every scroll ancestor. For an uncapped editor the page is
11304
+ * usually the only one. With a consumer scroll container in between, the
11305
+ * band sticks to that container, where the margin is right; the page, one
11306
+ * scroll ancestor further out, also keeps the caret that far from its edge
11307
+ * although no band is there, which errs toward visibility.
11308
+ */
11309
+ _obscuredToolbarExtent() {
11310
+ if (!this._stickyToolbar())
11311
+ return 0;
11312
+ const band = this._toolbarBand()?.nativeElement;
11313
+ if (!band)
11314
+ return 0;
11315
+ const side = this.toolbarPosition() === 'top'
11316
+ ? 'inset-block-start'
11317
+ : 'inset-block-end';
11318
+ const view = band.ownerDocument.defaultView;
11319
+ const offset = view
11320
+ ? Number.parseFloat(view.getComputedStyle(band).getPropertyValue(side)) ||
11321
+ 0
11322
+ : 0;
11323
+ return band.getBoundingClientRect().height + offset;
11324
+ }
10023
11325
  /** @private Builds the exact accessible attributes assigned to Tiptap's contenteditable root. */
10024
11326
  _editorAttributes() {
10025
11327
  const attributes = {
@@ -10057,6 +11359,14 @@ class MlvEditor extends MlvSignalFormControlBase {
10057
11359
  if (this.readonly()) {
10058
11360
  attributes['aria-readonly'] = 'true';
10059
11361
  }
11362
+ // The selection bubble stays hidden until a selection exists, so the key
11363
+ // that reaches it is the only hint assistive technology gets. Disabled or
11364
+ // `readonly`, the bubble never shows and the key does nothing.
11365
+ if (this.toolbarAppearance() === 'floating' &&
11366
+ !this.computedDisabled() &&
11367
+ !this.readonly()) {
11368
+ attributes['aria-keyshortcuts'] = 'Alt+F10';
11369
+ }
10060
11370
  return attributes;
10061
11371
  }
10062
11372
  /** @internal Capture handler for focus entering the host or an owned overlay root. */
@@ -10109,21 +11419,39 @@ class MlvEditor extends MlvSignalFormControlBase {
10109
11419
  (this._host.nativeElement.contains(node) ||
10110
11420
  this._overlayRegistry.contains(node)));
10111
11421
  }
10112
- /** @private Installs native capture listeners because overlay focus is outside Angular's view tree. */
11422
+ /**
11423
+ * @private Subscribes the composite's host listeners, because overlay focus
11424
+ * happens outside Angular's view tree.
11425
+ *
11426
+ * Every one is registered in the **capture** phase, which is load-bearing
11427
+ * rather than incidental: ProseMirror binds its own handlers to `view.dom`,
11428
+ * a descendant of this host, so only a capture-phase listener on the host
11429
+ * runs ahead of them. The disabled handlers depend on that to call
11430
+ * `stopImmediatePropagation()` while the event is still travelling down —
11431
+ * from the bubble phase ProseMirror would already have acted on it. Capture
11432
+ * is carried through `fromEvent`'s third argument, and `editor-focus.spec.ts`
11433
+ * pins the ordering against descendant listeners.
11434
+ *
11435
+ * Called from `ngAfterViewInit`, which is not an injection context, so
11436
+ * `takeUntilDestroyed` is given the ref explicitly.
11437
+ */
10113
11438
  _bindCompositeFocusEvents() {
10114
11439
  const host = this._host.nativeElement;
10115
- host.addEventListener('focusin', this._onHostFocusIn, true);
10116
- host.addEventListener('focusout', this._onHostFocusOut, true);
10117
- host.addEventListener('pointerdown', this._onDisabledPointerOrClick, true);
10118
- host.addEventListener('click', this._onDisabledPointerOrClick, true);
10119
- host.addEventListener('keydown', this._onDisabledKeydown, true);
10120
- this._destroyRef.onDestroy(() => {
10121
- host.removeEventListener('focusin', this._onHostFocusIn, true);
10122
- host.removeEventListener('focusout', this._onHostFocusOut, true);
10123
- host.removeEventListener('pointerdown', this._onDisabledPointerOrClick, true);
10124
- host.removeEventListener('click', this._onDisabledPointerOrClick, true);
10125
- host.removeEventListener('keydown', this._onDisabledKeydown, true);
10126
- });
11440
+ fromEvent(host, 'focusin', { capture: true })
11441
+ .pipe(takeUntilDestroyed(this._destroyRef))
11442
+ .subscribe((event) => this._handleCompositeFocusIn(event));
11443
+ fromEvent(host, 'focusout', { capture: true })
11444
+ .pipe(takeUntilDestroyed(this._destroyRef))
11445
+ .subscribe((event) => this._handleCompositeFocusOut(event));
11446
+ fromEvent(host, 'pointerdown', { capture: true })
11447
+ .pipe(takeUntilDestroyed(this._destroyRef))
11448
+ .subscribe((event) => this._blockDisabledInteraction(event));
11449
+ fromEvent(host, 'click', { capture: true })
11450
+ .pipe(takeUntilDestroyed(this._destroyRef))
11451
+ .subscribe((event) => this._blockDisabledInteraction(event));
11452
+ fromEvent(host, 'keydown', { capture: true })
11453
+ .pipe(takeUntilDestroyed(this._destroyRef))
11454
+ .subscribe((event) => this._onDisabledKeydown(event));
10127
11455
  }
10128
11456
  /** @private Stops all pointer/click/activation-key command surfaces while disabled. */
10129
11457
  _blockDisabledInteraction(event) {
@@ -10154,6 +11482,7 @@ class MlvEditor extends MlvSignalFormControlBase {
10154
11482
  'aria-readonly',
10155
11483
  'tabindex',
10156
11484
  'aria-invalid',
11485
+ 'aria-keyshortcuts',
10157
11486
  ];
10158
11487
  for (const name of managedAttributes) {
10159
11488
  const value = attributes[name];
@@ -10165,6 +11494,29 @@ class MlvEditor extends MlvSignalFormControlBase {
10165
11494
  }
10166
11495
  }
10167
11496
  }
11497
+ /**
11498
+ * @private `readonly` just turned on over a docked bar, which this render
11499
+ * tears down (#498). Every popup the editor owns closes, and focus in the
11500
+ * band or in such a popup returns to the content first, through the same
11501
+ * registry call the selection bubble makes. Called from the constructor
11502
+ * effect, which is registered on the parent view, so it runs before this
11503
+ * component's template (and the wrapper-stamped band `@if`) refreshes: the
11504
+ * band and its focus are still there. A disabled editor is left alone, like
11505
+ * the bubble's own focus return: it takes no focus, even from a projected
11506
+ * consumer control, which the editor does not disable and which can hold
11507
+ * focus in the band of a disabled editor.
11508
+ */
11509
+ _closeBarForReadonly() {
11510
+ if (this.toolbarAppearance() !== 'bar' || this._barRendered())
11511
+ return;
11512
+ if (this.computedDisabled())
11513
+ return;
11514
+ const band = this._toolbarBand()?.nativeElement;
11515
+ const editor = this._editor();
11516
+ if (!band || !editor || editor.isDestroyed)
11517
+ return;
11518
+ this._overlayRegistry.closeForReadonly(band, editor.view.dom, () => mlvEditorFocusContent(editor));
11519
+ }
10168
11520
  /** @private Aborts registered upload work while preserving the document/editor instance. */
10169
11521
  _abortInFlightUploads() {
10170
11522
  this._uploadAbortRegistry.abortAll();
@@ -10177,8 +11529,8 @@ class MlvEditor extends MlvSignalFormControlBase {
10177
11529
  this._editor.set(null);
10178
11530
  editor?.destroy();
10179
11531
  }
10180
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
10181
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: MlvEditor, isStandalone: true, selector: "mlv-editor", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: false, transformFunction: null }, extensions: { classPropertyName: "extensions", publicName: "extensions", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, characterLimit: { classPropertyName: "characterLimit", publicName: "characterLimit", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "ariaDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, imageUploader: { classPropertyName: "imageUploader", publicName: "imageUploader", isSignal: true, isRequired: false, transformFunction: null }, imageUploadOptions: { classPropertyName: "imageUploadOptions", publicName: "imageUploadOptions", isSignal: true, isRequired: false, transformFunction: null }, aiProvider: { classPropertyName: "aiProvider", publicName: "aiProvider", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", editorReady: "editorReady", focus: "focus", blur: "blur", selectionChange: "selectionChange", transaction: "transaction", editorError: "editorError", imageUploadSuccess: "imageUploadSuccess", imageUploadFailure: "imageUploadFailure", imageUploadCancelled: "imageUploadCancelled" }, host: { properties: { "class": "\"mlv-editor--content-width-\" + contentWidth()", "class.mlv-editor--disabled": "computedDisabled()", "class.mlv-editor--readonly": "readonly()", "class.mlv-editor--focused": "focused()", "attr.aria-disabled": "computedDisabled() || null", "attr.inert": "computedDisabled() ? \"\" : null", "style.--mlv-editor-zoom": "zoom() / 100" }, classAttribute: "mlv-editor" }, providers: [
11532
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
11533
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: MlvEditor, isStandalone: true, selector: "mlv-editor", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, contentWidth: { classPropertyName: "contentWidth", publicName: "contentWidth", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, toolbarPosition: { classPropertyName: "toolbarPosition", publicName: "toolbarPosition", isSignal: true, isRequired: false, transformFunction: null }, toolbarAppearance: { classPropertyName: "toolbarAppearance", publicName: "toolbarAppearance", isSignal: true, isRequired: false, transformFunction: null }, toolbarSticky: { classPropertyName: "toolbarSticky", publicName: "toolbarSticky", isSignal: true, isRequired: false, transformFunction: null }, extensions: { classPropertyName: "extensions", publicName: "extensions", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, characterLimit: { classPropertyName: "characterLimit", publicName: "characterLimit", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "ariaLabelledBy", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "ariaDescribedBy", isSignal: true, isRequired: false, transformFunction: null }, imageUploader: { classPropertyName: "imageUploader", publicName: "imageUploader", isSignal: true, isRequired: false, transformFunction: null }, imageUploadOptions: { classPropertyName: "imageUploadOptions", publicName: "imageUploadOptions", isSignal: true, isRequired: false, transformFunction: null }, aiProvider: { classPropertyName: "aiProvider", publicName: "aiProvider", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", editorReady: "editorReady", focus: "focus", blur: "blur", selectionChange: "selectionChange", transaction: "transaction", editorError: "editorError", imageUploadSuccess: "imageUploadSuccess", imageUploadFailure: "imageUploadFailure", imageUploadCancelled: "imageUploadCancelled" }, host: { properties: { "class": "\"mlv-editor--content-width-\" + contentWidth()", "class.mlv-editor--disabled": "computedDisabled()", "class.mlv-editor--readonly": "readonly()", "class.mlv-editor--focused": "focused()", "class.mlv-editor--capped": "_capped()", "class.mlv-editor--toolbar-top": "toolbarPosition() === \"top\"", "class.mlv-editor--toolbar-bottom": "toolbarPosition() === \"bottom\"", "class.mlv-editor--toolbar-bar": "toolbarAppearance() === \"bar\"", "class.mlv-editor--toolbar-floating": "toolbarAppearance() === \"floating\"", "class.mlv-editor--toolbar-sticky": "_stickyToolbar()", "attr.aria-disabled": "computedDisabled() || null", "attr.inert": "computedDisabled() ? \"\" : null", "style.--mlv-editor-zoom": "zoom() / 100", "style.--mlv-editor-height": "_heightStyle()", "style.--mlv-editor-min-height": "_minHeightStyle()", "style.--mlv-editor-max-height": "_maxHeightStyle()" }, classAttribute: "mlv-editor" }, providers: [
10182
11534
  MlvEditorOverlayRegistry,
10183
11535
  MlvEditorToolbarRevision,
10184
11536
  MlvEditorToolbarRovingRegistry,
@@ -10237,9 +11589,9 @@ class MlvEditor extends MlvSignalFormControlBase {
10237
11589
  }, inject(MLV_EDITOR_IMAGE_UPLOADER, { optional: true }) ?? undefined, inject(MLV_EDITOR_UPLOAD_ABORT_REGISTRY));
10238
11590
  },
10239
11591
  },
10240
- ], queries: [{ propertyName: "_toolbarDefs", predicate: MlvEditorToolbarDef, isSignal: true }, { propertyName: "_toolbarStartDefs", predicate: MlvEditorToolbarStartDef, isSignal: true }, { propertyName: "_toolbarEndDefs", predicate: MlvEditorToolbarEndDef, isSignal: true }], viewQueries: [{ propertyName: "_content", first: true, predicate: ["content"], descendants: true, isSignal: true }, { propertyName: "_view", first: true, predicate: ["view"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<mlv-form-control-wrapper (clear)=\"clearValue()\">\n @if (label() || hint()) {\n <mlv-label [for]=\"id()\" [id]=\"_labelId()\">\n {{ label() }} @if (hint()) {\n <mlv-hint>{{ hint() }}</mlv-hint>\n }\n </mlv-label>\n }\n\n <ng-template mlvFormControlWrapperControl>\n <div class=\"mlv-editor__surface\">\n <div\n mlvEditorToolbarRoot\n #toolbarRoot=\"mlvEditorToolbarRoot\"\n class=\"mlv-editor__toolbar\"\n [ariaLabel]=\"_toolbarAriaLabel()\"\n [disabled]=\"computedDisabled()\"\n >\n @if (_hasToolbarDef()) {\n <ng-content select=\"[mlvEditorToolbar]\" />\n @for (definition of _toolbarDefs(); track definition) { @if\n (definition.templateRef; as toolbarTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"toolbarTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } } } @else {\n <ng-content select=\"[mlvEditorToolbarStart]\" />\n @for (definition of _toolbarStartDefs(); track definition) { @if\n (definition.templateRef; as startTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"startTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } }\n <div mlvFade class=\"mlv-editor__toolbar-scroll\">\n <mlv-toolbar>\n <mlv-editor-undo-redo />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-zoom />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-heading />\n <mlv-editor-list />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-inline-marks [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-text-color />\n <mlv-editor-highlight />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-alignment [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-link />\n <mlv-editor-table />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-block-insert [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-image-upload />\n </mlv-toolbar>\n </div>\n <mlv-editor-toolbar-overflow />\n @for (definition of _toolbarEndDefs(); track definition) { @if\n (definition.templateRef; as endTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"endTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } }\n <ng-content select=\"[mlvEditorToolbarEnd]\" />\n }\n </div>\n\n <div class=\"mlv-editor__viewport\">\n <div #view class=\"mlv-editor__view\">\n <div #content class=\"mlv-editor__content\"></div>\n <mlv-editor-table-controls [layer]=\"view\" />\n </div>\n </div>\n <mlv-editor-image-upload-status />\n </div>\n </ng-template>\n\n @if (message()) {\n <mlv-message [state]=\"resolvedState()\" [id]=\"id() + '-message'\">\n {{ message() }}\n </mlv-message>\n }\n</mlv-form-control-wrapper>\n\n<div\n class=\"mlv-editor__status\"\n [attr.aria-disabled]=\"computedDisabled() || null\"\n>\n <ng-content select=\"[mlvEditorStatus]\" />\n <mlv-spacer />\n <mlv-editor-status [characterLimit]=\"characterLimit()\" />\n</div>\n", styles: ["@layer mlv.components{.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-6);--mlv-editor-measure: 45rem;--mlv-editor-drop-gap: 1.5rem;box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor *{box-sizing:border-box}.mlv-editor{display:block;color:var(--mlv-text-primary)}.mlv-editor .mlv-form-control-wrapper__control-container{height:auto;min-height:var(--form-ctrl-height);padding:0;overflow:hidden;background:var(--mlv-background-base);transition:border-color var(--mlv-duration-fast) var(--mlv-ease-default),box-shadow var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .mlv-form-control-wrapper__control-row{align-items:stretch}.mlv-form-control-wrapper--state-error .mlv-editor__surface{box-shadow:inset 0 0 0 var(--mlv-stroke-width) var(--mlv-border-error)}.mlv-editor__surface{display:flex;flex:1 1 auto;flex-direction:column;min-width:0;width:100%;background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--mlv-spacing-1);min-width:0;padding:var(--mlv-padding-xs);overflow:hidden;border-bottom:var(--mlv-stroke-width) solid var(--mlv-border-normal);background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar [hidden]{display:none!important}.mlv-editor__toolbar-scroll{flex:1 1 auto;min-width:0}.mlv-editor__toolbar-scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor .mlv-editor-toolbar__overflow,.mlv-editor .mlv-editor-undo-redo,.mlv-editor .mlv-editor-inline-marks,.mlv-editor .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor mlv-editor-zoom,.mlv-editor mlv-editor-heading,.mlv-editor mlv-editor-list,.mlv-editor mlv-editor-text-color,.mlv-editor mlv-editor-highlight,.mlv-editor mlv-editor-alignment,.mlv-editor mlv-editor-link,.mlv-editor mlv-editor-table,.mlv-editor mlv-editor-image-upload{flex:0 0 auto}.mlv-editor__viewport{min-block-size:8rem;overflow:auto;overscroll-behavior:contain}.mlv-editor__view{position:relative;min-inline-size:100%;transform:scale(var(--mlv-editor-zoom, 1));transform-origin:left top;transition:transform var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__content{min-block-size:8rem;padding-block:var(--mlv-spacing-2);padding-inline:var(--mlv-editor-gutter);border:0;outline:none}.mlv-editor__block-handle{position:absolute;inset-inline-start:max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2);display:inline-flex;align-items:center;justify-content:center;inline-size:var(--mlv-editor-gutter);block-size:var(--mlv-height-xs);border-radius:var(--mlv-radius-s);color:var(--mlv-text-tertiary);cursor:grab;opacity:0;pointer-events:none;transition:opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__block-handle svg{inline-size:var(--mlv-spacing-4);block-size:var(--mlv-spacing-4);fill:currentcolor}.mlv-editor__block-handle:hover{background-color:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-secondary)}.mlv-editor__block-handle[data-visible=true]{opacity:1;pointer-events:auto}.mlv-editor__drop-indicator{position:absolute;inset-inline:calc(max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2) + var(--mlv-editor-gutter));block-size:var(--mlv-stroke-width-medium);border-radius:var(--mlv-radius-full);background-color:var(--mlv-border-focus);opacity:0;pointer-events:none;transform:translateY(calc(var(--mlv-editor-drop-gap) / 2 - var(--mlv-stroke-width-medium) / 2));transition:top var(--mlv-duration-normal) var(--mlv-ease-out),opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__drop-indicator[data-visible=true]{opacity:1}.mlv-editor .mlv-editor__block--dragging{opacity:var(--mlv-disabled-opacity)}.mlv-editor .ProseMirror--block-dragging>*{transition:transform var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__status{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-2);min-block-size:var(--mlv-spacing-4);margin-block-start:var(--mlv-spacing-1)}.mlv-editor__upload-placeholder{position:relative;display:inline-block;inline-size:var(--mlv-height-l);block-size:var(--mlv-height-l);margin-inline:var(--mlv-spacing-1);border:var(--mlv-stroke-width) dashed var(--mlv-border-focus);border-radius:var(--mlv-radius-m);background:var(--mlv-background-accent-1-pale);vertical-align:middle}.mlv-editor__upload-placeholder:after{position:absolute;inset:var(--mlv-spacing-2);border:var(--mlv-stroke-width-medium) solid var(--mlv-border-normal);border-block-start-color:var(--mlv-border-focus);border-radius:var(--mlv-radius-full);animation:mlv-editor-upload-spin var(--mlv-duration-slower) var(--mlv-ease-linear) infinite;content:\"\"}.mlv-editor__ai-streaming{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-accent-1-pale)}.mlv-editor__ai-streaming-chunk{animation:mlv-editor-ai-chunk-in var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__ai-caret{position:relative;display:inline-block;inline-size:0;block-size:1em;pointer-events:none}.mlv-editor__ai-caret:after{position:absolute;inset-block:0;inset-inline-start:.0625rem;inline-size:.35em;border-radius:var(--mlv-radius-xs);background:linear-gradient(to bottom,var(--mlv-background-accent-1),var(--mlv-background-accent-2));content:\"\";animation:mlv-editor-ai-caret-pulse var(--mlv-duration-sluggish) var(--mlv-ease-in-out) infinite}.mlv-editor__ai-suggestion-insert{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-success-pale)}.mlv-editor__ai-suggestion-delete{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-danger-1-pale);color:var(--mlv-text-negative);text-decoration-line:line-through}.mlv-editor__ai-suggestion-current{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor--content-width-default{--mlv-editor-measure: 45rem}.mlv-editor--content-width-wide{--mlv-editor-measure: 60rem}.mlv-editor--content-width-full{--mlv-editor-measure: 100%}.mlv-editor--focused .mlv-form-control-wrapper__control-container{border-color:var(--mlv-border-focus);box-shadow:var(--mlv-focus-ring)}.mlv-editor--readonly .ProseMirror{cursor:text}.mlv-editor--readonly .ProseMirror ::selection{background:var(--mlv-background-accent-1-pale)}.mlv-editor--disabled{opacity:var(--mlv-disabled-opacity)}.mlv-editor--disabled .mlv-editor__surface,.mlv-editor--disabled .mlv-editor__status{pointer-events:none}.mlv-editor--disabled .mlv-editor__content,.mlv-editor--disabled .ProseMirror{cursor:default;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror{min-block-size:inherit;max-inline-size:var(--mlv-editor-measure);margin-inline:auto;color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size);font-weight:var(--mlv-typography-body-m-weight);line-height:var(--mlv-typography-body-m-line-height);overflow-wrap:anywhere;white-space:pre-wrap}.mlv-editor .ProseMirror:focus-visible{outline:none}.mlv-editor .ProseMirror>*:first-child{margin-block-start:0}.mlv-editor .ProseMirror>*:last-child{margin-block-end:0}.mlv-editor .ProseMirror p{margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror h1,.mlv-editor .ProseMirror h2,.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{margin-block:var(--mlv-spacing-6) var(--mlv-spacing-3);color:var(--mlv-text-heading);font-family:var(--mlv-typography-family-heading)}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h1-size);font-weight:var(--mlv-typography-heading-h1-weight);line-height:var(--mlv-typography-heading-h1-line-height)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h2-size);font-weight:var(--mlv-typography-heading-h2-weight);line-height:var(--mlv-typography-heading-h2-line-height)}.mlv-editor .ProseMirror h3{font-size:var(--mlv-typography-heading-h3-size);font-weight:var(--mlv-typography-heading-h3-weight);line-height:var(--mlv-typography-heading-h3-line-height)}.mlv-editor .ProseMirror h4{font-size:var(--mlv-typography-heading-h4-size);font-weight:var(--mlv-typography-heading-h4-weight);line-height:var(--mlv-typography-heading-h4-line-height)}.mlv-editor .ProseMirror h5{font-size:var(--mlv-typography-heading-h5-size);font-weight:var(--mlv-typography-heading-h5-weight);line-height:var(--mlv-typography-heading-h5-line-height)}.mlv-editor .ProseMirror h6{font-size:var(--mlv-typography-heading-h6-size);font-weight:var(--mlv-typography-heading-h6-weight);line-height:var(--mlv-typography-heading-h6-line-height)}.mlv-editor .ProseMirror h1{letter-spacing:var(--mlv-typography-heading-h1-letter-spacing)}.mlv-editor .ProseMirror h2{letter-spacing:var(--mlv-typography-heading-h2-letter-spacing)}.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{letter-spacing:var(--mlv-letter-spacing-normal)}.mlv-editor .ProseMirror strong{font-weight:var(--mlv-font-weight-bold)}.mlv-editor .ProseMirror em{font-style:italic}.mlv-editor .ProseMirror s{text-decoration:line-through}.mlv-editor .ProseMirror u{text-underline-offset:var(--mlv-spacing-0-5)}.mlv-editor .ProseMirror a{color:var(--mlv-text-action);text-decoration:underline;text-underline-offset:var(--mlv-spacing-0-5);transition:color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .ProseMirror a:visited{color:var(--mlv-text-action)}.mlv-editor .ProseMirror a:hover{color:var(--mlv-text-action-hover)}.mlv-editor .ProseMirror a:focus-visible{border-radius:var(--mlv-radius-xs);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul,.mlv-editor .ProseMirror ol{padding-inline-start:var(--mlv-spacing-6);margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror ul ul,.mlv-editor .ProseMirror ul ol,.mlv-editor .ProseMirror ol ul,.mlv-editor .ProseMirror ol ol{margin-block:var(--mlv-spacing-1) 0}.mlv-editor .ProseMirror ul{list-style:disc}.mlv-editor .ProseMirror ul ul{list-style:circle}.mlv-editor .ProseMirror ol{list-style:decimal}.mlv-editor .ProseMirror li{margin-block:var(--mlv-spacing-1)}.mlv-editor .ProseMirror li>p{margin:0}.mlv-editor .ProseMirror ul[data-type=taskList]{padding:0;list-style:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]{display:flex;align-items:flex-start;gap:var(--mlv-spacing-2)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label{display:inline-flex;align-items:center;min-block-size:var(--mlv-height-xs);cursor:pointer;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input{accent-color:var(--mlv-background-accent-1)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>div{flex:1 1 auto;min-width:0}.mlv-editor .ProseMirror blockquote{padding-inline-start:var(--mlv-spacing-4);margin:var(--mlv-spacing-4) 0;border-inline-start:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);color:var(--mlv-text-secondary)}.mlv-editor .ProseMirror code{padding:var(--mlv-spacing-0-5) var(--mlv-spacing-1);border-radius:var(--mlv-radius-s);background:var(--mlv-background-neutral-1);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size)}.mlv-editor .ProseMirror pre{padding:var(--mlv-padding-m);margin-block:var(--mlv-spacing-4);overflow:auto;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-m);background:var(--mlv-background-sunken);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size);line-height:var(--mlv-typography-body-m-line-height);tab-size:2}.mlv-editor .ProseMirror pre code{padding:0;background:transparent;color:inherit;font-size:inherit}.mlv-editor .ProseMirror hr{height:var(--mlv-stroke-width);margin-block:var(--mlv-spacing-6);border:0;background:var(--mlv-border-normal)}.mlv-editor .ProseMirror img{display:block;max-inline-size:100%;block-size:auto;margin-block:var(--mlv-spacing-4);border-radius:var(--mlv-radius-m)}.mlv-editor .ProseMirror img.ProseMirror-selectednode{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror .ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-state=true]{border-radius:var(--mlv-radius-s);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-spacing-1)}.mlv-editor .ProseMirror [data-resize-container]{margin-block:var(--mlv-spacing-4)}.mlv-editor .ProseMirror [data-resize-container] img{margin-block:0}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]{outline:none}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode>[data-resize-wrapper],.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]>[data-resize-wrapper]{border-radius:var(--mlv-radius-m);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror [data-resize-handle],.mlv-editor .ProseMirror .resize-handle{inline-size:var(--mlv-spacing-3);block-size:var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-background-base);border-radius:var(--mlv-radius-full);background:var(--mlv-background-accent-1);box-shadow:var(--mlv-shadow-raised)}.mlv-editor .ProseMirror [data-resize-handle]:focus-visible,.mlv-editor .ProseMirror .resize-handle:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror .is-editor-empty:first-child:before{float:left;height:0;color:var(--mlv-text-placeholder);content:attr(data-placeholder);pointer-events:none}.mlv-editor .tableWrapper{margin-block:var(--mlv-spacing-3);overflow-x:auto}.mlv-editor table{width:100%;border-collapse:collapse;table-layout:fixed}.mlv-editor th,.mlv-editor td{position:relative;min-width:3rem;padding:var(--mlv-spacing-2) var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-border-normal);vertical-align:top}.mlv-editor th{background:var(--mlv-background-neutral-1);font-weight:var(--mlv-font-weight-semibold)}.mlv-editor .selectedCell:after{position:absolute;inset:0;z-index:1;background:var(--mlv-background-accent-1-pale);content:\"\";opacity:.55;pointer-events:none}.mlv-editor .ProseMirror-focused .selectedCell:after{box-shadow:inset 0 0 0 var(--mlv-stroke-width-medium) var(--mlv-border-focus)}.mlv-editor .column-resize-handle{position:absolute;inset-block:0;inset-inline-end:calc(var(--mlv-stroke-width) * -1);z-index:2;width:calc(var(--mlv-stroke-width) * 2);background:var(--mlv-border-focus);pointer-events:none}.mlv-editor .resize-cursor{cursor:col-resize}@keyframes mlv-editor-upload-spin{to{transform:rotate(1turn)}}@keyframes mlv-editor-ai-chunk-in{0%{opacity:0;filter:blur(.125rem)}to{opacity:1;filter:blur(0)}}@keyframes mlv-editor-ai-caret-pulse{0%,to{opacity:1}50%{opacity:.35}}@media(max-width:40rem){.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-3)}.mlv-editor__toolbar{flex-wrap:nowrap;overflow-x:auto}.mlv-editor__content{padding-block:var(--mlv-spacing-1-5)}.mlv-editor__status{justify-content:flex-start}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h2-size)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h3-size)}}@media print{.mlv-editor__toolbar,.mlv-editor__status,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .mlv-editor-image-upload-status,.mlv-editor__upload-placeholder,.mlv-editor__ai-suggestion-delete,.mlv-editor__ai-caret{display:none}.mlv-editor__ai-suggestion-current{outline:none}.mlv-editor__ai-streaming-chunk{animation:none}.mlv-editor__viewport{overflow:visible}.mlv-editor__view{transform:none}.mlv-editor__content,.mlv-editor .ProseMirror{min-block-size:0;padding:0}.mlv-editor table{break-inside:avoid}}@media(hover:none){.mlv-editor__block-handle{display:none}}@media(prefers-reduced-motion:reduce){.mlv-editor .mlv-form-control-wrapper__control-container,.mlv-editor__view,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .ProseMirror--block-dragging>*,.mlv-editor .ProseMirror a{transition-duration:var(--mlv-duration-instant)}.mlv-editor__upload-placeholder:after,.mlv-editor__ai-streaming-chunk,.mlv-editor__ai-caret:after{animation:none}}.mlv-editor__drag-ghost{max-block-size:12rem;padding:var(--mlv-spacing-2);overflow:hidden;border-radius:var(--mlv-radius-m);background-color:var(--mlv-background-raised);box-shadow:var(--mlv-shadow-floating);opacity:.9;-webkit-mask-image:linear-gradient(to bottom,black 70%,transparent 100%);mask-image:linear-gradient(to bottom,black 70%,transparent 100%);scale:.85;transform-origin:top left}}\n"], dependencies: [{ kind: "component", type: MlvFormControlWrapper, selector: "mlv-form-control-wrapper", outputs: ["clear"] }, { kind: "directive", type: MlvFormControlWrapperControl, selector: "[mlvFormControlWrapperControl]" }, { kind: "component", type: MlvHint, selector: "mlv-hint" }, { kind: "component", type: MlvLabel, selector: "mlv-label", inputs: ["for", "required"] }, { kind: "component", type: MlvMessage, selector: "mlv-message", inputs: ["state"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvFade, selector: "[mlvFade]", inputs: ["mlvFadeHeight", "mlvFadeSize", "mlvFadeOffset", "mlvFade"] }, { kind: "component", type: MlvToolbar, selector: "mlv-toolbar", inputs: ["gap", "equalSize"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "directive", type: MlvEditorToolbarRoot, selector: "[mlvEditorToolbarRoot]", inputs: ["ariaLabel", "disabled", "orientation", "wrap"], exportAs: ["mlvEditorToolbarRoot"] }, { kind: "component", type: MlvEditorToolbarOverflow, selector: "mlv-editor-toolbar-overflow" }, { kind: "component", type: MlvEditorUndoRedo, selector: "mlv-editor-undo-redo" }, { kind: "component", type: MlvEditorZoom, selector: "mlv-editor-zoom", inputs: ["zoom", "zoomLevels", "min", "max", "fitToContainer"], outputs: ["zoomChange"] }, { kind: "component", type: MlvEditorHeading, selector: "mlv-editor-heading", inputs: ["levels"] }, { kind: "component", type: MlvEditorList, selector: "mlv-editor-list" }, { kind: "component", type: MlvEditorInlineMarks, selector: "mlv-editor-inline-marks" }, { kind: "component", type: MlvEditorTextColor, selector: "mlv-editor-text-color" }, { kind: "component", type: MlvEditorStatus, selector: "mlv-editor-status", inputs: ["characterLimit"] }, { kind: "component", type: MlvEditorHighlight, selector: "mlv-editor-highlight" }, { kind: "component", type: MlvEditorLink, selector: "mlv-editor-link", inputs: ["allowedProtocols"] }, { kind: "component", type: MlvEditorImageUpload, selector: "mlv-editor-image-upload" }, { kind: "component", type: MlvEditorImageUploadStatus, selector: "mlv-editor-image-upload-status" }, { kind: "component", type: MlvEditorTable, selector: "mlv-editor-table" }, { kind: "component", type: MlvEditorTableControls, selector: "mlv-editor-table-controls", inputs: ["layer"] }, { kind: "component", type: MlvEditorAlignment, selector: "mlv-editor-alignment" }, { kind: "component", type: MlvEditorBlockInsert, selector: "mlv-editor-block-insert" }, { kind: "component", type: MlvSpacer, selector: "mlv-spacer" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
11592
+ ], queries: [{ propertyName: "_toolbarDefs", predicate: MlvEditorToolbarDef, isSignal: true }, { propertyName: "_toolbarStartDefs", predicate: MlvEditorToolbarStartDef, isSignal: true }, { propertyName: "_toolbarEndDefs", predicate: MlvEditorToolbarEndDef, isSignal: true }], viewQueries: [{ propertyName: "_content", first: true, predicate: ["content"], descendants: true, isSignal: true }, { propertyName: "_view", first: true, predicate: ["view"], descendants: true, isSignal: true }, { propertyName: "_toolbarBand", first: true, predicate: ["toolbarBand"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<mlv-form-control-wrapper (clear)=\"clearValue()\">\n @if (label() || hint()) {\n <mlv-label\n [for]=\"_ownLabelFor()\"\n [id]=\"_labelId()\"\n (click)=\"_onLabelClick()\"\n >\n {{ label() }}\n @if (hint()) {\n <mlv-hint>{{ hint() }}</mlv-hint>\n }\n </mlv-label>\n }\n\n <ng-template mlvFormControlWrapperControl>\n <div #surface class=\"mlv-editor__surface\">\n <!--\n Declared once and stamped by one outlet: before or after the viewport\n for the docked bar (#416), so the DOM order is always the visual\n order, with no CSS `order`; into the selection bubble's overlay for\n the floating appearance (#483). Re-stamping re-creates the view, which\n closes an open toolbar popup; projected slot content is re-attached.\n Neither stamps anything while `readonly` (#498): no band, no reserved\n space; `_toolbarRendered()` is the one switch.\n -->\n <ng-template #toolbarTemplate>\n <div #toolbarBand class=\"mlv-editor__toolbar-band\">\n <div\n mlvEditorToolbarRoot\n #toolbarRoot=\"mlvEditorToolbarRoot\"\n class=\"mlv-editor__toolbar\"\n [ariaLabel]=\"_toolbarAriaLabel()\"\n [disabled]=\"computedDisabled()\"\n [measureTarget]=\"surface\"\n >\n @if (_hasToolbarDef()) {\n <ng-content select=\"[mlvEditorToolbar]\" />\n @for (definition of _toolbarDefs(); track definition) {\n @if (definition.templateRef; as toolbarTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"toolbarTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n } @else {\n <ng-content select=\"[mlvEditorToolbarStart]\" />\n @for (definition of _toolbarStartDefs(); track definition) {\n @if (definition.templateRef; as startTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"startTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n <div mlvFade class=\"mlv-editor__toolbar-scroll\">\n <mlv-toolbar>\n <mlv-editor-undo-redo />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-zoom />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-heading />\n <mlv-editor-list />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-inline-marks [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-text-color />\n <mlv-editor-highlight />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-alignment [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-link />\n <mlv-editor-table />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-block-insert [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-image-upload />\n </mlv-toolbar>\n </div>\n <mlv-editor-toolbar-overflow />\n @for (definition of _toolbarEndDefs(); track definition) {\n @if (definition.templateRef; as endTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"endTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n <ng-content select=\"[mlvEditorToolbarEnd]\" />\n }\n </div>\n </div>\n </ng-template>\n\n @if (_barRendered() && toolbarPosition() === 'top') {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n\n <div #viewport class=\"mlv-editor__viewport\">\n <div #view class=\"mlv-editor__view\">\n <div #content class=\"mlv-editor__content\"></div>\n <mlv-editor-table-controls [layer]=\"view\" />\n </div>\n </div>\n @if (_barRendered() && toolbarPosition() === 'bottom') {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n @if (toolbarAppearance() === 'floating') {\n <ng-template mlvEditorBubble [viewport]=\"viewport\" [capped]=\"_capped()\">\n @if (_toolbarRendered()) {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n </ng-template>\n }\n <mlv-editor-image-upload-status />\n </div>\n </ng-template>\n\n @if (message()) {\n <mlv-message [state]=\"resolvedState()\" [id]=\"id() + '-message'\">\n {{ message() }}\n </mlv-message>\n }\n</mlv-form-control-wrapper>\n\n<div\n class=\"mlv-editor__status\"\n [attr.aria-disabled]=\"computedDisabled() || null\"\n>\n <ng-content select=\"[mlvEditorStatus]\" />\n <mlv-spacer />\n <mlv-editor-status [characterLimit]=\"characterLimit()\" />\n</div>\n", styles: ["@layer mlv.components{.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-6);--mlv-editor-measure: 45rem;box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor *{box-sizing:border-box}.mlv-editor{display:block;color:var(--mlv-text-primary)}.mlv-editor .mlv-form-control-wrapper__control-container{height:auto;min-height:var(--form-ctrl-height);padding:0;overflow:clip;background:var(--mlv-background-base);transition:border-color var(--mlv-duration-fast) var(--mlv-ease-default),box-shadow var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .mlv-form-control-wrapper__control-row{align-items:stretch}.mlv-form-control-wrapper--state-error .mlv-editor__surface{box-shadow:inset 0 0 0 var(--mlv-stroke-width) var(--mlv-border-error)}.mlv-editor__surface{display:flex;flex:1 1 auto;flex-direction:column;min-width:0;width:100%;background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--mlv-spacing-1);min-width:0;padding:var(--mlv-padding-xs);overflow:hidden;border-block-end:var(--mlv-stroke-width) solid var(--mlv-border-normal);background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar-band{display:flex;flex:0 0 auto;flex-direction:column;min-inline-size:0}.mlv-editor--toolbar-bottom .mlv-editor__toolbar{border-block-start:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-block-end:0}.mlv-editor__toolbar [hidden]{display:none!important}.mlv-editor__toolbar-scroll{flex:1 1 auto;min-width:0}.mlv-editor__toolbar-scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor .mlv-editor-toolbar__overflow,.mlv-editor .mlv-editor-undo-redo,.mlv-editor .mlv-editor-inline-marks,.mlv-editor .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor mlv-editor-zoom,.mlv-editor mlv-editor-heading,.mlv-editor mlv-editor-list,.mlv-editor mlv-editor-text-color,.mlv-editor mlv-editor-highlight,.mlv-editor mlv-editor-alignment,.mlv-editor mlv-editor-link,.mlv-editor mlv-editor-table,.mlv-editor mlv-editor-image-upload{flex:0 0 auto}.mlv-editor__viewport{flex:1 1 auto;min-block-size:var(--mlv-editor-min-height, 8rem)}.mlv-editor--capped .mlv-editor__surface{block-size:var(--mlv-editor-height, auto);max-block-size:var(--mlv-editor-max-height, none)}.mlv-editor--capped .mlv-editor__viewport{min-block-size:0;overflow:auto;overscroll-behavior:contain}.mlv-editor__view{position:relative;isolation:isolate;min-inline-size:100%;zoom:var(--mlv-editor-zoom, 1)}.mlv-editor--capped .mlv-editor__view{min-inline-size:0;inline-size:calc(100% * var(--mlv-editor-zoom, 1))}.mlv-editor__content{min-block-size:var(--mlv-editor-min-height, 8rem);padding-block:var(--mlv-spacing-2);padding-inline:var(--mlv-editor-gutter);border:0;outline:none}.mlv-editor--capped .mlv-editor__content{min-block-size:calc(var(--mlv-editor-min-height, 8rem) / var(--mlv-editor-zoom, 1))}.mlv-editor__block-handle{position:absolute;inset-inline-start:max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2);display:inline-flex;align-items:center;justify-content:center;inline-size:var(--mlv-editor-gutter);block-size:var(--mlv-height-xs);border-radius:var(--mlv-radius-s);color:var(--mlv-text-tertiary);cursor:grab;opacity:0;pointer-events:none;transition:opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__block-handle svg{inline-size:var(--mlv-spacing-4);block-size:var(--mlv-spacing-4);fill:currentcolor}.mlv-editor__block-handle:hover{background-color:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-secondary)}.mlv-editor__block-handle[data-visible=true]{opacity:1;pointer-events:auto}.mlv-editor__drop-indicator{position:absolute;inset-inline:calc(max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2) + var(--mlv-editor-gutter));block-size:var(--mlv-stroke-width-medium);border-radius:var(--mlv-radius-full);background-color:var(--mlv-border-focus);opacity:0;pointer-events:none;transform:translateY(-50%);transition:top var(--mlv-duration-normal) var(--mlv-ease-out),opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__drop-indicator[data-visible=true]{opacity:1}.mlv-editor .mlv-editor__block--dragging{opacity:var(--mlv-disabled-opacity)}.mlv-editor__status{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-2);min-block-size:var(--mlv-spacing-4);margin-block-start:var(--mlv-spacing-1)}.mlv-editor__upload-placeholder{position:relative;display:inline-block;inline-size:var(--mlv-height-l);block-size:var(--mlv-height-l);margin-inline:var(--mlv-spacing-1);border:var(--mlv-stroke-width) dashed var(--mlv-border-focus);border-radius:var(--mlv-radius-m);background:var(--mlv-background-accent-1-pale);vertical-align:middle}.mlv-editor__upload-placeholder:after{position:absolute;inset:var(--mlv-spacing-2);border:var(--mlv-stroke-width-medium) solid var(--mlv-border-normal);border-block-start-color:var(--mlv-border-focus);border-radius:var(--mlv-radius-full);animation:mlv-editor-upload-spin var(--mlv-duration-slower) var(--mlv-ease-linear) infinite;content:\"\"}.mlv-editor__ai-streaming{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-accent-1-pale)}.mlv-editor__ai-streaming-chunk{animation:mlv-editor-ai-chunk-in var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__ai-caret{position:relative;display:inline-block;inline-size:0;block-size:1em;pointer-events:none}.mlv-editor__ai-caret:after{position:absolute;inset-block:0;inset-inline-start:.0625rem;inline-size:.35em;border-radius:var(--mlv-radius-xs);background:linear-gradient(to bottom,var(--mlv-background-accent-1),var(--mlv-background-accent-2));content:\"\";animation:mlv-editor-ai-caret-pulse var(--mlv-duration-sluggish) var(--mlv-ease-in-out) infinite}.mlv-editor__ai-suggestion-insert{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-success-pale)}.mlv-editor__ai-suggestion-delete{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-danger-1-pale);color:var(--mlv-text-negative);text-decoration-line:line-through}.mlv-editor__ai-suggestion-current{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor--content-width-default{--mlv-editor-measure: 45rem}.mlv-editor--content-width-wide{--mlv-editor-measure: 60rem}.mlv-editor--content-width-full{--mlv-editor-measure: 100%}.mlv-editor--focused .mlv-form-control-wrapper__control-container{border-color:var(--mlv-border-focus);box-shadow:var(--mlv-focus-ring)}.mlv-editor--readonly .ProseMirror{cursor:text}.mlv-editor--readonly .ProseMirror ::selection{background:var(--mlv-background-accent-1-pale)}.mlv-editor--disabled{opacity:var(--mlv-disabled-opacity)}.mlv-editor--disabled .mlv-editor__surface,.mlv-editor--disabled .mlv-editor__status{pointer-events:none}.mlv-editor--disabled .mlv-editor__content,.mlv-editor--disabled .ProseMirror{cursor:default;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror{min-block-size:inherit;max-inline-size:var(--mlv-editor-measure);margin-inline:auto;color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size);font-weight:var(--mlv-typography-body-m-weight);line-height:var(--mlv-typography-body-m-line-height);overflow-wrap:anywhere;white-space:pre-wrap}.mlv-editor .ProseMirror:focus-visible{outline:none}.mlv-editor .ProseMirror>*:first-child{margin-block-start:0}.mlv-editor .ProseMirror>*:last-child{margin-block-end:0}.mlv-editor .ProseMirror p{margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror h1,.mlv-editor .ProseMirror h2,.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{margin-block:var(--mlv-spacing-6) var(--mlv-spacing-3);color:var(--mlv-text-heading);font-family:var(--mlv-typography-family-heading)}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h1-size);font-weight:var(--mlv-typography-heading-h1-weight);line-height:var(--mlv-typography-heading-h1-line-height)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h2-size);font-weight:var(--mlv-typography-heading-h2-weight);line-height:var(--mlv-typography-heading-h2-line-height)}.mlv-editor .ProseMirror h3{font-size:var(--mlv-typography-heading-h3-size);font-weight:var(--mlv-typography-heading-h3-weight);line-height:var(--mlv-typography-heading-h3-line-height)}.mlv-editor .ProseMirror h4{font-size:var(--mlv-typography-heading-h4-size);font-weight:var(--mlv-typography-heading-h4-weight);line-height:var(--mlv-typography-heading-h4-line-height)}.mlv-editor .ProseMirror h5{font-size:var(--mlv-typography-heading-h5-size);font-weight:var(--mlv-typography-heading-h5-weight);line-height:var(--mlv-typography-heading-h5-line-height)}.mlv-editor .ProseMirror h6{font-size:var(--mlv-typography-heading-h6-size);font-weight:var(--mlv-typography-heading-h6-weight);line-height:var(--mlv-typography-heading-h6-line-height)}.mlv-editor .ProseMirror h1{letter-spacing:var(--mlv-typography-heading-h1-letter-spacing)}.mlv-editor .ProseMirror h2{letter-spacing:var(--mlv-typography-heading-h2-letter-spacing)}.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{letter-spacing:var(--mlv-letter-spacing-normal)}.mlv-editor .ProseMirror strong{font-weight:var(--mlv-font-weight-bold)}.mlv-editor .ProseMirror em{font-style:italic}.mlv-editor .ProseMirror s{text-decoration:line-through}.mlv-editor .ProseMirror u{text-underline-offset:var(--mlv-spacing-0-5)}.mlv-editor .ProseMirror a{color:var(--mlv-text-action);text-decoration:underline;text-underline-offset:var(--mlv-spacing-0-5);transition:color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .ProseMirror a:visited{color:var(--mlv-text-action)}.mlv-editor .ProseMirror a:hover{color:var(--mlv-text-action-hover)}.mlv-editor .ProseMirror a:focus-visible{border-radius:var(--mlv-radius-xs);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul,.mlv-editor .ProseMirror ol{padding-inline-start:var(--mlv-spacing-6);margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror ul ul,.mlv-editor .ProseMirror ul ol,.mlv-editor .ProseMirror ol ul,.mlv-editor .ProseMirror ol ol{margin-block:var(--mlv-spacing-1) 0}.mlv-editor .ProseMirror ul{list-style:disc}.mlv-editor .ProseMirror ul ul{list-style:circle}.mlv-editor .ProseMirror ol{list-style:decimal}.mlv-editor .ProseMirror li{margin-block:var(--mlv-spacing-1)}.mlv-editor .ProseMirror li>p{margin:0}.mlv-editor .ProseMirror ul[data-type=taskList]{padding:0;list-style:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]{display:flex;align-items:flex-start;gap:var(--mlv-spacing-2)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label{display:inline-flex;align-items:center;min-block-size:var(--mlv-height-xs);cursor:pointer;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input{accent-color:var(--mlv-background-accent-1)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>div{flex:1 1 auto;min-width:0}.mlv-editor .ProseMirror blockquote{padding-inline-start:var(--mlv-spacing-4);margin:var(--mlv-spacing-4) 0;border-inline-start:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);color:var(--mlv-text-secondary)}.mlv-editor .ProseMirror code{padding:var(--mlv-spacing-0-5) var(--mlv-spacing-1);border-radius:var(--mlv-radius-s);background:var(--mlv-background-neutral-1);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size)}.mlv-editor .ProseMirror pre{padding:var(--mlv-padding-m);margin-block:var(--mlv-spacing-4);overflow:auto;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-m);background:var(--mlv-background-sunken);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size);line-height:var(--mlv-typography-body-m-line-height);tab-size:2}.mlv-editor .ProseMirror pre code{padding:0;background:transparent;color:inherit;font-size:inherit}.mlv-editor .ProseMirror hr{height:var(--mlv-stroke-width);margin-block:var(--mlv-spacing-6);border:0;background:var(--mlv-border-normal)}.mlv-editor .ProseMirror img{display:block;max-inline-size:100%;block-size:auto;margin-block:var(--mlv-spacing-4);border-radius:var(--mlv-radius-m)}.mlv-editor .ProseMirror img.ProseMirror-selectednode{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror .ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-state=true]{border-radius:var(--mlv-radius-s);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-spacing-1)}.mlv-editor .ProseMirror [data-resize-container]{margin-block:var(--mlv-spacing-4)}.mlv-editor .ProseMirror [data-resize-container] img{margin-block:0}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]{outline:none}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode>[data-resize-wrapper],.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]>[data-resize-wrapper]{border-radius:var(--mlv-radius-m);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror [data-resize-handle],.mlv-editor .ProseMirror .resize-handle{inline-size:var(--mlv-spacing-3);block-size:var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-background-base);border-radius:var(--mlv-radius-full);background:var(--mlv-background-accent-1);box-shadow:var(--mlv-shadow-raised)}.mlv-editor .ProseMirror [data-resize-handle]:focus-visible,.mlv-editor .ProseMirror .resize-handle:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror .is-editor-empty:first-child:before{float:left;height:0;color:var(--mlv-text-placeholder);content:attr(data-placeholder);pointer-events:none}.mlv-editor .tableWrapper{margin-block:var(--mlv-spacing-3);overflow-x:auto}.mlv-editor table{width:100%;border-collapse:collapse;table-layout:fixed}.mlv-editor th,.mlv-editor td{position:relative;min-width:3rem;padding:var(--mlv-spacing-2) var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-border-normal);vertical-align:top}.mlv-editor th{background:var(--mlv-background-neutral-1);font-weight:var(--mlv-font-weight-semibold)}.mlv-editor .selectedCell:after{position:absolute;inset:0;z-index:1;background:var(--mlv-background-accent-1-pale);content:\"\";opacity:.55;pointer-events:none}.mlv-editor .ProseMirror-focused .selectedCell:after{box-shadow:inset 0 0 0 var(--mlv-stroke-width-medium) var(--mlv-border-focus)}.mlv-editor .column-resize-handle{position:absolute;inset-block:0;inset-inline-end:calc(var(--mlv-stroke-width) * -1);z-index:2;width:calc(var(--mlv-stroke-width) * 2);background:var(--mlv-border-focus);pointer-events:none}.mlv-editor .resize-cursor{cursor:col-resize}@keyframes mlv-editor-upload-spin{to{transform:rotate(1turn)}}@keyframes mlv-editor-ai-chunk-in{0%{opacity:0;filter:blur(.125rem)}to{opacity:1;filter:blur(0)}}@keyframes mlv-editor-ai-caret-pulse{0%,to{opacity:1}50%{opacity:.35}}.mlv-editor--toolbar-sticky.mlv-editor--toolbar-top .mlv-editor__toolbar-band{position:sticky;inset-block-start:var(--mlv-editor-toolbar-sticky-offset, 0);z-index:var(--mlv-z-raised)}.mlv-editor--toolbar-sticky.mlv-editor--toolbar-bottom .mlv-editor__toolbar-band{position:sticky;inset-block-end:var(--mlv-editor-toolbar-sticky-offset, 0);z-index:var(--mlv-z-raised)}.mlv-editor-bubble{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-bubble *{box-sizing:border-box}.mlv-editor-bubble .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor-bubble .mlv-editor-toolbar__overflow,.mlv-editor-bubble .mlv-editor-undo-redo,.mlv-editor-bubble .mlv-editor-inline-marks,.mlv-editor-bubble .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-bubble mlv-editor-zoom,.mlv-editor-bubble mlv-editor-heading,.mlv-editor-bubble mlv-editor-list,.mlv-editor-bubble mlv-editor-text-color,.mlv-editor-bubble mlv-editor-highlight,.mlv-editor-bubble mlv-editor-alignment,.mlv-editor-bubble mlv-editor-link,.mlv-editor-bubble mlv-editor-table,.mlv-editor-bubble mlv-editor-image-upload{flex:0 0 auto}.mlv-editor-bubble{max-inline-size:calc(100vw - 2 * var(--mlv-spacing-2));border-radius:var(--mlv-radius-panel);background:var(--mlv-elevation-bg-4);box-shadow:var(--mlv-shadow-floating);color:var(--mlv-text-primary);animation:mlv-editor-bubble-enter var(--mlv-duration-fast) var(--mlv-ease-out)}.mlv-editor-bubble--hidden{visibility:hidden;animation:none}.mlv-editor-bubble .mlv-editor__toolbar-band{flex:1 1 auto;min-inline-size:0}.mlv-editor-bubble .mlv-editor__toolbar{border:0;border-radius:inherit;background:transparent}@keyframes mlv-editor-bubble-enter{0%{opacity:0}}@media(max-width:40rem){.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-3)}.mlv-editor__toolbar{flex-wrap:nowrap;overflow-x:auto}.mlv-editor__content{padding-block:var(--mlv-spacing-1-5)}.mlv-editor__status{justify-content:flex-start}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h2-size)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h3-size)}}@media print{.mlv-editor__toolbar-band,.mlv-editor__toolbar,.mlv-editor__status,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .mlv-editor-image-upload-status,.mlv-editor__upload-placeholder,.mlv-editor__ai-suggestion-delete,.mlv-editor__ai-caret{display:none}.mlv-editor__ai-suggestion-current{outline:none}.mlv-editor__ai-streaming-chunk{animation:none}.mlv-editor__viewport,.mlv-editor--capped .mlv-editor__viewport{overflow:visible;padding-block:0}.mlv-editor--capped .mlv-editor__surface{block-size:auto;max-block-size:none}.mlv-editor__view,.mlv-editor--capped .mlv-editor__view{zoom:1;inline-size:auto;min-inline-size:0}.mlv-editor__content,.mlv-editor--capped .mlv-editor__content,.mlv-editor .ProseMirror{min-block-size:0;padding:0}.mlv-editor table{break-inside:avoid}}@media print{.mlv-editor-bubble{display:none}}@media(hover:none){.mlv-editor__block-handle{display:none}}@media(prefers-reduced-motion:reduce){.mlv-editor .mlv-form-control-wrapper__control-container,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .ProseMirror a{transition-duration:var(--mlv-duration-instant)}.mlv-editor__upload-placeholder:after,.mlv-editor__ai-streaming-chunk,.mlv-editor__ai-caret:after{animation:none}.mlv-editor-bubble{animation:none}}.mlv-editor__drag-ghost{max-block-size:12rem;padding:var(--mlv-spacing-2);overflow:hidden;border-radius:var(--mlv-radius-m);background-color:var(--mlv-background-accent-1-pale);opacity:.9;-webkit-mask-image:linear-gradient(to bottom,black 70%,transparent 100%);mask-image:linear-gradient(to bottom,black 70%,transparent 100%)}}\n"], dependencies: [{ kind: "component", type: MlvFormControlWrapper, selector: "mlv-form-control-wrapper", outputs: ["clear"] }, { kind: "directive", type: MlvFormControlWrapperControl, selector: "[mlvFormControlWrapperControl]" }, { kind: "component", type: MlvHint, selector: "mlv-hint" }, { kind: "component", type: MlvLabel, selector: "mlv-label", inputs: ["for", "required"] }, { kind: "component", type: MlvMessage, selector: "mlv-message", inputs: ["state"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MlvFade, selector: "[mlvFade]", inputs: ["mlvFadeHeight", "mlvFadeSize", "mlvFadeOffset", "mlvFade"] }, { kind: "component", type: MlvToolbar, selector: "mlv-toolbar", inputs: ["gap", "equalSize"] }, { kind: "component", type: MlvDivider, selector: "mlv-divider", inputs: ["orientation", "dashed", "muted"] }, { kind: "directive", type: MlvEditorToolbarRoot, selector: "[mlvEditorToolbarRoot]", inputs: ["ariaLabel", "disabled", "orientation", "wrap", "measureTarget"], exportAs: ["mlvEditorToolbarRoot"] }, { kind: "component", type: MlvEditorToolbarOverflow, selector: "mlv-editor-toolbar-overflow" }, { kind: "component", type: MlvEditorUndoRedo, selector: "mlv-editor-undo-redo" }, { kind: "component", type: MlvEditorZoom, selector: "mlv-editor-zoom", inputs: ["zoom", "zoomLevels", "min", "max", "fitToContainer"], outputs: ["zoomChange"] }, { kind: "component", type: MlvEditorHeading, selector: "mlv-editor-heading", inputs: ["levels"] }, { kind: "component", type: MlvEditorList, selector: "mlv-editor-list" }, { kind: "component", type: MlvEditorInlineMarks, selector: "mlv-editor-inline-marks" }, { kind: "component", type: MlvEditorTextColor, selector: "mlv-editor-text-color" }, { kind: "component", type: MlvEditorStatus, selector: "mlv-editor-status", inputs: ["characterLimit"] }, { kind: "component", type: MlvEditorHighlight, selector: "mlv-editor-highlight" }, { kind: "component", type: MlvEditorLink, selector: "mlv-editor-link", inputs: ["allowedProtocols"] }, { kind: "component", type: MlvEditorImageUpload, selector: "mlv-editor-image-upload" }, { kind: "component", type: MlvEditorImageUploadStatus, selector: "mlv-editor-image-upload-status" }, { kind: "component", type: MlvEditorTable, selector: "mlv-editor-table" }, { kind: "component", type: MlvEditorTableControls, selector: "mlv-editor-table-controls", inputs: ["layer"] }, { kind: "component", type: MlvEditorAlignment, selector: "mlv-editor-alignment" }, { kind: "component", type: MlvEditorBlockInsert, selector: "mlv-editor-block-insert" }, { kind: "directive", type: MlvEditorBubble, selector: "ng-template[mlvEditorBubble]", inputs: ["viewport", "capped"] }, { kind: "component", type: MlvSpacer, selector: "mlv-spacer" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
10241
11593
  }
10242
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditor, decorators: [{
11594
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: MlvEditor, decorators: [{
10243
11595
  type: Component,
10244
11596
  args: [{ selector: 'mlv-editor', imports: [
10245
11597
  MlvFormControlWrapper,
@@ -10268,6 +11620,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
10268
11620
  MlvEditorTableControls,
10269
11621
  MlvEditorAlignment,
10270
11622
  MlvEditorBlockInsert,
11623
+ MlvEditorBubble,
10271
11624
  MlvSpacer,
10272
11625
  ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
10273
11626
  MlvEditorOverlayRegistry,
@@ -10334,11 +11687,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
10334
11687
  '[class.mlv-editor--disabled]': 'computedDisabled()',
10335
11688
  '[class.mlv-editor--readonly]': 'readonly()',
10336
11689
  '[class.mlv-editor--focused]': 'focused()',
11690
+ '[class.mlv-editor--capped]': '_capped()',
11691
+ '[class.mlv-editor--toolbar-top]': 'toolbarPosition() === "top"',
11692
+ '[class.mlv-editor--toolbar-bottom]': 'toolbarPosition() === "bottom"',
11693
+ '[class.mlv-editor--toolbar-bar]': 'toolbarAppearance() === "bar"',
11694
+ '[class.mlv-editor--toolbar-floating]': 'toolbarAppearance() === "floating"',
11695
+ '[class.mlv-editor--toolbar-sticky]': '_stickyToolbar()',
10337
11696
  '[attr.aria-disabled]': 'computedDisabled() || null',
10338
11697
  '[attr.inert]': 'computedDisabled() ? "" : null',
10339
11698
  '[style.--mlv-editor-zoom]': 'zoom() / 100',
10340
- }, template: "<mlv-form-control-wrapper (clear)=\"clearValue()\">\n @if (label() || hint()) {\n <mlv-label [for]=\"id()\" [id]=\"_labelId()\">\n {{ label() }} @if (hint()) {\n <mlv-hint>{{ hint() }}</mlv-hint>\n }\n </mlv-label>\n }\n\n <ng-template mlvFormControlWrapperControl>\n <div class=\"mlv-editor__surface\">\n <div\n mlvEditorToolbarRoot\n #toolbarRoot=\"mlvEditorToolbarRoot\"\n class=\"mlv-editor__toolbar\"\n [ariaLabel]=\"_toolbarAriaLabel()\"\n [disabled]=\"computedDisabled()\"\n >\n @if (_hasToolbarDef()) {\n <ng-content select=\"[mlvEditorToolbar]\" />\n @for (definition of _toolbarDefs(); track definition) { @if\n (definition.templateRef; as toolbarTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"toolbarTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } } } @else {\n <ng-content select=\"[mlvEditorToolbarStart]\" />\n @for (definition of _toolbarStartDefs(); track definition) { @if\n (definition.templateRef; as startTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"startTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } }\n <div mlvFade class=\"mlv-editor__toolbar-scroll\">\n <mlv-toolbar>\n <mlv-editor-undo-redo />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-zoom />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-heading />\n <mlv-editor-list />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-inline-marks [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-text-color />\n <mlv-editor-highlight />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-alignment [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-link />\n <mlv-editor-table />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-block-insert [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-image-upload />\n </mlv-toolbar>\n </div>\n <mlv-editor-toolbar-overflow />\n @for (definition of _toolbarEndDefs(); track definition) { @if\n (definition.templateRef; as endTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"endTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n } }\n <ng-content select=\"[mlvEditorToolbarEnd]\" />\n }\n </div>\n\n <div class=\"mlv-editor__viewport\">\n <div #view class=\"mlv-editor__view\">\n <div #content class=\"mlv-editor__content\"></div>\n <mlv-editor-table-controls [layer]=\"view\" />\n </div>\n </div>\n <mlv-editor-image-upload-status />\n </div>\n </ng-template>\n\n @if (message()) {\n <mlv-message [state]=\"resolvedState()\" [id]=\"id() + '-message'\">\n {{ message() }}\n </mlv-message>\n }\n</mlv-form-control-wrapper>\n\n<div\n class=\"mlv-editor__status\"\n [attr.aria-disabled]=\"computedDisabled() || null\"\n>\n <ng-content select=\"[mlvEditorStatus]\" />\n <mlv-spacer />\n <mlv-editor-status [characterLimit]=\"characterLimit()\" />\n</div>\n", styles: ["@layer mlv.components{.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-6);--mlv-editor-measure: 45rem;--mlv-editor-drop-gap: 1.5rem;box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor *{box-sizing:border-box}.mlv-editor{display:block;color:var(--mlv-text-primary)}.mlv-editor .mlv-form-control-wrapper__control-container{height:auto;min-height:var(--form-ctrl-height);padding:0;overflow:hidden;background:var(--mlv-background-base);transition:border-color var(--mlv-duration-fast) var(--mlv-ease-default),box-shadow var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .mlv-form-control-wrapper__control-row{align-items:stretch}.mlv-form-control-wrapper--state-error .mlv-editor__surface{box-shadow:inset 0 0 0 var(--mlv-stroke-width) var(--mlv-border-error)}.mlv-editor__surface{display:flex;flex:1 1 auto;flex-direction:column;min-width:0;width:100%;background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--mlv-spacing-1);min-width:0;padding:var(--mlv-padding-xs);overflow:hidden;border-bottom:var(--mlv-stroke-width) solid var(--mlv-border-normal);background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar [hidden]{display:none!important}.mlv-editor__toolbar-scroll{flex:1 1 auto;min-width:0}.mlv-editor__toolbar-scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor .mlv-editor-toolbar__overflow,.mlv-editor .mlv-editor-undo-redo,.mlv-editor .mlv-editor-inline-marks,.mlv-editor .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor mlv-editor-zoom,.mlv-editor mlv-editor-heading,.mlv-editor mlv-editor-list,.mlv-editor mlv-editor-text-color,.mlv-editor mlv-editor-highlight,.mlv-editor mlv-editor-alignment,.mlv-editor mlv-editor-link,.mlv-editor mlv-editor-table,.mlv-editor mlv-editor-image-upload{flex:0 0 auto}.mlv-editor__viewport{min-block-size:8rem;overflow:auto;overscroll-behavior:contain}.mlv-editor__view{position:relative;min-inline-size:100%;transform:scale(var(--mlv-editor-zoom, 1));transform-origin:left top;transition:transform var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__content{min-block-size:8rem;padding-block:var(--mlv-spacing-2);padding-inline:var(--mlv-editor-gutter);border:0;outline:none}.mlv-editor__block-handle{position:absolute;inset-inline-start:max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2);display:inline-flex;align-items:center;justify-content:center;inline-size:var(--mlv-editor-gutter);block-size:var(--mlv-height-xs);border-radius:var(--mlv-radius-s);color:var(--mlv-text-tertiary);cursor:grab;opacity:0;pointer-events:none;transition:opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__block-handle svg{inline-size:var(--mlv-spacing-4);block-size:var(--mlv-spacing-4);fill:currentcolor}.mlv-editor__block-handle:hover{background-color:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-secondary)}.mlv-editor__block-handle[data-visible=true]{opacity:1;pointer-events:auto}.mlv-editor__drop-indicator{position:absolute;inset-inline:calc(max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2) + var(--mlv-editor-gutter));block-size:var(--mlv-stroke-width-medium);border-radius:var(--mlv-radius-full);background-color:var(--mlv-border-focus);opacity:0;pointer-events:none;transform:translateY(calc(var(--mlv-editor-drop-gap) / 2 - var(--mlv-stroke-width-medium) / 2));transition:top var(--mlv-duration-normal) var(--mlv-ease-out),opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__drop-indicator[data-visible=true]{opacity:1}.mlv-editor .mlv-editor__block--dragging{opacity:var(--mlv-disabled-opacity)}.mlv-editor .ProseMirror--block-dragging>*{transition:transform var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__status{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-2);min-block-size:var(--mlv-spacing-4);margin-block-start:var(--mlv-spacing-1)}.mlv-editor__upload-placeholder{position:relative;display:inline-block;inline-size:var(--mlv-height-l);block-size:var(--mlv-height-l);margin-inline:var(--mlv-spacing-1);border:var(--mlv-stroke-width) dashed var(--mlv-border-focus);border-radius:var(--mlv-radius-m);background:var(--mlv-background-accent-1-pale);vertical-align:middle}.mlv-editor__upload-placeholder:after{position:absolute;inset:var(--mlv-spacing-2);border:var(--mlv-stroke-width-medium) solid var(--mlv-border-normal);border-block-start-color:var(--mlv-border-focus);border-radius:var(--mlv-radius-full);animation:mlv-editor-upload-spin var(--mlv-duration-slower) var(--mlv-ease-linear) infinite;content:\"\"}.mlv-editor__ai-streaming{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-accent-1-pale)}.mlv-editor__ai-streaming-chunk{animation:mlv-editor-ai-chunk-in var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__ai-caret{position:relative;display:inline-block;inline-size:0;block-size:1em;pointer-events:none}.mlv-editor__ai-caret:after{position:absolute;inset-block:0;inset-inline-start:.0625rem;inline-size:.35em;border-radius:var(--mlv-radius-xs);background:linear-gradient(to bottom,var(--mlv-background-accent-1),var(--mlv-background-accent-2));content:\"\";animation:mlv-editor-ai-caret-pulse var(--mlv-duration-sluggish) var(--mlv-ease-in-out) infinite}.mlv-editor__ai-suggestion-insert{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-success-pale)}.mlv-editor__ai-suggestion-delete{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-danger-1-pale);color:var(--mlv-text-negative);text-decoration-line:line-through}.mlv-editor__ai-suggestion-current{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor--content-width-default{--mlv-editor-measure: 45rem}.mlv-editor--content-width-wide{--mlv-editor-measure: 60rem}.mlv-editor--content-width-full{--mlv-editor-measure: 100%}.mlv-editor--focused .mlv-form-control-wrapper__control-container{border-color:var(--mlv-border-focus);box-shadow:var(--mlv-focus-ring)}.mlv-editor--readonly .ProseMirror{cursor:text}.mlv-editor--readonly .ProseMirror ::selection{background:var(--mlv-background-accent-1-pale)}.mlv-editor--disabled{opacity:var(--mlv-disabled-opacity)}.mlv-editor--disabled .mlv-editor__surface,.mlv-editor--disabled .mlv-editor__status{pointer-events:none}.mlv-editor--disabled .mlv-editor__content,.mlv-editor--disabled .ProseMirror{cursor:default;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror{min-block-size:inherit;max-inline-size:var(--mlv-editor-measure);margin-inline:auto;color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size);font-weight:var(--mlv-typography-body-m-weight);line-height:var(--mlv-typography-body-m-line-height);overflow-wrap:anywhere;white-space:pre-wrap}.mlv-editor .ProseMirror:focus-visible{outline:none}.mlv-editor .ProseMirror>*:first-child{margin-block-start:0}.mlv-editor .ProseMirror>*:last-child{margin-block-end:0}.mlv-editor .ProseMirror p{margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror h1,.mlv-editor .ProseMirror h2,.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{margin-block:var(--mlv-spacing-6) var(--mlv-spacing-3);color:var(--mlv-text-heading);font-family:var(--mlv-typography-family-heading)}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h1-size);font-weight:var(--mlv-typography-heading-h1-weight);line-height:var(--mlv-typography-heading-h1-line-height)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h2-size);font-weight:var(--mlv-typography-heading-h2-weight);line-height:var(--mlv-typography-heading-h2-line-height)}.mlv-editor .ProseMirror h3{font-size:var(--mlv-typography-heading-h3-size);font-weight:var(--mlv-typography-heading-h3-weight);line-height:var(--mlv-typography-heading-h3-line-height)}.mlv-editor .ProseMirror h4{font-size:var(--mlv-typography-heading-h4-size);font-weight:var(--mlv-typography-heading-h4-weight);line-height:var(--mlv-typography-heading-h4-line-height)}.mlv-editor .ProseMirror h5{font-size:var(--mlv-typography-heading-h5-size);font-weight:var(--mlv-typography-heading-h5-weight);line-height:var(--mlv-typography-heading-h5-line-height)}.mlv-editor .ProseMirror h6{font-size:var(--mlv-typography-heading-h6-size);font-weight:var(--mlv-typography-heading-h6-weight);line-height:var(--mlv-typography-heading-h6-line-height)}.mlv-editor .ProseMirror h1{letter-spacing:var(--mlv-typography-heading-h1-letter-spacing)}.mlv-editor .ProseMirror h2{letter-spacing:var(--mlv-typography-heading-h2-letter-spacing)}.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{letter-spacing:var(--mlv-letter-spacing-normal)}.mlv-editor .ProseMirror strong{font-weight:var(--mlv-font-weight-bold)}.mlv-editor .ProseMirror em{font-style:italic}.mlv-editor .ProseMirror s{text-decoration:line-through}.mlv-editor .ProseMirror u{text-underline-offset:var(--mlv-spacing-0-5)}.mlv-editor .ProseMirror a{color:var(--mlv-text-action);text-decoration:underline;text-underline-offset:var(--mlv-spacing-0-5);transition:color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .ProseMirror a:visited{color:var(--mlv-text-action)}.mlv-editor .ProseMirror a:hover{color:var(--mlv-text-action-hover)}.mlv-editor .ProseMirror a:focus-visible{border-radius:var(--mlv-radius-xs);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul,.mlv-editor .ProseMirror ol{padding-inline-start:var(--mlv-spacing-6);margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror ul ul,.mlv-editor .ProseMirror ul ol,.mlv-editor .ProseMirror ol ul,.mlv-editor .ProseMirror ol ol{margin-block:var(--mlv-spacing-1) 0}.mlv-editor .ProseMirror ul{list-style:disc}.mlv-editor .ProseMirror ul ul{list-style:circle}.mlv-editor .ProseMirror ol{list-style:decimal}.mlv-editor .ProseMirror li{margin-block:var(--mlv-spacing-1)}.mlv-editor .ProseMirror li>p{margin:0}.mlv-editor .ProseMirror ul[data-type=taskList]{padding:0;list-style:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]{display:flex;align-items:flex-start;gap:var(--mlv-spacing-2)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label{display:inline-flex;align-items:center;min-block-size:var(--mlv-height-xs);cursor:pointer;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input{accent-color:var(--mlv-background-accent-1)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>div{flex:1 1 auto;min-width:0}.mlv-editor .ProseMirror blockquote{padding-inline-start:var(--mlv-spacing-4);margin:var(--mlv-spacing-4) 0;border-inline-start:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);color:var(--mlv-text-secondary)}.mlv-editor .ProseMirror code{padding:var(--mlv-spacing-0-5) var(--mlv-spacing-1);border-radius:var(--mlv-radius-s);background:var(--mlv-background-neutral-1);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size)}.mlv-editor .ProseMirror pre{padding:var(--mlv-padding-m);margin-block:var(--mlv-spacing-4);overflow:auto;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-m);background:var(--mlv-background-sunken);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size);line-height:var(--mlv-typography-body-m-line-height);tab-size:2}.mlv-editor .ProseMirror pre code{padding:0;background:transparent;color:inherit;font-size:inherit}.mlv-editor .ProseMirror hr{height:var(--mlv-stroke-width);margin-block:var(--mlv-spacing-6);border:0;background:var(--mlv-border-normal)}.mlv-editor .ProseMirror img{display:block;max-inline-size:100%;block-size:auto;margin-block:var(--mlv-spacing-4);border-radius:var(--mlv-radius-m)}.mlv-editor .ProseMirror img.ProseMirror-selectednode{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror .ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-state=true]{border-radius:var(--mlv-radius-s);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-spacing-1)}.mlv-editor .ProseMirror [data-resize-container]{margin-block:var(--mlv-spacing-4)}.mlv-editor .ProseMirror [data-resize-container] img{margin-block:0}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]{outline:none}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode>[data-resize-wrapper],.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]>[data-resize-wrapper]{border-radius:var(--mlv-radius-m);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror [data-resize-handle],.mlv-editor .ProseMirror .resize-handle{inline-size:var(--mlv-spacing-3);block-size:var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-background-base);border-radius:var(--mlv-radius-full);background:var(--mlv-background-accent-1);box-shadow:var(--mlv-shadow-raised)}.mlv-editor .ProseMirror [data-resize-handle]:focus-visible,.mlv-editor .ProseMirror .resize-handle:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror .is-editor-empty:first-child:before{float:left;height:0;color:var(--mlv-text-placeholder);content:attr(data-placeholder);pointer-events:none}.mlv-editor .tableWrapper{margin-block:var(--mlv-spacing-3);overflow-x:auto}.mlv-editor table{width:100%;border-collapse:collapse;table-layout:fixed}.mlv-editor th,.mlv-editor td{position:relative;min-width:3rem;padding:var(--mlv-spacing-2) var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-border-normal);vertical-align:top}.mlv-editor th{background:var(--mlv-background-neutral-1);font-weight:var(--mlv-font-weight-semibold)}.mlv-editor .selectedCell:after{position:absolute;inset:0;z-index:1;background:var(--mlv-background-accent-1-pale);content:\"\";opacity:.55;pointer-events:none}.mlv-editor .ProseMirror-focused .selectedCell:after{box-shadow:inset 0 0 0 var(--mlv-stroke-width-medium) var(--mlv-border-focus)}.mlv-editor .column-resize-handle{position:absolute;inset-block:0;inset-inline-end:calc(var(--mlv-stroke-width) * -1);z-index:2;width:calc(var(--mlv-stroke-width) * 2);background:var(--mlv-border-focus);pointer-events:none}.mlv-editor .resize-cursor{cursor:col-resize}@keyframes mlv-editor-upload-spin{to{transform:rotate(1turn)}}@keyframes mlv-editor-ai-chunk-in{0%{opacity:0;filter:blur(.125rem)}to{opacity:1;filter:blur(0)}}@keyframes mlv-editor-ai-caret-pulse{0%,to{opacity:1}50%{opacity:.35}}@media(max-width:40rem){.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-3)}.mlv-editor__toolbar{flex-wrap:nowrap;overflow-x:auto}.mlv-editor__content{padding-block:var(--mlv-spacing-1-5)}.mlv-editor__status{justify-content:flex-start}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h2-size)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h3-size)}}@media print{.mlv-editor__toolbar,.mlv-editor__status,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .mlv-editor-image-upload-status,.mlv-editor__upload-placeholder,.mlv-editor__ai-suggestion-delete,.mlv-editor__ai-caret{display:none}.mlv-editor__ai-suggestion-current{outline:none}.mlv-editor__ai-streaming-chunk{animation:none}.mlv-editor__viewport{overflow:visible}.mlv-editor__view{transform:none}.mlv-editor__content,.mlv-editor .ProseMirror{min-block-size:0;padding:0}.mlv-editor table{break-inside:avoid}}@media(hover:none){.mlv-editor__block-handle{display:none}}@media(prefers-reduced-motion:reduce){.mlv-editor .mlv-form-control-wrapper__control-container,.mlv-editor__view,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .ProseMirror--block-dragging>*,.mlv-editor .ProseMirror a{transition-duration:var(--mlv-duration-instant)}.mlv-editor__upload-placeholder:after,.mlv-editor__ai-streaming-chunk,.mlv-editor__ai-caret:after{animation:none}}.mlv-editor__drag-ghost{max-block-size:12rem;padding:var(--mlv-spacing-2);overflow:hidden;border-radius:var(--mlv-radius-m);background-color:var(--mlv-background-raised);box-shadow:var(--mlv-shadow-floating);opacity:.9;-webkit-mask-image:linear-gradient(to bottom,black 70%,transparent 100%);mask-image:linear-gradient(to bottom,black 70%,transparent 100%);scale:.85;transform-origin:top left}}\n"] }]
10341
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], contentWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentWidth", required: false }] }], extensions: [{ type: i0.Input, args: [{ isSignal: true, alias: "extensions", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], characterLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "characterLimit", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledBy", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedBy", required: false }] }], imageUploader: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageUploader", required: false }] }], imageUploadOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageUploadOptions", required: false }] }], aiProvider: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiProvider", required: false }] }], editorReady: [{ type: i0.Output, args: ["editorReady"] }], focus: [{ type: i0.Output, args: ["focus"] }], blur: [{ type: i0.Output, args: ["blur"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], transaction: [{ type: i0.Output, args: ["transaction"] }], editorError: [{ type: i0.Output, args: ["editorError"] }], imageUploadSuccess: [{ type: i0.Output, args: ["imageUploadSuccess"] }], imageUploadFailure: [{ type: i0.Output, args: ["imageUploadFailure"] }], imageUploadCancelled: [{ type: i0.Output, args: ["imageUploadCancelled"] }], _content: [{ type: i0.ViewChild, args: ['content', { isSignal: true }] }], _view: [{ type: i0.ViewChild, args: ['view', { isSignal: true }] }], _toolbarDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvEditorToolbarDef), { isSignal: true }] }], _toolbarStartDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvEditorToolbarStartDef), { isSignal: true }] }], _toolbarEndDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvEditorToolbarEndDef), { isSignal: true }] }] } });
11699
+ '[style.--mlv-editor-height]': '_heightStyle()',
11700
+ '[style.--mlv-editor-min-height]': '_minHeightStyle()',
11701
+ '[style.--mlv-editor-max-height]': '_maxHeightStyle()',
11702
+ }, template: "<mlv-form-control-wrapper (clear)=\"clearValue()\">\n @if (label() || hint()) {\n <mlv-label\n [for]=\"_ownLabelFor()\"\n [id]=\"_labelId()\"\n (click)=\"_onLabelClick()\"\n >\n {{ label() }}\n @if (hint()) {\n <mlv-hint>{{ hint() }}</mlv-hint>\n }\n </mlv-label>\n }\n\n <ng-template mlvFormControlWrapperControl>\n <div #surface class=\"mlv-editor__surface\">\n <!--\n Declared once and stamped by one outlet: before or after the viewport\n for the docked bar (#416), so the DOM order is always the visual\n order, with no CSS `order`; into the selection bubble's overlay for\n the floating appearance (#483). Re-stamping re-creates the view, which\n closes an open toolbar popup; projected slot content is re-attached.\n Neither stamps anything while `readonly` (#498): no band, no reserved\n space; `_toolbarRendered()` is the one switch.\n -->\n <ng-template #toolbarTemplate>\n <div #toolbarBand class=\"mlv-editor__toolbar-band\">\n <div\n mlvEditorToolbarRoot\n #toolbarRoot=\"mlvEditorToolbarRoot\"\n class=\"mlv-editor__toolbar\"\n [ariaLabel]=\"_toolbarAriaLabel()\"\n [disabled]=\"computedDisabled()\"\n [measureTarget]=\"surface\"\n >\n @if (_hasToolbarDef()) {\n <ng-content select=\"[mlvEditorToolbar]\" />\n @for (definition of _toolbarDefs(); track definition) {\n @if (definition.templateRef; as toolbarTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"toolbarTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n } @else {\n <ng-content select=\"[mlvEditorToolbarStart]\" />\n @for (definition of _toolbarStartDefs(); track definition) {\n @if (definition.templateRef; as startTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"startTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n <div mlvFade class=\"mlv-editor__toolbar-scroll\">\n <mlv-toolbar>\n <mlv-editor-undo-redo />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-zoom />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n />\n <mlv-editor-heading />\n <mlv-editor-list />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-inline-marks [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-text-color />\n <mlv-editor-highlight />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-alignment [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-link />\n <mlv-editor-table />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-block-insert [hidden]=\"toolbarRoot.narrow()\" />\n <mlv-divider\n class=\"mlv-editor-toolbar__separator\"\n orientation=\"vertical\"\n muted\n [hidden]=\"toolbarRoot.narrow()\"\n />\n <mlv-editor-image-upload />\n </mlv-toolbar>\n </div>\n <mlv-editor-toolbar-overflow />\n @for (definition of _toolbarEndDefs(); track definition) {\n @if (definition.templateRef; as endTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"endTemplate\"\n [ngTemplateOutletContext]=\"{ $implicit: _toolbarContext }\"\n />\n }\n }\n <ng-content select=\"[mlvEditorToolbarEnd]\" />\n }\n </div>\n </div>\n </ng-template>\n\n @if (_barRendered() && toolbarPosition() === 'top') {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n\n <div #viewport class=\"mlv-editor__viewport\">\n <div #view class=\"mlv-editor__view\">\n <div #content class=\"mlv-editor__content\"></div>\n <mlv-editor-table-controls [layer]=\"view\" />\n </div>\n </div>\n @if (_barRendered() && toolbarPosition() === 'bottom') {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n @if (toolbarAppearance() === 'floating') {\n <ng-template mlvEditorBubble [viewport]=\"viewport\" [capped]=\"_capped()\">\n @if (_toolbarRendered()) {\n <ng-container [ngTemplateOutlet]=\"toolbarTemplate\" />\n }\n </ng-template>\n }\n <mlv-editor-image-upload-status />\n </div>\n </ng-template>\n\n @if (message()) {\n <mlv-message [state]=\"resolvedState()\" [id]=\"id() + '-message'\">\n {{ message() }}\n </mlv-message>\n }\n</mlv-form-control-wrapper>\n\n<div\n class=\"mlv-editor__status\"\n [attr.aria-disabled]=\"computedDisabled() || null\"\n>\n <ng-content select=\"[mlvEditorStatus]\" />\n <mlv-spacer />\n <mlv-editor-status [characterLimit]=\"characterLimit()\" />\n</div>\n", styles: ["@layer mlv.components{.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-6);--mlv-editor-measure: 45rem;box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor *{box-sizing:border-box}.mlv-editor{display:block;color:var(--mlv-text-primary)}.mlv-editor .mlv-form-control-wrapper__control-container{height:auto;min-height:var(--form-ctrl-height);padding:0;overflow:clip;background:var(--mlv-background-base);transition:border-color var(--mlv-duration-fast) var(--mlv-ease-default),box-shadow var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .mlv-form-control-wrapper__control-row{align-items:stretch}.mlv-form-control-wrapper--state-error .mlv-editor__surface{box-shadow:inset 0 0 0 var(--mlv-stroke-width) var(--mlv-border-error)}.mlv-editor__surface{display:flex;flex:1 1 auto;flex-direction:column;min-width:0;width:100%;background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--mlv-spacing-1);min-width:0;padding:var(--mlv-padding-xs);overflow:hidden;border-block-end:var(--mlv-stroke-width) solid var(--mlv-border-normal);background:var(--mlv-elevation-bg-3)}.mlv-editor__toolbar-band{display:flex;flex:0 0 auto;flex-direction:column;min-inline-size:0}.mlv-editor--toolbar-bottom .mlv-editor__toolbar{border-block-start:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-block-end:0}.mlv-editor__toolbar [hidden]{display:none!important}.mlv-editor__toolbar-scroll{flex:1 1 auto;min-width:0}.mlv-editor__toolbar-scroll>.mlv-toolbar{width:max-content;min-width:100%;flex-wrap:nowrap}.mlv-editor .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor .mlv-editor-toolbar__overflow,.mlv-editor .mlv-editor-undo-redo,.mlv-editor .mlv-editor-inline-marks,.mlv-editor .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor mlv-editor-zoom,.mlv-editor mlv-editor-heading,.mlv-editor mlv-editor-list,.mlv-editor mlv-editor-text-color,.mlv-editor mlv-editor-highlight,.mlv-editor mlv-editor-alignment,.mlv-editor mlv-editor-link,.mlv-editor mlv-editor-table,.mlv-editor mlv-editor-image-upload{flex:0 0 auto}.mlv-editor__viewport{flex:1 1 auto;min-block-size:var(--mlv-editor-min-height, 8rem)}.mlv-editor--capped .mlv-editor__surface{block-size:var(--mlv-editor-height, auto);max-block-size:var(--mlv-editor-max-height, none)}.mlv-editor--capped .mlv-editor__viewport{min-block-size:0;overflow:auto;overscroll-behavior:contain}.mlv-editor__view{position:relative;isolation:isolate;min-inline-size:100%;zoom:var(--mlv-editor-zoom, 1)}.mlv-editor--capped .mlv-editor__view{min-inline-size:0;inline-size:calc(100% * var(--mlv-editor-zoom, 1))}.mlv-editor__content{min-block-size:var(--mlv-editor-min-height, 8rem);padding-block:var(--mlv-spacing-2);padding-inline:var(--mlv-editor-gutter);border:0;outline:none}.mlv-editor--capped .mlv-editor__content{min-block-size:calc(var(--mlv-editor-min-height, 8rem) / var(--mlv-editor-zoom, 1))}.mlv-editor__block-handle{position:absolute;inset-inline-start:max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2);display:inline-flex;align-items:center;justify-content:center;inline-size:var(--mlv-editor-gutter);block-size:var(--mlv-height-xs);border-radius:var(--mlv-radius-s);color:var(--mlv-text-tertiary);cursor:grab;opacity:0;pointer-events:none;transition:opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__block-handle svg{inline-size:var(--mlv-spacing-4);block-size:var(--mlv-spacing-4);fill:currentcolor}.mlv-editor__block-handle:hover{background-color:var(--mlv-background-neutral-1-hover);color:var(--mlv-text-secondary)}.mlv-editor__block-handle[data-visible=true]{opacity:1;pointer-events:auto}.mlv-editor__drop-indicator{position:absolute;inset-inline:calc(max(0rem,(100% - 2 * var(--mlv-editor-gutter) - var(--mlv-editor-measure)) / 2) + var(--mlv-editor-gutter));block-size:var(--mlv-stroke-width-medium);border-radius:var(--mlv-radius-full);background-color:var(--mlv-border-focus);opacity:0;pointer-events:none;transform:translateY(-50%);transition:top var(--mlv-duration-normal) var(--mlv-ease-out),opacity var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor__drop-indicator[data-visible=true]{opacity:1}.mlv-editor .mlv-editor__block--dragging{opacity:var(--mlv-disabled-opacity)}.mlv-editor__status{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--mlv-spacing-2);min-block-size:var(--mlv-spacing-4);margin-block-start:var(--mlv-spacing-1)}.mlv-editor__upload-placeholder{position:relative;display:inline-block;inline-size:var(--mlv-height-l);block-size:var(--mlv-height-l);margin-inline:var(--mlv-spacing-1);border:var(--mlv-stroke-width) dashed var(--mlv-border-focus);border-radius:var(--mlv-radius-m);background:var(--mlv-background-accent-1-pale);vertical-align:middle}.mlv-editor__upload-placeholder:after{position:absolute;inset:var(--mlv-spacing-2);border:var(--mlv-stroke-width-medium) solid var(--mlv-border-normal);border-block-start-color:var(--mlv-border-focus);border-radius:var(--mlv-radius-full);animation:mlv-editor-upload-spin var(--mlv-duration-slower) var(--mlv-ease-linear) infinite;content:\"\"}.mlv-editor__ai-streaming{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-accent-1-pale)}.mlv-editor__ai-streaming-chunk{animation:mlv-editor-ai-chunk-in var(--mlv-duration-normal) var(--mlv-ease-out)}.mlv-editor__ai-caret{position:relative;display:inline-block;inline-size:0;block-size:1em;pointer-events:none}.mlv-editor__ai-caret:after{position:absolute;inset-block:0;inset-inline-start:.0625rem;inline-size:.35em;border-radius:var(--mlv-radius-xs);background:linear-gradient(to bottom,var(--mlv-background-accent-1),var(--mlv-background-accent-2));content:\"\";animation:mlv-editor-ai-caret-pulse var(--mlv-duration-sluggish) var(--mlv-ease-in-out) infinite}.mlv-editor__ai-suggestion-insert{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-success-pale)}.mlv-editor__ai-suggestion-delete{border-radius:var(--mlv-radius-xs);background-color:var(--mlv-background-danger-1-pale);color:var(--mlv-text-negative);text-decoration-line:line-through}.mlv-editor__ai-suggestion-current{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor--content-width-default{--mlv-editor-measure: 45rem}.mlv-editor--content-width-wide{--mlv-editor-measure: 60rem}.mlv-editor--content-width-full{--mlv-editor-measure: 100%}.mlv-editor--focused .mlv-form-control-wrapper__control-container{border-color:var(--mlv-border-focus);box-shadow:var(--mlv-focus-ring)}.mlv-editor--readonly .ProseMirror{cursor:text}.mlv-editor--readonly .ProseMirror ::selection{background:var(--mlv-background-accent-1-pale)}.mlv-editor--disabled{opacity:var(--mlv-disabled-opacity)}.mlv-editor--disabled .mlv-editor__surface,.mlv-editor--disabled .mlv-editor__status{pointer-events:none}.mlv-editor--disabled .mlv-editor__content,.mlv-editor--disabled .ProseMirror{cursor:default;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror{min-block-size:inherit;max-inline-size:var(--mlv-editor-measure);margin-inline:auto;color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-text);font-size:var(--mlv-typography-body-m-size);font-weight:var(--mlv-typography-body-m-weight);line-height:var(--mlv-typography-body-m-line-height);overflow-wrap:anywhere;white-space:pre-wrap}.mlv-editor .ProseMirror:focus-visible{outline:none}.mlv-editor .ProseMirror>*:first-child{margin-block-start:0}.mlv-editor .ProseMirror>*:last-child{margin-block-end:0}.mlv-editor .ProseMirror p{margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror h1,.mlv-editor .ProseMirror h2,.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{margin-block:var(--mlv-spacing-6) var(--mlv-spacing-3);color:var(--mlv-text-heading);font-family:var(--mlv-typography-family-heading)}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h1-size);font-weight:var(--mlv-typography-heading-h1-weight);line-height:var(--mlv-typography-heading-h1-line-height)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h2-size);font-weight:var(--mlv-typography-heading-h2-weight);line-height:var(--mlv-typography-heading-h2-line-height)}.mlv-editor .ProseMirror h3{font-size:var(--mlv-typography-heading-h3-size);font-weight:var(--mlv-typography-heading-h3-weight);line-height:var(--mlv-typography-heading-h3-line-height)}.mlv-editor .ProseMirror h4{font-size:var(--mlv-typography-heading-h4-size);font-weight:var(--mlv-typography-heading-h4-weight);line-height:var(--mlv-typography-heading-h4-line-height)}.mlv-editor .ProseMirror h5{font-size:var(--mlv-typography-heading-h5-size);font-weight:var(--mlv-typography-heading-h5-weight);line-height:var(--mlv-typography-heading-h5-line-height)}.mlv-editor .ProseMirror h6{font-size:var(--mlv-typography-heading-h6-size);font-weight:var(--mlv-typography-heading-h6-weight);line-height:var(--mlv-typography-heading-h6-line-height)}.mlv-editor .ProseMirror h1{letter-spacing:var(--mlv-typography-heading-h1-letter-spacing)}.mlv-editor .ProseMirror h2{letter-spacing:var(--mlv-typography-heading-h2-letter-spacing)}.mlv-editor .ProseMirror h3,.mlv-editor .ProseMirror h4,.mlv-editor .ProseMirror h5,.mlv-editor .ProseMirror h6{letter-spacing:var(--mlv-letter-spacing-normal)}.mlv-editor .ProseMirror strong{font-weight:var(--mlv-font-weight-bold)}.mlv-editor .ProseMirror em{font-style:italic}.mlv-editor .ProseMirror s{text-decoration:line-through}.mlv-editor .ProseMirror u{text-underline-offset:var(--mlv-spacing-0-5)}.mlv-editor .ProseMirror a{color:var(--mlv-text-action);text-decoration:underline;text-underline-offset:var(--mlv-spacing-0-5);transition:color var(--mlv-duration-fast) var(--mlv-ease-default)}.mlv-editor .ProseMirror a:visited{color:var(--mlv-text-action)}.mlv-editor .ProseMirror a:hover{color:var(--mlv-text-action-hover)}.mlv-editor .ProseMirror a:focus-visible{border-radius:var(--mlv-radius-xs);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul,.mlv-editor .ProseMirror ol{padding-inline-start:var(--mlv-spacing-6);margin-block:0 var(--mlv-spacing-3)}.mlv-editor .ProseMirror ul ul,.mlv-editor .ProseMirror ul ol,.mlv-editor .ProseMirror ol ul,.mlv-editor .ProseMirror ol ol{margin-block:var(--mlv-spacing-1) 0}.mlv-editor .ProseMirror ul{list-style:disc}.mlv-editor .ProseMirror ul ul{list-style:circle}.mlv-editor .ProseMirror ol{list-style:decimal}.mlv-editor .ProseMirror li{margin-block:var(--mlv-spacing-1)}.mlv-editor .ProseMirror li>p{margin:0}.mlv-editor .ProseMirror ul[data-type=taskList]{padding:0;list-style:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]{display:flex;align-items:flex-start;gap:var(--mlv-spacing-2)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label{display:inline-flex;align-items:center;min-block-size:var(--mlv-height-xs);cursor:pointer;-webkit-user-select:none;user-select:none}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input{accent-color:var(--mlv-background-accent-1)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>label input:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror ul[data-type=taskList] li[data-type=taskItem]>div{flex:1 1 auto;min-width:0}.mlv-editor .ProseMirror blockquote{padding-inline-start:var(--mlv-spacing-4);margin:var(--mlv-spacing-4) 0;border-inline-start:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);color:var(--mlv-text-secondary)}.mlv-editor .ProseMirror code{padding:var(--mlv-spacing-0-5) var(--mlv-spacing-1);border-radius:var(--mlv-radius-s);background:var(--mlv-background-neutral-1);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size)}.mlv-editor .ProseMirror pre{padding:var(--mlv-padding-m);margin-block:var(--mlv-spacing-4);overflow:auto;border:var(--mlv-stroke-width) solid var(--mlv-border-normal);border-radius:var(--mlv-radius-m);background:var(--mlv-background-sunken);color:var(--mlv-text-primary);font-family:var(--mlv-typography-family-code);font-size:var(--mlv-typography-code-size);line-height:var(--mlv-typography-body-m-line-height);tab-size:2}.mlv-editor .ProseMirror pre code{padding:0;background:transparent;color:inherit;font-size:inherit}.mlv-editor .ProseMirror hr{height:var(--mlv-stroke-width);margin-block:var(--mlv-spacing-6);border:0;background:var(--mlv-border-normal)}.mlv-editor .ProseMirror img{display:block;max-inline-size:100%;block-size:auto;margin-block:var(--mlv-spacing-4);border-radius:var(--mlv-radius-m)}.mlv-editor .ProseMirror img.ProseMirror-selectednode{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror .ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-state=true]{border-radius:var(--mlv-radius-s);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-spacing-1)}.mlv-editor .ProseMirror [data-resize-container]{margin-block:var(--mlv-spacing-4)}.mlv-editor .ProseMirror [data-resize-container] img{margin-block:0}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode,.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]{outline:none}.mlv-editor .ProseMirror [data-resize-container].ProseMirror-selectednode>[data-resize-wrapper],.mlv-editor .ProseMirror [data-resize-container][data-resize-state=true]>[data-resize-wrapper]{border-radius:var(--mlv-radius-m);outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:0}.mlv-editor .ProseMirror [data-resize-handle],.mlv-editor .ProseMirror .resize-handle{inline-size:var(--mlv-spacing-3);block-size:var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-background-base);border-radius:var(--mlv-radius-full);background:var(--mlv-background-accent-1);box-shadow:var(--mlv-shadow-raised)}.mlv-editor .ProseMirror [data-resize-handle]:focus-visible,.mlv-editor .ProseMirror .resize-handle:focus-visible{outline:var(--mlv-stroke-width-medium) solid var(--mlv-border-focus);outline-offset:var(--mlv-focus-ring-offset)}.mlv-editor .ProseMirror .is-editor-empty:first-child:before{float:left;height:0;color:var(--mlv-text-placeholder);content:attr(data-placeholder);pointer-events:none}.mlv-editor .tableWrapper{margin-block:var(--mlv-spacing-3);overflow-x:auto}.mlv-editor table{width:100%;border-collapse:collapse;table-layout:fixed}.mlv-editor th,.mlv-editor td{position:relative;min-width:3rem;padding:var(--mlv-spacing-2) var(--mlv-spacing-3);border:var(--mlv-stroke-width) solid var(--mlv-border-normal);vertical-align:top}.mlv-editor th{background:var(--mlv-background-neutral-1);font-weight:var(--mlv-font-weight-semibold)}.mlv-editor .selectedCell:after{position:absolute;inset:0;z-index:1;background:var(--mlv-background-accent-1-pale);content:\"\";opacity:.55;pointer-events:none}.mlv-editor .ProseMirror-focused .selectedCell:after{box-shadow:inset 0 0 0 var(--mlv-stroke-width-medium) var(--mlv-border-focus)}.mlv-editor .column-resize-handle{position:absolute;inset-block:0;inset-inline-end:calc(var(--mlv-stroke-width) * -1);z-index:2;width:calc(var(--mlv-stroke-width) * 2);background:var(--mlv-border-focus);pointer-events:none}.mlv-editor .resize-cursor{cursor:col-resize}@keyframes mlv-editor-upload-spin{to{transform:rotate(1turn)}}@keyframes mlv-editor-ai-chunk-in{0%{opacity:0;filter:blur(.125rem)}to{opacity:1;filter:blur(0)}}@keyframes mlv-editor-ai-caret-pulse{0%,to{opacity:1}50%{opacity:.35}}.mlv-editor--toolbar-sticky.mlv-editor--toolbar-top .mlv-editor__toolbar-band{position:sticky;inset-block-start:var(--mlv-editor-toolbar-sticky-offset, 0);z-index:var(--mlv-z-raised)}.mlv-editor--toolbar-sticky.mlv-editor--toolbar-bottom .mlv-editor__toolbar-band{position:sticky;inset-block-end:var(--mlv-editor-toolbar-sticky-offset, 0);z-index:var(--mlv-z-raised)}.mlv-editor-bubble{box-sizing:border-box;padding:0;margin:0;font-family:var(--mlv-typography-family-text);font-size:var(--mlv-font-size-m)}.mlv-editor-bubble *{box-sizing:border-box}.mlv-editor-bubble .mlv-editor-toolbar__separator{flex:0 0 auto;min-height:var(--mlv-height-s);margin-inline:var(--mlv-spacing-1)}.mlv-editor-bubble .mlv-editor-toolbar__overflow,.mlv-editor-bubble .mlv-editor-undo-redo,.mlv-editor-bubble .mlv-editor-inline-marks,.mlv-editor-bubble .mlv-editor-block-insert{display:inline-flex;flex:0 0 auto;flex-direction:row;align-items:center;gap:var(--mlv-spacing-1)}.mlv-editor-bubble mlv-editor-zoom,.mlv-editor-bubble mlv-editor-heading,.mlv-editor-bubble mlv-editor-list,.mlv-editor-bubble mlv-editor-text-color,.mlv-editor-bubble mlv-editor-highlight,.mlv-editor-bubble mlv-editor-alignment,.mlv-editor-bubble mlv-editor-link,.mlv-editor-bubble mlv-editor-table,.mlv-editor-bubble mlv-editor-image-upload{flex:0 0 auto}.mlv-editor-bubble{max-inline-size:calc(100vw - 2 * var(--mlv-spacing-2));border-radius:var(--mlv-radius-panel);background:var(--mlv-elevation-bg-4);box-shadow:var(--mlv-shadow-floating);color:var(--mlv-text-primary);animation:mlv-editor-bubble-enter var(--mlv-duration-fast) var(--mlv-ease-out)}.mlv-editor-bubble--hidden{visibility:hidden;animation:none}.mlv-editor-bubble .mlv-editor__toolbar-band{flex:1 1 auto;min-inline-size:0}.mlv-editor-bubble .mlv-editor__toolbar{border:0;border-radius:inherit;background:transparent}@keyframes mlv-editor-bubble-enter{0%{opacity:0}}@media(max-width:40rem){.mlv-editor{--mlv-editor-gutter: var(--mlv-spacing-3)}.mlv-editor__toolbar{flex-wrap:nowrap;overflow-x:auto}.mlv-editor__content{padding-block:var(--mlv-spacing-1-5)}.mlv-editor__status{justify-content:flex-start}.mlv-editor .ProseMirror h1{font-size:var(--mlv-typography-heading-h2-size)}.mlv-editor .ProseMirror h2{font-size:var(--mlv-typography-heading-h3-size)}}@media print{.mlv-editor__toolbar-band,.mlv-editor__toolbar,.mlv-editor__status,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .mlv-editor-image-upload-status,.mlv-editor__upload-placeholder,.mlv-editor__ai-suggestion-delete,.mlv-editor__ai-caret{display:none}.mlv-editor__ai-suggestion-current{outline:none}.mlv-editor__ai-streaming-chunk{animation:none}.mlv-editor__viewport,.mlv-editor--capped .mlv-editor__viewport{overflow:visible;padding-block:0}.mlv-editor--capped .mlv-editor__surface{block-size:auto;max-block-size:none}.mlv-editor__view,.mlv-editor--capped .mlv-editor__view{zoom:1;inline-size:auto;min-inline-size:0}.mlv-editor__content,.mlv-editor--capped .mlv-editor__content,.mlv-editor .ProseMirror{min-block-size:0;padding:0}.mlv-editor table{break-inside:avoid}}@media print{.mlv-editor-bubble{display:none}}@media(hover:none){.mlv-editor__block-handle{display:none}}@media(prefers-reduced-motion:reduce){.mlv-editor .mlv-form-control-wrapper__control-container,.mlv-editor__block-handle,.mlv-editor__drop-indicator,.mlv-editor .ProseMirror a{transition-duration:var(--mlv-duration-instant)}.mlv-editor__upload-placeholder:after,.mlv-editor__ai-streaming-chunk,.mlv-editor__ai-caret:after{animation:none}.mlv-editor-bubble{animation:none}}.mlv-editor__drag-ghost{max-block-size:12rem;padding:var(--mlv-spacing-2);overflow:hidden;border-radius:var(--mlv-radius-m);background-color:var(--mlv-background-accent-1-pale);opacity:.9;-webkit-mask-image:linear-gradient(to bottom,black 70%,transparent 100%);mask-image:linear-gradient(to bottom,black 70%,transparent 100%)}}\n"] }]
11703
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], contentWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "contentWidth", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], minHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "minHeight", required: false }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], toolbarPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbarPosition", required: false }] }], toolbarAppearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbarAppearance", required: false }] }], toolbarSticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbarSticky", required: false }] }], extensions: [{ type: i0.Input, args: [{ isSignal: true, alias: "extensions", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], characterLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "characterLimit", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabelledBy", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedBy", required: false }] }], imageUploader: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageUploader", required: false }] }], imageUploadOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageUploadOptions", required: false }] }], aiProvider: [{ type: i0.Input, args: [{ isSignal: true, alias: "aiProvider", required: false }] }], editorReady: [{ type: i0.Output, args: ["editorReady"] }], focus: [{ type: i0.Output, args: ["focus"] }], blur: [{ type: i0.Output, args: ["blur"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], transaction: [{ type: i0.Output, args: ["transaction"] }], editorError: [{ type: i0.Output, args: ["editorError"] }], imageUploadSuccess: [{ type: i0.Output, args: ["imageUploadSuccess"] }], imageUploadFailure: [{ type: i0.Output, args: ["imageUploadFailure"] }], imageUploadCancelled: [{ type: i0.Output, args: ["imageUploadCancelled"] }], _content: [{ type: i0.ViewChild, args: ['content', { isSignal: true }] }], _view: [{ type: i0.ViewChild, args: ['view', { isSignal: true }] }], _toolbarBand: [{ type: i0.ViewChild, args: ['toolbarBand', { isSignal: true }] }], _toolbarDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvEditorToolbarDef), { isSignal: true }] }], _toolbarStartDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvEditorToolbarStartDef), { isSignal: true }] }], _toolbarEndDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MlvEditorToolbarEndDef), { isSignal: true }] }] } });
10342
11704
 
10343
11705
  /**
10344
11706
  * Generated bundle index. Do not edit.