@malva-ui/editor 0.1.13 → 0.1.14
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.
- package/README.md +5 -5
- package/fesm2022/malva-ui-editor-ai.mjs +17 -0
- package/fesm2022/malva-ui-editor-ai.mjs.map +1 -0
- package/fesm2022/malva-ui-editor.mjs +10227 -0
- package/fesm2022/malva-ui-editor.mjs.map +1 -0
- package/package.json +35 -26
- package/types/malva-ui-editor-ai.d.ts +1 -0
- package/types/malva-ui-editor.d.ts +2995 -0
- package/ai/index.d.ts +0 -12
- package/ai/malva-ui-editor-ai.d.ts +0 -5
- package/esm2022/ai/index.js +0 -12
- package/esm2022/ai/index.js.map +0 -1
- package/esm2022/ai/malva-ui-editor-ai.js +0 -5
- package/esm2022/ai/malva-ui-editor-ai.js.map +0 -1
- package/esm2022/index.js +0 -37
- package/esm2022/index.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-actions.js +0 -69
- package/esm2022/lib/ai/editor-ai-actions.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-context.js +0 -612
- package/esm2022/lib/ai/editor-ai-context.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-menu.js +0 -288
- package/esm2022/lib/ai/editor-ai-menu.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-review-bar.js +0 -223
- package/esm2022/lib/ai/editor-ai-review-bar.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-stream.js +0 -677
- package/esm2022/lib/ai/editor-ai-stream.js.map +0 -1
- package/esm2022/lib/ai/editor-ai-suggestions.js +0 -751
- package/esm2022/lib/ai/editor-ai-suggestions.js.map +0 -1
- package/esm2022/lib/ai/editor-ai.tokens.js +0 -10
- package/esm2022/lib/ai/editor-ai.tokens.js.map +0 -1
- package/esm2022/lib/ai/editor-ai.types.js +0 -1
- package/esm2022/lib/ai/editor-ai.types.js.map +0 -1
- package/esm2022/lib/editor/editor.js +0 -985
- package/esm2022/lib/editor/editor.js.map +0 -1
- package/esm2022/lib/editor-extension-preflight.js +0 -83
- package/esm2022/lib/editor-extension-preflight.js.map +0 -1
- package/esm2022/lib/editor-serialization.js +0 -152
- package/esm2022/lib/editor-serialization.js.map +0 -1
- package/esm2022/lib/editor-toolbar-context.js +0 -259
- package/esm2022/lib/editor-toolbar-context.js.map +0 -1
- package/esm2022/lib/editor.tokens.js +0 -10
- package/esm2022/lib/editor.tokens.js.map +0 -1
- package/esm2022/lib/editor.types.js +0 -48
- package/esm2022/lib/editor.types.js.map +0 -1
- package/esm2022/lib/extensions/editor-block-handle.js +0 -760
- package/esm2022/lib/extensions/editor-block-handle.js.map +0 -1
- package/esm2022/lib/extensions/editor-extensions.js +0 -321
- package/esm2022/lib/extensions/editor-extensions.js.map +0 -1
- package/esm2022/lib/extensions/editor-markdown-compatibility.js +0 -162
- package/esm2022/lib/extensions/editor-markdown-compatibility.js.map +0 -1
- package/esm2022/lib/extensions/editor-upload-placeholder.js +0 -156
- package/esm2022/lib/extensions/editor-upload-placeholder.js.map +0 -1
- package/esm2022/lib/status/editor-status.js +0 -64
- package/esm2022/lib/status/editor-status.js.map +0 -1
- package/esm2022/lib/toolbar/editor-alignment.js +0 -203
- package/esm2022/lib/toolbar/editor-alignment.js.map +0 -1
- package/esm2022/lib/toolbar/editor-block-insert.js +0 -101
- package/esm2022/lib/toolbar/editor-block-insert.js.map +0 -1
- package/esm2022/lib/toolbar/editor-color-control.js +0 -71
- package/esm2022/lib/toolbar/editor-color-control.js.map +0 -1
- package/esm2022/lib/toolbar/editor-command-button.js +0 -115
- package/esm2022/lib/toolbar/editor-command-button.js.map +0 -1
- package/esm2022/lib/toolbar/editor-heading.js +0 -266
- package/esm2022/lib/toolbar/editor-heading.js.map +0 -1
- package/esm2022/lib/toolbar/editor-highlight.js +0 -84
- package/esm2022/lib/toolbar/editor-highlight.js.map +0 -1
- package/esm2022/lib/toolbar/editor-image-upload-dialog.js +0 -289
- package/esm2022/lib/toolbar/editor-image-upload-dialog.js.map +0 -1
- package/esm2022/lib/toolbar/editor-image-upload.js +0 -315
- package/esm2022/lib/toolbar/editor-image-upload.js.map +0 -1
- package/esm2022/lib/toolbar/editor-inline-marks.js +0 -124
- package/esm2022/lib/toolbar/editor-inline-marks.js.map +0 -1
- package/esm2022/lib/toolbar/editor-link.js +0 -265
- package/esm2022/lib/toolbar/editor-link.js.map +0 -1
- package/esm2022/lib/toolbar/editor-list.js +0 -249
- package/esm2022/lib/toolbar/editor-list.js.map +0 -1
- package/esm2022/lib/toolbar/editor-table.js +0 -292
- package/esm2022/lib/toolbar/editor-table.js.map +0 -1
- package/esm2022/lib/toolbar/editor-text-color.js +0 -82
- package/esm2022/lib/toolbar/editor-text-color.js.map +0 -1
- package/esm2022/lib/toolbar/editor-toolbar-widget.js +0 -41
- package/esm2022/lib/toolbar/editor-toolbar-widget.js.map +0 -1
- package/esm2022/lib/toolbar/editor-toolbar.defs.js +0 -60
- package/esm2022/lib/toolbar/editor-toolbar.defs.js.map +0 -1
- package/esm2022/lib/toolbar/editor-toolbar.js +0 -826
- package/esm2022/lib/toolbar/editor-toolbar.js.map +0 -1
- package/esm2022/lib/toolbar/editor-undo-redo.js +0 -72
- package/esm2022/lib/toolbar/editor-undo-redo.js.map +0 -1
- package/esm2022/lib/toolbar/editor-zoom.js +0 -330
- package/esm2022/lib/toolbar/editor-zoom.js.map +0 -1
- package/esm2022/lib/upload/editor-image-upload-coordinator.js +0 -480
- package/esm2022/lib/upload/editor-image-upload-coordinator.js.map +0 -1
- package/esm2022/malva-ui-editor.js +0 -5
- package/esm2022/malva-ui-editor.js.map +0 -1
- package/index.d.ts +0 -35
- package/lib/ai/editor-ai-actions.d.ts +0 -27
- package/lib/ai/editor-ai-context.d.ts +0 -307
- package/lib/ai/editor-ai-menu.d.ts +0 -142
- package/lib/ai/editor-ai-review-bar.d.ts +0 -116
- package/lib/ai/editor-ai-stream.d.ts +0 -207
- package/lib/ai/editor-ai-suggestions.d.ts +0 -219
- package/lib/ai/editor-ai.tokens.d.ts +0 -10
- package/lib/ai/editor-ai.types.d.ts +0 -176
- package/lib/editor/editor.d.ts +0 -243
- package/lib/editor-extension-preflight.d.ts +0 -23
- package/lib/editor-serialization.d.ts +0 -74
- package/lib/editor-toolbar-context.d.ts +0 -116
- package/lib/editor.tokens.d.ts +0 -6
- package/lib/editor.types.d.ts +0 -194
- package/lib/extensions/editor-block-handle.d.ts +0 -59
- package/lib/extensions/editor-extensions.d.ts +0 -165
- package/lib/extensions/editor-markdown-compatibility.d.ts +0 -11
- package/lib/extensions/editor-upload-placeholder.d.ts +0 -75
- package/lib/status/editor-status.d.ts +0 -24
- package/lib/toolbar/editor-alignment.d.ts +0 -61
- package/lib/toolbar/editor-block-insert.d.ts +0 -26
- package/lib/toolbar/editor-color-control.d.ts +0 -32
- package/lib/toolbar/editor-command-button.d.ts +0 -29
- package/lib/toolbar/editor-heading.d.ts +0 -68
- package/lib/toolbar/editor-highlight.d.ts +0 -21
- package/lib/toolbar/editor-image-upload-dialog.d.ts +0 -96
- package/lib/toolbar/editor-image-upload.d.ts +0 -71
- package/lib/toolbar/editor-inline-marks.d.ts +0 -30
- package/lib/toolbar/editor-link.d.ts +0 -78
- package/lib/toolbar/editor-list.d.ts +0 -67
- package/lib/toolbar/editor-table.d.ts +0 -133
- package/lib/toolbar/editor-text-color.d.ts +0 -21
- package/lib/toolbar/editor-toolbar-widget.d.ts +0 -19
- package/lib/toolbar/editor-toolbar.d.ts +0 -157
- package/lib/toolbar/editor-toolbar.defs.d.ts +0 -35
- package/lib/toolbar/editor-undo-redo.d.ts +0 -26
- package/lib/toolbar/editor-zoom.d.ts +0 -108
- package/lib/upload/editor-image-upload-coordinator.d.ts +0 -55
- package/malva-ui-editor.d.ts +0 -5
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, ViewEncapsulation, computed, effect, inject, signal, } from '@angular/core';
|
|
2
|
-
import { LucideCheck, LucideChevronLeft, LucideChevronRight, LucideCircleStop, LucideX, } from '@lucide/angular';
|
|
3
|
-
import { MlvButton, MlvButtonIcon } from '@malva-ui/core/button';
|
|
4
|
-
import { MlvTooltip } from '@malva-ui/core/tooltip';
|
|
5
|
-
import { MLV_EDITOR_I18N, MlvI18nResolverService } from '@malva-ui/i18n';
|
|
6
|
-
import { MLV_EDITOR_OVERLAY_REGISTRY, MLV_EDITOR_TOOLBAR_CONTEXT, } from '../editor-toolbar-context';
|
|
7
|
-
import { MLV_EDITOR_AI_CONTEXT } from './editor-ai-context';
|
|
8
|
-
import * as i0 from "@angular/core";
|
|
9
|
-
/** @internal Source of unique description-element ids across bar instances. */
|
|
10
|
-
let reviewBarDescriptionIdSequence = 0;
|
|
11
|
-
/**
|
|
12
|
-
* Review surface for pending AI suggestions.
|
|
13
|
-
*
|
|
14
|
-
* Rendered inside the editor composite — hosts project it into the status
|
|
15
|
-
* region (`<mlv-editor-ai-review-bar mlvEditorStatus />`), following the
|
|
16
|
-
* `MlvEditorStatus` placement. Hidden while the nearest
|
|
17
|
-
* {@link MLV_EDITOR_AI_CONTEXT} is `'idle'` (or absent); while `'running'` it
|
|
18
|
-
* shows a stop affordance that cancels the in-flight request, and while
|
|
19
|
-
* `'reviewing'` it shows the pending-suggestion count, previous/next
|
|
20
|
-
* navigation, accept/reject for the current suggestion, and accept-all/
|
|
21
|
-
* reject-all.
|
|
22
|
-
*
|
|
23
|
-
* Navigation keeps an internal cursor over the context's suggestion list;
|
|
24
|
-
* moving it outlines the current suggestion in the document
|
|
25
|
-
* (`MLV_EDITOR_AI_SUGGESTION_CURRENT_CLASS` decoration) and scrolls its range
|
|
26
|
-
* into view through {@link MlvEditorAiContext.revealSuggestion}. Accepting or
|
|
27
|
-
* rejecting the current suggestion advances to the next remaining one (the
|
|
28
|
-
* list shrinks under a clamped cursor); when the set empties the context
|
|
29
|
-
* returns to `'idle'` and the bar disappears.
|
|
30
|
-
*
|
|
31
|
-
* Announcement choice: the count region is deliberately **not** an
|
|
32
|
-
* `aria-live` region. The AI context already announces the review lifecycle —
|
|
33
|
-
* `aiReviewStarted` with the count, and every accept/reject outcome — through
|
|
34
|
-
* the CDK `LiveAnnouncer`; a live count would double-announce each change.
|
|
35
|
-
*
|
|
36
|
-
* The host element registers with the editor's composite-overlay registry, so
|
|
37
|
-
* interacting with the bar never emits a false editor blur wherever a host
|
|
38
|
-
* renders it. All controls are plain tab stops inside the composite. Accept,
|
|
39
|
-
* reject, and navigation disable while the editor is readonly or disabled
|
|
40
|
-
* (mutations are refused by the context anyway; the disabled state makes the
|
|
41
|
-
* refusal visible), and the whole bar disables with the editor.
|
|
42
|
-
*/
|
|
43
|
-
export class MlvEditorAiReviewBar {
|
|
44
|
-
/** @protected AI command state of the nearest editor, if provided. */
|
|
45
|
-
_ai = inject(MLV_EDITOR_AI_CONTEXT, { optional: true });
|
|
46
|
-
/** @protected Editor-scoped form state gating executability. */
|
|
47
|
-
_context = inject(MLV_EDITOR_TOOLBAR_CONTEXT);
|
|
48
|
-
/** @private Composite focus ownership for the bar's host element. */
|
|
49
|
-
_overlays = inject(MLV_EDITOR_OVERLAY_REGISTRY);
|
|
50
|
-
/** @private Host element registered with the composite-overlay registry. */
|
|
51
|
-
_host = inject(ElementRef);
|
|
52
|
-
/** @private Clears the current-suggestion outline on teardown. */
|
|
53
|
-
_destroyRef = inject(DestroyRef);
|
|
54
|
-
/** @private Optional localized editor copy. */
|
|
55
|
-
_i18n = inject(MLV_EDITOR_I18N, { optional: true });
|
|
56
|
-
/** @private Resolves the ICU pending-count template. */
|
|
57
|
-
_resolver = inject(MlvI18nResolverService);
|
|
58
|
-
/**
|
|
59
|
-
* @private Navigation cursor over the suggestion list. Reset for every new
|
|
60
|
-
* review; reads clamp through {@link _index}, so a cursor left beyond the
|
|
61
|
-
* end after the list shrinks points at the last remaining suggestion.
|
|
62
|
-
*/
|
|
63
|
-
_cursor = signal(0, /* @ts-ignore */
|
|
64
|
-
...(ngDevMode ? [{ debugName: "_cursor" }] : /* istanbul ignore next */ []));
|
|
65
|
-
/** @protected Reactive localized copy for every control and the count. */
|
|
66
|
-
_copy = computed(() => {
|
|
67
|
-
const copy = this._i18n?.();
|
|
68
|
-
return {
|
|
69
|
-
aiReviewBar: copy?.aiReviewBar ?? 'AI suggestion review',
|
|
70
|
-
aiReviewCount: copy?.aiReviewCount ??
|
|
71
|
-
'{count, plural, one {# suggestion} other {# suggestions}}',
|
|
72
|
-
aiPreviousSuggestion: copy?.aiPreviousSuggestion ?? 'Previous suggestion',
|
|
73
|
-
aiNextSuggestion: copy?.aiNextSuggestion ?? 'Next suggestion',
|
|
74
|
-
aiAcceptSuggestion: copy?.aiAcceptSuggestion ?? 'Accept suggestion',
|
|
75
|
-
aiRejectSuggestion: copy?.aiRejectSuggestion ?? 'Reject suggestion',
|
|
76
|
-
aiAcceptAll: copy?.aiAcceptAll ?? 'Accept all',
|
|
77
|
-
aiRejectAll: copy?.aiRejectAll ?? 'Reject all',
|
|
78
|
-
aiStopGeneration: copy?.aiStopGeneration ?? 'Stop generating',
|
|
79
|
-
};
|
|
80
|
-
}, /* @ts-ignore */
|
|
81
|
-
...(ngDevMode ? [{ debugName: "_copy" }] : /* istanbul ignore next */ []));
|
|
82
|
-
/** @protected Whether a transform is streaming, showing the stop affordance. */
|
|
83
|
-
_running = computed(() => this._ai?.status() === 'running', /* @ts-ignore */
|
|
84
|
-
...(ngDevMode ? [{ debugName: "_running" }] : /* istanbul ignore next */ []));
|
|
85
|
-
/** @protected Whether a review with pending suggestions is active. */
|
|
86
|
-
_reviewing = computed(() => this._ai?.status() === 'reviewing', /* @ts-ignore */
|
|
87
|
-
...(ngDevMode ? [{ debugName: "_reviewing" }] : /* istanbul ignore next */ []));
|
|
88
|
-
/** @protected The bar renders only while AI work is running or under review. */
|
|
89
|
-
_visible = computed(() => this._running() || this._reviewing(), /* @ts-ignore */
|
|
90
|
-
...(ngDevMode ? [{ debugName: "_visible" }] : /* istanbul ignore next */ []));
|
|
91
|
-
/** @protected Whether readonly/disabled state blocks every AI action. */
|
|
92
|
-
_disabled = computed(() => this._context.disabled() || this._context.readonly(), /* @ts-ignore */
|
|
93
|
-
...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
|
|
94
|
-
/** @protected Pending suggestions mirrored from the AI context. */
|
|
95
|
-
_suggestions = computed(() => this._ai?.suggestions() ?? [], /* @ts-ignore */
|
|
96
|
-
...(ngDevMode ? [{ debugName: "_suggestions" }] : /* istanbul ignore next */ []));
|
|
97
|
-
/** @protected Pending-suggestion count shown in the bar. */
|
|
98
|
-
_count = computed(() => this._suggestions().length, /* @ts-ignore */
|
|
99
|
-
...(ngDevMode ? [{ debugName: "_count" }] : /* istanbul ignore next */ []));
|
|
100
|
-
/** @protected Clamped cursor index of the current suggestion. */
|
|
101
|
-
_index = computed(() => Math.max(0, Math.min(this._cursor(), this._count() - 1)), /* @ts-ignore */
|
|
102
|
-
...(ngDevMode ? [{ debugName: "_index" }] : /* istanbul ignore next */ []));
|
|
103
|
-
/** @private Identifier of the current suggestion, if any is pending. */
|
|
104
|
-
_currentId = computed(() => this._suggestions()[this._index()]?.id ?? null, /* @ts-ignore */
|
|
105
|
-
...(ngDevMode ? [{ debugName: "_currentId" }] : /* istanbul ignore next */ []));
|
|
106
|
-
/**
|
|
107
|
-
* @protected Id of the visually hidden current-suggestion description
|
|
108
|
-
* element the accept/reject buttons reference through `aria-describedby`.
|
|
109
|
-
* Unique per bar instance so multiple editors on one page cannot collide.
|
|
110
|
-
*/
|
|
111
|
-
_descriptionId = `mlv-editor-ai-review-bar-description-${++reviewBarDescriptionIdSequence}`;
|
|
112
|
-
/**
|
|
113
|
-
* @protected Non-visual description of the current suggestion — index,
|
|
114
|
-
* count, and the removed/added text — from
|
|
115
|
-
* {@link MlvEditorAiContext.describeSuggestion}. The removed text otherwise
|
|
116
|
-
* exists only inside the `aria-hidden` strikethrough widget, so this is
|
|
117
|
-
* what makes the accept/reject decision inspectable without sight.
|
|
118
|
-
*/
|
|
119
|
-
_currentDescription = computed(() => {
|
|
120
|
-
const id = this._currentId();
|
|
121
|
-
return id === null ? '' : (this._ai?.describeSuggestion(id) ?? '');
|
|
122
|
-
}, /* @ts-ignore */
|
|
123
|
-
...(ngDevMode ? [{ debugName: "_currentDescription" }] : /* istanbul ignore next */ []));
|
|
124
|
-
/** @protected Localized pending count resolved through the ICU template. */
|
|
125
|
-
_countText = computed(() => this._resolver.resolve({ aiReviewCount: this._copy().aiReviewCount }, 'aiReviewCount', { count: this._count() }), /* @ts-ignore */
|
|
126
|
-
...(ngDevMode ? [{ debugName: "_countText" }] : /* istanbul ignore next */ []));
|
|
127
|
-
constructor() {
|
|
128
|
-
// The bar joins the editor composite wherever a host renders it: the
|
|
129
|
-
// registry adds its focus handlers to this element, so focus moving into
|
|
130
|
-
// the bar never emits a false editor blur. Registered per enabled state
|
|
131
|
-
// because disabling the editor force-clears the registry (`closeAll`),
|
|
132
|
-
// which would silently drop a one-time registration.
|
|
133
|
-
effect((onCleanup) => {
|
|
134
|
-
if (this._context.disabled())
|
|
135
|
-
return;
|
|
136
|
-
const unregister = this._overlays.register(this._host.nativeElement,
|
|
137
|
-
// Nothing to close: the bar is inline, its visibility follows the AI
|
|
138
|
-
// context status.
|
|
139
|
-
() => undefined);
|
|
140
|
-
onCleanup(unregister);
|
|
141
|
-
});
|
|
142
|
-
// The navigation cursor resets whenever no review is active, so the next
|
|
143
|
-
// review starts at its first suggestion.
|
|
144
|
-
effect(() => {
|
|
145
|
-
if (!this._reviewing())
|
|
146
|
-
this._cursor.set(0);
|
|
147
|
-
});
|
|
148
|
-
// Outline follows the current suggestion: on review start, navigation,
|
|
149
|
-
// and the auto-advance after accept/reject the context outlines the
|
|
150
|
-
// suggestion and scrolls it into view. Ids are compared by value, so the
|
|
151
|
-
// signal graph settles without re-firing on the mirror update the reveal
|
|
152
|
-
// transaction itself causes.
|
|
153
|
-
effect(() => {
|
|
154
|
-
const id = this._currentId();
|
|
155
|
-
if (id !== null)
|
|
156
|
-
this._ai?.revealSuggestion(id);
|
|
157
|
-
});
|
|
158
|
-
this._destroyRef.onDestroy(() => this._ai?.revealSuggestion(null));
|
|
159
|
-
}
|
|
160
|
-
/** @protected Cancels the in-flight AI request from the stop affordance. */
|
|
161
|
-
_stop() {
|
|
162
|
-
this._ai?.cancel();
|
|
163
|
-
}
|
|
164
|
-
/** @protected Moves the cursor to the previous suggestion, wrapping at the start. */
|
|
165
|
-
_previous() {
|
|
166
|
-
const count = this._count();
|
|
167
|
-
if (count === 0)
|
|
168
|
-
return;
|
|
169
|
-
this._cursor.set((this._index() - 1 + count) % count);
|
|
170
|
-
}
|
|
171
|
-
/** @protected Moves the cursor to the next suggestion, wrapping at the end. */
|
|
172
|
-
_next() {
|
|
173
|
-
const count = this._count();
|
|
174
|
-
if (count === 0)
|
|
175
|
-
return;
|
|
176
|
-
this._cursor.set((this._index() + 1) % count);
|
|
177
|
-
}
|
|
178
|
-
/** @protected Accepts the current suggestion; the clamped cursor advances. */
|
|
179
|
-
_acceptCurrent() {
|
|
180
|
-
const id = this._currentId();
|
|
181
|
-
if (id === null)
|
|
182
|
-
return;
|
|
183
|
-
this._cursor.set(this._index());
|
|
184
|
-
this._ai?.acceptSuggestion(id);
|
|
185
|
-
}
|
|
186
|
-
/** @protected Rejects the current suggestion; the clamped cursor advances. */
|
|
187
|
-
_rejectCurrent() {
|
|
188
|
-
const id = this._currentId();
|
|
189
|
-
if (id === null)
|
|
190
|
-
return;
|
|
191
|
-
this._cursor.set(this._index());
|
|
192
|
-
this._ai?.rejectSuggestion(id);
|
|
193
|
-
}
|
|
194
|
-
/** @protected Accepts every pending suggestion. */
|
|
195
|
-
_acceptAll() {
|
|
196
|
-
this._ai?.acceptAll();
|
|
197
|
-
}
|
|
198
|
-
/** @protected Rejects every pending suggestion. */
|
|
199
|
-
_rejectAll() {
|
|
200
|
-
this._ai?.rejectAll();
|
|
201
|
-
}
|
|
202
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAiReviewBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
203
|
-
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: [".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"] }, { 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 });
|
|
204
|
-
}
|
|
205
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MlvEditorAiReviewBar, decorators: [{
|
|
206
|
-
type: Component,
|
|
207
|
-
args: [{ selector: 'mlv-editor-ai-review-bar', imports: [
|
|
208
|
-
LucideCheck,
|
|
209
|
-
LucideChevronLeft,
|
|
210
|
-
LucideChevronRight,
|
|
211
|
-
LucideCircleStop,
|
|
212
|
-
LucideX,
|
|
213
|
-
MlvButton,
|
|
214
|
-
MlvButtonIcon,
|
|
215
|
-
MlvTooltip,
|
|
216
|
-
], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
217
|
-
class: 'mlv-editor-ai-review-bar',
|
|
218
|
-
role: 'group',
|
|
219
|
-
'[attr.aria-label]': '_copy().aiReviewBar',
|
|
220
|
-
'[hidden]': '!_visible()',
|
|
221
|
-
}, 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: [".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"] }]
|
|
222
|
-
}], ctorParameters: () => [] });
|
|
223
|
-
//# sourceMappingURL=editor-ai-review-bar.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"editor-ai-review-bar.js","sourceRoot":"","sources":["../../../../../../libs/editor/src/lib/ai/editor-ai-review-bar.ts","../../../../../../libs/editor/src/lib/ai/editor-ai-review-bar.html"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ElementRef,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n signal,\n} from '@angular/core';\nimport {\n LucideCheck,\n LucideChevronLeft,\n LucideChevronRight,\n LucideCircleStop,\n LucideX,\n} from '@lucide/angular';\nimport { MlvButton, MlvButtonIcon } from '@malva-ui/core/button';\nimport { MlvTooltip } from '@malva-ui/core/tooltip';\nimport { MLV_EDITOR_I18N, MlvI18nResolverService } from '@malva-ui/i18n';\nimport {\n MLV_EDITOR_OVERLAY_REGISTRY,\n MLV_EDITOR_TOOLBAR_CONTEXT,\n} from '../editor-toolbar-context';\nimport type { MlvEditorOverlayRegistry } from '../editor-toolbar-context';\nimport { MLV_EDITOR_AI_CONTEXT } from './editor-ai-context';\n\n/** @internal Source of unique description-element ids across bar instances. */\nlet reviewBarDescriptionIdSequence = 0;\n\n/**\n * Review surface for pending AI suggestions.\n *\n * Rendered inside the editor composite — hosts project it into the status\n * region (`<mlv-editor-ai-review-bar mlvEditorStatus />`), following the\n * `MlvEditorStatus` placement. Hidden while the nearest\n * {@link MLV_EDITOR_AI_CONTEXT} is `'idle'` (or absent); while `'running'` it\n * shows a stop affordance that cancels the in-flight request, and while\n * `'reviewing'` it shows the pending-suggestion count, previous/next\n * navigation, accept/reject for the current suggestion, and accept-all/\n * reject-all.\n *\n * Navigation keeps an internal cursor over the context's suggestion list;\n * moving it outlines the current suggestion in the document\n * (`MLV_EDITOR_AI_SUGGESTION_CURRENT_CLASS` decoration) and scrolls its range\n * into view through {@link MlvEditorAiContext.revealSuggestion}. Accepting or\n * rejecting the current suggestion advances to the next remaining one (the\n * list shrinks under a clamped cursor); when the set empties the context\n * returns to `'idle'` and the bar disappears.\n *\n * Announcement choice: the count region is deliberately **not** an\n * `aria-live` region. The AI context already announces the review lifecycle —\n * `aiReviewStarted` with the count, and every accept/reject outcome — through\n * the CDK `LiveAnnouncer`; a live count would double-announce each change.\n *\n * The host element registers with the editor's composite-overlay registry, so\n * interacting with the bar never emits a false editor blur wherever a host\n * renders it. All controls are plain tab stops inside the composite. Accept,\n * reject, and navigation disable while the editor is readonly or disabled\n * (mutations are refused by the context anyway; the disabled state makes the\n * refusal visible), and the whole bar disables with the editor.\n */\n@Component({\n selector: 'mlv-editor-ai-review-bar',\n imports: [\n LucideCheck,\n LucideChevronLeft,\n LucideChevronRight,\n LucideCircleStop,\n LucideX,\n MlvButton,\n MlvButtonIcon,\n MlvTooltip,\n ],\n templateUrl: './editor-ai-review-bar.html',\n styleUrl: './editor-ai-review-bar.scss',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'mlv-editor-ai-review-bar',\n role: 'group',\n '[attr.aria-label]': '_copy().aiReviewBar',\n '[hidden]': '!_visible()',\n },\n})\nexport class MlvEditorAiReviewBar {\n /** @protected AI command state of the nearest editor, if provided. */\n protected readonly _ai = inject(MLV_EDITOR_AI_CONTEXT, { optional: true });\n\n /** @protected Editor-scoped form state gating executability. */\n protected readonly _context = inject(MLV_EDITOR_TOOLBAR_CONTEXT);\n\n /** @private Composite focus ownership for the bar's host element. */\n private readonly _overlays = inject<MlvEditorOverlayRegistry>(\n MLV_EDITOR_OVERLAY_REGISTRY,\n );\n\n /** @private Host element registered with the composite-overlay registry. */\n private readonly _host = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /** @private Clears the current-suggestion outline on teardown. */\n private readonly _destroyRef = inject(DestroyRef);\n\n /** @private Optional localized editor copy. */\n private readonly _i18n = inject(MLV_EDITOR_I18N, { optional: true });\n\n /** @private Resolves the ICU pending-count template. */\n private readonly _resolver = inject(MlvI18nResolverService);\n\n /**\n * @private Navigation cursor over the suggestion list. Reset for every new\n * review; reads clamp through {@link _index}, so a cursor left beyond the\n * end after the list shrinks points at the last remaining suggestion.\n */\n private readonly _cursor = signal(0);\n\n /** @protected Reactive localized copy for every control and the count. */\n protected readonly _copy = computed(() => {\n const copy = this._i18n?.();\n return {\n aiReviewBar: copy?.aiReviewBar ?? 'AI suggestion review',\n aiReviewCount:\n copy?.aiReviewCount ??\n '{count, plural, one {# suggestion} other {# suggestions}}',\n aiPreviousSuggestion: copy?.aiPreviousSuggestion ?? 'Previous suggestion',\n aiNextSuggestion: copy?.aiNextSuggestion ?? 'Next suggestion',\n aiAcceptSuggestion: copy?.aiAcceptSuggestion ?? 'Accept suggestion',\n aiRejectSuggestion: copy?.aiRejectSuggestion ?? 'Reject suggestion',\n aiAcceptAll: copy?.aiAcceptAll ?? 'Accept all',\n aiRejectAll: copy?.aiRejectAll ?? 'Reject all',\n aiStopGeneration: copy?.aiStopGeneration ?? 'Stop generating',\n };\n });\n\n /** @protected Whether a transform is streaming, showing the stop affordance. */\n protected readonly _running = computed(\n () => this._ai?.status() === 'running',\n );\n\n /** @protected Whether a review with pending suggestions is active. */\n protected readonly _reviewing = computed(\n () => this._ai?.status() === 'reviewing',\n );\n\n /** @protected The bar renders only while AI work is running or under review. */\n protected readonly _visible = computed(\n () => this._running() || this._reviewing(),\n );\n\n /** @protected Whether readonly/disabled state blocks every AI action. */\n protected readonly _disabled = computed(\n () => this._context.disabled() || this._context.readonly(),\n );\n\n /** @protected Pending suggestions mirrored from the AI context. */\n protected readonly _suggestions = computed(\n () => this._ai?.suggestions() ?? [],\n );\n\n /** @protected Pending-suggestion count shown in the bar. */\n protected readonly _count = computed(() => this._suggestions().length);\n\n /** @protected Clamped cursor index of the current suggestion. */\n protected readonly _index = computed(() =>\n Math.max(0, Math.min(this._cursor(), this._count() - 1)),\n );\n\n /** @private Identifier of the current suggestion, if any is pending. */\n private readonly _currentId = computed(\n () => this._suggestions()[this._index()]?.id ?? null,\n );\n\n /**\n * @protected Id of the visually hidden current-suggestion description\n * element the accept/reject buttons reference through `aria-describedby`.\n * Unique per bar instance so multiple editors on one page cannot collide.\n */\n protected readonly _descriptionId = `mlv-editor-ai-review-bar-description-${++reviewBarDescriptionIdSequence}`;\n\n /**\n * @protected Non-visual description of the current suggestion — index,\n * count, and the removed/added text — from\n * {@link MlvEditorAiContext.describeSuggestion}. The removed text otherwise\n * exists only inside the `aria-hidden` strikethrough widget, so this is\n * what makes the accept/reject decision inspectable without sight.\n */\n protected readonly _currentDescription = computed(() => {\n const id = this._currentId();\n return id === null ? '' : (this._ai?.describeSuggestion(id) ?? '');\n });\n\n /** @protected Localized pending count resolved through the ICU template. */\n protected readonly _countText = computed(() =>\n this._resolver.resolve(\n { aiReviewCount: this._copy().aiReviewCount },\n 'aiReviewCount',\n { count: this._count() },\n ),\n );\n\n constructor() {\n // The bar joins the editor composite wherever a host renders it: the\n // registry adds its focus handlers to this element, so focus moving into\n // the bar never emits a false editor blur. Registered per enabled state\n // because disabling the editor force-clears the registry (`closeAll`),\n // which would silently drop a one-time registration.\n effect((onCleanup) => {\n if (this._context.disabled()) return;\n const unregister = this._overlays.register(\n this._host.nativeElement,\n // Nothing to close: the bar is inline, its visibility follows the AI\n // context status.\n () => undefined,\n );\n onCleanup(unregister);\n });\n\n // The navigation cursor resets whenever no review is active, so the next\n // review starts at its first suggestion.\n effect(() => {\n if (!this._reviewing()) this._cursor.set(0);\n });\n\n // Outline follows the current suggestion: on review start, navigation,\n // and the auto-advance after accept/reject the context outlines the\n // suggestion and scrolls it into view. Ids are compared by value, so the\n // signal graph settles without re-firing on the mirror update the reveal\n // transaction itself causes.\n effect(() => {\n const id = this._currentId();\n if (id !== null) this._ai?.revealSuggestion(id);\n });\n\n this._destroyRef.onDestroy(() => this._ai?.revealSuggestion(null));\n }\n\n /** @protected Cancels the in-flight AI request from the stop affordance. */\n protected _stop(): void {\n this._ai?.cancel();\n }\n\n /** @protected Moves the cursor to the previous suggestion, wrapping at the start. */\n protected _previous(): void {\n const count = this._count();\n if (count === 0) return;\n this._cursor.set((this._index() - 1 + count) % count);\n }\n\n /** @protected Moves the cursor to the next suggestion, wrapping at the end. */\n protected _next(): void {\n const count = this._count();\n if (count === 0) return;\n this._cursor.set((this._index() + 1) % count);\n }\n\n /** @protected Accepts the current suggestion; the clamped cursor advances. */\n protected _acceptCurrent(): void {\n const id = this._currentId();\n if (id === null) return;\n this._cursor.set(this._index());\n this._ai?.acceptSuggestion(id);\n }\n\n /** @protected Rejects the current suggestion; the clamped cursor advances. */\n protected _rejectCurrent(): void {\n const id = this._currentId();\n if (id === null) return;\n this._cursor.set(this._index());\n this._ai?.rejectSuggestion(id);\n }\n\n /** @protected Accepts every pending suggestion. */\n protected _acceptAll(): void {\n this._ai?.acceptAll();\n }\n\n /** @protected Rejects every pending suggestion. */\n protected _rejectAll(): void {\n this._ai?.rejectAll();\n }\n}\n","@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"]}
|