@myrmidon/gve-core 5.0.1 → 5.0.2

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,33 +1,33 @@
1
1
  import * as i0 from '@angular/core';
2
- import { model, input, output, effect, Component, signal, ViewChild, Injectable, Optional, Inject, computed, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2
+ import { model, input, output, effect, Component, signal, computed, ViewChild, Injectable, Optional, Inject, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
4
  import { Validators, ReactiveFormsModule, FormControl, FormGroup, FormsModule } from '@angular/forms';
5
5
  import * as i2 from '@angular/material/button';
6
6
  import { MatButtonModule } from '@angular/material/button';
7
- import * as i3$1 from '@angular/material/checkbox';
7
+ import * as i3$2 from '@angular/material/checkbox';
8
8
  import { MatCheckboxModule } from '@angular/material/checkbox';
9
- import * as i3 from '@angular/material/expansion';
9
+ import * as i3$1 from '@angular/material/expansion';
10
10
  import { MatExpansionModule } from '@angular/material/expansion';
11
- import * as i4 from '@angular/material/form-field';
11
+ import * as i3 from '@angular/material/form-field';
12
12
  import { MatFormFieldModule } from '@angular/material/form-field';
13
- import * as i4$1 from '@angular/material/icon';
13
+ import * as i4 from '@angular/material/icon';
14
14
  import { MatIconModule } from '@angular/material/icon';
15
15
  import * as i5 from '@angular/material/input';
16
16
  import { MatInputModule } from '@angular/material/input';
17
17
  import * as i6 from '@angular/material/select';
18
18
  import { MatSelectModule } from '@angular/material/select';
19
- import * as i15 from '@angular/material/tabs';
19
+ import * as i14 from '@angular/material/tabs';
20
20
  import { MatTabsModule } from '@angular/material/tabs';
21
21
  import * as i7 from '@angular/material/tooltip';
22
- import { MatTooltipModule } from '@angular/material/tooltip';
22
+ import { MatTooltipModule, MatTooltip } from '@angular/material/tooltip';
23
23
  import * as i2$1 from '@myrmidon/ngx-tools';
24
24
  import { NgxToolsValidators, deepCopy, ColorToContrastPipe, FlatLookupPipe, SafeHtmlPipe } from '@myrmidon/ngx-tools';
25
25
  import { debounceTime, distinctUntilChanged, catchError, BehaviorSubject } from 'rxjs';
26
- import * as i4$2 from '@myrmidon/ngx-mat-tools';
26
+ import * as i4$1 from '@myrmidon/ngx-mat-tools';
27
27
  import * as i1$1 from '@angular/material/core';
28
28
  import { MatRippleModule } from '@angular/material/core';
29
29
  import { FeatureSetPolicy, SnapshotViewService, OperationType, DEFAULT_SVG_BASE_TEXT_OPTIONS } from '@myrmidon/gve-snapshot-view';
30
- import * as i3$2 from '@angular/material/dialog';
30
+ import * as i3$3 from '@angular/material/dialog';
31
31
  import { MAT_DIALOG_DATA } from '@angular/material/dialog';
32
32
  import * as i1$2 from '@angular/common/http';
33
33
  import { filter, debounceTime as debounceTime$1 } from 'rxjs/operators';
@@ -35,20 +35,21 @@ import * as i2$2 from '@angular/cdk/clipboard';
35
35
  import { ClipboardModule } from '@angular/cdk/clipboard';
36
36
  import * as i5$1 from '@angular/material/badge';
37
37
  import { MatBadgeModule } from '@angular/material/badge';
38
- import * as i15$1 from '@cisstech/nge/monaco';
38
+ import * as i15 from '@cisstech/nge/monaco';
39
39
  import { NgeMonacoModule } from '@cisstech/nge/monaco';
40
40
  import { customAlphabet } from 'nanoid';
41
- import * as i17 from '@angular/common';
41
+ import * as i16 from '@angular/common';
42
42
  import { CommonModule } from '@angular/common';
43
43
  import { VizComponent } from '@myrmidon/ngx-viz';
44
- import * as i4$3 from '@angular/material/snack-bar';
44
+ import * as i4$2 from '@angular/material/snack-bar';
45
45
  import { MatSnackBarModule } from '@angular/material/snack-bar';
46
46
  import * as i7$1 from '@angular/material/button-toggle';
47
47
  import { MatButtonToggleModule } from '@angular/material/button-toggle';
48
- import * as i13 from '@angular/material/progress-bar';
48
+ import * as i12 from '@angular/material/progress-bar';
49
49
  import { MatProgressBarModule } from '@angular/material/progress-bar';
50
- import * as i14 from '@angular/material/slider';
50
+ import * as i13 from '@angular/material/slider';
51
51
  import { MatSliderModule } from '@angular/material/slider';
52
+ import { MatSlideToggle } from '@angular/material/slide-toggle';
52
53
 
53
54
  /**
54
55
  * 🔑 `gve-animation-tween`
@@ -179,10 +180,10 @@ class AnimationTweenComponent {
179
180
  }
180
181
  this.tween.set(this.getTween());
181
182
  }
182
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AnimationTweenComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
183
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: AnimationTweenComponent, isStandalone: true, selector: "gve-animation-tween", inputs: { tween: { classPropertyName: "tween", publicName: "tween", isSignal: true, isRequired: false, transformFunction: null }, elementIds: { classPropertyName: "elementIds", publicName: "elementIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tween: "tweenChange", tweenCancel: "tweenCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- label -->\r\n <mat-form-field>\r\n <mat-label>label</mat-label>\r\n <input matInput [formControl]=\"label\" />\r\n @if ($any(label).errors?.required && (label.dirty || label.touched)) {\r\n <mat-error>label required</mat-error>\r\n } @if ($any(label).errors?.maxLength && (label.dirty || label.touched)) {\r\n <mat-error>label too long</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n @for (t of types; track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- position -->\r\n <mat-form-field>\r\n <mat-label>position</mat-label>\r\n <input matInput [formControl]=\"position\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n [attr.aria-label]=\"'Information about position'\"\r\n (click)=\"\r\n openUrl(\r\n 'https://gsap.com/docs/v3/GSAP/Timeline/#positioning-animations-in-a-timeline'\r\n )\r\n \"\r\n >\r\n <mat-icon>info</mat-icon>\r\n </button>\r\n <mat-hint>time label &lt; &gt; += -=</mat-hint>\r\n @if ( $any(position).errors?.maxLength && (position.dirty ||\r\n position.touched) ) {\r\n <mat-error>position too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <div class=\"form-row\">\r\n <!-- selector -->\r\n <mat-form-field>\r\n <mat-label>selector</mat-label>\r\n <input matInput [formControl]=\"selector\" />\r\n <mat-hint>CSS selector</mat-hint>\r\n @if ( $any(selector).errors?.required && (selector.dirty ||\r\n selector.touched) ) {\r\n <mat-error>selector required</mat-error>\r\n } @if ( $any(selector).errors?.maxLength && (selector.dirty ||\r\n selector.touched) ) {\r\n <mat-error>selector too long</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- element id -->\r\n @if ((elementIds())?.length) {\r\n <mat-form-field>\r\n <mat-label>element ID</mat-label>\r\n <mat-select [formControl]=\"elementId\">\r\n @for (id of elementIds(); track id) {\r\n <mat-option [value]=\"id\">{{ id }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <!-- note -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>note</mat-label>\r\n <textarea rows=\"2\" matInput [formControl]=\"note\"></textarea>\r\n @if ($any(note).errors?.maxLength && (note.dirty || note.touched)) {\r\n <mat-error>note too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n @if ($any(vars).errors?.required && (vars.dirty || vars.touched)) {\r\n <mat-error>vars required</mat-error>\r\n } @if ($any(vars).errors?.json && (vars.dirty || vars.touched)) {\r\n <mat-error>invalid JSON</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- vars2 only when type is fromTo -->\r\n @if (type.value === 'fromTo') {\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>2nd vars</mat-label>\r\n <textarea matInput [formControl]=\"vars2\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n @if ($any(vars2).errors?.required && (vars2.dirty || vars2.touched)) {\r\n <mat-error>vars required</mat-error>\r\n } @if ($any(vars2).errors?.json && (vars2.dirty || vars2.touched)) {\r\n <mat-error>invalid JSON</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n }\r\n\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close tween\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-icon-button\r\n matTooltip=\"Save tween\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
183
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: AnimationTweenComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
184
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: AnimationTweenComponent, isStandalone: true, selector: "gve-animation-tween", inputs: { tween: { classPropertyName: "tween", publicName: "tween", isSignal: true, isRequired: false, transformFunction: null }, elementIds: { classPropertyName: "elementIds", publicName: "elementIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tween: "tweenChange", tweenCancel: "tweenCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- label -->\r\n <mat-form-field>\r\n <mat-label>label</mat-label>\r\n <input matInput [formControl]=\"label\" />\r\n @if ($any(label).errors?.required && (label.dirty || label.touched)) {\r\n <mat-error>label required</mat-error>\r\n } @if ($any(label).errors?.maxLength && (label.dirty || label.touched)) {\r\n <mat-error>label too long</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n @for (t of types; track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- position -->\r\n <mat-form-field>\r\n <mat-label>position</mat-label>\r\n <input matInput [formControl]=\"position\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n [attr.aria-label]=\"'Information about position'\"\r\n (click)=\"\r\n openUrl(\r\n 'https://gsap.com/docs/v3/GSAP/Timeline/#positioning-animations-in-a-timeline'\r\n )\r\n \"\r\n >\r\n <mat-icon>info</mat-icon>\r\n </button>\r\n <mat-hint>time label &lt; &gt; += -=</mat-hint>\r\n @if ( $any(position).errors?.maxLength && (position.dirty ||\r\n position.touched) ) {\r\n <mat-error>position too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <div class=\"form-row\">\r\n <!-- selector -->\r\n <mat-form-field>\r\n <mat-label>selector</mat-label>\r\n <input matInput [formControl]=\"selector\" />\r\n <mat-hint>CSS selector</mat-hint>\r\n @if ( $any(selector).errors?.required && (selector.dirty ||\r\n selector.touched) ) {\r\n <mat-error>selector required</mat-error>\r\n } @if ( $any(selector).errors?.maxLength && (selector.dirty ||\r\n selector.touched) ) {\r\n <mat-error>selector too long</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- element id -->\r\n @if ((elementIds())?.length) {\r\n <mat-form-field>\r\n <mat-label>element ID</mat-label>\r\n <mat-select [formControl]=\"elementId\">\r\n @for (id of elementIds(); track id) {\r\n <mat-option [value]=\"id\">{{ id }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <!-- note -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>note</mat-label>\r\n <textarea rows=\"2\" matInput [formControl]=\"note\"></textarea>\r\n @if ($any(note).errors?.maxLength && (note.dirty || note.touched)) {\r\n <mat-error>note too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n @if ($any(vars).errors?.required && (vars.dirty || vars.touched)) {\r\n <mat-error>vars required</mat-error>\r\n } @if ($any(vars).errors?.json && (vars.dirty || vars.touched)) {\r\n <mat-error>invalid JSON</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- vars2 only when type is fromTo -->\r\n @if (type.value === 'fromTo') {\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>2nd vars</mat-label>\r\n <textarea matInput [formControl]=\"vars2\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n @if ($any(vars2).errors?.required && (vars2.dirty || vars2.touched)) {\r\n <mat-error>vars required</mat-error>\r\n } @if ($any(vars2).errors?.json && (vars2.dirty || vars2.touched)) {\r\n <mat-error>invalid JSON</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n }\r\n\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close tween\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-icon-button\r\n matTooltip=\"Save tween\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
184
185
  }
185
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AnimationTweenComponent, decorators: [{
186
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: AnimationTweenComponent, decorators: [{
186
187
  type: Component,
187
188
  args: [{ selector: 'gve-animation-tween', imports: [
188
189
  ReactiveFormsModule,
@@ -195,7 +196,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
195
196
  MatTabsModule,
196
197
  MatTooltipModule,
197
198
  ], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- label -->\r\n <mat-form-field>\r\n <mat-label>label</mat-label>\r\n <input matInput [formControl]=\"label\" />\r\n @if ($any(label).errors?.required && (label.dirty || label.touched)) {\r\n <mat-error>label required</mat-error>\r\n } @if ($any(label).errors?.maxLength && (label.dirty || label.touched)) {\r\n <mat-error>label too long</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n @for (t of types; track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- position -->\r\n <mat-form-field>\r\n <mat-label>position</mat-label>\r\n <input matInput [formControl]=\"position\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n [attr.aria-label]=\"'Information about position'\"\r\n (click)=\"\r\n openUrl(\r\n 'https://gsap.com/docs/v3/GSAP/Timeline/#positioning-animations-in-a-timeline'\r\n )\r\n \"\r\n >\r\n <mat-icon>info</mat-icon>\r\n </button>\r\n <mat-hint>time label &lt; &gt; += -=</mat-hint>\r\n @if ( $any(position).errors?.maxLength && (position.dirty ||\r\n position.touched) ) {\r\n <mat-error>position too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <div class=\"form-row\">\r\n <!-- selector -->\r\n <mat-form-field>\r\n <mat-label>selector</mat-label>\r\n <input matInput [formControl]=\"selector\" />\r\n <mat-hint>CSS selector</mat-hint>\r\n @if ( $any(selector).errors?.required && (selector.dirty ||\r\n selector.touched) ) {\r\n <mat-error>selector required</mat-error>\r\n } @if ( $any(selector).errors?.maxLength && (selector.dirty ||\r\n selector.touched) ) {\r\n <mat-error>selector too long</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- element id -->\r\n @if ((elementIds())?.length) {\r\n <mat-form-field>\r\n <mat-label>element ID</mat-label>\r\n <mat-select [formControl]=\"elementId\">\r\n @for (id of elementIds(); track id) {\r\n <mat-option [value]=\"id\">{{ id }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <!-- note -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>note</mat-label>\r\n <textarea rows=\"2\" matInput [formControl]=\"note\"></textarea>\r\n @if ($any(note).errors?.maxLength && (note.dirty || note.touched)) {\r\n <mat-error>note too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n @if ($any(vars).errors?.required && (vars.dirty || vars.touched)) {\r\n <mat-error>vars required</mat-error>\r\n } @if ($any(vars).errors?.json && (vars.dirty || vars.touched)) {\r\n <mat-error>invalid JSON</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- vars2 only when type is fromTo -->\r\n @if (type.value === 'fromTo') {\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>2nd vars</mat-label>\r\n <textarea matInput [formControl]=\"vars2\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n @if ($any(vars2).errors?.required && (vars2.dirty || vars2.touched)) {\r\n <mat-error>vars required</mat-error>\r\n } @if ($any(vars2).errors?.json && (vars2.dirty || vars2.touched)) {\r\n <mat-error>invalid JSON</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n }\r\n\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close tween\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-icon-button\r\n matTooltip=\"Save tween\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}\n"] }]
198
- }], ctorParameters: () => [{ type: i1.FormBuilder }] });
199
+ }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { tween: [{ type: i0.Input, args: [{ isSignal: true, alias: "tween", required: false }] }, { type: i0.Output, args: ["tweenChange"] }], elementIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementIds", required: false }] }], tweenCancel: [{ type: i0.Output, args: ["tweenCancel"] }] } });
199
200
 
200
201
  /**
201
202
  * 🔑 `gve-animation-timeline`
@@ -351,10 +352,10 @@ class AnimationTimelineComponent {
351
352
  this.timelineChange.emit(this.timeline());
352
353
  this.form.markAsPristine();
353
354
  }
354
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AnimationTimelineComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
355
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: AnimationTimelineComponent, isStandalone: true, selector: "gve-animation-timeline", inputs: { timeline: { classPropertyName: "timeline", publicName: "timeline", isSignal: true, isRequired: false, transformFunction: null }, elementIds: { classPropertyName: "elementIds", publicName: "elementIds", isSignal: true, isRequired: false, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timeline: "timelineChange", timelineChange: "timelineChange", timelineCancel: "timelineCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- tag (bound) -->\r\n @if (tags().length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(tag).errors?.required && (tag.dirty || tag.touched)) {\r\n <mat-error>tag required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- tag (free) -->\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <input matInput [formControl]=\"tag\" />\r\n @if ($any(tag).errors?.required && (tag.dirty || tag.touched)) {\r\n <mat-error>tag required</mat-error>\r\n } @if ($any(tag).errors?.maxLength && (tag.dirty || tag.touched)) {\r\n <mat-error>tag too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <button\r\n mat-flat-button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addTween()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> tween\r\n </button>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n @if ($any(vars).errors?.json && (vars.dirty || vars.touched)) {\r\n <mat-error>invalid JSON</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- tweens -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>label</th>\r\n <th>type</th>\r\n <th>selector</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of tweens.value; track t; let index = $index) {\r\n <tr [class.selected]=\"index === editedTweenIndex()\">\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"primary\"\r\n (click)=\"editTween(index)\"\r\n matTooltip=\"Edit tween\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"warn\"\r\n (click)=\"deleteTween(index)\"\r\n matTooltip=\"Delete tween\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n <!-- up -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenUp(index)\"\r\n matTooltip=\"Move tween up\"\r\n [disabled]=\"index === 0\"\r\n >\r\n <mat-icon>arrow_circle_up</mat-icon>\r\n </button>\r\n <!-- down -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenDown(index)\"\r\n matTooltip=\"Move tween down\"\r\n [disabled]=\"index === tweens.value.length - 1\"\r\n >\r\n <mat-icon>arrow_circle_down</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n {{ t.label }}\r\n </td>\r\n <td>\r\n {{ t.type }}\r\n </td>\r\n <td>\r\n {{ t.selector }}\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- tween editor -->\r\n @if (editedTween()) {\r\n <mat-expansion-panel [expanded]=\"editedTween()\" [disabled]=\"!editedTween()\">\r\n @if (editedTween()) {\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>tween #{{ editedTweenIndex() + 1 }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n }\r\n <fieldset>\r\n <gve-animation-tween\r\n [elementIds]=\"elementIds()\"\r\n [tween]=\"editedTween()\"\r\n (tweenChange)=\"saveTween($event)\"\r\n (tweenCancel)=\"closeTween()\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n }\r\n\r\n <!-- buttons -->\r\n <div class=\"button-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close timeline\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save timeline\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: AnimationTweenComponent, selector: "gve-animation-tween", inputs: ["tween", "elementIds"], outputs: ["tweenChange", "tweenCancel"] }] }); }
355
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: AnimationTimelineComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
356
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: AnimationTimelineComponent, isStandalone: true, selector: "gve-animation-timeline", inputs: { timeline: { classPropertyName: "timeline", publicName: "timeline", isSignal: true, isRequired: false, transformFunction: null }, elementIds: { classPropertyName: "elementIds", publicName: "elementIds", isSignal: true, isRequired: false, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timeline: "timelineChange", timelineChange: "timelineChange", timelineCancel: "timelineCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- tag (bound) -->\r\n @if (tags().length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(tag).errors?.required && (tag.dirty || tag.touched)) {\r\n <mat-error>tag required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- tag (free) -->\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <input matInput [formControl]=\"tag\" />\r\n @if ($any(tag).errors?.required && (tag.dirty || tag.touched)) {\r\n <mat-error>tag required</mat-error>\r\n } @if ($any(tag).errors?.maxLength && (tag.dirty || tag.touched)) {\r\n <mat-error>tag too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <button\r\n mat-flat-button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addTween()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> tween\r\n </button>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n @if ($any(vars).errors?.json && (vars.dirty || vars.touched)) {\r\n <mat-error>invalid JSON</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- tweens -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>label</th>\r\n <th>type</th>\r\n <th>selector</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of tweens.value; track t; let index = $index) {\r\n <tr [class.selected]=\"index === editedTweenIndex()\">\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"primary\"\r\n (click)=\"editTween(index)\"\r\n matTooltip=\"Edit tween\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"warn\"\r\n (click)=\"deleteTween(index)\"\r\n matTooltip=\"Delete tween\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n <!-- up -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenUp(index)\"\r\n matTooltip=\"Move tween up\"\r\n [disabled]=\"index === 0\"\r\n >\r\n <mat-icon>arrow_circle_up</mat-icon>\r\n </button>\r\n <!-- down -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenDown(index)\"\r\n matTooltip=\"Move tween down\"\r\n [disabled]=\"index === tweens.value.length - 1\"\r\n >\r\n <mat-icon>arrow_circle_down</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n {{ t.label }}\r\n </td>\r\n <td>\r\n {{ t.type }}\r\n </td>\r\n <td>\r\n {{ t.selector }}\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- tween editor -->\r\n @if (editedTween()) {\r\n <mat-expansion-panel [expanded]=\"editedTween()\" [disabled]=\"!editedTween()\">\r\n @if (editedTween()) {\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>tween #{{ editedTweenIndex() + 1 }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n }\r\n <fieldset>\r\n <gve-animation-tween\r\n [elementIds]=\"elementIds()\"\r\n [tween]=\"editedTween()\"\r\n (tweenChange)=\"saveTween($event)\"\r\n (tweenCancel)=\"closeTween()\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n }\r\n\r\n <!-- buttons -->\r\n <div class=\"button-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close timeline\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save timeline\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: AnimationTweenComponent, selector: "gve-animation-tween", inputs: ["tween", "elementIds"], outputs: ["tweenChange", "tweenCancel"] }] }); }
356
357
  }
357
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AnimationTimelineComponent, decorators: [{
358
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: AnimationTimelineComponent, decorators: [{
358
359
  type: Component,
359
360
  args: [{ selector: 'gve-animation-timeline', imports: [
360
361
  ReactiveFormsModule,
@@ -369,7 +370,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
369
370
  MatTooltipModule,
370
371
  AnimationTweenComponent
371
372
  ], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- tag (bound) -->\r\n @if (tags().length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(tag).errors?.required && (tag.dirty || tag.touched)) {\r\n <mat-error>tag required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- tag (free) -->\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <input matInput [formControl]=\"tag\" />\r\n @if ($any(tag).errors?.required && (tag.dirty || tag.touched)) {\r\n <mat-error>tag required</mat-error>\r\n } @if ($any(tag).errors?.maxLength && (tag.dirty || tag.touched)) {\r\n <mat-error>tag too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <button\r\n mat-flat-button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addTween()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> tween\r\n </button>\r\n </div>\r\n\r\n <!-- vars -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>vars</mat-label>\r\n <textarea matInput [formControl]=\"vars\"></textarea>\r\n <mat-hint>JSON object</mat-hint>\r\n @if ($any(vars).errors?.json && (vars.dirty || vars.touched)) {\r\n <mat-error>invalid JSON</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- tweens -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>label</th>\r\n <th>type</th>\r\n <th>selector</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of tweens.value; track t; let index = $index) {\r\n <tr [class.selected]=\"index === editedTweenIndex()\">\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"primary\"\r\n (click)=\"editTween(index)\"\r\n matTooltip=\"Edit tween\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n color=\"warn\"\r\n (click)=\"deleteTween(index)\"\r\n matTooltip=\"Delete tween\"\r\n >\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n <!-- up -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenUp(index)\"\r\n matTooltip=\"Move tween up\"\r\n [disabled]=\"index === 0\"\r\n >\r\n <mat-icon>arrow_circle_up</mat-icon>\r\n </button>\r\n <!-- down -->\r\n <button\r\n mat-icon-button\r\n type=\"button\"\r\n (click)=\"moveTweenDown(index)\"\r\n matTooltip=\"Move tween down\"\r\n [disabled]=\"index === tweens.value.length - 1\"\r\n >\r\n <mat-icon>arrow_circle_down</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n {{ t.label }}\r\n </td>\r\n <td>\r\n {{ t.type }}\r\n </td>\r\n <td>\r\n {{ t.selector }}\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- tween editor -->\r\n @if (editedTween()) {\r\n <mat-expansion-panel [expanded]=\"editedTween()\" [disabled]=\"!editedTween()\">\r\n @if (editedTween()) {\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>tween #{{ editedTweenIndex() + 1 }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n }\r\n <fieldset>\r\n <gve-animation-tween\r\n [elementIds]=\"elementIds()\"\r\n [tween]=\"editedTween()\"\r\n (tweenChange)=\"saveTween($event)\"\r\n (tweenCancel)=\"closeTween()\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n }\r\n\r\n <!-- buttons -->\r\n <div class=\"button-row\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-icon-button\r\n matTooltip=\"Close timeline\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save timeline\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}\n"] }]
372
- }], ctorParameters: () => [{ type: i1.FormBuilder }] });
373
+ }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { timeline: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeline", required: false }] }, { type: i0.Output, args: ["timelineChange"] }], elementIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementIds", required: false }] }], tags: [{ type: i0.Input, args: [{ isSignal: true, alias: "tags", required: false }] }], timelineChange: [{ type: i0.Output, args: ["timelineChange"] }], timelineCancel: [{ type: i0.Output, args: ["timelineCancel"] }] } });
373
374
 
374
375
  /**
375
376
  * 🔑 `gve-animation-timeline-set`
@@ -445,10 +446,10 @@ class AnimationTimelineSetComponent {
445
446
  }
446
447
  });
447
448
  }
448
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AnimationTimelineSetComponent, deps: [{ token: i4$2.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
449
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: AnimationTimelineSetComponent, isStandalone: true, selector: "gve-animation-timeline-set", inputs: { timelines: { classPropertyName: "timelines", publicName: "timelines", isSignal: true, isRequired: false, transformFunction: null }, elementIds: { classPropertyName: "elementIds", publicName: "elementIds", isSignal: true, isRequired: false, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timelines: "timelinesChange", timelinesCancel: "timelinesCancel" }, ngImport: i0, template: "<div>\r\n <!-- add -->\r\n <div>\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n (click)=\"newTimeline()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n\r\n <!-- table -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>tag</th>\r\n <th>tweens</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of timelines(); track t.tag; let index = $index) {\r\n <tr [class.selected]=\"editedIndex() === index\">\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button type=\"button\" mat-icon-button (click)=\"editTimeline(index)\">\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button type=\"button\" mat-icon-button (click)=\"deleteTimeline(index)\">\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ t.tag }}</td>\r\n <td>{{ t.tweens.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- editor -->\r\n @if (editedTimeline()) {\r\n <mat-expansion-panel [disabled]=\"!editedTimeline\" [expanded]=\"editedTimeline\">\r\n <mat-expansion-panel-header>\r\n timeline {{ editedTimeline()?.tag }}\r\n </mat-expansion-panel-header>\r\n <gve-animation-timeline\r\n [elementIds]=\"elementIds()\"\r\n [tags]=\"tags()\"\r\n [timeline]=\"editedTimeline()\"\r\n (timelineChange)=\"onTimelineChange($event!)\"\r\n (timelineCancel)=\"closeTimeline()\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "component", type: AnimationTimelineComponent, selector: "gve-animation-timeline", inputs: ["timeline", "elementIds", "tags"], outputs: ["timelineChange", "timelineCancel"] }] }); }
449
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: AnimationTimelineSetComponent, deps: [{ token: i4$1.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
450
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: AnimationTimelineSetComponent, isStandalone: true, selector: "gve-animation-timeline-set", inputs: { timelines: { classPropertyName: "timelines", publicName: "timelines", isSignal: true, isRequired: false, transformFunction: null }, elementIds: { classPropertyName: "elementIds", publicName: "elementIds", isSignal: true, isRequired: false, transformFunction: null }, tags: { classPropertyName: "tags", publicName: "tags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { timelines: "timelinesChange", timelinesCancel: "timelinesCancel" }, ngImport: i0, template: "<div>\r\n <!-- add -->\r\n <div>\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n (click)=\"newTimeline()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n\r\n <!-- table -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>tag</th>\r\n <th>tweens</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of timelines(); track t.tag; let index = $index) {\r\n <tr [class.selected]=\"editedIndex() === index\">\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button type=\"button\" mat-icon-button (click)=\"editTimeline(index)\">\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button type=\"button\" mat-icon-button (click)=\"deleteTimeline(index)\">\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ t.tag }}</td>\r\n <td>{{ t.tweens.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- editor -->\r\n @if (editedTimeline()) {\r\n <mat-expansion-panel [disabled]=\"!editedTimeline\" [expanded]=\"editedTimeline\">\r\n <mat-expansion-panel-header>\r\n timeline {{ editedTimeline()?.tag }}\r\n </mat-expansion-panel-header>\r\n <gve-animation-timeline\r\n [elementIds]=\"elementIds()\"\r\n [tags]=\"tags()\"\r\n [timeline]=\"editedTimeline()\"\r\n (timelineChange)=\"onTimelineChange($event!)\"\r\n (timelineCancel)=\"closeTimeline()\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "component", type: AnimationTimelineComponent, selector: "gve-animation-timeline", inputs: ["timeline", "elementIds", "tags"], outputs: ["timelineChange", "timelineCancel"] }] }); }
450
451
  }
451
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AnimationTimelineSetComponent, decorators: [{
452
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: AnimationTimelineSetComponent, decorators: [{
452
453
  type: Component,
453
454
  args: [{ selector: 'gve-animation-timeline-set', imports: [
454
455
  ReactiveFormsModule,
@@ -463,7 +464,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
463
464
  MatTooltipModule,
464
465
  AnimationTimelineComponent,
465
466
  ], template: "<div>\r\n <!-- add -->\r\n <div>\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n (click)=\"newTimeline()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n timeline\r\n </button>\r\n </div>\r\n\r\n <!-- table -->\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>tag</th>\r\n <th>tweens</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (t of timelines(); track t.tag; let index = $index) {\r\n <tr [class.selected]=\"editedIndex() === index\">\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button type=\"button\" mat-icon-button (click)=\"editTimeline(index)\">\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button type=\"button\" mat-icon-button (click)=\"deleteTimeline(index)\">\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ t.tag }}</td>\r\n <td>{{ t.tweens.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n\r\n <!-- editor -->\r\n @if (editedTimeline()) {\r\n <mat-expansion-panel [disabled]=\"!editedTimeline\" [expanded]=\"editedTimeline\">\r\n <mat-expansion-panel-header>\r\n timeline {{ editedTimeline()?.tag }}\r\n </mat-expansion-panel-header>\r\n <gve-animation-timeline\r\n [elementIds]=\"elementIds()\"\r\n [tags]=\"tags()\"\r\n [timeline]=\"editedTimeline()\"\r\n (timelineChange)=\"onTimelineChange($event!)\"\r\n (timelineCancel)=\"closeTimeline()\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}\n"] }]
466
- }], ctorParameters: () => [{ type: i4$2.DialogService }] });
467
+ }], ctorParameters: () => [{ type: i4$1.DialogService }], propDecorators: { timelines: [{ type: i0.Input, args: [{ isSignal: true, alias: "timelines", required: false }] }, { type: i0.Output, args: ["timelinesChange"] }], elementIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementIds", required: false }] }], tags: [{ type: i0.Input, args: [{ isSignal: true, alias: "tags", required: false }] }], timelinesCancel: [{ type: i0.Output, args: ["timelinesCancel"] }] } });
467
468
 
468
469
  /**
469
470
  * 🔑 `gve-base-text-char`
@@ -488,13 +489,13 @@ class BaseTextCharComponent {
488
489
  onCharClick(event) {
489
490
  this.charPick.emit({ char: this.char(), event });
490
491
  }
491
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: BaseTextCharComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
492
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: BaseTextCharComponent, isStandalone: true, selector: "gve-base-text-char", inputs: { char: { classPropertyName: "char", publicName: "char", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { charPick: "charPick" }, ngImport: i0, template: "@if (char()) {\r\n<div matRipple id=\"container\" (click)=\"onCharClick($event)\">\r\n <div\r\n id=\"c-label\"\r\n [style.fontSize]=\"char()!.emSize + 'em'\"\r\n [style.borderColor]=\"char()!.borderColor\"\r\n >\r\n {{ char()!.label }}\r\n </div>\r\n <div\r\n id=\"c-id\"\r\n [style.fontSize]=\"char()!.emSize / 2 + 'em'\"\r\n [style.color]=\"char()!.color | colorToContrast\"\r\n [style.borderColor]=\"char()!.borderColor\"\r\n [style.backgroundColor]=\"char()!.color\"\r\n >\r\n {{ char()!.id }}\r\n </div>\r\n</div>\r\n}\r\n", styles: ["div#container{cursor:pointer;flex-direction:column;align-items:center}div#c-label{border:1px solid silver;border-radius:6px;padding:6px;height:1.5em;align-items:center;justify-content:center;text-align:center}div#c-id{margin-top:4px;margin-bottom:16px;border:1px solid silver;border-radius:6px;padding:6px;align-items:center;justify-content:center;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i1$1.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "pipe", type: ColorToContrastPipe, name: "colorToContrast" }] }); }
492
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: BaseTextCharComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
493
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: BaseTextCharComponent, isStandalone: true, selector: "gve-base-text-char", inputs: { char: { classPropertyName: "char", publicName: "char", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { charPick: "charPick" }, ngImport: i0, template: "@if (char()) {\r\n<div matRipple id=\"container\" (click)=\"onCharClick($event)\">\r\n <div\r\n id=\"c-label\"\r\n [style.fontSize]=\"char()!.emSize + 'em'\"\r\n [style.borderColor]=\"char()!.borderColor\"\r\n >\r\n {{ char()!.label }}\r\n </div>\r\n <div\r\n id=\"c-id\"\r\n [style.fontSize]=\"char()!.emSize / 2 + 'em'\"\r\n [style.color]=\"char()!.color | colorToContrast\"\r\n [style.borderColor]=\"char()!.borderColor\"\r\n [style.backgroundColor]=\"char()!.color\"\r\n >\r\n {{ char()!.id }}\r\n </div>\r\n</div>\r\n}\r\n", styles: ["div#container{cursor:pointer;flex-direction:column;align-items:center}div#c-label{border:1px solid silver;border-radius:6px;padding:6px;height:1.5em;align-items:center;justify-content:center;text-align:center}div#c-id{margin-top:4px;margin-bottom:16px;border:1px solid silver;border-radius:6px;padding:6px;align-items:center;justify-content:center;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i1$1.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "pipe", type: ColorToContrastPipe, name: "colorToContrast" }] }); }
493
494
  }
494
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: BaseTextCharComponent, decorators: [{
495
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: BaseTextCharComponent, decorators: [{
495
496
  type: Component,
496
497
  args: [{ selector: 'gve-base-text-char', imports: [MatRippleModule, ColorToContrastPipe], template: "@if (char()) {\r\n<div matRipple id=\"container\" (click)=\"onCharClick($event)\">\r\n <div\r\n id=\"c-label\"\r\n [style.fontSize]=\"char()!.emSize + 'em'\"\r\n [style.borderColor]=\"char()!.borderColor\"\r\n >\r\n {{ char()!.label }}\r\n </div>\r\n <div\r\n id=\"c-id\"\r\n [style.fontSize]=\"char()!.emSize / 2 + 'em'\"\r\n [style.color]=\"char()!.color | colorToContrast\"\r\n [style.borderColor]=\"char()!.borderColor\"\r\n [style.backgroundColor]=\"char()!.color\"\r\n >\r\n {{ char()!.id }}\r\n </div>\r\n</div>\r\n}\r\n", styles: ["div#container{cursor:pointer;flex-direction:column;align-items:center}div#c-label{border:1px solid silver;border-radius:6px;padding:6px;height:1.5em;align-items:center;justify-content:center;text-align:center}div#c-id{margin-top:4px;margin-bottom:16px;border:1px solid silver;border-radius:6px;padding:6px;align-items:center;justify-content:center;text-align:center}\n"] }]
497
- }] });
498
+ }], propDecorators: { char: [{ type: i0.Input, args: [{ isSignal: true, alias: "char", required: false }] }], charPick: [{ type: i0.Output, args: ["charPick"] }] } });
498
499
 
499
500
  /**
500
501
  * 🔑 `gve-base-text-view`
@@ -558,21 +559,51 @@ class BaseTextViewComponent {
558
559
  * The range is inclusive.
559
560
  */
560
561
  this.rangePick = output();
561
- this.lines = signal([], ...(ngDevMode ? [{ debugName: "lines" }] : []));
562
- // build lines when text or border color callback change
563
- effect(() => {
564
- this.buildLines(this.text(), this.colorCallback(), this.borderColorCallback());
565
- });
566
- // reset _lastSelectedChar when text changes
562
+ /**
563
+ * The base characters organized by lines, computed from text input.
564
+ */
565
+ this.baseLines = computed(() => {
566
+ return this.buildLines(this.text(), this.colorCallback(), this.borderColorCallback());
567
+ }, ...(ngDevMode ? [{ debugName: "baseLines" }] : []));
568
+ /**
569
+ * Signal for tracking selected range state.
570
+ */
571
+ this.selectedRange = signal(null, ...(ngDevMode ? [{ debugName: "selectedRange" }] : []));
572
+ /**
573
+ * The lines with selection state applied, computed from base lines and selection.
574
+ */
575
+ this.lines = computed(() => {
576
+ const base = this.baseLines();
577
+ const range = this.selectedRange();
578
+ if (!range) {
579
+ return base;
580
+ }
581
+ const { start, end } = range;
582
+ let position = 0;
583
+ return base.map((line) => line.map((char) => {
584
+ const currentPos = position++;
585
+ if (currentPos >= start && currentPos <= end) {
586
+ return {
587
+ ...char,
588
+ oldColor: char.oldColor || char.color,
589
+ oldBorderColor: char.oldBorderColor || char.borderColor,
590
+ color: this.selectionColor(),
591
+ borderColor: this.selectionColor(),
592
+ };
593
+ }
594
+ return char;
595
+ }));
596
+ }, ...(ngDevMode ? [{ debugName: "lines" }] : []));
597
+ // reset selection when text changes
567
598
  effect(() => {
568
599
  this.text();
569
- this._lastSelectedChar = undefined;
600
+ this._lastSelectedPosition = undefined;
601
+ this.selectedRange.set(null);
570
602
  });
571
603
  }
572
604
  buildLines(text, colorCallback, borderColorCallback) {
573
- if (!text) {
574
- this.lines.set([]);
575
- return;
605
+ if (!text || text.length === 0) {
606
+ return [];
576
607
  }
577
608
  const newLines = [];
578
609
  newLines.push([]);
@@ -597,88 +628,85 @@ class BaseTextViewComponent {
597
628
  newLines.push([]);
598
629
  }
599
630
  }
600
- this.lines.set(newLines);
601
- }
602
- resetColors() {
603
- if (!this.lines())
604
- return;
605
- const newLines = this.lines().map((line) => line.map((c) => {
606
- if (!c)
607
- return c;
608
- return {
609
- ...c,
610
- color: c.oldColor || this.defaultColor(),
611
- borderColor: c.oldBorderColor || this.defaultBorderColor(),
612
- };
613
- }));
614
- this.lines.set(newLines);
631
+ return newLines;
615
632
  }
616
633
  onCharPick(event) {
617
634
  // guard against null/undefined event.char
618
635
  if (!event?.char) {
619
636
  return;
620
637
  }
621
- // first reset all colors before updating
622
- this.resetColors();
638
+ // find the position of the clicked character in the flat array
639
+ const clickedPosition = this.findCharPosition(event.char.id);
640
+ if (clickedPosition === -1) {
641
+ return; // character not found
642
+ }
623
643
  // calculate selection state
624
- const isRangeSelection = Boolean(this._lastSelectedChar) &&
625
- this._lastSelectedChar !== event.char &&
644
+ const isRangeSelection = this._lastSelectedPosition !== undefined &&
645
+ this._lastSelectedPosition !== clickedPosition &&
626
646
  event.event?.ctrlKey;
627
647
  let rangeToEmit;
628
- if (isRangeSelection && this._lastSelectedChar) {
629
- const minId = Math.min(this._lastSelectedChar.id, event.char.id);
630
- const maxId = Math.max(this._lastSelectedChar.id, event.char.id);
631
- // update UI to highlight range - create new objects
632
- const newLines = this.lines().map((line) => line.map((c) => {
633
- if (!c)
634
- return c;
635
- if (c.id >= minId && c.id <= maxId) {
636
- return {
637
- ...c,
638
- oldColor: c.color,
639
- oldBorderColor: c.borderColor,
640
- color: this.selectionColor(),
641
- borderColor: this.selectionColor(),
642
- };
643
- }
644
- return c;
645
- }));
646
- this.lines.set(newLines);
647
- rangeToEmit = { at: minId, run: maxId - minId + 1 };
648
+ if (isRangeSelection && this._lastSelectedPosition !== undefined) {
649
+ // range selection: use positions, not IDs
650
+ const minPos = Math.min(this._lastSelectedPosition, clickedPosition);
651
+ const maxPos = Math.max(this._lastSelectedPosition, clickedPosition);
652
+ // update selection range
653
+ this.selectedRange.set({ start: minPos, end: maxPos });
654
+ // emit range based on the first character's ID at minPos and the count
655
+ const firstCharId = this.getCharIdAtPosition(minPos);
656
+ rangeToEmit = { at: firstCharId, run: maxPos - minPos + 1 };
648
657
  }
649
658
  else {
650
- // single character selection - create new objects
651
- const newLines = this.lines().map((line) => line.map((c) => {
652
- if (!c)
653
- return c;
654
- if (c.id === event.char.id) {
655
- const updatedChar = {
656
- ...c,
657
- oldColor: c.color,
658
- oldBorderColor: c.borderColor,
659
- color: this.selectionColor(),
660
- borderColor: this.selectionColor(),
661
- };
662
- // update last selected character reference
663
- this._lastSelectedChar = updatedChar;
664
- return updatedChar;
665
- }
666
- return c;
667
- }));
668
- this.lines.set(newLines);
659
+ // single character selection
660
+ this.selectedRange.set({ start: clickedPosition, end: clickedPosition });
661
+ // update last selected character and position
662
+ this._lastSelectedPosition = clickedPosition;
669
663
  rangeToEmit = { at: event.char.id, run: 1 };
670
664
  }
671
665
  // emit events last, after all internal state is updated
672
666
  this.charPick.emit(event);
673
667
  this.rangePick.emit(rangeToEmit);
674
668
  }
675
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: BaseTextViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
676
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: BaseTextViewComponent, isStandalone: true, selector: "gve-base-text-view", inputs: { defaultColor: { classPropertyName: "defaultColor", publicName: "defaultColor", isSignal: true, isRequired: false, transformFunction: null }, defaultBorderColor: { classPropertyName: "defaultBorderColor", publicName: "defaultBorderColor", isSignal: true, isRequired: false, transformFunction: null }, selectionColor: { classPropertyName: "selectionColor", publicName: "selectionColor", isSignal: true, isRequired: false, transformFunction: null }, hasLineNumber: { classPropertyName: "hasLineNumber", publicName: "hasLineNumber", isSignal: true, isRequired: false, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, colorCallback: { classPropertyName: "colorCallback", publicName: "colorCallback", isSignal: true, isRequired: false, transformFunction: null }, borderColorCallback: { classPropertyName: "borderColorCallback", publicName: "borderColorCallback", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { charPick: "charPick", rangePick: "rangePick" }, ngImport: i0, template: "<div id=\"text\">\r\n @for (line of lines(); track $index) {\r\n <div class=\"line\">\r\n @if (hasLineNumber()) {\r\n <div class=\"nr\">\r\n {{ lines().indexOf(line) + 1 }}\r\n </div>\r\n } @for (c of line; track c.id) {\r\n <gve-base-text-char [char]=\"c\" (charPick)=\"onCharPick($event)\" />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".line{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.line *{flex:0 0 auto}.nr{font-size:.8em;font-weight:700;color:silver;margin:0 4px}\n"], dependencies: [{ kind: "component", type: BaseTextCharComponent, selector: "gve-base-text-char", inputs: ["char"], outputs: ["charPick"] }] }); }
669
+ /**
670
+ * Find the position (index in flat array) of a character by its ID.
671
+ * Returns -1 if not found.
672
+ */
673
+ findCharPosition(charId) {
674
+ const baseLines = this.baseLines();
675
+ let position = 0;
676
+ for (const line of baseLines) {
677
+ for (const char of line) {
678
+ if (char.id === charId) {
679
+ return position;
680
+ }
681
+ position++;
682
+ }
683
+ }
684
+ return -1;
685
+ }
686
+ /**
687
+ * Get the character ID at a specific position in the flat array.
688
+ * Returns the ID or -1 if position is out of bounds.
689
+ */
690
+ getCharIdAtPosition(position) {
691
+ const baseLines = this.baseLines();
692
+ let currentPos = 0;
693
+ for (const line of baseLines) {
694
+ for (const char of line) {
695
+ if (currentPos === position) {
696
+ return char.id;
697
+ }
698
+ currentPos++;
699
+ }
700
+ }
701
+ return -1;
702
+ }
703
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: BaseTextViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
704
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: BaseTextViewComponent, isStandalone: true, selector: "gve-base-text-view", inputs: { defaultColor: { classPropertyName: "defaultColor", publicName: "defaultColor", isSignal: true, isRequired: false, transformFunction: null }, defaultBorderColor: { classPropertyName: "defaultBorderColor", publicName: "defaultBorderColor", isSignal: true, isRequired: false, transformFunction: null }, selectionColor: { classPropertyName: "selectionColor", publicName: "selectionColor", isSignal: true, isRequired: false, transformFunction: null }, hasLineNumber: { classPropertyName: "hasLineNumber", publicName: "hasLineNumber", isSignal: true, isRequired: false, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, colorCallback: { classPropertyName: "colorCallback", publicName: "colorCallback", isSignal: true, isRequired: false, transformFunction: null }, borderColorCallback: { classPropertyName: "borderColorCallback", publicName: "borderColorCallback", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { charPick: "charPick", rangePick: "rangePick" }, ngImport: i0, template: "<div id=\"text\">\r\n @for (line of lines(); track $index) {\r\n <div class=\"line\">\r\n @if (hasLineNumber()) {\r\n <div class=\"nr\">\r\n {{ lines().indexOf(line) + 1 }}\r\n </div>\r\n } @for (c of line; track c.id) {\r\n <gve-base-text-char [char]=\"c\" (charPick)=\"onCharPick($event)\" />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".line{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.line *{flex:0 0 auto}.nr{font-size:.8em;font-weight:700;color:silver;margin:0 4px}\n"], dependencies: [{ kind: "component", type: BaseTextCharComponent, selector: "gve-base-text-char", inputs: ["char"], outputs: ["charPick"] }] }); }
677
705
  }
678
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: BaseTextViewComponent, decorators: [{
706
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: BaseTextViewComponent, decorators: [{
679
707
  type: Component,
680
708
  args: [{ selector: 'gve-base-text-view', imports: [BaseTextCharComponent], template: "<div id=\"text\">\r\n @for (line of lines(); track $index) {\r\n <div class=\"line\">\r\n @if (hasLineNumber()) {\r\n <div class=\"nr\">\r\n {{ lines().indexOf(line) + 1 }}\r\n </div>\r\n } @for (c of line; track c.id) {\r\n <gve-base-text-char [char]=\"c\" (charPick)=\"onCharPick($event)\" />\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [".line{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.line *{flex:0 0 auto}.nr{font-size:.8em;font-weight:700;color:silver;margin:0 4px}\n"] }]
681
- }], ctorParameters: () => [] });
709
+ }], ctorParameters: () => [], propDecorators: { defaultColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultColor", required: false }] }], defaultBorderColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultBorderColor", required: false }] }], selectionColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionColor", required: false }] }], hasLineNumber: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasLineNumber", required: false }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], colorCallback: [{ type: i0.Input, args: [{ isSignal: true, alias: "colorCallback", required: false }] }], borderColorCallback: [{ type: i0.Input, args: [{ isSignal: true, alias: "borderColorCallback", required: false }] }], charPick: [{ type: i0.Output, args: ["charPick"] }], rangePick: [{ type: i0.Output, args: ["rangePick"] }] } });
682
710
 
683
711
  /**
684
712
  * 🔑 `gve-feature-editor`
@@ -815,10 +843,10 @@ class FeatureEditorComponent {
815
843
  setPolicy: this.setPolicy.value,
816
844
  });
817
845
  }
818
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: FeatureEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
819
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: FeatureEditorComponent, isStandalone: true, selector: "gve-feature-editor", inputs: { featNames: { classPropertyName: "featNames", publicName: "featNames", isSignal: true, isRequired: false, transformFunction: null }, featValues: { classPropertyName: "featValues", publicName: "featValues", isSignal: true, isRequired: false, transformFunction: null }, feature: { classPropertyName: "feature", publicName: "feature", isSignal: true, isRequired: false, transformFunction: null }, isVar: { classPropertyName: "isVar", publicName: "isVar", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { feature: "featureChange", featureCancel: "featureCancel" }, viewQueries: [{ propertyName: "nameControl", first: true, predicate: ["nameCtl"], descendants: true }], ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n @if (featNames()?.length) {\r\n <!-- name (bound) -->\r\n <mat-form-field>\r\n <mat-label>name</mat-label>\r\n <mat-select #nameCtl [formControl]=\"name\">\r\n @for (i of featNames(); track i) {\r\n <mat-option [value]=\"i.id\">{{ i.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(name).errors?.required && (name.dirty || name.touched)) {\r\n <mat-error>name required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- name (free) -->\r\n <mat-form-field>\r\n <mat-label>name</mat-label>\r\n <input #nameCtl matInput [formControl]=\"name\" />\r\n @if ($any(name).errors?.required && (name.dirty || name.touched)) {\r\n <mat-error>name required</mat-error>\r\n } @if ($any(name).errors?.maxLength && (name.dirty || name.touched)) {\r\n <mat-error>name too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- value (bound) -->\r\n @if (nameIds()?.length) {\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <mat-select [formControl]=\"value\">\r\n @for (e of nameIds(); track e) {\r\n <mat-option [value]=\"e.id\">{{ e.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(value).errors?.required && (value.dirty || value.touched)) {\r\n <mat-error>value required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- value (free) -->\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n @if ($any(value).errors?.required && (value.dirty || value.touched)) {\r\n <mat-error>value required</mat-error>\r\n } @if ($any(value).errors?.maxLength && (value.dirty || value.touched)) {\r\n <mat-error>value too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- setPolicy -->\r\n <mat-form-field>\r\n <mat-label>set policy</mat-label>\r\n <mat-select [formControl]=\"setPolicy\">\r\n <mat-option [value]=\"0\">multiple</mat-option>\r\n <mat-option [value]=\"1\">single</mat-option>\r\n <mat-option [value]=\"2\">single first</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n </div>\r\n\r\n @if (isVar()) {\r\n <div class=\"form-row\">\r\n <!-- isNegated -->\r\n <mat-checkbox [formControl]=\"isNegated\">negated</mat-checkbox>\r\n\r\n <!-- isShortLived -->\r\n <mat-checkbox [formControl]=\"isShortLived\">short-lived</mat-checkbox>\r\n\r\n <!-- isGlobal -->\r\n <mat-checkbox [formControl]=\"isGlobal\">global</mat-checkbox>\r\n </div>\r\n }\r\n\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Accept changes\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
846
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: FeatureEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
847
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: FeatureEditorComponent, isStandalone: true, selector: "gve-feature-editor", inputs: { featNames: { classPropertyName: "featNames", publicName: "featNames", isSignal: true, isRequired: false, transformFunction: null }, featValues: { classPropertyName: "featValues", publicName: "featValues", isSignal: true, isRequired: false, transformFunction: null }, feature: { classPropertyName: "feature", publicName: "feature", isSignal: true, isRequired: false, transformFunction: null }, isVar: { classPropertyName: "isVar", publicName: "isVar", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { feature: "featureChange", featureCancel: "featureCancel" }, viewQueries: [{ propertyName: "nameControl", first: true, predicate: ["nameCtl"], descendants: true }], ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n @if (featNames()?.length) {\r\n <!-- name (bound) -->\r\n <mat-form-field>\r\n <mat-label>name</mat-label>\r\n <mat-select #nameCtl [formControl]=\"name\">\r\n @for (i of featNames(); track i) {\r\n <mat-option [value]=\"i.id\">{{ i.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(name).errors?.required && (name.dirty || name.touched)) {\r\n <mat-error>name required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- name (free) -->\r\n <mat-form-field>\r\n <mat-label>name</mat-label>\r\n <input #nameCtl matInput [formControl]=\"name\" />\r\n @if ($any(name).errors?.required && (name.dirty || name.touched)) {\r\n <mat-error>name required</mat-error>\r\n } @if ($any(name).errors?.maxLength && (name.dirty || name.touched)) {\r\n <mat-error>name too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- value (bound) -->\r\n @if (nameIds()?.length) {\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <mat-select [formControl]=\"value\">\r\n @for (e of nameIds(); track e) {\r\n <mat-option [value]=\"e.id\">{{ e.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(value).errors?.required && (value.dirty || value.touched)) {\r\n <mat-error>value required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- value (free) -->\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n @if ($any(value).errors?.required && (value.dirty || value.touched)) {\r\n <mat-error>value required</mat-error>\r\n } @if ($any(value).errors?.maxLength && (value.dirty || value.touched)) {\r\n <mat-error>value too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- setPolicy -->\r\n <mat-form-field>\r\n <mat-label>set policy</mat-label>\r\n <mat-select [formControl]=\"setPolicy\">\r\n <mat-option [value]=\"0\">multiple</mat-option>\r\n <mat-option [value]=\"1\">single</mat-option>\r\n <mat-option [value]=\"2\">single first</mat-option>\r\n </mat-select>\r\n </mat-form-field>\r\n </div>\r\n\r\n @if (isVar()) {\r\n <div class=\"form-row\">\r\n <!-- isNegated -->\r\n <mat-checkbox [formControl]=\"isNegated\">negated</mat-checkbox>\r\n\r\n <!-- isShortLived -->\r\n <mat-checkbox [formControl]=\"isShortLived\">short-lived</mat-checkbox>\r\n\r\n <!-- isGlobal -->\r\n <mat-checkbox [formControl]=\"isGlobal\">global</mat-checkbox>\r\n </div>\r\n }\r\n\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Accept changes\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$2.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
820
848
  }
821
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: FeatureEditorComponent, decorators: [{
849
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: FeatureEditorComponent, decorators: [{
822
850
  type: Component,
823
851
  args: [{ selector: 'gve-feature-editor', imports: [
824
852
  ReactiveFormsModule,
@@ -833,7 +861,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
833
861
  }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { nameControl: [{
834
862
  type: ViewChild,
835
863
  args: ['nameCtl']
836
- }] } });
864
+ }], featNames: [{ type: i0.Input, args: [{ isSignal: true, alias: "featNames", required: false }] }], featValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "featValues", required: false }] }], feature: [{ type: i0.Input, args: [{ isSignal: true, alias: "feature", required: false }] }, { type: i0.Output, args: ["featureChange"] }], isVar: [{ type: i0.Input, args: [{ isSignal: true, alias: "isVar", required: false }] }], featureCancel: [{ type: i0.Output, args: ["featureCancel"] }] } });
837
865
 
838
866
  /**
839
867
  * 🔑 `gve-feature-set-editor`
@@ -864,7 +892,7 @@ class FeatureSetEditorComponent {
864
892
  * True if the feature is a variant operation feature, which has
865
893
  * additional properties like negation, global, and short-lived.
866
894
  */
867
- this.isVar = input(false, ...(ngDevMode ? [{ debugName: "isVar" }] : []));
895
+ this.isVar = input(true, ...(ngDevMode ? [{ debugName: "isVar" }] : []));
868
896
  /**
869
897
  * The list of feature names to display in the name selection.
870
898
  * This is used when you have a closed list of features.
@@ -959,10 +987,10 @@ class FeatureSetEditorComponent {
959
987
  this.editedFeature.set(undefined);
960
988
  this.editedFeatureIndex.set(-1);
961
989
  }
962
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: FeatureSetEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
963
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: FeatureSetEditorComponent, isStandalone: true, selector: "gve-feature-set-editor", inputs: { isVar: { classPropertyName: "isVar", publicName: "isVar", isSignal: true, isRequired: false, transformFunction: null }, featNames: { classPropertyName: "featNames", publicName: "featNames", isSignal: true, isRequired: false, transformFunction: null }, featValues: { classPropertyName: "featValues", publicName: "featValues", isSignal: true, isRequired: false, transformFunction: null }, filterThreshold: { classPropertyName: "filterThreshold", publicName: "filterThreshold", isSignal: true, isRequired: false, transformFunction: null }, features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { features: "featuresChange" }, ngImport: i0, template: "<div>\r\n <div class=\"form-row\">\r\n <!-- filter -->\r\n @if (filterThreshold() === 0 || features().length >= filterThreshold()) {\r\n <mat-form-field>\r\n <input matInput placeholder=\"filter\" [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- add feature button -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Add a new feature\"\r\n (click)=\"addFeature()\"\r\n [class.in-row-button]=\"\r\n filterThreshold() === 0 || features()!.length >= filterThreshold()\r\n \"\r\n >\r\n <mat-icon>add</mat-icon>\r\n feature\r\n </button>\r\n </div>\r\n\r\n <!-- list -->\r\n @if (features().length) {\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>feature</th>\r\n <th>value</th>\r\n <th>policy</th>\r\n @if (isVar()) {\r\n <th>negated</th>\r\n <th>global</th>\r\n <th>short-lived</th>\r\n }\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (feature of filteredFeatures(); track feature; let i = $index) {\r\n <tr [class.selected]=\"i === editedFeatureIndex()\">\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Edit this feature\"\r\n (click)=\"editFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Delete this feature\"\r\n (click)=\"deleteFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n @if (featNames()?.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </td>\r\n <td>\r\n @if (featValues()) {\r\n <span>{{\r\n feature.value | flatLookup : featValues()![feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n <td>\r\n <span>{{ POLICIES[feature.setPolicy] }}</span>\r\n </td>\r\n @if (isVar()) {\r\n <td>\r\n <span>{{ $any(feature).isNegated ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n <td>\r\n <span>{{ $any(feature).isGlobal ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n <td>\r\n <span>{{ $any(feature).isShortLived ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- editor -->\r\n @if (editedFeature()) {\r\n <mat-expansion-panel\r\n [disabled]=\"!editedFeature()\"\r\n [expanded]=\"editedFeature()\"\r\n >\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n <span>{{ editedFeature()?.name || \"feature\" }}</span>\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-feature-editor\r\n [isVar]=\"isVar()\"\r\n [featNames]=\"featNames()\"\r\n [featValues]=\"featValues()\"\r\n [feature]=\"editedFeature()\"\r\n (featureChange)=\"onFeatureChange($event)\"\r\n (featureCancel)=\"onFeatureCancel()\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: FeatureEditorComponent, selector: "gve-feature-editor", inputs: ["featNames", "featValues", "feature", "isVar"], outputs: ["featureChange", "featureCancel"] }, { kind: "pipe", type: FlatLookupPipe, name: "flatLookup" }] }); }
990
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: FeatureSetEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
991
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: FeatureSetEditorComponent, isStandalone: true, selector: "gve-feature-set-editor", inputs: { isVar: { classPropertyName: "isVar", publicName: "isVar", isSignal: true, isRequired: false, transformFunction: null }, featNames: { classPropertyName: "featNames", publicName: "featNames", isSignal: true, isRequired: false, transformFunction: null }, featValues: { classPropertyName: "featValues", publicName: "featValues", isSignal: true, isRequired: false, transformFunction: null }, filterThreshold: { classPropertyName: "filterThreshold", publicName: "filterThreshold", isSignal: true, isRequired: false, transformFunction: null }, features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { features: "featuresChange" }, ngImport: i0, template: "<div>\r\n <div class=\"form-row\">\r\n <!-- filter -->\r\n @if (filterThreshold() === 0 || features().length >= filterThreshold()) {\r\n <mat-form-field>\r\n <input matInput placeholder=\"filter\" [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- add feature button -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Add a new feature\"\r\n (click)=\"addFeature()\"\r\n [class.in-row-button]=\"\r\n filterThreshold() === 0 || features()!.length >= filterThreshold()\r\n \"\r\n >\r\n <mat-icon>add</mat-icon>\r\n feature\r\n </button>\r\n </div>\r\n\r\n <!-- list -->\r\n @if (features().length) {\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>feature</th>\r\n <th>value</th>\r\n <th>policy</th>\r\n @if (isVar()) {\r\n <th>flags</th>\r\n }\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (feature of filteredFeatures(); track feature; let i = $index) {\r\n <tr [class.selected]=\"i === editedFeatureIndex()\">\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Edit this feature\"\r\n (click)=\"editFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Delete this feature\"\r\n (click)=\"deleteFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n @if (featNames()?.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </td>\r\n <td>\r\n @if (featValues()) {\r\n <span>{{\r\n feature.value | flatLookup : featValues()![feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n <td>\r\n <span>{{ POLICIES[feature.setPolicy] }}</span>\r\n </td>\r\n @if (isVar()) {\r\n <td>\r\n @if ($any(feature).isNegated) {\r\n <span class=\"icon\" matTooltip=\"Negated: remove if present\">\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </span>\r\n } @if ($any(feature).isGlobal) {\r\n <span class=\"icon\" matTooltip=\"Global: apply to whole output\">\r\n <mat-icon class=\"mat-primary\">public</mat-icon>\r\n </span>\r\n } @if ($any(feature).isShortLived) {\r\n <span class=\"icon\" matTooltip=\"Short-lived: removed on next operation\">\r\n <mat-icon class=\"mat-accent\">timer</mat-icon>\r\n </span>\r\n }\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- editor -->\r\n @if (editedFeature()) {\r\n <mat-expansion-panel\r\n [disabled]=\"!editedFeature()\"\r\n [expanded]=\"editedFeature()\"\r\n >\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n <span>{{ editedFeature()?.name || \"feature\" }}</span>\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-feature-editor\r\n [isVar]=\"isVar()\"\r\n [featNames]=\"featNames()\"\r\n [featValues]=\"featValues()\"\r\n [feature]=\"editedFeature()\"\r\n (featureChange)=\"onFeatureChange($event)\"\r\n (featureCancel)=\"onFeatureCancel()\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: FeatureEditorComponent, selector: "gve-feature-editor", inputs: ["featNames", "featValues", "feature", "isVar"], outputs: ["featureChange", "featureCancel"] }, { kind: "pipe", type: FlatLookupPipe, name: "flatLookup" }] }); }
964
992
  }
965
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: FeatureSetEditorComponent, decorators: [{
993
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: FeatureSetEditorComponent, decorators: [{
966
994
  type: Component,
967
995
  args: [{ selector: 'gve-feature-set-editor', imports: [
968
996
  ReactiveFormsModule,
@@ -975,8 +1003,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
975
1003
  MatTooltipModule,
976
1004
  FlatLookupPipe,
977
1005
  FeatureEditorComponent,
978
- ], template: "<div>\r\n <div class=\"form-row\">\r\n <!-- filter -->\r\n @if (filterThreshold() === 0 || features().length >= filterThreshold()) {\r\n <mat-form-field>\r\n <input matInput placeholder=\"filter\" [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- add feature button -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Add a new feature\"\r\n (click)=\"addFeature()\"\r\n [class.in-row-button]=\"\r\n filterThreshold() === 0 || features()!.length >= filterThreshold()\r\n \"\r\n >\r\n <mat-icon>add</mat-icon>\r\n feature\r\n </button>\r\n </div>\r\n\r\n <!-- list -->\r\n @if (features().length) {\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>feature</th>\r\n <th>value</th>\r\n <th>policy</th>\r\n @if (isVar()) {\r\n <th>negated</th>\r\n <th>global</th>\r\n <th>short-lived</th>\r\n }\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (feature of filteredFeatures(); track feature; let i = $index) {\r\n <tr [class.selected]=\"i === editedFeatureIndex()\">\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Edit this feature\"\r\n (click)=\"editFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Delete this feature\"\r\n (click)=\"deleteFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n @if (featNames()?.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </td>\r\n <td>\r\n @if (featValues()) {\r\n <span>{{\r\n feature.value | flatLookup : featValues()![feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n <td>\r\n <span>{{ POLICIES[feature.setPolicy] }}</span>\r\n </td>\r\n @if (isVar()) {\r\n <td>\r\n <span>{{ $any(feature).isNegated ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n <td>\r\n <span>{{ $any(feature).isGlobal ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n <td>\r\n <span>{{ $any(feature).isShortLived ? \"yes\" : \"no\" }}</span>\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- editor -->\r\n @if (editedFeature()) {\r\n <mat-expansion-panel\r\n [disabled]=\"!editedFeature()\"\r\n [expanded]=\"editedFeature()\"\r\n >\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n <span>{{ editedFeature()?.name || \"feature\" }}</span>\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-feature-editor\r\n [isVar]=\"isVar()\"\r\n [featNames]=\"featNames()\"\r\n [featValues]=\"featValues()\"\r\n [feature]=\"editedFeature()\"\r\n (featureChange)=\"onFeatureChange($event)\"\r\n (featureCancel)=\"onFeatureCancel()\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}\n"] }]
979
- }], ctorParameters: () => [{ type: i1.FormBuilder }] });
1006
+ ], template: "<div>\r\n <div class=\"form-row\">\r\n <!-- filter -->\r\n @if (filterThreshold() === 0 || features().length >= filterThreshold()) {\r\n <mat-form-field>\r\n <input matInput placeholder=\"filter\" [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n }\r\n\r\n <!-- add feature button -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Add a new feature\"\r\n (click)=\"addFeature()\"\r\n [class.in-row-button]=\"\r\n filterThreshold() === 0 || features()!.length >= filterThreshold()\r\n \"\r\n >\r\n <mat-icon>add</mat-icon>\r\n feature\r\n </button>\r\n </div>\r\n\r\n <!-- list -->\r\n @if (features().length) {\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>feature</th>\r\n <th>value</th>\r\n <th>policy</th>\r\n @if (isVar()) {\r\n <th>flags</th>\r\n }\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (feature of filteredFeatures(); track feature; let i = $index) {\r\n <tr [class.selected]=\"i === editedFeatureIndex()\">\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Edit this feature\"\r\n (click)=\"editFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Delete this feature\"\r\n (click)=\"deleteFeature(feature)\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n @if (featNames()?.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </td>\r\n <td>\r\n @if (featValues()) {\r\n <span>{{\r\n feature.value | flatLookup : featValues()![feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n <td>\r\n <span>{{ POLICIES[feature.setPolicy] }}</span>\r\n </td>\r\n @if (isVar()) {\r\n <td>\r\n @if ($any(feature).isNegated) {\r\n <span class=\"icon\" matTooltip=\"Negated: remove if present\">\r\n <mat-icon class=\"mat-warn\">remove_circle</mat-icon>\r\n </span>\r\n } @if ($any(feature).isGlobal) {\r\n <span class=\"icon\" matTooltip=\"Global: apply to whole output\">\r\n <mat-icon class=\"mat-primary\">public</mat-icon>\r\n </span>\r\n } @if ($any(feature).isShortLived) {\r\n <span class=\"icon\" matTooltip=\"Short-lived: removed on next operation\">\r\n <mat-icon class=\"mat-accent\">timer</mat-icon>\r\n </span>\r\n }\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- editor -->\r\n @if (editedFeature()) {\r\n <mat-expansion-panel\r\n [disabled]=\"!editedFeature()\"\r\n [expanded]=\"editedFeature()\"\r\n >\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n <span>{{ editedFeature()?.name || \"feature\" }}</span>\r\n </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-feature-editor\r\n [isVar]=\"isVar()\"\r\n [featNames]=\"featNames()\"\r\n [featValues]=\"featValues()\"\r\n [feature]=\"editedFeature()\"\r\n (featureChange)=\"onFeatureChange($event)\"\r\n (featureCancel)=\"onFeatureCancel()\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}\n"] }]
1007
+ }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { isVar: [{ type: i0.Input, args: [{ isSignal: true, alias: "isVar", required: false }] }], featNames: [{ type: i0.Input, args: [{ isSignal: true, alias: "featNames", required: false }] }], featValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "featValues", required: false }] }], filterThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterThreshold", required: false }] }], features: [{ type: i0.Input, args: [{ isSignal: true, alias: "features", required: false }] }, { type: i0.Output, args: ["featuresChange"] }] } });
980
1008
 
981
1009
  /**
982
1010
  * 🔑 `gve-base-text-editor`
@@ -1041,7 +1069,7 @@ class BaseTextEditorComponent {
1041
1069
  }
1042
1070
  else {
1043
1071
  this.userText.setValue(this.text()
1044
- .map((c) => c.label)
1072
+ .map((c) => c.data)
1045
1073
  .join(''));
1046
1074
  this.userText.markAsPristine();
1047
1075
  this.userText.updateValueAndValidity();
@@ -1080,10 +1108,10 @@ class BaseTextEditorComponent {
1080
1108
  }
1081
1109
  });
1082
1110
  }
1083
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: BaseTextEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i4$2.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
1084
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: BaseTextEditorComponent, isStandalone: true, selector: "gve-base-text-editor", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { textChange: "textChange" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"setTextFromUser()\">\r\n <!-- text -->\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"5\"></textarea>\r\n @if ( $any(userText).errors?.required && (userText.dirty ||\r\n userText.touched) ) {\r\n <mat-error>text required</mat-error>\r\n } @if ( $any(userText).errors?.maxLength && (userText.dirty ||\r\n userText.touched) ) {\r\n <mat-error>text too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <!-- set -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-warn\"\r\n matTooltip=\"Reset characters to newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"setTextFromUser()\"\r\n >\r\n set\r\n </button>\r\n <!-- patch: TODO -->\r\n <!-- <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\r\n matTooltip=\"Patch characters with newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"patchTextFromUser()\"\r\n >\r\n patch\r\n </button> -->\r\n </div>\r\n\r\n <!-- base text -->\r\n <div id=\"text-view\">\r\n <gve-base-text-view\r\n [text]=\"text() || []\"\r\n (charPick)=\"onSelectedChar($event)\"\r\n (rangePick)=\"textRange.set($event)\"\r\n />\r\n\r\n <!-- text range -->\r\n @if (textRange()) {\r\n <div id=\"text-range\">{{ textRange()!.at }} \u00D7 {{ textRange()!.run }}</div>\r\n }\r\n </div>\r\n\r\n <!-- char features -->\r\n @if (selectedChar()) {\r\n <fieldset>\r\n <legend>features</legend>\r\n <gve-feature-set-editor\r\n [features]=\"selectedChar()!.features || []\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n />\r\n </fieldset>\r\n }\r\n</form>\r\n", styles: [".full-width{width:100%}#text-view{margin:8px 0}fieldset{border:1px solid #ccc;padding:10px;margin:10px 0;border-radius:5px}legend{color:silver}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: BaseTextViewComponent, selector: "gve-base-text-view", inputs: ["defaultColor", "defaultBorderColor", "selectionColor", "hasLineNumber", "text", "colorCallback", "borderColorCallback"], outputs: ["charPick", "rangePick"] }, { kind: "component", type: FeatureSetEditorComponent, selector: "gve-feature-set-editor", inputs: ["isVar", "featNames", "featValues", "filterThreshold", "features"], outputs: ["featuresChange"] }] }); }
1111
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: BaseTextEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i4$1.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
1112
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: BaseTextEditorComponent, isStandalone: true, selector: "gve-base-text-editor", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { textChange: "textChange" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"setTextFromUser()\">\r\n <!-- text -->\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"5\"></textarea>\r\n @if ( $any(userText).errors?.required && (userText.dirty ||\r\n userText.touched) ) {\r\n <mat-error>text required</mat-error>\r\n } @if ( $any(userText).errors?.maxLength && (userText.dirty ||\r\n userText.touched) ) {\r\n <mat-error>text too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <!-- set -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-warn\"\r\n matTooltip=\"Reset characters to newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"setTextFromUser()\"\r\n >\r\n set\r\n </button>\r\n <!-- patch: TODO -->\r\n <!-- <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\r\n matTooltip=\"Patch characters with newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"patchTextFromUser()\"\r\n >\r\n patch\r\n </button> -->\r\n </div>\r\n\r\n <!-- base text -->\r\n <div id=\"text-view\">\r\n <gve-base-text-view\r\n [text]=\"text() || []\"\r\n (charPick)=\"onSelectedChar($event)\"\r\n (rangePick)=\"textRange.set($event)\"\r\n />\r\n\r\n <!-- text range -->\r\n @if (textRange()) {\r\n <div id=\"text-range\">{{ textRange()!.at }} \u00D7 {{ textRange()!.run }}</div>\r\n }\r\n </div>\r\n\r\n <!-- char features -->\r\n @if (selectedChar()) {\r\n <fieldset>\r\n <legend>features</legend>\r\n <gve-feature-set-editor\r\n [features]=\"selectedChar()!.features || []\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n />\r\n </fieldset>\r\n }\r\n</form>\r\n", styles: [".full-width{width:100%}#text-view{margin:8px 0}fieldset{border:1px solid #ccc;padding:10px;margin:10px 0;border-radius:5px}legend{color:silver}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: BaseTextViewComponent, selector: "gve-base-text-view", inputs: ["defaultColor", "defaultBorderColor", "selectionColor", "hasLineNumber", "text", "colorCallback", "borderColorCallback"], outputs: ["charPick", "rangePick"] }, { kind: "component", type: FeatureSetEditorComponent, selector: "gve-feature-set-editor", inputs: ["isVar", "featNames", "featValues", "filterThreshold", "features"], outputs: ["featuresChange"] }] }); }
1085
1113
  }
1086
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: BaseTextEditorComponent, decorators: [{
1114
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: BaseTextEditorComponent, decorators: [{
1087
1115
  type: Component,
1088
1116
  args: [{ selector: 'gve-base-text-editor', imports: [
1089
1117
  ReactiveFormsModule,
@@ -1095,7 +1123,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
1095
1123
  BaseTextViewComponent,
1096
1124
  FeatureSetEditorComponent,
1097
1125
  ], template: "<form [formGroup]=\"form\" (submit)=\"setTextFromUser()\">\r\n <!-- text -->\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"5\"></textarea>\r\n @if ( $any(userText).errors?.required && (userText.dirty ||\r\n userText.touched) ) {\r\n <mat-error>text required</mat-error>\r\n } @if ( $any(userText).errors?.maxLength && (userText.dirty ||\r\n userText.touched) ) {\r\n <mat-error>text too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <!-- set -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-warn\"\r\n matTooltip=\"Reset characters to newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"setTextFromUser()\"\r\n >\r\n set\r\n </button>\r\n <!-- patch: TODO -->\r\n <!-- <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\r\n matTooltip=\"Patch characters with newly entered text\"\r\n [disabled]=\"!userText.value\"\r\n (click)=\"patchTextFromUser()\"\r\n >\r\n patch\r\n </button> -->\r\n </div>\r\n\r\n <!-- base text -->\r\n <div id=\"text-view\">\r\n <gve-base-text-view\r\n [text]=\"text() || []\"\r\n (charPick)=\"onSelectedChar($event)\"\r\n (rangePick)=\"textRange.set($event)\"\r\n />\r\n\r\n <!-- text range -->\r\n @if (textRange()) {\r\n <div id=\"text-range\">{{ textRange()!.at }} \u00D7 {{ textRange()!.run }}</div>\r\n }\r\n </div>\r\n\r\n <!-- char features -->\r\n @if (selectedChar()) {\r\n <fieldset>\r\n <legend>features</legend>\r\n <gve-feature-set-editor\r\n [features]=\"selectedChar()!.features || []\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n />\r\n </fieldset>\r\n }\r\n</form>\r\n", styles: [".full-width{width:100%}#text-view{margin:8px 0}fieldset{border:1px solid #ccc;padding:10px;margin:10px 0;border-radius:5px}legend{color:silver}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"] }]
1098
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i4$2.DialogService }] });
1126
+ }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i4$1.DialogService }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }], textChange: [{ type: i0.Output, args: ["textChange"] }] } });
1099
1127
 
1100
1128
  /**
1101
1129
  * Service to interact with the GVE API.
@@ -1164,10 +1192,10 @@ class GveApiService {
1164
1192
  })
1165
1193
  .pipe(catchError(this._error.handleError));
1166
1194
  }
1167
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: GveApiService, deps: [{ token: i1$2.HttpClient }, { token: i2$1.ErrorService }, { token: i2$1.EnvService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1168
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: GveApiService, providedIn: 'root' }); }
1195
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: GveApiService, deps: [{ token: i1$2.HttpClient }, { token: i2$1.ErrorService }, { token: i2$1.EnvService }], target: i0.ɵɵFactoryTarget.Injectable }); }
1196
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: GveApiService, providedIn: 'root' }); }
1169
1197
  }
1170
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: GveApiService, decorators: [{
1198
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: GveApiService, decorators: [{
1171
1199
  type: Injectable,
1172
1200
  args: [{
1173
1201
  providedIn: 'root',
@@ -1245,10 +1273,10 @@ class BatchOperationEditorComponent {
1245
1273
  close() {
1246
1274
  this.dialogRef?.close();
1247
1275
  }
1248
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: BatchOperationEditorComponent, deps: [{ token: i1.FormBuilder }, { token: GveApiService }, { token: i3$2.MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
1249
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: BatchOperationEditorComponent, isStandalone: true, selector: "gve-batch-operation-editor", inputs: { preset: { classPropertyName: "preset", publicName: "preset", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operationsChange: "operationsChange" }, ngImport: i0, template: "<div [style.padding]=\"dialogRef ? '8px' : '0'\">\r\n @if (dialogRef) {\r\n <div id=\"heading\">\r\n <h2>Add Operations</h2>\r\n </div>\r\n }\r\n <fieldset>\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>operations</mat-label>\r\n <textarea\r\n class=\"code\"\r\n matInput\r\n [formControl]=\"text\"\r\n rows=\"8\"\r\n spellcheck=\"false\"\r\n ></textarea>\r\n </mat-form-field>\r\n @if (parseError()) {\r\n <div class=\"error\">{{ parseError() }}</div>\r\n }\r\n </div>\r\n </div>\r\n <div id=\"batch-help\">\r\n <ul>\r\n <li>\r\n <strong>replace</strong>:\r\n <code>ATxRUN<strong>=</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>delete</strong>:\r\n <code>ATxRUN<strong>-</strong></code>\r\n </li>\r\n <li>\r\n <strong>add-before</strong>:\r\n <code>ATxRUN<strong>+[</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>add-after</strong>:\r\n <code>ATxRUN<strong>+]</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>move-before</strong>:\r\n <code>ATxRUN<strong>&gt;[</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>move-after</strong>:\r\n <code>ATxRUN<strong>&gt;]</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>swap</strong>:\r\n <code>ATxRUN<strong>&lt;&gt;</strong>TOxRUN</code>\r\n </li>\r\n <li>\r\n <strong>annotate</strong>:\r\n <code>ATxRUN<strong>:</strong></code>\r\n </li>\r\n </ul>\r\n <p>For all the operations:</p>\r\n <ul>\r\n <li>\r\n prefix <code>(ITAG:OTAG)</code> to define input and/or output tags,\r\n separated by colon.\r\n </li>\r\n <li>\r\n prepend <code>&#x40;</code> to AT to use character indexes (0-N)\r\n rather than IDs.\r\n </li>\r\n <li>RUN (where applicable) defaults to 1.</li>\r\n <li>\r\n append features like <code>[NAME OPERATOR VALUE]</code>, separated by\r\n space, where:\r\n </li>\r\n <li>\r\n <ol>\r\n <li>\r\n NAME is an arbitrary string. Prefixes:\r\n <code>*</code>=global features, <code>!</code>=remove feature (no\r\n value). Suffixes: <code>^</code>=short-lived (like\r\n <code>*version^=alpha</code>).\r\n </li>\r\n <li>\r\n OPERATOR is <code>=</code> (multiple), <code>:=</code> (single),\r\n <code>==</code> (first-single).\r\n </li>\r\n <li>\r\n VALUE is a string. If it includes spaces, wrap it in\r\n <code>\"\"</code>.\r\n </li>\r\n </ol>\r\n </li>\r\n </ul>\r\n </div>\r\n <div class=\"form-row-center\">\r\n @if (dialogRef) {\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Close dialog\"\r\n (click)=\"close()\"\r\n >\r\n close\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Parse text into operations\"\r\n [disabled]=\"!text.value || busy()\"\r\n (click)=\"parseOperations(text.value!)\"\r\n >\r\n batch add\r\n </button>\r\n </div>\r\n </fieldset>\r\n</div>\r\n", styles: [".full-width{width:100%}.error{color:red}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}div#heading{margin:8px;text-align:center}div#batch-help strong{color:#ff8c00}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }] }); }
1276
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: BatchOperationEditorComponent, deps: [{ token: i1.FormBuilder }, { token: GveApiService }, { token: i3$3.MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
1277
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: BatchOperationEditorComponent, isStandalone: true, selector: "gve-batch-operation-editor", inputs: { preset: { classPropertyName: "preset", publicName: "preset", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operationsChange: "operationsChange" }, ngImport: i0, template: "<div [style.padding]=\"dialogRef ? '8px' : '0'\">\r\n @if (dialogRef) {\r\n <div id=\"heading\">\r\n <h2>Add Operations</h2>\r\n </div>\r\n }\r\n <fieldset>\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>operations</mat-label>\r\n <textarea\r\n class=\"code\"\r\n matInput\r\n [formControl]=\"text\"\r\n rows=\"8\"\r\n spellcheck=\"false\"\r\n ></textarea>\r\n </mat-form-field>\r\n @if (parseError()) {\r\n <div class=\"error\">{{ parseError() }}</div>\r\n }\r\n </div>\r\n </div>\r\n <div id=\"batch-help\">\r\n <ul>\r\n <li>\r\n <strong>replace</strong>:\r\n <code>ATxRUN<strong>=</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>delete</strong>:\r\n <code>ATxRUN<strong>-</strong></code>\r\n </li>\r\n <li>\r\n <strong>add-before</strong>:\r\n <code>ATxRUN<strong>+[</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>add-after</strong>:\r\n <code>ATxRUN<strong>+]</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>move-before</strong>:\r\n <code>ATxRUN<strong>&gt;[</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>move-after</strong>:\r\n <code>ATxRUN<strong>&gt;]</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>swap</strong>:\r\n <code>ATxRUN<strong>&lt;&gt;</strong>TOxRUN</code>\r\n </li>\r\n <li>\r\n <strong>annotate</strong>:\r\n <code>ATxRUN<strong>:</strong></code>\r\n </li>\r\n </ul>\r\n <p>For all the operations:</p>\r\n <ul>\r\n <li>\r\n prefix <code>(ITAG:OTAG)</code> to define input and/or output tags,\r\n separated by colon.\r\n </li>\r\n <li>\r\n prepend <code>&#x40;</code> to AT to use character indexes (0-N)\r\n rather than IDs.\r\n </li>\r\n <li>RUN (where applicable) defaults to 1.</li>\r\n <li>\r\n append features like <code>[NAME OPERATOR VALUE]</code>, separated by\r\n space, where:\r\n </li>\r\n <li>\r\n <ol>\r\n <li>\r\n NAME is an arbitrary string. Prefixes:\r\n <code>*</code>=global features, <code>!</code>=remove feature (no\r\n value). Suffixes: <code>^</code>=short-lived (like\r\n <code>*version^=alpha</code>).\r\n </li>\r\n <li>\r\n OPERATOR is <code>=</code> (multiple), <code>:=</code> (single),\r\n <code>==</code> (first-single).\r\n </li>\r\n <li>\r\n VALUE is a string. If it includes spaces, wrap it in\r\n <code>\"\"</code>.\r\n </li>\r\n </ol>\r\n </li>\r\n </ul>\r\n </div>\r\n <div class=\"form-row-center\">\r\n @if (dialogRef) {\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Close dialog\"\r\n (click)=\"close()\"\r\n >\r\n close\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Parse text into operations\"\r\n [disabled]=\"!text.value || busy()\"\r\n (click)=\"parseOperations(text.value!)\"\r\n >\r\n batch add\r\n </button>\r\n </div>\r\n </fieldset>\r\n</div>\r\n", styles: [".full-width{width:100%}.error{color:red}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}div#heading{margin:8px;text-align:center}div#batch-help strong{color:#ff8c00}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }] }); }
1250
1278
  }
1251
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: BatchOperationEditorComponent, decorators: [{
1279
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: BatchOperationEditorComponent, decorators: [{
1252
1280
  type: Component,
1253
1281
  args: [{ selector: 'gve-batch-operation-editor', imports: [
1254
1282
  ReactiveFormsModule,
@@ -1257,14 +1285,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
1257
1285
  MatIconModule,
1258
1286
  MatInputModule,
1259
1287
  ], template: "<div [style.padding]=\"dialogRef ? '8px' : '0'\">\r\n @if (dialogRef) {\r\n <div id=\"heading\">\r\n <h2>Add Operations</h2>\r\n </div>\r\n }\r\n <fieldset>\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>operations</mat-label>\r\n <textarea\r\n class=\"code\"\r\n matInput\r\n [formControl]=\"text\"\r\n rows=\"8\"\r\n spellcheck=\"false\"\r\n ></textarea>\r\n </mat-form-field>\r\n @if (parseError()) {\r\n <div class=\"error\">{{ parseError() }}</div>\r\n }\r\n </div>\r\n </div>\r\n <div id=\"batch-help\">\r\n <ul>\r\n <li>\r\n <strong>replace</strong>:\r\n <code>ATxRUN<strong>=</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>delete</strong>:\r\n <code>ATxRUN<strong>-</strong></code>\r\n </li>\r\n <li>\r\n <strong>add-before</strong>:\r\n <code>ATxRUN<strong>+[</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>add-after</strong>:\r\n <code>ATxRUN<strong>+]</strong>\"VALUE\"</code>\r\n </li>\r\n <li>\r\n <strong>move-before</strong>:\r\n <code>ATxRUN<strong>&gt;[</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>move-after</strong>:\r\n <code>ATxRUN<strong>&gt;]</strong>TO</code>\r\n </li>\r\n <li>\r\n <strong>swap</strong>:\r\n <code>ATxRUN<strong>&lt;&gt;</strong>TOxRUN</code>\r\n </li>\r\n <li>\r\n <strong>annotate</strong>:\r\n <code>ATxRUN<strong>:</strong></code>\r\n </li>\r\n </ul>\r\n <p>For all the operations:</p>\r\n <ul>\r\n <li>\r\n prefix <code>(ITAG:OTAG)</code> to define input and/or output tags,\r\n separated by colon.\r\n </li>\r\n <li>\r\n prepend <code>&#x40;</code> to AT to use character indexes (0-N)\r\n rather than IDs.\r\n </li>\r\n <li>RUN (where applicable) defaults to 1.</li>\r\n <li>\r\n append features like <code>[NAME OPERATOR VALUE]</code>, separated by\r\n space, where:\r\n </li>\r\n <li>\r\n <ol>\r\n <li>\r\n NAME is an arbitrary string. Prefixes:\r\n <code>*</code>=global features, <code>!</code>=remove feature (no\r\n value). Suffixes: <code>^</code>=short-lived (like\r\n <code>*version^=alpha</code>).\r\n </li>\r\n <li>\r\n OPERATOR is <code>=</code> (multiple), <code>:=</code> (single),\r\n <code>==</code> (first-single).\r\n </li>\r\n <li>\r\n VALUE is a string. If it includes spaces, wrap it in\r\n <code>\"\"</code>.\r\n </li>\r\n </ol>\r\n </li>\r\n </ul>\r\n </div>\r\n <div class=\"form-row-center\">\r\n @if (dialogRef) {\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Close dialog\"\r\n (click)=\"close()\"\r\n >\r\n close\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Parse text into operations\"\r\n [disabled]=\"!text.value || busy()\"\r\n (click)=\"parseOperations(text.value!)\"\r\n >\r\n batch add\r\n </button>\r\n </div>\r\n </fieldset>\r\n</div>\r\n", styles: [".full-width{width:100%}.error{color:red}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}div#heading{margin:8px;text-align:center}div#batch-help strong{color:#ff8c00}\n"] }]
1260
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: GveApiService }, { type: i3$2.MatDialogRef, decorators: [{
1288
+ }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: GveApiService }, { type: i3$3.MatDialogRef, decorators: [{
1261
1289
  type: Optional
1262
1290
  }] }, { type: undefined, decorators: [{
1263
1291
  type: Optional
1264
1292
  }, {
1265
1293
  type: Inject,
1266
1294
  args: [MAT_DIALOG_DATA]
1267
- }] }] });
1295
+ }] }], propDecorators: { preset: [{ type: i0.Input, args: [{ isSignal: true, alias: "preset", required: false }] }], operationsChange: [{ type: i0.Output, args: ["operationsChange"] }] } });
1268
1296
 
1269
1297
  /**
1270
1298
  * 🔑 `gve-operation-source-editor`
@@ -1350,10 +1378,10 @@ class OperationSourceEditorComponent {
1350
1378
  note: this.note.value || undefined,
1351
1379
  });
1352
1380
  }
1353
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: OperationSourceEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
1354
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: OperationSourceEditorComponent, isStandalone: true, selector: "gve-operation-source-editor", inputs: { source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, ids: { classPropertyName: "ids", publicName: "ids", isSignal: true, isRequired: false, transformFunction: null }, types: { classPropertyName: "types", publicName: "types", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { source: "sourceChange", sourceCancel: "sourceCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <!-- id (bound) -->\r\n @if (ids()?.length) {\r\n <mat-form-field>\r\n <mat-label>id</mat-label>\r\n <mat-select [formControl]=\"id\">\r\n @for (i of ids(); track i.id) {\r\n <mat-option [value]=\"i.id\">{{ i.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(id.errors)?.required && (id.dirty || id.touched)) {\r\n <mat-error>ID required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- id (free) -->\r\n <mat-form-field>\r\n <mat-label>id</mat-label>\r\n <input matInput [formControl]=\"id\" />\r\n @if ($any(id.errors)?.required && (id.dirty || id.touched)) {\r\n <mat-error>ID required</mat-error>\r\n } @if ($any(id.errors)?.maxLength && (id.dirty || id.touched)) {\r\n <mat-error>id too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- type (bound) -->\r\n @if (types()?.length) {\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n @for (i of types(); track i.id) {\r\n <mat-option [value]=\"i.id\">{{ i.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(type.errors)?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- type (free) -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <input matInput [formControl]=\"type\" />\r\n @if ($any(type.errors)?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n } @if ($any(type.errors)?.maxLength && (type.dirty || type.touched)) {\r\n <mat-error>type too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n </mat-form-field>\r\n </div>\r\n <!-- note -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>note</mat-label>\r\n <textarea matInput [formControl]=\"note\" class=\"long-text\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Accept changes\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".long-text{width:100%;max-width:800px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.nr{width:5em}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
1381
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: OperationSourceEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
1382
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: OperationSourceEditorComponent, isStandalone: true, selector: "gve-operation-source-editor", inputs: { source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, ids: { classPropertyName: "ids", publicName: "ids", isSignal: true, isRequired: false, transformFunction: null }, types: { classPropertyName: "types", publicName: "types", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { source: "sourceChange", sourceCancel: "sourceCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <!-- id (bound) -->\r\n @if (ids()?.length) {\r\n <mat-form-field>\r\n <mat-label>id</mat-label>\r\n <mat-select [formControl]=\"id\">\r\n @for (i of ids(); track i.id) {\r\n <mat-option [value]=\"i.id\">{{ i.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(id.errors)?.required && (id.dirty || id.touched)) {\r\n <mat-error>ID required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- id (free) -->\r\n <mat-form-field>\r\n <mat-label>id</mat-label>\r\n <input matInput [formControl]=\"id\" />\r\n @if ($any(id.errors)?.required && (id.dirty || id.touched)) {\r\n <mat-error>ID required</mat-error>\r\n } @if ($any(id.errors)?.maxLength && (id.dirty || id.touched)) {\r\n <mat-error>id too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- type (bound) -->\r\n @if (types()?.length) {\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n @for (i of types(); track i.id) {\r\n <mat-option [value]=\"i.id\">{{ i.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(type.errors)?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- type (free) -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <input matInput [formControl]=\"type\" />\r\n @if ($any(type.errors)?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n } @if ($any(type.errors)?.maxLength && (type.dirty || type.touched)) {\r\n <mat-error>type too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n </mat-form-field>\r\n </div>\r\n <!-- note -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>note</mat-label>\r\n <textarea matInput [formControl]=\"note\" class=\"long-text\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Accept changes\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".long-text{width:100%;max-width:800px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.nr{width:5em}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
1355
1383
  }
1356
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: OperationSourceEditorComponent, decorators: [{
1384
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: OperationSourceEditorComponent, decorators: [{
1357
1385
  type: Component,
1358
1386
  args: [{ selector: 'gve-operation-source-editor', imports: [
1359
1387
  ReactiveFormsModule,
@@ -1364,7 +1392,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
1364
1392
  MatSelectModule,
1365
1393
  MatTooltipModule
1366
1394
  ], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <!-- id (bound) -->\r\n @if (ids()?.length) {\r\n <mat-form-field>\r\n <mat-label>id</mat-label>\r\n <mat-select [formControl]=\"id\">\r\n @for (i of ids(); track i.id) {\r\n <mat-option [value]=\"i.id\">{{ i.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(id.errors)?.required && (id.dirty || id.touched)) {\r\n <mat-error>ID required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- id (free) -->\r\n <mat-form-field>\r\n <mat-label>id</mat-label>\r\n <input matInput [formControl]=\"id\" />\r\n @if ($any(id.errors)?.required && (id.dirty || id.touched)) {\r\n <mat-error>ID required</mat-error>\r\n } @if ($any(id.errors)?.maxLength && (id.dirty || id.touched)) {\r\n <mat-error>id too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- type (bound) -->\r\n @if (types()?.length) {\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n @for (i of types(); track i.id) {\r\n <mat-option [value]=\"i.id\">{{ i.label }}</mat-option>\r\n }\r\n </mat-select>\r\n @if ($any(type.errors)?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n }\r\n </mat-form-field>\r\n } @else {\r\n <!-- type (free) -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <input matInput [formControl]=\"type\" />\r\n @if ($any(type.errors)?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n } @if ($any(type.errors)?.maxLength && (type.dirty || type.touched)) {\r\n <mat-error>type too long</mat-error>\r\n }\r\n </mat-form-field>\r\n }\r\n\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n </mat-form-field>\r\n </div>\r\n <!-- note -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>note</mat-label>\r\n <textarea matInput [formControl]=\"note\" class=\"long-text\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n\r\n <!-- buttons -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Accept changes\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon class=\"mat-primary\">check_circle</mat-icon>\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".long-text{width:100%;max-width:800px}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.nr{width:5em}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}\n"] }]
1367
- }], ctorParameters: () => [{ type: i1.FormBuilder }] });
1395
+ }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: false }] }, { type: i0.Output, args: ["sourceChange"] }], ids: [{ type: i0.Input, args: [{ isSignal: true, alias: "ids", required: false }] }], types: [{ type: i0.Input, args: [{ isSignal: true, alias: "types", required: false }] }], sourceCancel: [{ type: i0.Output, args: ["sourceCancel"] }] } });
1368
1396
 
1369
1397
  /**
1370
1398
  * Validators for SVG.
@@ -1582,10 +1610,10 @@ class SettingsService {
1582
1610
  // for when the cache was not in sync with the local storage
1583
1611
  removedKeys.forEach((key) => this._subject.next(key));
1584
1612
  }
1585
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: SettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1586
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: SettingsService, providedIn: 'root' }); }
1613
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: SettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1614
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: SettingsService, providedIn: 'root' }); }
1587
1615
  }
1588
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: SettingsService, decorators: [{
1616
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: SettingsService, decorators: [{
1589
1617
  type: Injectable,
1590
1618
  args: [{
1591
1619
  providedIn: 'root',
@@ -1674,6 +1702,10 @@ class ChainOperationEditorComponent {
1674
1702
  * Definitions for diplomatic features, including names and values.
1675
1703
  */
1676
1704
  this.diplomaticFeatureDefs = input(...(ngDevMode ? [undefined, { debugName: "diplomaticFeatureDefs" }] : []));
1705
+ /**
1706
+ * Set when the edited operation's text range is to be patched.
1707
+ */
1708
+ this.rangePatch = input(...(ngDevMode ? [undefined, { debugName: "rangePatch" }] : []));
1677
1709
  /**
1678
1710
  * Emitted when the operation is changed.
1679
1711
  */
@@ -1768,6 +1800,23 @@ class ChainOperationEditorComponent {
1768
1800
  this._editorModel?.setValue(this.svg.value || '');
1769
1801
  }
1770
1802
  });
1803
+ // when rangePatch changes, patch operation range (at and run,
1804
+ // resetting atAsIndex to false)
1805
+ effect(() => {
1806
+ const range = this.rangePatch();
1807
+ if (range) {
1808
+ console.log('patching range', range);
1809
+ this.at.setValue(range.at);
1810
+ this.at.markAsDirty();
1811
+ this.at.updateValueAndValidity();
1812
+ this.atAsIndex.setValue(false);
1813
+ this.atAsIndex.markAsDirty();
1814
+ this.atAsIndex.updateValueAndValidity();
1815
+ this.run.setValue(range.run);
1816
+ this.run.markAsDirty();
1817
+ this.run.updateValueAndValidity();
1818
+ }
1819
+ });
1771
1820
  }
1772
1821
  ngOnInit() {
1773
1822
  // whenever svg or base change, update preview
@@ -1785,6 +1834,11 @@ class ChainOperationEditorComponent {
1785
1834
  sub.unsubscribe();
1786
1835
  }
1787
1836
  }
1837
+ toggleLfEscape(text, on) {
1838
+ if (!text)
1839
+ return text;
1840
+ return on ? text.replace(/\\/g, '\n') : text.replace(/\n/g, '\\');
1841
+ }
1788
1842
  hasTextChanges(snapshot) {
1789
1843
  if ((!snapshot && this.snapshot()) || (snapshot && !this.snapshot())) {
1790
1844
  return true;
@@ -2056,7 +2110,8 @@ class ChainOperationEditorComponent {
2056
2110
  this.outputTag.setValue(operation.outputTag || null);
2057
2111
  this.run.setValue(operation.run);
2058
2112
  this.toRun.setValue(operation.toRun || 0);
2059
- this.value.setValue(operation.value || null);
2113
+ // escape line feeds in value for editing
2114
+ this.value.setValue(this.toggleLfEscape(operation.value, true) || null);
2060
2115
  this.svg.setValue(operation.diplomatics?.g || '');
2061
2116
  this._editorModel?.setValue(this.svg.value || '');
2062
2117
  this.elementFeatures.setValue(operation.diplomatics?.elementFeatures || {});
@@ -2089,7 +2144,8 @@ class ChainOperationEditorComponent {
2089
2144
  outputTag: this.outputTag.value || undefined,
2090
2145
  run: this.run.value,
2091
2146
  toRun: this.toRun.value || undefined,
2092
- value: this.value.value || undefined,
2147
+ // un-escape line feeds in value for storage
2148
+ value: this.toggleLfEscape(this.value.value, false) || undefined,
2093
2149
  };
2094
2150
  }
2095
2151
  requestPreview() {
@@ -2102,10 +2158,10 @@ class ChainOperationEditorComponent {
2102
2158
  this.operation.set(this.getOperation());
2103
2159
  this.operationChange.emit(this.operation());
2104
2160
  }
2105
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: ChainOperationEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i2$2.Clipboard }, { token: SettingsService }, { token: i4$2.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
2106
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: ChainOperationEditorComponent, isStandalone: true, selector: "gve-chain-operation-editor", inputs: { operation: { classPropertyName: "operation", publicName: "operation", isSignal: true, isRequired: false, transformFunction: null }, snapshot: { classPropertyName: "snapshot", publicName: "snapshot", isSignal: true, isRequired: false, transformFunction: null }, hidePreview: { classPropertyName: "hidePreview", publicName: "hidePreview", isSignal: true, isRequired: false, transformFunction: null }, featureDefs: { classPropertyName: "featureDefs", publicName: "featureDefs", isSignal: true, isRequired: false, transformFunction: null }, elementFeatureDefs: { classPropertyName: "elementFeatureDefs", publicName: "elementFeatureDefs", isSignal: true, isRequired: false, transformFunction: null }, diplomaticFeatureDefs: { classPropertyName: "diplomaticFeatureDefs", publicName: "diplomaticFeatureDefs", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operation: "operationChange", operationChange: "operationChange", operationPreview: "operationPreview", operationCancel: "operationCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <!-- tabs -->\r\n <mat-tab-group\r\n [selectedIndex]=\"tabIndex()\"\r\n (selectedIndexChange)=\"onTabIndexChange($event)\"\r\n >\r\n <!-- GENERAL -->\r\n <mat-tab label=\"general\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <div id=\"id\" class=\"muted\">{{ id() }}</div>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n <mat-option [value]=\"0\"\r\n ><mat-icon>layers</mat-icon> replace</mat-option\r\n >\r\n <mat-option [value]=\"1\"\r\n ><mat-icon>close</mat-icon>delete</mat-option\r\n >\r\n <mat-option [value]=\"2\"\r\n ><mat-icon>last_page</mat-icon>add-before</mat-option\r\n >\r\n <mat-option [value]=\"3\"\r\n ><mat-icon>first_page</mat-icon>add-after</mat-option\r\n >\r\n <mat-option [value]=\"4\"\r\n ><mat-icon>login</mat-icon>move-before</mat-option\r\n >\r\n <mat-option [value]=\"5\"\r\n ><mat-icon>logout</mat-icon>move-after</mat-option\r\n >\r\n <mat-option [value]=\"6\"\r\n ><mat-icon>compare_arrows</mat-icon>swap</mat-option\r\n >\r\n <mat-option [value]=\"7\"\r\n ><mat-icon>edit_note</mat-icon>annotate</mat-option\r\n >\r\n </mat-select>\r\n @if ($any(type).errors?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- at -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>at</mat-label>\r\n <input matInput [formControl]=\"at\" type=\"number\" min=\"0\" />\r\n @if ($any(at).errors?.required && (at.dirty || at.touched)) {\r\n <mat-error>at required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- atAsIndex -->\r\n <mat-checkbox [formControl]=\"atAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- run -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>run</mat-label>\r\n <input matInput [formControl]=\"run\" type=\"number\" min=\"0\" />\r\n @if ($any(run).errors?.required && (run.dirty || run.touched)) {\r\n <mat-error>run required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- to -->\r\n @if (hasTo()) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to</mat-label>\r\n <input matInput [formControl]=\"to\" type=\"number\" min=\"0\" />\r\n @if ($any(to).errors?.required && (to.dirty || to.touched)) {\r\n <mat-error>to required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- toAsIndex -->\r\n <mat-checkbox [formControl]=\"toAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- toRun -->\r\n @if (hasToRun()) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to run</mat-label>\r\n <input matInput [formControl]=\"toRun\" type=\"number\" min=\"0\" />\r\n @if ( $any(toRun).errors?.required && (toRun.dirty || toRun.touched) )\r\n {\r\n <mat-error>to run required</mat-error>\r\n }\r\n </mat-form-field>\r\n } }\r\n\r\n <!-- value -->\r\n @if (hasValue()) {\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n </mat-form-field>\r\n }\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n @if ($any(rank).errors?.required && (rank.dirty || rank.touched)) {\r\n <mat-error>rank required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- groupId -->\r\n <mat-form-field>\r\n <mat-label>group ID</mat-label>\r\n <input matInput [formControl]=\"groupId\" />\r\n @if ( $any(groupId).errors?.required && (groupId.dirty ||\r\n groupId.touched) ) {\r\n <mat-error>group ID required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- inputTag -->\r\n <mat-form-field>\r\n <mat-label>input tag</mat-label>\r\n <input matInput [formControl]=\"inputTag\" />\r\n <mat-hint>blank=latest</mat-hint>\r\n </mat-form-field>\r\n\r\n <!-- outputTag -->\r\n <mat-form-field>\r\n <mat-label>output tag</mat-label>\r\n <input matInput [formControl]=\"outputTag\" />\r\n <mat-hint>blank=auto (vN)</mat-hint>\r\n </mat-form-field>\r\n </div>\r\n <div>\r\n <!-- features -->\r\n <fieldset>\r\n <legend>operation features</legend>\r\n <gve-feature-set-editor\r\n [isVar]=\"true\"\r\n [featNames]=\"featureDefs()?.names\"\r\n [featValues]=\"featureDefs()?.values\"\r\n [features]=\"features.value\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n />\r\n </fieldset>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- SOURCES -->\r\n <mat-tab label=\"sources\">\r\n <div>\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addSource()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n source\r\n </button>\r\n </div>\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>type</th>\r\n <th>id</th>\r\n <th>rank</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (s of sources.value; track s) {\r\n <tr [class.selected]=\"s === editedSource()\">\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editSource($index)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button type=\"button\" mat-icon-button color=\"warn\">\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ s.type }}</td>\r\n <td>{{ s.id }}</td>\r\n <td>{{ s.rank }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n @if (editedSource()) {\r\n <!-- source editor -->\r\n <mat-expansion-panel\r\n [disabled]=\"!editedSource()\"\r\n [expanded]=\"editedSource()\"\r\n >\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>source</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-operation-source-editor\r\n [source]=\"editedSource()\"\r\n (sourceChange)=\"onSourceChange($event!)\"\r\n (sourceCancel)=\"closeSource()\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n </mat-tab>\r\n\r\n <!-- DIPLOMATIC -->\r\n <mat-tab label=\"diplomatic\">\r\n <div class=\"toolbar-row\">\r\n <button\r\n id=\"btn-save\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Save to file\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"saveSvg()\"\r\n >\r\n <mat-icon>save</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-load\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Load from file\"\r\n (click)=\"loadSvg()\"\r\n >\r\n <mat-icon>folder</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-copy\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy\"\r\n [cdkCopyToClipboard]=\"svg.value\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-paste\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Set SVG from clipboard\"\r\n (click)=\"setSvgFromClipboard()\"\r\n >\r\n <mat-icon>content_paste_go</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-editor\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open SVG external editor\"\r\n (click)=\"openSvgEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-decimals\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Remove decimals\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"removeDecimals()\"\r\n >\r\n <mat-icon>pin</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-group\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Wrap code in group\"\r\n (click)=\"wrapInGroup()\"\r\n >\r\n <mat-icon>code</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div id=\"code\">\r\n <nge-monaco-editor\r\n style=\"--editor-height: 400px\"\r\n (ready)=\"onCreateEditor($event)\"\r\n />\r\n @if (svg.invalid) {\r\n <mat-error>invalid SVG</mat-error>\r\n }\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- ELEMENTS -->\r\n <mat-tab label=\"elements\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>id</th>\r\n <th>visual</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (e of elements(); track e.id) {\r\n <tr [class.selected]=\"e.id === editedElementId()\">\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Edit element features\"\r\n (click)=\"editElementFeatures(e)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n matTooltip=\"Delete all element features\"\r\n (click)=\"deleteElementFeatures(e)\"\r\n [disabled]=\"!$any(elementFeatures.value)[e.id]?.length\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n <span\r\n [matBadge]=\"$any(elementFeatures.value)[e.id]?.length || 0\"\r\n [matBadgeHidden]=\"!$any(elementFeatures.value)[e.id]?.length\"\r\n matBadgeOverlap=\"false\"\r\n >{{ e.id }}</span\r\n >\r\n </td>\r\n <td class=\"svg-cell\">\r\n <svg [innerHTML]=\"e.outerHTML | safeHtml : 'html'\"></svg>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n @if (editedElementId()) {\r\n <mat-expansion-panel\r\n [disabled]=\"!editedElementId()\"\r\n [expanded]=\"editedElementId()\"\r\n >\r\n <mat-expansion-panel-header>{{\r\n editedElementId()\r\n }}</mat-expansion-panel-header>\r\n <gve-feature-set-editor\r\n [featNames]=\"elementFeatureDefs()?.names\"\r\n [featValues]=\"elementFeatureDefs()?.values\"\r\n [features]=\"elementFeatures.value[editedElementId()!] || []\"\r\n (featuresChange)=\"onElementFeaturesChange($event)\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n </mat-tab>\r\n\r\n <!-- DP FEATS -->\r\n <mat-tab label=\"d-features\">\r\n <div>\r\n <mat-checkbox [formControl]=\"newTextHidden\">hide new text</mat-checkbox>\r\n </div>\r\n <gve-feature-set-editor\r\n [featNames]=\"diplomaticFeatureDefs()?.names\"\r\n [featValues]=\"diplomaticFeatureDefs()?.values\"\r\n [features]=\"dpFeatures.value\"\r\n (featuresChange)=\"onDpFeaturesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- buttons -->\r\n <div id=\"submit-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard operation\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n @if (!hidePreview()) {\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Preview operation\"\r\n (click)=\"requestPreview()\"\r\n >\r\n <mat-icon class=\"mat-primary\">preview</mat-icon>\r\n </button>\r\n }\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save operation\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#submit-row{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap;margin-top:8px;border-top:1px solid silver}.toolbar-row{display:flex;align-items:center;flex-wrap:wrap}.nr{width:5em}.long-text{width:100%;max-width:800px}#id{border-radius:6px;padding:4px;background-color:#beb9b9;color:#fff;margin-top:-16px}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:silver}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}#btn-save{color:#072d3e}#btn-load{color:#dbd112}#btn-copy{color:#22549f}#btn-preview{color:#095409}#code{height:500px;border:1px solid silver}#monaco{height:100%}.svg-cell{padding:0 8px}.svg-cell svg{border:1px solid silver;width:100%;height:auto}#preview{box-sizing:border-box;width:100%;border:1px solid silver;border-radius:4px;padding:8px}.tree-invisible{display:none}.tree ul,.tree li{margin-top:0;margin-bottom:0;list-style-type:none}.selected-node{background-color:#e5e5e5}.child-title{font-weight:700;margin:0;background-color:#ccc;color:#fff;padding:8px}#tree-container{display:grid;grid-template-rows:auto;grid-template-columns:auto 1fr;grid-template-areas:\"nav ed\";gap:0 16px;align-items:stretch}#tree-nav{grid-area:nav;border:1px solid silver;border-radius:6px;margin:8px 0;padding-right:8px}#tree-ed{grid-area:ed;border:1px solid silver;border-radius:6px;margin:8px 0}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"nav\" \"ed\";gap:16px 0;align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ClipboardModule }, { kind: "directive", type: i2$2.CdkCopyToClipboard, selector: "[cdkCopyToClipboard]", inputs: ["cdkCopyToClipboard", "cdkCopyToClipboardAttempts"], outputs: ["cdkCopyToClipboardCopied"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i5$1.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i15.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i15.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: NgeMonacoModule }, { kind: "component", type: i15$1.NgeMonacoEditorComponent, selector: "nge-monaco-editor", inputs: ["autoLayout", "options"], outputs: ["ready"] }, { kind: "component", type: FeatureSetEditorComponent, selector: "gve-feature-set-editor", inputs: ["isVar", "featNames", "featValues", "filterThreshold", "features"], outputs: ["featuresChange"] }, { kind: "component", type: OperationSourceEditorComponent, selector: "gve-operation-source-editor", inputs: ["source", "ids", "types"], outputs: ["sourceChange", "sourceCancel"] }, { kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }] }); }
2161
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ChainOperationEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i2$2.Clipboard }, { token: SettingsService }, { token: i4$1.DialogService }], target: i0.ɵɵFactoryTarget.Component }); }
2162
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: ChainOperationEditorComponent, isStandalone: true, selector: "gve-chain-operation-editor", inputs: { operation: { classPropertyName: "operation", publicName: "operation", isSignal: true, isRequired: false, transformFunction: null }, snapshot: { classPropertyName: "snapshot", publicName: "snapshot", isSignal: true, isRequired: false, transformFunction: null }, hidePreview: { classPropertyName: "hidePreview", publicName: "hidePreview", isSignal: true, isRequired: false, transformFunction: null }, featureDefs: { classPropertyName: "featureDefs", publicName: "featureDefs", isSignal: true, isRequired: false, transformFunction: null }, elementFeatureDefs: { classPropertyName: "elementFeatureDefs", publicName: "elementFeatureDefs", isSignal: true, isRequired: false, transformFunction: null }, diplomaticFeatureDefs: { classPropertyName: "diplomaticFeatureDefs", publicName: "diplomaticFeatureDefs", isSignal: true, isRequired: false, transformFunction: null }, rangePatch: { classPropertyName: "rangePatch", publicName: "rangePatch", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operation: "operationChange", operationChange: "operationChange", operationPreview: "operationPreview", operationCancel: "operationCancel" }, ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <!-- tabs -->\r\n <mat-tab-group\r\n [selectedIndex]=\"tabIndex()\"\r\n (selectedIndexChange)=\"onTabIndexChange($event)\"\r\n >\r\n <!-- GENERAL -->\r\n <mat-tab label=\"general\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <div id=\"id\" class=\"muted\">{{ id() }}</div>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n <mat-option [value]=\"0\"\r\n ><mat-icon>layers</mat-icon> replace</mat-option\r\n >\r\n <mat-option [value]=\"1\"\r\n ><mat-icon>close</mat-icon>delete</mat-option\r\n >\r\n <mat-option [value]=\"2\"\r\n ><mat-icon>last_page</mat-icon>add-before</mat-option\r\n >\r\n <mat-option [value]=\"3\"\r\n ><mat-icon>first_page</mat-icon>add-after</mat-option\r\n >\r\n <mat-option [value]=\"4\"\r\n ><mat-icon>login</mat-icon>move-before</mat-option\r\n >\r\n <mat-option [value]=\"5\"\r\n ><mat-icon>logout</mat-icon>move-after</mat-option\r\n >\r\n <mat-option [value]=\"6\"\r\n ><mat-icon>compare_arrows</mat-icon>swap</mat-option\r\n >\r\n <mat-option [value]=\"7\"\r\n ><mat-icon>edit_note</mat-icon>annotate</mat-option\r\n >\r\n </mat-select>\r\n @if ($any(type).errors?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- at -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>at</mat-label>\r\n <input matInput [formControl]=\"at\" type=\"number\" min=\"0\" />\r\n @if ($any(at).errors?.required && (at.dirty || at.touched)) {\r\n <mat-error>at required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- atAsIndex -->\r\n <mat-checkbox [formControl]=\"atAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- run -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>run</mat-label>\r\n <input matInput [formControl]=\"run\" type=\"number\" min=\"0\" />\r\n @if ($any(run).errors?.required && (run.dirty || run.touched)) {\r\n <mat-error>run required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- to -->\r\n @if (hasTo()) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to</mat-label>\r\n <input matInput [formControl]=\"to\" type=\"number\" min=\"0\" />\r\n @if ($any(to).errors?.required && (to.dirty || to.touched)) {\r\n <mat-error>to required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- toAsIndex -->\r\n <mat-checkbox [formControl]=\"toAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- toRun -->\r\n @if (hasToRun()) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to run</mat-label>\r\n <input matInput [formControl]=\"toRun\" type=\"number\" min=\"0\" />\r\n @if ( $any(toRun).errors?.required && (toRun.dirty || toRun.touched) )\r\n {\r\n <mat-error>to run required</mat-error>\r\n }\r\n </mat-form-field>\r\n } }\r\n\r\n <!-- value -->\r\n @if (hasValue()) {\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n </mat-form-field>\r\n }\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n @if ($any(rank).errors?.required && (rank.dirty || rank.touched)) {\r\n <mat-error>rank required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- groupId -->\r\n <mat-form-field>\r\n <mat-label>group ID</mat-label>\r\n <input matInput [formControl]=\"groupId\" />\r\n @if ( $any(groupId).errors?.required && (groupId.dirty ||\r\n groupId.touched) ) {\r\n <mat-error>group ID required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- inputTag -->\r\n <mat-form-field>\r\n <mat-label>input tag</mat-label>\r\n <input matInput [formControl]=\"inputTag\" />\r\n <mat-hint>blank=latest</mat-hint>\r\n </mat-form-field>\r\n\r\n <!-- outputTag -->\r\n <mat-form-field>\r\n <mat-label>output tag</mat-label>\r\n <input matInput [formControl]=\"outputTag\" />\r\n <mat-hint>blank=auto (vN)</mat-hint>\r\n </mat-form-field>\r\n </div>\r\n <div>\r\n <!-- features -->\r\n <fieldset>\r\n <legend>operation features</legend>\r\n <gve-feature-set-editor\r\n [isVar]=\"true\"\r\n [featNames]=\"featureDefs()?.names\"\r\n [featValues]=\"featureDefs()?.values\"\r\n [features]=\"features.value\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n />\r\n </fieldset>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- SOURCES -->\r\n <mat-tab label=\"sources\">\r\n <div>\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addSource()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n source\r\n </button>\r\n </div>\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>type</th>\r\n <th>id</th>\r\n <th>rank</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (s of sources.value; track s) {\r\n <tr [class.selected]=\"s === editedSource()\">\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editSource($index)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button type=\"button\" mat-icon-button color=\"warn\">\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ s.type }}</td>\r\n <td>{{ s.id }}</td>\r\n <td>{{ s.rank }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n @if (editedSource()) {\r\n <!-- source editor -->\r\n <mat-expansion-panel\r\n [disabled]=\"!editedSource()\"\r\n [expanded]=\"editedSource()\"\r\n >\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>source</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-operation-source-editor\r\n [source]=\"editedSource()\"\r\n (sourceChange)=\"onSourceChange($event!)\"\r\n (sourceCancel)=\"closeSource()\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n </mat-tab>\r\n\r\n <!-- DIPLOMATIC -->\r\n <mat-tab label=\"diplomatic\">\r\n <div class=\"toolbar-row\">\r\n <button\r\n id=\"btn-save\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Save to file\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"saveSvg()\"\r\n >\r\n <mat-icon>save</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-load\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Load from file\"\r\n (click)=\"loadSvg()\"\r\n >\r\n <mat-icon>folder</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-copy\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy\"\r\n [cdkCopyToClipboard]=\"svg.value\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-paste\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Set SVG from clipboard\"\r\n (click)=\"setSvgFromClipboard()\"\r\n >\r\n <mat-icon>content_paste_go</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-editor\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open SVG external editor\"\r\n (click)=\"openSvgEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-decimals\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Remove decimals\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"removeDecimals()\"\r\n >\r\n <mat-icon>pin</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-group\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Wrap code in group\"\r\n (click)=\"wrapInGroup()\"\r\n >\r\n <mat-icon>code</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div id=\"code\">\r\n <nge-monaco-editor\r\n style=\"--editor-height: 400px\"\r\n (ready)=\"onCreateEditor($event)\"\r\n />\r\n @if (svg.invalid) {\r\n <mat-error>invalid SVG</mat-error>\r\n }\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- ELEMENTS -->\r\n <mat-tab label=\"elements\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>id</th>\r\n <th>visual</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (e of elements(); track e.id) {\r\n <tr [class.selected]=\"e.id === editedElementId()\">\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Edit element features\"\r\n (click)=\"editElementFeatures(e)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n matTooltip=\"Delete all element features\"\r\n (click)=\"deleteElementFeatures(e)\"\r\n [disabled]=\"!$any(elementFeatures.value)[e.id]?.length\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n <span\r\n [matBadge]=\"$any(elementFeatures.value)[e.id]?.length || 0\"\r\n [matBadgeHidden]=\"!$any(elementFeatures.value)[e.id]?.length\"\r\n matBadgeOverlap=\"false\"\r\n >{{ e.id }}</span\r\n >\r\n </td>\r\n <td class=\"svg-cell\">\r\n <svg [innerHTML]=\"e.outerHTML | safeHtml : 'html'\"></svg>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n @if (editedElementId()) {\r\n <mat-expansion-panel\r\n [disabled]=\"!editedElementId()\"\r\n [expanded]=\"editedElementId()\"\r\n >\r\n <mat-expansion-panel-header>{{\r\n editedElementId()\r\n }}</mat-expansion-panel-header>\r\n <gve-feature-set-editor\r\n [featNames]=\"elementFeatureDefs()?.names\"\r\n [featValues]=\"elementFeatureDefs()?.values\"\r\n [features]=\"elementFeatures.value[editedElementId()!] || []\"\r\n (featuresChange)=\"onElementFeaturesChange($event)\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n </mat-tab>\r\n\r\n <!-- DP FEATS -->\r\n <mat-tab label=\"d-features\">\r\n <div>\r\n <mat-checkbox [formControl]=\"newTextHidden\">hide new text</mat-checkbox>\r\n </div>\r\n <gve-feature-set-editor\r\n [featNames]=\"diplomaticFeatureDefs()?.names\"\r\n [featValues]=\"diplomaticFeatureDefs()?.values\"\r\n [features]=\"dpFeatures.value\"\r\n (featuresChange)=\"onDpFeaturesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- buttons -->\r\n <div id=\"submit-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard operation\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n @if (!hidePreview()) {\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Preview operation\"\r\n (click)=\"requestPreview()\"\r\n >\r\n <mat-icon class=\"mat-primary\">preview</mat-icon>\r\n </button>\r\n }\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save operation\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#submit-row{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap;margin-top:8px;border-top:1px solid silver}.toolbar-row{display:flex;align-items:center;flex-wrap:wrap}.nr{width:5em}.long-text{width:100%;max-width:800px}#id{border-radius:6px;padding:4px;background-color:#beb9b9;color:#fff;margin-top:-16px}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:silver}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}#btn-save{color:#072d3e}#btn-load{color:#dbd112}#btn-copy{color:#22549f}#btn-preview{color:#095409}#code{height:500px;border:1px solid silver}#monaco{height:100%}.svg-cell{padding:0 8px}.svg-cell svg{border:1px solid silver;width:100%;height:auto}#preview{box-sizing:border-box;width:100%;border:1px solid silver;border-radius:4px;padding:8px}.tree-invisible{display:none}.tree ul,.tree li{margin-top:0;margin-bottom:0;list-style-type:none}.selected-node{background-color:#e5e5e5}.child-title{font-weight:700;margin:0;background-color:#ccc;color:#fff;padding:8px}#tree-container{display:grid;grid-template-rows:auto;grid-template-columns:auto 1fr;grid-template-areas:\"nav ed\";gap:0 16px;align-items:stretch}#tree-nav{grid-area:nav;border:1px solid silver;border-radius:6px;margin:8px 0;padding-right:8px}#tree-ed{grid-area:ed;border:1px solid silver;border-radius:6px;margin:8px 0}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"nav\" \"ed\";gap:16px 0;align-items:start}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ClipboardModule }, { kind: "directive", type: i2$2.CdkCopyToClipboard, selector: "[cdkCopyToClipboard]", inputs: ["cdkCopyToClipboard", "cdkCopyToClipboardAttempts"], outputs: ["cdkCopyToClipboardCopied"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i5$1.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$2.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i14.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i14.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: NgeMonacoModule }, { kind: "component", type: i15.NgeMonacoEditorComponent, selector: "nge-monaco-editor", inputs: ["autoLayout", "options"], outputs: ["ready"] }, { kind: "component", type: FeatureSetEditorComponent, selector: "gve-feature-set-editor", inputs: ["isVar", "featNames", "featValues", "filterThreshold", "features"], outputs: ["featuresChange"] }, { kind: "component", type: OperationSourceEditorComponent, selector: "gve-operation-source-editor", inputs: ["source", "ids", "types"], outputs: ["sourceChange", "sourceCancel"] }, { kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }] }); }
2107
2163
  }
2108
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: ChainOperationEditorComponent, decorators: [{
2164
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ChainOperationEditorComponent, decorators: [{
2109
2165
  type: Component,
2110
2166
  args: [{ selector: 'gve-chain-operation-editor', imports: [
2111
2167
  ReactiveFormsModule,
@@ -2125,7 +2181,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
2125
2181
  FeatureSetEditorComponent,
2126
2182
  OperationSourceEditorComponent,
2127
2183
  ], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <!-- tabs -->\r\n <mat-tab-group\r\n [selectedIndex]=\"tabIndex()\"\r\n (selectedIndexChange)=\"onTabIndexChange($event)\"\r\n >\r\n <!-- GENERAL -->\r\n <mat-tab label=\"general\">\r\n <div class=\"form-row\">\r\n <!-- id -->\r\n <div id=\"id\" class=\"muted\">{{ id() }}</div>\r\n\r\n <!-- type -->\r\n <mat-form-field>\r\n <mat-label>type</mat-label>\r\n <mat-select [formControl]=\"type\">\r\n <mat-option [value]=\"0\"\r\n ><mat-icon>layers</mat-icon> replace</mat-option\r\n >\r\n <mat-option [value]=\"1\"\r\n ><mat-icon>close</mat-icon>delete</mat-option\r\n >\r\n <mat-option [value]=\"2\"\r\n ><mat-icon>last_page</mat-icon>add-before</mat-option\r\n >\r\n <mat-option [value]=\"3\"\r\n ><mat-icon>first_page</mat-icon>add-after</mat-option\r\n >\r\n <mat-option [value]=\"4\"\r\n ><mat-icon>login</mat-icon>move-before</mat-option\r\n >\r\n <mat-option [value]=\"5\"\r\n ><mat-icon>logout</mat-icon>move-after</mat-option\r\n >\r\n <mat-option [value]=\"6\"\r\n ><mat-icon>compare_arrows</mat-icon>swap</mat-option\r\n >\r\n <mat-option [value]=\"7\"\r\n ><mat-icon>edit_note</mat-icon>annotate</mat-option\r\n >\r\n </mat-select>\r\n @if ($any(type).errors?.required && (type.dirty || type.touched)) {\r\n <mat-error>type required</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- at -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>at</mat-label>\r\n <input matInput [formControl]=\"at\" type=\"number\" min=\"0\" />\r\n @if ($any(at).errors?.required && (at.dirty || at.touched)) {\r\n <mat-error>at required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- atAsIndex -->\r\n <mat-checkbox [formControl]=\"atAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- run -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>run</mat-label>\r\n <input matInput [formControl]=\"run\" type=\"number\" min=\"0\" />\r\n @if ($any(run).errors?.required && (run.dirty || run.touched)) {\r\n <mat-error>run required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- to -->\r\n @if (hasTo()) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to</mat-label>\r\n <input matInput [formControl]=\"to\" type=\"number\" min=\"0\" />\r\n @if ($any(to).errors?.required && (to.dirty || to.touched)) {\r\n <mat-error>to required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- toAsIndex -->\r\n <mat-checkbox [formControl]=\"toAsIndex\">idx</mat-checkbox>\r\n\r\n <!-- toRun -->\r\n @if (hasToRun()) {\r\n <mat-form-field class=\"nr\">\r\n <mat-label>to run</mat-label>\r\n <input matInput [formControl]=\"toRun\" type=\"number\" min=\"0\" />\r\n @if ( $any(toRun).errors?.required && (toRun.dirty || toRun.touched) )\r\n {\r\n <mat-error>to run required</mat-error>\r\n }\r\n </mat-form-field>\r\n } }\r\n\r\n <!-- value -->\r\n @if (hasValue()) {\r\n <mat-form-field>\r\n <mat-label>value</mat-label>\r\n <input matInput [formControl]=\"value\" />\r\n </mat-form-field>\r\n }\r\n </div>\r\n <div class=\"form-row\">\r\n <!-- rank -->\r\n <mat-form-field class=\"nr\">\r\n <mat-label>rank</mat-label>\r\n <input matInput [formControl]=\"rank\" type=\"number\" min=\"0\" />\r\n @if ($any(rank).errors?.required && (rank.dirty || rank.touched)) {\r\n <mat-error>rank required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- groupId -->\r\n <mat-form-field>\r\n <mat-label>group ID</mat-label>\r\n <input matInput [formControl]=\"groupId\" />\r\n @if ( $any(groupId).errors?.required && (groupId.dirty ||\r\n groupId.touched) ) {\r\n <mat-error>group ID required</mat-error>\r\n }\r\n </mat-form-field>\r\n\r\n <!-- inputTag -->\r\n <mat-form-field>\r\n <mat-label>input tag</mat-label>\r\n <input matInput [formControl]=\"inputTag\" />\r\n <mat-hint>blank=latest</mat-hint>\r\n </mat-form-field>\r\n\r\n <!-- outputTag -->\r\n <mat-form-field>\r\n <mat-label>output tag</mat-label>\r\n <input matInput [formControl]=\"outputTag\" />\r\n <mat-hint>blank=auto (vN)</mat-hint>\r\n </mat-form-field>\r\n </div>\r\n <div>\r\n <!-- features -->\r\n <fieldset>\r\n <legend>operation features</legend>\r\n <gve-feature-set-editor\r\n [isVar]=\"true\"\r\n [featNames]=\"featureDefs()?.names\"\r\n [featValues]=\"featureDefs()?.values\"\r\n [features]=\"features.value\"\r\n (featuresChange)=\"onFeaturesChange($event)\"\r\n />\r\n </fieldset>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- SOURCES -->\r\n <mat-tab label=\"sources\">\r\n <div>\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n (click)=\"addSource()\"\r\n >\r\n <mat-icon>add_circle</mat-icon>\r\n source\r\n </button>\r\n </div>\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>type</th>\r\n <th>id</th>\r\n <th>rank</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (s of sources.value; track s) {\r\n <tr [class.selected]=\"s === editedSource()\">\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n (click)=\"editSource($index)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button type=\"button\" mat-icon-button color=\"warn\">\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ s.type }}</td>\r\n <td>{{ s.id }}</td>\r\n <td>{{ s.rank }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n @if (editedSource()) {\r\n <!-- source editor -->\r\n <mat-expansion-panel\r\n [disabled]=\"!editedSource()\"\r\n [expanded]=\"editedSource()\"\r\n >\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>source</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <gve-operation-source-editor\r\n [source]=\"editedSource()\"\r\n (sourceChange)=\"onSourceChange($event!)\"\r\n (sourceCancel)=\"closeSource()\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n </mat-tab>\r\n\r\n <!-- DIPLOMATIC -->\r\n <mat-tab label=\"diplomatic\">\r\n <div class=\"toolbar-row\">\r\n <button\r\n id=\"btn-save\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Save to file\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"saveSvg()\"\r\n >\r\n <mat-icon>save</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-load\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Load from file\"\r\n (click)=\"loadSvg()\"\r\n >\r\n <mat-icon>folder</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-copy\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy\"\r\n [cdkCopyToClipboard]=\"svg.value\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-paste\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Set SVG from clipboard\"\r\n (click)=\"setSvgFromClipboard()\"\r\n >\r\n <mat-icon>content_paste_go</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-editor\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open SVG external editor\"\r\n (click)=\"openSvgEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-decimals\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Remove decimals\"\r\n [disabled]=\"!svg.value\"\r\n (click)=\"removeDecimals()\"\r\n >\r\n <mat-icon>pin</mat-icon>\r\n </button>\r\n <button\r\n id=\"btn-group\"\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Wrap code in group\"\r\n (click)=\"wrapInGroup()\"\r\n >\r\n <mat-icon>code</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div id=\"code\">\r\n <nge-monaco-editor\r\n style=\"--editor-height: 400px\"\r\n (ready)=\"onCreateEditor($event)\"\r\n />\r\n @if (svg.invalid) {\r\n <mat-error>invalid SVG</mat-error>\r\n }\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- ELEMENTS -->\r\n <mat-tab label=\"elements\">\r\n <table>\r\n <thead>\r\n <tr>\r\n <th></th>\r\n <th>id</th>\r\n <th>visual</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (e of elements(); track e.id) {\r\n <tr [class.selected]=\"e.id === editedElementId()\">\r\n <td class=\"fit-width\">\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Edit element features\"\r\n (click)=\"editElementFeatures(e)\"\r\n >\r\n <mat-icon class=\"mat-primary\">edit</mat-icon>\r\n </button>\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n matTooltip=\"Delete all element features\"\r\n (click)=\"deleteElementFeatures(e)\"\r\n [disabled]=\"!$any(elementFeatures.value)[e.id]?.length\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n </td>\r\n <td>\r\n <span\r\n [matBadge]=\"$any(elementFeatures.value)[e.id]?.length || 0\"\r\n [matBadgeHidden]=\"!$any(elementFeatures.value)[e.id]?.length\"\r\n matBadgeOverlap=\"false\"\r\n >{{ e.id }}</span\r\n >\r\n </td>\r\n <td class=\"svg-cell\">\r\n <svg [innerHTML]=\"e.outerHTML | safeHtml : 'html'\"></svg>\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n @if (editedElementId()) {\r\n <mat-expansion-panel\r\n [disabled]=\"!editedElementId()\"\r\n [expanded]=\"editedElementId()\"\r\n >\r\n <mat-expansion-panel-header>{{\r\n editedElementId()\r\n }}</mat-expansion-panel-header>\r\n <gve-feature-set-editor\r\n [featNames]=\"elementFeatureDefs()?.names\"\r\n [featValues]=\"elementFeatureDefs()?.values\"\r\n [features]=\"elementFeatures.value[editedElementId()!] || []\"\r\n (featuresChange)=\"onElementFeaturesChange($event)\"\r\n />\r\n </mat-expansion-panel>\r\n }\r\n </mat-tab>\r\n\r\n <!-- DP FEATS -->\r\n <mat-tab label=\"d-features\">\r\n <div>\r\n <mat-checkbox [formControl]=\"newTextHidden\">hide new text</mat-checkbox>\r\n </div>\r\n <gve-feature-set-editor\r\n [featNames]=\"diplomaticFeatureDefs()?.names\"\r\n [featValues]=\"diplomaticFeatureDefs()?.values\"\r\n [features]=\"dpFeatures.value\"\r\n (featuresChange)=\"onDpFeaturesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- buttons -->\r\n <div id=\"submit-row\">\r\n <button\r\n type=\"button\"\r\n color=\"warn\"\r\n mat-icon-button\r\n matTooltip=\"Discard operation\"\r\n (click)=\"cancel()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n @if (!hidePreview()) {\r\n <button\r\n type=\"button\"\r\n color=\"primary\"\r\n mat-icon-button\r\n matTooltip=\"Preview operation\"\r\n (click)=\"requestPreview()\"\r\n >\r\n <mat-icon class=\"mat-primary\">preview</mat-icon>\r\n </button>\r\n }\r\n <button\r\n type=\"submit\"\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save operation\"\r\n [disabled]=\"form.invalid || form.pristine\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}#submit-row{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap;margin-top:8px;border-top:1px solid silver}.toolbar-row{display:flex;align-items:center;flex-wrap:wrap}.nr{width:5em}.long-text{width:100%;max-width:800px}#id{border-radius:6px;padding:4px;background-color:#beb9b9;color:#fff;margin-top:-16px}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:silver}table{width:100%;border-collapse:collapse}tbody tr:nth-child(odd){background-color:#e2e2e2}th{text-align:left;font-weight:400;color:silver}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#d0d0d0!important}#btn-save{color:#072d3e}#btn-load{color:#dbd112}#btn-copy{color:#22549f}#btn-preview{color:#095409}#code{height:500px;border:1px solid silver}#monaco{height:100%}.svg-cell{padding:0 8px}.svg-cell svg{border:1px solid silver;width:100%;height:auto}#preview{box-sizing:border-box;width:100%;border:1px solid silver;border-radius:4px;padding:8px}.tree-invisible{display:none}.tree ul,.tree li{margin-top:0;margin-bottom:0;list-style-type:none}.selected-node{background-color:#e5e5e5}.child-title{font-weight:700;margin:0;background-color:#ccc;color:#fff;padding:8px}#tree-container{display:grid;grid-template-rows:auto;grid-template-columns:auto 1fr;grid-template-areas:\"nav ed\";gap:0 16px;align-items:stretch}#tree-nav{grid-area:nav;border:1px solid silver;border-radius:6px;margin:8px 0;padding-right:8px}#tree-ed{grid-area:ed;border:1px solid silver;border-radius:6px;margin:8px 0}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"nav\" \"ed\";gap:16px 0;align-items:start}}\n"] }]
2128
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$2.Clipboard }, { type: SettingsService }, { type: i4$2.DialogService }] });
2184
+ }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i2$2.Clipboard }, { type: SettingsService }, { type: i4$1.DialogService }], propDecorators: { operation: [{ type: i0.Input, args: [{ isSignal: true, alias: "operation", required: false }] }, { type: i0.Output, args: ["operationChange"] }], snapshot: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapshot", required: false }] }], hidePreview: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidePreview", required: false }] }], featureDefs: [{ type: i0.Input, args: [{ isSignal: true, alias: "featureDefs", required: false }] }], elementFeatureDefs: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementFeatureDefs", required: false }] }], diplomaticFeatureDefs: [{ type: i0.Input, args: [{ isSignal: true, alias: "diplomaticFeatureDefs", required: false }] }], rangePatch: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangePatch", required: false }] }], operationChange: [{ type: i0.Output, args: ["operationChange"] }], operationPreview: [{ type: i0.Output, args: ["operationPreview"] }], operationCancel: [{ type: i0.Output, args: ["operationCancel"] }] } });
2129
2185
 
2130
2186
  /**
2131
2187
  * 🔑 `gve-feature-set-view`
@@ -2193,10 +2249,10 @@ class FeatureSetViewComponent {
2193
2249
  ngOnDestroy() {
2194
2250
  this._sub?.unsubscribe();
2195
2251
  }
2196
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: FeatureSetViewComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
2197
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: FeatureSetViewComponent, isStandalone: true, selector: "gve-feature-set-view", inputs: { features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null }, featNames: { classPropertyName: "featNames", publicName: "featNames", isSignal: true, isRequired: false, transformFunction: null }, featValues: { classPropertyName: "featValues", publicName: "featValues", isSignal: true, isRequired: false, transformFunction: null }, filterThreshold: { classPropertyName: "filterThreshold", publicName: "filterThreshold", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div>\r\n <!-- filter -->\r\n <div>\r\n @if (filterThreshold() === 0 || features()!.length > filterThreshold()) {\r\n <div class=\"form-row\">\r\n <mat-form-field id=\"filter\">\r\n <mat-label>filter</mat-label>\r\n <input matInput [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n <span id=\"badge\">{{ filteredFeatures().length }}</span>\r\n </div>\r\n }\r\n\r\n <!-- list -->\r\n @if (filteredFeatures().length) {\r\n <table>\r\n <tbody>\r\n @for (feature of filteredFeatures(); track $index) {\r\n <tr>\r\n <th>\r\n @if (featNames.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </th>\r\n <td>\r\n @if (featValues()) {\r\n <span>{{\r\n feature.value | flatLookup : featValues()![feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse}th{text-align:left;background-color:#c8d9eb;color:#333;font-weight:400}th,td{border:1px solid silver;padding:4px}tr:nth-child(2n){background-color:#dfdfdf}#filter{width:7em}#badge{border:1px solid silver;border-radius:6px;padding:4px;margin-top:-18px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "pipe", type: FlatLookupPipe, name: "flatLookup" }] }); }
2252
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: FeatureSetViewComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
2253
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: FeatureSetViewComponent, isStandalone: true, selector: "gve-feature-set-view", inputs: { features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null }, featNames: { classPropertyName: "featNames", publicName: "featNames", isSignal: true, isRequired: false, transformFunction: null }, featValues: { classPropertyName: "featValues", publicName: "featValues", isSignal: true, isRequired: false, transformFunction: null }, filterThreshold: { classPropertyName: "filterThreshold", publicName: "filterThreshold", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div>\r\n <!-- filter -->\r\n <div>\r\n @if (filterThreshold() === 0 || features()!.length > filterThreshold()) {\r\n <div class=\"form-row\">\r\n <mat-form-field id=\"filter\">\r\n <mat-label>filter</mat-label>\r\n <input matInput [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n <span id=\"badge\">{{ filteredFeatures().length }}</span>\r\n </div>\r\n }\r\n\r\n <!-- list -->\r\n @if (filteredFeatures().length) {\r\n <table>\r\n <tbody>\r\n @for (feature of filteredFeatures(); track $index) {\r\n <tr>\r\n <th>\r\n @if (featNames.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </th>\r\n <td>\r\n @if (featValues()) {\r\n <span>{{\r\n feature.value | flatLookup : featValues()![feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse}th{text-align:left;background-color:#c8d9eb;color:#333;font-weight:400}th,td{border:1px solid silver;padding:4px}tr:nth-child(2n){background-color:#dfdfdf}#filter{width:7em}#badge{border:1px solid silver;border-radius:6px;padding:4px;margin-top:-18px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "pipe", type: FlatLookupPipe, name: "flatLookup" }] }); }
2198
2254
  }
2199
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: FeatureSetViewComponent, decorators: [{
2255
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: FeatureSetViewComponent, decorators: [{
2200
2256
  type: Component,
2201
2257
  args: [{ selector: 'gve-feature-set-view', imports: [
2202
2258
  ReactiveFormsModule,
@@ -2206,7 +2262,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
2206
2262
  MatInputModule,
2207
2263
  FlatLookupPipe,
2208
2264
  ], template: "<div>\r\n <!-- filter -->\r\n <div>\r\n @if (filterThreshold() === 0 || features()!.length > filterThreshold()) {\r\n <div class=\"form-row\">\r\n <mat-form-field id=\"filter\">\r\n <mat-label>filter</mat-label>\r\n <input matInput [formControl]=\"filter\" />\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matSuffix\r\n (click)=\"filter.reset()\"\r\n [disabled]=\"!filter.value\"\r\n [attr.aria-label]=\"'Reset filter'\"\r\n >\r\n <mat-icon color=\"warn\" class=\"mat-warn\">cancel</mat-icon>\r\n </button>\r\n </mat-form-field>\r\n <span id=\"badge\">{{ filteredFeatures().length }}</span>\r\n </div>\r\n }\r\n\r\n <!-- list -->\r\n @if (filteredFeatures().length) {\r\n <table>\r\n <tbody>\r\n @for (feature of filteredFeatures(); track $index) {\r\n <tr>\r\n <th>\r\n @if (featNames.length) {\r\n <span>{{\r\n feature.name | flatLookup : featNames : \"id\" : \"label\"\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.name }}</span>\r\n }\r\n </th>\r\n <td>\r\n @if (featValues()) {\r\n <span>{{\r\n feature.value | flatLookup : featValues()![feature.name]\r\n }}</span>\r\n } @else {\r\n <span>{{ feature.value }}</span>\r\n }\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.in-row-button{margin-top:-16px}table{width:100%;border-collapse:collapse}th{text-align:left;background-color:#c8d9eb;color:#333;font-weight:400}th,td{border:1px solid silver;padding:4px}tr:nth-child(2n){background-color:#dfdfdf}#filter{width:7em}#badge{border:1px solid silver;border-radius:6px;padding:4px;margin-top:-18px}\n"] }]
2209
- }], ctorParameters: () => [{ type: i1.FormBuilder }] });
2265
+ }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { features: [{ type: i0.Input, args: [{ isSignal: true, alias: "features", required: false }] }], featNames: [{ type: i0.Input, args: [{ isSignal: true, alias: "featNames", required: false }] }], featValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "featValues", required: false }] }], filterThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterThreshold", required: false }] }] } });
2210
2266
 
2211
2267
  /**
2212
2268
  * 🔑 `gve-steps-map`
@@ -2316,13 +2372,13 @@ class StepsMapComponent {
2316
2372
  onStepClick(step) {
2317
2373
  this.selectedStep.set(step);
2318
2374
  }
2319
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: StepsMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2320
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: StepsMapComponent, isStandalone: true, selector: "gve-steps-map", inputs: { steps: { classPropertyName: "steps", publicName: "steps", isSignal: true, isRequired: false, transformFunction: null }, selectedStep: { classPropertyName: "selectedStep", publicName: "selectedStep", isSignal: true, isRequired: false, transformFunction: null }, textFontSize: { classPropertyName: "textFontSize", publicName: "textFontSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedStep: "selectedStepChange" }, ngImport: i0, template: "<div>\r\n @if (steps().length) { @for (step of steps(); track step.outputTag) {\r\n <div\r\n matRipple\r\n class=\"step form-row\"\r\n [class.selected]=\"step === selectedStep()\"\r\n (click)=\"onStepClick(step)\"\r\n >\r\n <!-- tag -->\r\n <div class=\"tag\">\r\n <span class=\"muted-tag\">{{ step.inputTag }} &#x25b6; </span>\r\n {{ step.outputTag }}\r\n </div>\r\n <!-- text lines -->\r\n <div class=\"text\" [style.fontSize]=\"textFontSize()\">\r\n @for (line of lines()[step.outputTag]; track $index) {\r\n <div class=\"line\">{{ line }}</div>\r\n }\r\n <div></div>\r\n </div>\r\n </div>\r\n } }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:start;flex-wrap:wrap}.form-row *{flex:0 0 auto}.selected{background-color:#ffc}.step{border:1px solid rgb(68,114,253);border-radius:6px;margin:8px 0}.tag{background-color:#4472fd;color:#fff;border:1px solid rgb(68,114,253);border-radius:6px;padding:4px;cursor:pointer}.muted-tag{color:silver}.text{font-size:.5em}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i1$1.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: MatTooltipModule }] }); }
2375
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: StepsMapComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2376
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: StepsMapComponent, isStandalone: true, selector: "gve-steps-map", inputs: { steps: { classPropertyName: "steps", publicName: "steps", isSignal: true, isRequired: false, transformFunction: null }, selectedStep: { classPropertyName: "selectedStep", publicName: "selectedStep", isSignal: true, isRequired: false, transformFunction: null }, textFontSize: { classPropertyName: "textFontSize", publicName: "textFontSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedStep: "selectedStepChange" }, ngImport: i0, template: "<div>\r\n @if (steps().length) { @for (step of steps(); track step.outputTag) {\r\n <div\r\n matRipple\r\n class=\"step form-row\"\r\n [class.selected]=\"step === selectedStep()\"\r\n (click)=\"onStepClick(step)\"\r\n >\r\n <!-- tag -->\r\n <div class=\"tag\">\r\n <span class=\"muted-tag\">{{ step.inputTag }} &#x25b6; </span>\r\n {{ step.outputTag }}\r\n </div>\r\n <!-- text lines -->\r\n <div class=\"text\" [style.fontSize]=\"textFontSize()\">\r\n @for (line of lines()[step.outputTag]; track $index) {\r\n <div class=\"line\">{{ line }}</div>\r\n }\r\n <div></div>\r\n </div>\r\n </div>\r\n } }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:start;flex-wrap:wrap}.form-row *{flex:0 0 auto}.selected{background-color:#ffc}.step{border:1px solid rgb(68,114,253);border-radius:6px;margin:8px 0}.tag{background-color:#4472fd;color:#fff;border:1px solid rgb(68,114,253);border-radius:6px;padding:4px;cursor:pointer}.muted-tag{color:silver}.text{font-size:.5em}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i1$1.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: MatTooltipModule }] }); }
2321
2377
  }
2322
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: StepsMapComponent, decorators: [{
2378
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: StepsMapComponent, decorators: [{
2323
2379
  type: Component,
2324
2380
  args: [{ selector: 'gve-steps-map', imports: [CommonModule, MatButtonModule, MatRippleModule, MatTooltipModule], template: "<div>\r\n @if (steps().length) { @for (step of steps(); track step.outputTag) {\r\n <div\r\n matRipple\r\n class=\"step form-row\"\r\n [class.selected]=\"step === selectedStep()\"\r\n (click)=\"onStepClick(step)\"\r\n >\r\n <!-- tag -->\r\n <div class=\"tag\">\r\n <span class=\"muted-tag\">{{ step.inputTag }} &#x25b6; </span>\r\n {{ step.outputTag }}\r\n </div>\r\n <!-- text lines -->\r\n <div class=\"text\" [style.fontSize]=\"textFontSize()\">\r\n @for (line of lines()[step.outputTag]; track $index) {\r\n <div class=\"line\">{{ line }}</div>\r\n }\r\n <div></div>\r\n </div>\r\n </div>\r\n } }\r\n</div>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:start;flex-wrap:wrap}.form-row *{flex:0 0 auto}.selected{background-color:#ffc}.step{border:1px solid rgb(68,114,253);border-radius:6px;margin:8px 0}.tag{background-color:#4472fd;color:#fff;border:1px solid rgb(68,114,253);border-radius:6px;padding:4px;cursor:pointer}.muted-tag{color:silver}.text{font-size:.5em}\n"] }]
2325
- }], ctorParameters: () => [] });
2381
+ }], ctorParameters: () => [], propDecorators: { steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: false }] }], selectedStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedStep", required: false }] }, { type: i0.Output, args: ["selectedStepChange"] }], textFontSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "textFontSize", required: false }] }] } });
2326
2382
 
2327
2383
  /**
2328
2384
  * 🔑 `gve-chain-result-view`
@@ -2361,6 +2417,17 @@ class ChainResultViewComponent {
2361
2417
  this.step = signal(undefined, ...(ngDevMode ? [{ debugName: "step" }] : []));
2362
2418
  this.selection = signal(undefined, ...(ngDevMode ? [{ debugName: "selection" }] : []));
2363
2419
  this.selectionFeatures = signal([], ...(ngDevMode ? [{ debugName: "selectionFeatures" }] : []));
2420
+ this.selectionRange = signal(undefined, ...(ngDevMode ? [{ debugName: "selectionRange" }] : []));
2421
+ /**
2422
+ * True to disable range picking. For instance the snapshot editor,
2423
+ * which uses rangePick to set the edited operation's range, disables
2424
+ * this when no operation is being edited.
2425
+ */
2426
+ this.disabledRangePick = input(true, ...(ngDevMode ? [{ debugName: "disabledRangePick" }] : []));
2427
+ /**
2428
+ * Emitted when a range is picked.
2429
+ */
2430
+ this.rangePick = output();
2364
2431
  // bind callback methods
2365
2432
  this.getCharColor = this.getCharColor.bind(this);
2366
2433
  // this.getCharBorderColor = this.getCharBorderColor.bind(this);
@@ -2514,6 +2581,7 @@ class ChainResultViewComponent {
2514
2581
  const node = this.findNode(event.char.id);
2515
2582
  this.selection.set(`${event.char.label} (${node.sourceTag})`);
2516
2583
  this.selectionFeatures.set([...features]);
2584
+ this.selectionRange.set({ at: event.char.id, run: 1 });
2517
2585
  }
2518
2586
  onTextRangePick(range) {
2519
2587
  if (range.run === 1) {
@@ -2521,6 +2589,7 @@ class ChainResultViewComponent {
2521
2589
  return;
2522
2590
  }
2523
2591
  this.selection.set(range.at + '×' + range.run);
2592
+ this.selectionRange.set(range);
2524
2593
  const end = range.at + range.run;
2525
2594
  const features = [];
2526
2595
  const tagNodes = this.result().taggedNodes[this.tag.value];
@@ -2548,10 +2617,15 @@ class ChainResultViewComponent {
2548
2617
  this.tag.setValue(step.outputTag);
2549
2618
  }
2550
2619
  }
2551
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: ChainResultViewComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
2552
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: ChainResultViewComponent, isStandalone: true, selector: "gve-chain-result-view", inputs: { result: { classPropertyName: "result", publicName: "result", isSignal: true, isRequired: false, transformFunction: null }, initialStepIndex: { classPropertyName: "initialStepIndex", publicName: "initialStepIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { stepPick: "stepPick" }, ngImport: i0, template: "@if (result()) {\r\n<div id=\"container\">\r\n <div id=\"bar\" class=\"form-row\">\r\n <!-- version -->\r\n @if (versionTags().length) {\r\n <mat-form-field>\r\n <mat-label>version</mat-label>\r\n <mat-select [formControl]=\"versionTag\">\r\n <mat-option [value]=\"null\">-</mat-option>\r\n @for (t of versionTags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n <!-- tag -->\r\n @if (tags().length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <!-- step -->\r\n @if (step()) {\r\n <div id=\"step\">\r\n <!-- text -->\r\n <div id=\"text\">\r\n <gve-base-text-view\r\n [text]=\"result()!.taggedNodes[step()!.outputTag]\"\r\n [colorCallback]=\"getCharColor\"\r\n (charPick)=\"onTextCharPick($event)\"\r\n (rangePick)=\"onTextRangePick($event)\"\r\n />\r\n </div>\r\n <!-- features -->\r\n <div id=\"feats\" class=\"form-row-top\">\r\n <div id=\"g-feats\">\r\n <div class=\"feat-header\">{{ step()!.outputTag }}</div>\r\n <gve-feature-set-view [features]=\"step()!.featureSet.features\" />\r\n </div>\r\n <div id=\"n-feats\">\r\n @if (selectionFeatures().length) {\r\n <div class=\"feat-header\">{{ selection() }}</div>\r\n <gve-feature-set-view [features]=\"selectionFeatures()\" />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- steps map -->\r\n <div id=\"map\">\r\n <gve-steps-map\r\n [steps]=\"result()!.steps\"\r\n [selectedStep]=\"step()\"\r\n (selectedStepChange)=\"onStepChange($event)\"\r\n />\r\n </div>\r\n</div>\r\n}\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.form-row-top{display:flex;gap:8px;align-items:start;flex-wrap:wrap}div#container{display:grid;grid-template-rows:auto 1fr;grid-template-columns:3fr 1fr;grid-template-areas:\"bar map\" \"step map\";gap:8px}div#bar{grid-area:bar}div#map{grid-area:map}div#step{grid-area:step}.feat-header{background-color:#3e92cc;color:#fff;text-align:center;border:1px solid #3e92cc;border-top-left-radius:4px;border-top-right-radius:4px}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"bar\" \"step\"}#map{display:none}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: FeatureSetViewComponent, selector: "gve-feature-set-view", inputs: ["features", "featNames", "featValues", "filterThreshold"] }, { kind: "component", type: StepsMapComponent, selector: "gve-steps-map", inputs: ["steps", "selectedStep", "textFontSize"], outputs: ["selectedStepChange"] }, { kind: "component", type: BaseTextViewComponent, selector: "gve-base-text-view", inputs: ["defaultColor", "defaultBorderColor", "selectionColor", "hasLineNumber", "text", "colorCallback", "borderColorCallback"], outputs: ["charPick", "rangePick"] }] }); }
2620
+ pickRange() {
2621
+ if (this.selectionRange()) {
2622
+ this.rangePick.emit(this.selectionRange());
2623
+ }
2624
+ }
2625
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ChainResultViewComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
2626
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: ChainResultViewComponent, isStandalone: true, selector: "gve-chain-result-view", inputs: { result: { classPropertyName: "result", publicName: "result", isSignal: true, isRequired: false, transformFunction: null }, initialStepIndex: { classPropertyName: "initialStepIndex", publicName: "initialStepIndex", isSignal: true, isRequired: false, transformFunction: null }, disabledRangePick: { classPropertyName: "disabledRangePick", publicName: "disabledRangePick", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { stepPick: "stepPick", rangePick: "rangePick" }, ngImport: i0, template: "@if (result()) {\r\n<div id=\"container\">\r\n <div id=\"bar\" class=\"form-row\">\r\n <!-- version -->\r\n @if (versionTags().length) {\r\n <mat-form-field>\r\n <mat-label>version</mat-label>\r\n <mat-select [formControl]=\"versionTag\">\r\n <mat-option [value]=\"null\">-</mat-option>\r\n @for (t of versionTags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n <!-- tag -->\r\n @if (tags().length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n <!-- range picker -->\r\n @if (selectionRange()) {\r\n <div class=\"range\">\r\n {{ selectionRange()!.at }}\u00D7{{ selectionRange()!.run }}\r\n </div>\r\n @if (!disabledRangePick()) {\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Pick this range for the edited operation\"\r\n (click)=\"pickRange()\"\r\n [disabled]=\"disabledRangePick()\"\r\n >\r\n <mat-icon class=\"mat-warn\">file_open</mat-icon>\r\n </button>\r\n } }\r\n </div>\r\n\r\n <!-- step -->\r\n @if (step()) {\r\n <div id=\"step\">\r\n <!-- text -->\r\n <div id=\"text\">\r\n <gve-base-text-view\r\n [text]=\"result()!.taggedNodes[step()!.outputTag]\"\r\n [colorCallback]=\"getCharColor\"\r\n (charPick)=\"onTextCharPick($event)\"\r\n (rangePick)=\"onTextRangePick($event)\"\r\n />\r\n </div>\r\n <!-- features -->\r\n <div id=\"feats\" class=\"form-row-top\">\r\n <div id=\"g-feats\">\r\n <div class=\"feat-header\">{{ step()!.outputTag }}</div>\r\n <gve-feature-set-view [features]=\"step()!.featureSet.features\" />\r\n </div>\r\n <div id=\"n-feats\">\r\n @if (selectionFeatures().length) {\r\n <div class=\"feat-header\">{{ selection() }}</div>\r\n <gve-feature-set-view [features]=\"selectionFeatures()\" />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- steps map -->\r\n <div id=\"map\">\r\n <gve-steps-map\r\n [steps]=\"result()!.steps\"\r\n [selectedStep]=\"step()\"\r\n (selectedStepChange)=\"onStepChange($event)\"\r\n />\r\n </div>\r\n</div>\r\n}\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.form-row-top{display:flex;gap:8px;align-items:start;flex-wrap:wrap}.range{color:silver;border:1px solid silver;border-radius:4px;padding:4px}div#container{display:grid;grid-template-rows:auto 1fr;grid-template-columns:3fr 1fr;grid-template-areas:\"bar map\" \"step map\";gap:8px}div#bar{grid-area:bar}div#map{grid-area:map}div#step{grid-area:step}.feat-header{background-color:#3e92cc;color:#fff;text-align:center;border:1px solid #3e92cc;border-top-left-radius:4px;border-top-right-radius:4px}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"bar\" \"step\"}#map{display:none}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: FeatureSetViewComponent, selector: "gve-feature-set-view", inputs: ["features", "featNames", "featValues", "filterThreshold"] }, { kind: "component", type: StepsMapComponent, selector: "gve-steps-map", inputs: ["steps", "selectedStep", "textFontSize"], outputs: ["selectedStepChange"] }, { kind: "component", type: BaseTextViewComponent, selector: "gve-base-text-view", inputs: ["defaultColor", "defaultBorderColor", "selectionColor", "hasLineNumber", "text", "colorCallback", "borderColorCallback"], outputs: ["charPick", "rangePick"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
2553
2627
  }
2554
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: ChainResultViewComponent, decorators: [{
2628
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ChainResultViewComponent, decorators: [{
2555
2629
  type: Component,
2556
2630
  args: [{ selector: 'gve-chain-result-view', imports: [
2557
2631
  ReactiveFormsModule,
@@ -2562,8 +2636,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
2562
2636
  FeatureSetViewComponent,
2563
2637
  StepsMapComponent,
2564
2638
  BaseTextViewComponent,
2565
- ], template: "@if (result()) {\r\n<div id=\"container\">\r\n <div id=\"bar\" class=\"form-row\">\r\n <!-- version -->\r\n @if (versionTags().length) {\r\n <mat-form-field>\r\n <mat-label>version</mat-label>\r\n <mat-select [formControl]=\"versionTag\">\r\n <mat-option [value]=\"null\">-</mat-option>\r\n @for (t of versionTags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n <!-- tag -->\r\n @if (tags().length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n </div>\r\n\r\n <!-- step -->\r\n @if (step()) {\r\n <div id=\"step\">\r\n <!-- text -->\r\n <div id=\"text\">\r\n <gve-base-text-view\r\n [text]=\"result()!.taggedNodes[step()!.outputTag]\"\r\n [colorCallback]=\"getCharColor\"\r\n (charPick)=\"onTextCharPick($event)\"\r\n (rangePick)=\"onTextRangePick($event)\"\r\n />\r\n </div>\r\n <!-- features -->\r\n <div id=\"feats\" class=\"form-row-top\">\r\n <div id=\"g-feats\">\r\n <div class=\"feat-header\">{{ step()!.outputTag }}</div>\r\n <gve-feature-set-view [features]=\"step()!.featureSet.features\" />\r\n </div>\r\n <div id=\"n-feats\">\r\n @if (selectionFeatures().length) {\r\n <div class=\"feat-header\">{{ selection() }}</div>\r\n <gve-feature-set-view [features]=\"selectionFeatures()\" />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- steps map -->\r\n <div id=\"map\">\r\n <gve-steps-map\r\n [steps]=\"result()!.steps\"\r\n [selectedStep]=\"step()\"\r\n (selectedStepChange)=\"onStepChange($event)\"\r\n />\r\n </div>\r\n</div>\r\n}\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.form-row-top{display:flex;gap:8px;align-items:start;flex-wrap:wrap}div#container{display:grid;grid-template-rows:auto 1fr;grid-template-columns:3fr 1fr;grid-template-areas:\"bar map\" \"step map\";gap:8px}div#bar{grid-area:bar}div#map{grid-area:map}div#step{grid-area:step}.feat-header{background-color:#3e92cc;color:#fff;text-align:center;border:1px solid #3e92cc;border-top-left-radius:4px;border-top-right-radius:4px}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"bar\" \"step\"}#map{display:none}}\n"] }]
2566
- }], ctorParameters: () => [{ type: i1.FormBuilder }] });
2639
+ MatTooltip
2640
+ ], template: "@if (result()) {\r\n<div id=\"container\">\r\n <div id=\"bar\" class=\"form-row\">\r\n <!-- version -->\r\n @if (versionTags().length) {\r\n <mat-form-field>\r\n <mat-label>version</mat-label>\r\n <mat-select [formControl]=\"versionTag\">\r\n <mat-option [value]=\"null\">-</mat-option>\r\n @for (t of versionTags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n <!-- tag -->\r\n @if (tags().length) {\r\n <mat-form-field>\r\n <mat-label>tag</mat-label>\r\n <mat-select [formControl]=\"tag\">\r\n @for (t of tags(); track t) {\r\n <mat-option [value]=\"t\">{{ t }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n }\r\n <!-- range picker -->\r\n @if (selectionRange()) {\r\n <div class=\"range\">\r\n {{ selectionRange()!.at }}\u00D7{{ selectionRange()!.run }}\r\n </div>\r\n @if (!disabledRangePick()) {\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Pick this range for the edited operation\"\r\n (click)=\"pickRange()\"\r\n [disabled]=\"disabledRangePick()\"\r\n >\r\n <mat-icon class=\"mat-warn\">file_open</mat-icon>\r\n </button>\r\n } }\r\n </div>\r\n\r\n <!-- step -->\r\n @if (step()) {\r\n <div id=\"step\">\r\n <!-- text -->\r\n <div id=\"text\">\r\n <gve-base-text-view\r\n [text]=\"result()!.taggedNodes[step()!.outputTag]\"\r\n [colorCallback]=\"getCharColor\"\r\n (charPick)=\"onTextCharPick($event)\"\r\n (rangePick)=\"onTextRangePick($event)\"\r\n />\r\n </div>\r\n <!-- features -->\r\n <div id=\"feats\" class=\"form-row-top\">\r\n <div id=\"g-feats\">\r\n <div class=\"feat-header\">{{ step()!.outputTag }}</div>\r\n <gve-feature-set-view [features]=\"step()!.featureSet.features\" />\r\n </div>\r\n <div id=\"n-feats\">\r\n @if (selectionFeatures().length) {\r\n <div class=\"feat-header\">{{ selection() }}</div>\r\n <gve-feature-set-view [features]=\"selectionFeatures()\" />\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- steps map -->\r\n <div id=\"map\">\r\n <gve-steps-map\r\n [steps]=\"result()!.steps\"\r\n [selectedStep]=\"step()\"\r\n (selectedStepChange)=\"onStepChange($event)\"\r\n />\r\n </div>\r\n</div>\r\n}\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}.form-row-top{display:flex;gap:8px;align-items:start;flex-wrap:wrap}.range{color:silver;border:1px solid silver;border-radius:4px;padding:4px}div#container{display:grid;grid-template-rows:auto 1fr;grid-template-columns:3fr 1fr;grid-template-areas:\"bar map\" \"step map\";gap:8px}div#bar{grid-area:bar}div#map{grid-area:map}div#step{grid-area:step}.feat-header{background-color:#3e92cc;color:#fff;text-align:center;border:1px solid #3e92cc;border-top-left-radius:4px;border-top-right-radius:4px}@media only screen and (max-width: 959px){div#container{grid-template-columns:1fr;grid-template-areas:\"bar\" \"step\"}#map{display:none}}\n"] }]
2641
+ }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { result: [{ type: i0.Input, args: [{ isSignal: true, alias: "result", required: false }] }], initialStepIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialStepIndex", required: false }] }], stepPick: [{ type: i0.Output, args: ["stepPick"] }], disabledRangePick: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabledRangePick", required: false }] }], rangePick: [{ type: i0.Output, args: ["rangePick"] }] } });
2567
2642
 
2568
2643
  class GveGraphvizService {
2569
2644
  hashString(str) {
@@ -2670,10 +2745,10 @@ class GveGraphvizService {
2670
2745
  sb.push('}');
2671
2746
  return sb.join('\n');
2672
2747
  }
2673
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: GveGraphvizService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2674
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: GveGraphvizService, providedIn: 'root' }); }
2748
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: GveGraphvizService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2749
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: GveGraphvizService, providedIn: 'root' }); }
2675
2750
  }
2676
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: GveGraphvizService, decorators: [{
2751
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: GveGraphvizService, decorators: [{
2677
2752
  type: Injectable,
2678
2753
  args: [{
2679
2754
  providedIn: 'root',
@@ -2771,10 +2846,10 @@ class ChainViewComponent {
2771
2846
  }
2772
2847
  window.open(url, '_blank');
2773
2848
  }
2774
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: ChainViewComponent, deps: [{ token: GveGraphvizService }, { token: SettingsService }, { token: i2$2.Clipboard }, { token: i4$3.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component }); }
2775
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: ChainViewComponent, isStandalone: true, selector: "gve-chain-view", inputs: { chain: { classPropertyName: "chain", publicName: "chain", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, selectedTags: { classPropertyName: "selectedTags", publicName: "selectedTags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedTags: "selectedTagsChange" }, ngImport: i0, template: "<div id=\"container\">\r\n <div>\r\n <ngx-viz [code]=\"graph()\" />\r\n </div>\r\n <div class=\"button-row\">\r\n <!-- select -->\r\n <mat-form-field>\r\n <mat-label>tags</mat-label>\r\n <mat-select [formControl]=\"userTags\" multiple>\r\n @for (tag of tags(); track tag) {\r\n <mat-option [value]=\"tag\">{{ tag }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n <!-- copy -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy graphviz code\"\r\n [disabled]=\"!graph()\"\r\n (click)=\"copyGraph()\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <!--editor -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open in editor\"\r\n [disabled]=\"!graph()\"\r\n (click)=\"openExternalEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n </div>\r\n</div>\r\n", styles: ["div#container{border:1px solid silver;border-radius:6px;padding:4px}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: VizComponent, selector: "ngx-viz", inputs: ["code"] }] }); }
2849
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ChainViewComponent, deps: [{ token: GveGraphvizService }, { token: SettingsService }, { token: i2$2.Clipboard }, { token: i4$2.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component }); }
2850
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: ChainViewComponent, isStandalone: true, selector: "gve-chain-view", inputs: { chain: { classPropertyName: "chain", publicName: "chain", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, selectedTags: { classPropertyName: "selectedTags", publicName: "selectedTags", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedTags: "selectedTagsChange" }, ngImport: i0, template: "<div id=\"container\">\r\n <div>\r\n <ngx-viz [code]=\"graph()\" />\r\n </div>\r\n <div class=\"button-row\">\r\n <!-- select -->\r\n <mat-form-field>\r\n <mat-label>tags</mat-label>\r\n <mat-select [formControl]=\"userTags\" multiple>\r\n @for (tag of tags(); track tag) {\r\n <mat-option [value]=\"tag\">{{ tag }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n <!-- copy -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy graphviz code\"\r\n [disabled]=\"!graph()\"\r\n (click)=\"copyGraph()\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <!--editor -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open in editor\"\r\n [disabled]=\"!graph()\"\r\n (click)=\"openExternalEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n </div>\r\n</div>\r\n", styles: ["div#container{border:1px solid silver;border-radius:6px;padding:4px}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: VizComponent, selector: "ngx-viz", inputs: ["code"] }] }); }
2776
2851
  }
2777
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: ChainViewComponent, decorators: [{
2852
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: ChainViewComponent, decorators: [{
2778
2853
  type: Component,
2779
2854
  args: [{ selector: 'gve-chain-view', imports: [
2780
2855
  ReactiveFormsModule,
@@ -2785,7 +2860,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
2785
2860
  MatTooltipModule,
2786
2861
  VizComponent
2787
2862
  ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<div id=\"container\">\r\n <div>\r\n <ngx-viz [code]=\"graph()\" />\r\n </div>\r\n <div class=\"button-row\">\r\n <!-- select -->\r\n <mat-form-field>\r\n <mat-label>tags</mat-label>\r\n <mat-select [formControl]=\"userTags\" multiple>\r\n @for (tag of tags(); track tag) {\r\n <mat-option [value]=\"tag\">{{ tag }}</mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n <!-- copy -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Copy graphviz code\"\r\n [disabled]=\"!graph()\"\r\n (click)=\"copyGraph()\"\r\n >\r\n <mat-icon>content_copy</mat-icon>\r\n </button>\r\n <!--editor -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Open in editor\"\r\n [disabled]=\"!graph()\"\r\n (click)=\"openExternalEditor()\"\r\n >\r\n <mat-icon>launch</mat-icon>\r\n </button>\r\n </div>\r\n</div>\r\n", styles: ["div#container{border:1px solid silver;border-radius:6px;padding:4px}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}\n"] }]
2788
- }], ctorParameters: () => [{ type: GveGraphvizService }, { type: SettingsService }, { type: i2$2.Clipboard }, { type: i4$3.MatSnackBar }] });
2863
+ }], ctorParameters: () => [{ type: GveGraphvizService }, { type: SettingsService }, { type: i2$2.Clipboard }, { type: i4$2.MatSnackBar }], propDecorators: { chain: [{ type: i0.Input, args: [{ isSignal: true, alias: "chain", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], selectedTags: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedTags", required: false }] }, { type: i0.Output, args: ["selectedTagsChange"] }] } });
2789
2864
 
2790
2865
  /**
2791
2866
  * 🔑 `gve-ln-heights-editor`
@@ -2860,10 +2935,10 @@ class LnHeightsEditorComponent {
2860
2935
  this._heights = undefined;
2861
2936
  this.heightsChange.emit(undefined);
2862
2937
  }
2863
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LnHeightsEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
2864
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: LnHeightsEditorComponent, isStandalone: true, selector: "gve-ln-heights-editor", inputs: { lineCount: { classPropertyName: "lineCount", publicName: "lineCount", isSignal: true, isRequired: false, transformFunction: null }, heights: { classPropertyName: "heights", publicName: "heights", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { heightsChange: "heightsChange" }, ngImport: i0, template: "@if (lineNumbers().length) {\r\n <div class=\"form-row\">\r\n <!-- line number -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>line</mat-label>\r\n <mat-select [formControl]=\"lineNumber\">\r\n @for (n of lineNumbers(); track n) {\r\n <mat-option [value]=\"n\">\r\n {{ n }}\r\n </mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n @if (lineNumber.value) {\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input matInput type=\"number\" [formControl]=\"height\" min=\"0\" />\r\n </mat-form-field>\r\n }\r\n\r\n <!-- reset button -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"reset()\"\r\n matTooltip=\"Remove all the heights\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </div>\r\n}\r\n", styles: [".input-nr{width:5em}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
2938
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: LnHeightsEditorComponent, deps: [{ token: i1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); }
2939
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: LnHeightsEditorComponent, isStandalone: true, selector: "gve-ln-heights-editor", inputs: { lineCount: { classPropertyName: "lineCount", publicName: "lineCount", isSignal: true, isRequired: false, transformFunction: null }, heights: { classPropertyName: "heights", publicName: "heights", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { heightsChange: "heightsChange" }, ngImport: i0, template: "@if (lineNumbers().length) {\r\n <div class=\"form-row\">\r\n <!-- line number -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>line</mat-label>\r\n <mat-select [formControl]=\"lineNumber\">\r\n @for (n of lineNumbers(); track n) {\r\n <mat-option [value]=\"n\">\r\n {{ n }}\r\n </mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n @if (lineNumber.value) {\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input matInput type=\"number\" [formControl]=\"height\" min=\"0\" />\r\n </mat-form-field>\r\n }\r\n\r\n <!-- reset button -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"reset()\"\r\n matTooltip=\"Remove all the heights\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </div>\r\n}\r\n", styles: [".input-nr{width:5em}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
2865
2940
  }
2866
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LnHeightsEditorComponent, decorators: [{
2941
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: LnHeightsEditorComponent, decorators: [{
2867
2942
  type: Component,
2868
2943
  args: [{ selector: 'gve-ln-heights-editor', imports: [
2869
2944
  ReactiveFormsModule,
@@ -2874,7 +2949,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
2874
2949
  MatSelectModule,
2875
2950
  MatTooltipModule,
2876
2951
  ], template: "@if (lineNumbers().length) {\r\n <div class=\"form-row\">\r\n <!-- line number -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>line</mat-label>\r\n <mat-select [formControl]=\"lineNumber\">\r\n @for (n of lineNumbers(); track n) {\r\n <mat-option [value]=\"n\">\r\n {{ n }}\r\n </mat-option>\r\n }\r\n </mat-select>\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n @if (lineNumber.value) {\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input matInput type=\"number\" [formControl]=\"height\" min=\"0\" />\r\n </mat-form-field>\r\n }\r\n\r\n <!-- reset button -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"warn\"\r\n (click)=\"reset()\"\r\n matTooltip=\"Remove all the heights\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </div>\r\n}\r\n", styles: [".input-nr{width:5em}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row *{flex:0 0 auto}\n"] }]
2877
- }], ctorParameters: () => [{ type: i1.FormBuilder }] });
2952
+ }], ctorParameters: () => [{ type: i1.FormBuilder }], propDecorators: { lineCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "lineCount", required: false }] }], heights: [{ type: i0.Input, args: [{ isSignal: true, alias: "heights", required: false }] }], heightsChange: [{ type: i0.Output, args: ["heightsChange"] }] } });
2878
2953
 
2879
2954
  /**
2880
2955
  * 🔑 `gve-snapshot-text-editor`
@@ -2905,7 +2980,7 @@ class SnapshotTextEditorComponent {
2905
2980
  });
2906
2981
  }
2907
2982
  updateUserText(nodes) {
2908
- this.userText.setValue(nodes.map((node) => node.label).join(''));
2983
+ this.userText.setValue(nodes.map((node) => node.data).join(''));
2909
2984
  this.userText.markAsPristine();
2910
2985
  this.userText.updateValueAndValidity();
2911
2986
  }
@@ -2928,10 +3003,10 @@ class SnapshotTextEditorComponent {
2928
3003
  this.text.set(SnapshotViewService.stringToBaseChars(this.userText.value));
2929
3004
  this.dialogRef?.close(this.text());
2930
3005
  }
2931
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: SnapshotTextEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i3$2.MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
2932
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: SnapshotTextEditorComponent, isStandalone: true, selector: "gve-snapshot-text-editor", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { text: "textChange" }, ngImport: i0, template: "<div [style.padding]=\"dialogRef ? '8px' : '0'\">\r\n @if (dialogRef) {\r\n <div id=\"heading\">\r\n <h2>Set Base Text</h2>\r\n </div>\r\n }\r\n <form [formGroup]=\"form\" (submit)=\"save()\">\r\n <fieldset>\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>base text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"8\"></textarea>\r\n @if ($any(userText).errors?.required && (userText.dirty ||\r\n userText.touched)) {\r\n <mat-error>text required</mat-error>\r\n } @if ($any(userText).errors?.maxLength && (userText.dirty ||\r\n userText.touched)) {\r\n <mat-error>text too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div class=\"form-row-center\">\r\n @if (dialogRef) {\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Close dialog\"\r\n (click)=\"close()\"\r\n >\r\n close\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Set base text\"\r\n [disabled]=\"form.invalid\"\r\n (click)=\"save()\"\r\n >\r\n set\r\n </button>\r\n </div>\r\n </fieldset>\r\n </form>\r\n</div>\r\n", styles: [".full-width{width:100%}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}div#heading{margin:8px;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }] }); }
3006
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: SnapshotTextEditorComponent, deps: [{ token: i1.FormBuilder }, { token: i3$3.MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
3007
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: SnapshotTextEditorComponent, isStandalone: true, selector: "gve-snapshot-text-editor", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { text: "textChange" }, ngImport: i0, template: "<div [style.padding]=\"dialogRef ? '8px' : '0'\">\r\n @if (dialogRef) {\r\n <div id=\"heading\">\r\n <h2>Set Base Text</h2>\r\n </div>\r\n }\r\n <form [formGroup]=\"form\" (submit)=\"save()\">\r\n <fieldset>\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>base text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"8\"></textarea>\r\n @if ($any(userText).errors?.required && (userText.dirty ||\r\n userText.touched)) {\r\n <mat-error>text required</mat-error>\r\n } @if ($any(userText).errors?.maxLength && (userText.dirty ||\r\n userText.touched)) {\r\n <mat-error>text too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div class=\"form-row-center\">\r\n @if (dialogRef) {\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Close dialog\"\r\n (click)=\"close()\"\r\n >\r\n close\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Set base text\"\r\n [disabled]=\"form.invalid\"\r\n (click)=\"save()\"\r\n >\r\n set\r\n </button>\r\n </div>\r\n </fieldset>\r\n </form>\r\n</div>\r\n", styles: [".full-width{width:100%}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}div#heading{margin:8px;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }] }); }
2933
3008
  }
2934
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: SnapshotTextEditorComponent, decorators: [{
3009
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: SnapshotTextEditorComponent, decorators: [{
2935
3010
  type: Component,
2936
3011
  args: [{ selector: 'gve-snapshot-text-editor', imports: [
2937
3012
  ReactiveFormsModule,
@@ -2940,14 +3015,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
2940
3015
  MatIconModule,
2941
3016
  MatInputModule,
2942
3017
  ], template: "<div [style.padding]=\"dialogRef ? '8px' : '0'\">\r\n @if (dialogRef) {\r\n <div id=\"heading\">\r\n <h2>Set Base Text</h2>\r\n </div>\r\n }\r\n <form [formGroup]=\"form\" (submit)=\"save()\">\r\n <fieldset>\r\n <div id=\"batch-input\">\r\n <div>\r\n <mat-form-field class=\"full-width\">\r\n <mat-label>base text</mat-label>\r\n <textarea matInput [formControl]=\"userText\" rows=\"8\"></textarea>\r\n @if ($any(userText).errors?.required && (userText.dirty ||\r\n userText.touched)) {\r\n <mat-error>text required</mat-error>\r\n } @if ($any(userText).errors?.maxLength && (userText.dirty ||\r\n userText.touched)) {\r\n <mat-error>text too long</mat-error>\r\n }\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div class=\"form-row-center\">\r\n @if (dialogRef) {\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Close dialog\"\r\n (click)=\"close()\"\r\n >\r\n close\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Set base text\"\r\n [disabled]=\"form.invalid\"\r\n (click)=\"save()\"\r\n >\r\n set\r\n </button>\r\n </div>\r\n </fieldset>\r\n </form>\r\n</div>\r\n", styles: [".full-width{width:100%}.form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}div#heading{margin:8px;text-align:center}\n"] }]
2943
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i3$2.MatDialogRef, decorators: [{
3018
+ }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i3$3.MatDialogRef, decorators: [{
2944
3019
  type: Optional
2945
3020
  }] }, { type: undefined, decorators: [{
2946
3021
  type: Optional
2947
3022
  }, {
2948
3023
  type: Inject,
2949
3024
  args: [MAT_DIALOG_DATA]
2950
- }] }] });
3025
+ }] }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }, { type: i0.Output, args: ["textChange"] }] } });
2951
3026
 
2952
3027
  // default snapshot view title
2953
3028
  const VIEW_TITLE = 'preview';
@@ -3010,6 +3085,7 @@ class SnapshotEditorComponent {
3010
3085
  */
3011
3086
  this.snapshotCancel = output();
3012
3087
  this.showChain = new FormControl(false, { nonNullable: true });
3088
+ this.featDetails = new FormControl(false, { nonNullable: true });
3013
3089
  this.chain = signal(undefined, ...(ngDevMode ? [{ debugName: "chain" }] : []));
3014
3090
  // list of operations output tags
3015
3091
  this.opTags = signal([], ...(ngDevMode ? [{ debugName: "opTags" }] : []));
@@ -3022,6 +3098,8 @@ class SnapshotEditorComponent {
3022
3098
  // the currently edited operation
3023
3099
  this.editedOp = signal(undefined, ...(ngDevMode ? [{ debugName: "editedOp" }] : []));
3024
3100
  this.editedOpIndex = signal(-1, ...(ngDevMode ? [{ debugName: "editedOpIndex" }] : []));
3101
+ // the patch to apply to the edited operation's range
3102
+ this.editedOpRangePatch = signal(undefined, ...(ngDevMode ? [{ debugName: "editedOpRangePatch" }] : []));
3025
3103
  this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : []));
3026
3104
  this.opTypeMap = {
3027
3105
  0: 'replace',
@@ -3184,7 +3262,7 @@ class SnapshotEditorComponent {
3184
3262
  return;
3185
3263
  }
3186
3264
  const dialogRef = this._dialog.open(SnapshotTextEditorComponent, {
3187
- data: { payload: { text: this.baseText.value } },
3265
+ data: { payload: { text: [...this.baseText.value] } },
3188
3266
  width: '80%',
3189
3267
  });
3190
3268
  dialogRef.afterClosed().subscribe((text) => {
@@ -3218,9 +3296,13 @@ class SnapshotEditorComponent {
3218
3296
  /**
3219
3297
  * Handle the event fired by the base text editor to pick a text range.
3220
3298
  * @param range The picked range.
3299
+ * @param applyToOp True to also apply the picked range to the edited operation.
3221
3300
  */
3222
- onRangePick(range) {
3301
+ onRangePick(range, applyToOp = false) {
3223
3302
  this.textRange.set(range);
3303
+ if (applyToOp) {
3304
+ this.editedOpRangePatch.set({ ...range });
3305
+ }
3224
3306
  }
3225
3307
  // #endregion
3226
3308
  // #region operations
@@ -3249,12 +3331,14 @@ class SnapshotEditorComponent {
3249
3331
  * Edit a new operation.
3250
3332
  */
3251
3333
  editNewOperation() {
3252
- // create a new operation and edit it
3334
+ // create a new operation and edit it, using as the initial range
3335
+ // the base text range if any
3336
+ const range = this.textRange();
3253
3337
  this.editedOp.set({
3254
3338
  id: this._nanoid(),
3255
3339
  type: 0,
3256
- at: this.textRange()?.at || 0,
3257
- run: this.textRange()?.run || 1,
3340
+ at: range?.at || 0,
3341
+ run: range?.run || 1,
3258
3342
  });
3259
3343
  this.editedOpIndex.set(-1);
3260
3344
  }
@@ -3894,10 +3978,10 @@ class SnapshotEditorComponent {
3894
3978
  }
3895
3979
  this.snapshot.set(this.getSnapshot());
3896
3980
  }
3897
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: SnapshotEditorComponent, deps: [{ token: i1.FormBuilder }, { token: GveApiService }, { token: i3$2.MatDialog }, { token: i4$2.DialogService }, { token: i4$3.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component }); }
3898
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: SnapshotEditorComponent, isStandalone: true, selector: "gve-snapshot-editor", inputs: { snapshot: { classPropertyName: "snapshot", publicName: "snapshot", isSignal: true, isRequired: false, transformFunction: null }, batchOps: { classPropertyName: "batchOps", publicName: "batchOps", isSignal: true, isRequired: false, transformFunction: null }, noSave: { classPropertyName: "noSave", publicName: "noSave", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, noDecoration: { classPropertyName: "noDecoration", publicName: "noDecoration", isSignal: true, isRequired: false, transformFunction: null }, featureDefs: { classPropertyName: "featureDefs", publicName: "featureDefs", isSignal: true, isRequired: false, transformFunction: null }, elementFeatureDefs: { classPropertyName: "elementFeatureDefs", publicName: "elementFeatureDefs", isSignal: true, isRequired: false, transformFunction: null }, diplomaticFeatureDefs: { classPropertyName: "diplomaticFeatureDefs", publicName: "diplomaticFeatureDefs", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { snapshot: "snapshotChange", snapshotCancel: "snapshotCancel" }, viewQueries: [{ propertyName: "snapshotView", first: true, predicate: ["snapshotView"], descendants: true }], ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <mat-tab-group>\r\n <!-- text -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>article</mat-icon> <span class=\"label\">text</span>\r\n </ng-template>\r\n <mat-expansion-panel>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title> base text </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <!-- base text -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"mat-warn\"\r\n matTooltip=\"Enter base text\"\r\n (click)=\"inputBaseText()\"\r\n [disabled]=\"busy()\"\r\n >\r\n <mat-icon>edit</mat-icon> base text\r\n </button>\r\n\r\n @if (textRange()) {\r\n <span id=\"text-range\"\r\n >{{ textRange()!.at }}\u00D7{{ textRange()!.run }}</span\r\n >\r\n }\r\n </div>\r\n <!-- base text metadata -->\r\n <fieldset>\r\n <div class=\"form-row\">\r\n <!-- offsetX -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetX\"\r\n placeholder=\"X offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- offsetY -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetY\"\r\n placeholder=\"Y offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- lineHeightOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>ln h-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"lineHeightOffset\"\r\n placeholder=\"ln h-offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- charSpacingOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>char spacing</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"charSpacingOffset\"\r\n placeholder=\"char spacing\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- spcWidthOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>spc w-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"spcWidthOffset\"\r\n placeholder=\"spc w-offset\"\r\n />\r\n </mat-form-field>\r\n <!-- minLineHeights -->\r\n <div class=\"boxed\">\r\n <gve-ln-heights-editor\r\n [lineCount]=\"lineCount()\"\r\n [heights]=\"lnHeights.value || undefined\"\r\n (heightsChange)=\"onHeightsChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n <!-- textStyle -->\r\n <div>\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>text style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"textStyle\"\r\n placeholder=\"text style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </fieldset>\r\n <!-- text characters -->\r\n <gve-base-text-view\r\n [text]=\"baseText.value\"\r\n [hasLineNumber]=\"true\"\r\n (rangePick)=\"onRangePick($event)\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- operations -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>edit</mat-icon> <span class=\"label\">operations</span>\r\n </ng-template>\r\n\r\n <div id=\"snapshot-container\">\r\n <div id=\"general\">\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"width\"\r\n placeholder=\"width\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"height\"\r\n placeholder=\"height\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- chain -->\r\n <mat-checkbox [formControl]=\"showChain\">show chain</mat-checkbox>\r\n\r\n <div class=\"form-row right\">\r\n <!-- remove ops -->\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Remove all the operations\"\r\n [disabled]=\"!operations.value.length || busy()\"\r\n (click)=\"clearOperations()\"\r\n >\r\n <mat-icon>delete_forever</mat-icon> clear\r\n </button>\r\n\r\n <!-- batch add ops -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n matTooltip=\"Add a batch of operations\"\r\n class=\"mat-primary\"\r\n [disabled]=\"busy()\"\r\n (click)=\"parseOperations()\"\r\n >\r\n <mat-icon>post_add</mat-icon> batch\r\n </button>\r\n\r\n <!-- add op -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\r\n [disabled]=\"!baseText.value\"\r\n (click)=\"editNewOperation()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> operation\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- ops -->\r\n <div id=\"ops\">\r\n <!-- operations list -->\r\n @if (operations.value.length) {\r\n <table id=\"list\">\r\n <thead>\r\n <tr>\r\n <th>nr.</th>\r\n <th></th>\r\n <th>ID</th>\r\n <th>type</th>\r\n <th>at</th>\r\n <th>run</th>\r\n <th>value</th>\r\n <th>itag</th>\r\n <th>otag</th>\r\n <th>gid</th>\r\n <th>feats</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (operation of operations.value; track operation.id; let\r\n index=$index) {\r\n <tr\r\n [class.selected]=\"operation.id === resultOperationId\"\r\n [class.edited]=\"operation.id === editedOp()?.id\"\r\n >\r\n <td class=\"fit-width\">{{ index + 1 }}.</td>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-primary\"\r\n (click)=\"editOperation(index)\"\r\n matTooltip=\"Edit operation\"\r\n >\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-warn\"\r\n (click)=\"deleteOperation(index)\"\r\n matTooltip=\"Delete operation\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n <!-- run to -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-accent\"\r\n (click)=\"runTo(index)\"\r\n matTooltip=\"Run to this operation\"\r\n >\r\n <mat-icon class=\"mat-accent\">subscriptions</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ operation.id }}</td>\r\n <td>{{ operation.type | flatLookup : opTypeMap }}</td>\r\n <td>{{ operation.at }}</td>\r\n <td>{{ operation.run }}</td>\r\n <td>{{ operation.value }}</td>\r\n <td>{{ operation.inputTag }}</td>\r\n <td>{{ operation.outputTag }}</td>\r\n <td>{{ operation.groupId }}</td>\r\n <td>{{ operation.features?.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- operation editor -->\r\n @if (editedOp()) {\r\n <mat-expansion-panel [expanded]=\"editedOp()\" [disabled]=\"!editedOp()\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>operation {{ editedOp()?.id }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <gve-chain-operation-editor\r\n [featureDefs]=\"featureDefs()\"\r\n [elementFeatureDefs]=\"elementFeatureDefs()\"\r\n [diplomaticFeatureDefs]=\"diplomaticFeatureDefs()\"\r\n [hidePreview]=\"editedOpIndex() === -1\"\r\n [operation]=\"editedOp()\"\r\n (operationCancel)=\"onOperationCancel()\"\r\n (operationChange)=\"onOperationChange($event)\"\r\n (operationPreview)=\"onOperationPreview($event)\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n }\r\n\r\n <!-- opStyle -->\r\n <div id=\"opStyle\">\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>operations style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"opStyle\"\r\n placeholder=\"operations style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- image -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>image</mat-icon> <span class=\"label\">image</span>\r\n </ng-template>\r\n\r\n <div id=\"image\">\r\n <div id=\"image-ctl\">\r\n <!-- url -->\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>URL</mat-label>\r\n <input matInput [formControl]=\"imageUrl\" />\r\n </mat-form-field>\r\n <div class=\"form-row\">\r\n <!-- x -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageX\"\r\n placeholder=\"X\"\r\n />\r\n </mat-form-field>\r\n <!-- y -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageY\"\r\n placeholder=\"Y\"\r\n />\r\n </mat-form-field>\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageWidth\"\r\n />\r\n </mat-form-field>\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageHeight\"\r\n />\r\n </mat-form-field>\r\n <!-- reset -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Reset image metadata\"\r\n (click)=\"resetImgMetadata()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </div>\r\n <div>\r\n <!-- image opacity -->\r\n <mat-slider\r\n min=\"0\"\r\n max=\"1\"\r\n step=\"0.01\"\r\n thumbLabel\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n matTooltip=\"Image opacity\"\r\n >\r\n <input\r\n matSliderThumb\r\n [value]=\"imageOpacity.value\"\r\n (valueChange)=\"onImageOpacityChange($event)\"\r\n />\r\n </mat-slider>\r\n <span id=\"opacity-value\">{{\r\n imageOpacity.value | number : \"1.2-2\"\r\n }}</span>\r\n </div>\r\n <!-- defs -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>defs</mat-label>\r\n <textarea matInput [formControl]=\"defs\" rows=\"3\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div id=\"image-view\">\r\n @if (imageUrl.value) {\r\n <img\r\n #imgElement\r\n alt=\"background\"\r\n [src]=\"imageUrl.value\"\r\n width=\"600\"\r\n (load)=\"onImageLoad(imgElement)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- timelines -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>animation</mat-icon> <span class=\"label\">animation</span>\r\n </ng-template>\r\n\r\n <gve-animation-timeline-set\r\n [tags]=\"opTags()\"\r\n [elementIds]=\"opElementIds()\"\r\n [timelines]=\"timelines.value\"\r\n (timelinesChange)=\"onTimelinesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- progress -->\r\n <div>\r\n @if (busy()) {\r\n <mat-progress-bar mode=\"indeterminate\" />\r\n }\r\n </div>\r\n\r\n <!-- result -->\r\n @if (result) {\r\n <fieldset id=\"result\">\r\n <legend>result</legend>\r\n <gve-chain-result-view\r\n [result]=\"result\"\r\n [initialStepIndex]=\"initialStepIndex\"\r\n (stepPick)=\"onStepPick($event)\"\r\n />\r\n </fieldset>\r\n }\r\n\r\n <!-- snapshot view -->\r\n <fieldset id=\"preview\">\r\n <legend class=\"button-row\">\r\n <span>{{ viewTitle }}</span>\r\n </legend>\r\n <!-- snapshot view -->\r\n <gve-snapshot-view\r\n #snapshotView\r\n [debug]=\"debug()\"\r\n [data]=\"viewData\"\r\n (snapshotRender)=\"onSnapshotRender($any($event))\"\r\n (visualEvent)=\"onVisualEvent($any($event))\"\r\n />\r\n <div class=\"button-row\">\r\n <!-- toggle ruler -->\r\n <mat-button-toggle\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Toggle rulers\"\r\n [checked]=\"rulers\"\r\n (change)=\"toggleRulers()\"\r\n >\r\n <mat-icon class=\"mat-primary\">straighten</mat-icon>\r\n </mat-button-toggle>\r\n <!-- run to last -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"primary\"\r\n matTooltip=\"Refresh preview\"\r\n (click)=\"runToLast()\"\r\n >\r\n <mat-icon>refresh</mat-icon>\r\n </button>\r\n </div>\r\n @if (visualInfo) {\r\n <div id=\"visual-info\">{{ visualInfo }}</div>\r\n }\r\n </fieldset>\r\n\r\n <!-- chain view -->\r\n @if (chain()) {\r\n <div id=\"chain-view\">\r\n <gve-chain-view [chain]=\"chain()\" />\r\n </div>\r\n }\r\n\r\n <!--buttons -->\r\n <div class=\"form-row-center\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon>clear</mat-icon>\r\n close\r\n </button>\r\n @if (!noSave()) {\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save changes\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n }\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}.form-row .right{margin-left:auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}#text-range{margin:8px;border:1px solid silver;border-radius:6px;padding:6px}mat-expansion-panel{margin:8px 0}div#visual-info{font-size:95%;color:#909090;margin:8px}#list{margin:8px 0}#opStyle{margin-top:8px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#c8d9eb}tr.edited{background-color:#f6f6e4}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:#909090}.error{color:red}.input-nr{width:6em}.full-width{width:100%}.code{font-family:Courier New,Courier,monospace}.boxed{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}span.label{margin-left:8px}gve-animation-timeline-set{margin:8px 0}div#image{display:grid;gap:8px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"image-ctl image-view\"}div#image-ctl{grid-area:image-ctl}div#image-view{grid-area:image-view}div#chain-view{margin-bottom:8px}@media only screen and (max-width: 959px){div#image{grid-template-columns:1fr;grid-template-areas:\"image-ctl\" \"image-view\"}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "component", type: i7$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i3$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i13.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatSliderModule }, { kind: "component", type: i14.MatSlider, selector: "mat-slider", inputs: ["disabled", "discrete", "showTickMarks", "min", "color", "disableRipple", "max", "step", "displayWith"], exportAs: ["matSlider"] }, { kind: "directive", type: i14.MatSliderThumb, selector: "input[matSliderThumb]", inputs: ["value"], outputs: ["valueChange", "dragStart", "dragEnd"], exportAs: ["matSliderThumb"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i15.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i15.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i15.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ChainOperationEditorComponent, selector: "gve-chain-operation-editor", inputs: ["operation", "snapshot", "hidePreview", "featureDefs", "elementFeatureDefs", "diplomaticFeatureDefs"], outputs: ["operationChange", "operationPreview", "operationCancel"] }, { kind: "component", type: ChainResultViewComponent, selector: "gve-chain-result-view", inputs: ["result", "initialStepIndex"], outputs: ["stepPick"] }, { kind: "component", type: BaseTextViewComponent, selector: "gve-base-text-view", inputs: ["defaultColor", "defaultBorderColor", "selectionColor", "hasLineNumber", "text", "colorCallback", "borderColorCallback"], outputs: ["charPick", "rangePick"] }, { kind: "component", type: LnHeightsEditorComponent, selector: "gve-ln-heights-editor", inputs: ["lineCount", "heights"], outputs: ["heightsChange"] }, { kind: "component", type: AnimationTimelineSetComponent, selector: "gve-animation-timeline-set", inputs: ["timelines", "elementIds", "tags"], outputs: ["timelinesChange", "timelinesCancel"] }, { kind: "component", type: ChainViewComponent, selector: "gve-chain-view", inputs: ["chain", "direction", "selectedTags"], outputs: ["selectedTagsChange"] }, { kind: "pipe", type: i17.DecimalPipe, name: "number" }, { kind: "pipe", type: FlatLookupPipe, name: "flatLookup" }] }); }
3981
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: SnapshotEditorComponent, deps: [{ token: i1.FormBuilder }, { token: GveApiService }, { token: i3$3.MatDialog }, { token: i4$1.DialogService }, { token: i4$2.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component }); }
3982
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.7", type: SnapshotEditorComponent, isStandalone: true, selector: "gve-snapshot-editor", inputs: { snapshot: { classPropertyName: "snapshot", publicName: "snapshot", isSignal: true, isRequired: false, transformFunction: null }, batchOps: { classPropertyName: "batchOps", publicName: "batchOps", isSignal: true, isRequired: false, transformFunction: null }, noSave: { classPropertyName: "noSave", publicName: "noSave", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, noDecoration: { classPropertyName: "noDecoration", publicName: "noDecoration", isSignal: true, isRequired: false, transformFunction: null }, featureDefs: { classPropertyName: "featureDefs", publicName: "featureDefs", isSignal: true, isRequired: false, transformFunction: null }, elementFeatureDefs: { classPropertyName: "elementFeatureDefs", publicName: "elementFeatureDefs", isSignal: true, isRequired: false, transformFunction: null }, diplomaticFeatureDefs: { classPropertyName: "diplomaticFeatureDefs", publicName: "diplomaticFeatureDefs", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { snapshot: "snapshotChange", snapshotCancel: "snapshotCancel" }, viewQueries: [{ propertyName: "snapshotView", first: true, predicate: ["snapshotView"], descendants: true }], ngImport: i0, template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <mat-tab-group>\r\n <!-- text -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>article</mat-icon> <span class=\"label\">text</span>\r\n </ng-template>\r\n <mat-expansion-panel>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title> base text </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <!-- base text -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"mat-warn\"\r\n matTooltip=\"Enter base text\"\r\n (click)=\"inputBaseText()\"\r\n [disabled]=\"busy()\"\r\n >\r\n <mat-icon>edit</mat-icon> base text\r\n </button>\r\n\r\n @if (textRange()) {\r\n <span id=\"text-range\"\r\n >{{ textRange()!.at }}\u00D7{{ textRange()!.run }}</span\r\n >\r\n }\r\n </div>\r\n <!-- base text metadata -->\r\n <fieldset>\r\n <div class=\"form-row\">\r\n <!-- offsetX -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetX\"\r\n placeholder=\"X offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- offsetY -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetY\"\r\n placeholder=\"Y offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- lineHeightOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>ln h-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"lineHeightOffset\"\r\n placeholder=\"ln h-offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- charSpacingOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>char spacing</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"charSpacingOffset\"\r\n placeholder=\"char spacing\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- spcWidthOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>spc w-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"spcWidthOffset\"\r\n placeholder=\"spc w-offset\"\r\n />\r\n </mat-form-field>\r\n <!-- minLineHeights -->\r\n <div class=\"boxed\">\r\n <gve-ln-heights-editor\r\n [lineCount]=\"lineCount()\"\r\n [heights]=\"lnHeights.value || undefined\"\r\n (heightsChange)=\"onHeightsChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n <!-- textStyle -->\r\n <div>\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>text style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"textStyle\"\r\n placeholder=\"text style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </fieldset>\r\n <!-- text characters -->\r\n <gve-base-text-view\r\n [text]=\"baseText.value\"\r\n [hasLineNumber]=\"true\"\r\n (rangePick)=\"onRangePick($event)\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- operations -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>edit</mat-icon> <span class=\"label\">operations</span>\r\n </ng-template>\r\n\r\n <div id=\"snapshot-container\">\r\n <div id=\"general\">\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"width\"\r\n placeholder=\"width\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"height\"\r\n placeholder=\"height\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- chain -->\r\n <mat-slide-toggle [formControl]=\"showChain\"\r\n >show chain</mat-slide-toggle\r\n >\r\n\r\n <!-- feat details -->\r\n <mat-slide-toggle [formControl]=\"featDetails\"\r\n >feat. details</mat-slide-toggle\r\n >\r\n\r\n <div class=\"form-row right\">\r\n <!-- remove ops -->\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Remove all the operations\"\r\n [disabled]=\"!operations.value.length || busy()\"\r\n (click)=\"clearOperations()\"\r\n >\r\n <mat-icon>delete_forever</mat-icon> clear\r\n </button>\r\n\r\n <!-- batch add ops -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n matTooltip=\"Add a batch of operations\"\r\n class=\"mat-primary\"\r\n [disabled]=\"busy()\"\r\n (click)=\"parseOperations()\"\r\n >\r\n <mat-icon>post_add</mat-icon> batch\r\n </button>\r\n\r\n <!-- add op -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\r\n [disabled]=\"!baseText.value\"\r\n (click)=\"editNewOperation()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> operation\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- ops -->\r\n <div id=\"ops\">\r\n <!-- operations list -->\r\n @if (operations.value.length) {\r\n <table id=\"list\">\r\n <thead>\r\n <tr>\r\n <th>nr.</th>\r\n <th></th>\r\n <th>ID</th>\r\n <th>type</th>\r\n <th>at</th>\r\n <th>run</th>\r\n <th>value</th>\r\n <th>itag</th>\r\n <th>otag</th>\r\n <th>gid</th>\r\n <th>feats</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (operation of operations.value; track operation.id; let\r\n index=$index) {\r\n <tr\r\n [class.selected]=\"operation.id === resultOperationId\"\r\n [class.edited]=\"operation.id === editedOp()?.id\"\r\n >\r\n <td class=\"fit-width\">{{ index + 1 }}.</td>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-primary\"\r\n (click)=\"editOperation(index)\"\r\n matTooltip=\"Edit operation\"\r\n >\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-warn\"\r\n (click)=\"deleteOperation(index)\"\r\n matTooltip=\"Delete operation\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n <!-- run to -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-accent\"\r\n (click)=\"runTo(index)\"\r\n matTooltip=\"Run to this operation\"\r\n >\r\n <mat-icon class=\"mat-accent\">subscriptions</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ operation.id }}</td>\r\n <td>{{ operation.type | flatLookup : opTypeMap }}</td>\r\n <td>{{ operation.at }}</td>\r\n <td>{{ operation.run }}</td>\r\n <td>{{ operation.value }}</td>\r\n <td>{{ operation.inputTag }}</td>\r\n <td>{{ operation.outputTag }}</td>\r\n <td>{{ operation.groupId }}</td>\r\n <td>\r\n @if (operation.features?.length) { @if (featDetails.value) {\r\n <div class=\"form-row\">\r\n @for (feat of operation.features; track $index; let\r\n i=$index) {\r\n <div class=\"feature\">\r\n <span class=\"fname\">{{ feat.name }}</span\r\n >=<span class=\"fvalue\">{{ feat.value }}</span>\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n {{ operation.features?.length }}\r\n } }\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- operation editor -->\r\n @if (editedOp()) {\r\n <mat-expansion-panel [expanded]=\"editedOp()\" [disabled]=\"!editedOp()\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>operation {{ editedOp()?.id }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <gve-chain-operation-editor\r\n [featureDefs]=\"featureDefs()\"\r\n [elementFeatureDefs]=\"elementFeatureDefs()\"\r\n [diplomaticFeatureDefs]=\"diplomaticFeatureDefs()\"\r\n [hidePreview]=\"editedOpIndex() === -1\"\r\n [operation]=\"editedOp()\"\r\n [rangePatch]=\"editedOpRangePatch()\"\r\n (operationCancel)=\"onOperationCancel()\"\r\n (operationChange)=\"onOperationChange($event)\"\r\n (operationPreview)=\"onOperationPreview($event)\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n }\r\n\r\n <!-- opStyle -->\r\n <div id=\"opStyle\">\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>operations style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"opStyle\"\r\n placeholder=\"operations style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- image -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>image</mat-icon> <span class=\"label\">image</span>\r\n </ng-template>\r\n\r\n <div id=\"image\">\r\n <div id=\"image-ctl\">\r\n <!-- url -->\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>URL</mat-label>\r\n <input matInput [formControl]=\"imageUrl\" />\r\n </mat-form-field>\r\n <div class=\"form-row\">\r\n <!-- x -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageX\"\r\n placeholder=\"X\"\r\n />\r\n </mat-form-field>\r\n <!-- y -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageY\"\r\n placeholder=\"Y\"\r\n />\r\n </mat-form-field>\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageWidth\"\r\n />\r\n </mat-form-field>\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageHeight\"\r\n />\r\n </mat-form-field>\r\n <!-- reset -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Reset image metadata\"\r\n (click)=\"resetImgMetadata()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </div>\r\n <div>\r\n <!-- image opacity -->\r\n <mat-slider\r\n min=\"0\"\r\n max=\"1\"\r\n step=\"0.01\"\r\n thumbLabel\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n matTooltip=\"Image opacity\"\r\n >\r\n <input\r\n matSliderThumb\r\n [value]=\"imageOpacity.value\"\r\n (valueChange)=\"onImageOpacityChange($event)\"\r\n />\r\n </mat-slider>\r\n <span id=\"opacity-value\">{{\r\n imageOpacity.value | number : \"1.2-2\"\r\n }}</span>\r\n </div>\r\n <!-- defs -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>defs</mat-label>\r\n <textarea matInput [formControl]=\"defs\" rows=\"3\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div id=\"image-view\">\r\n @if (imageUrl.value) {\r\n <img\r\n #imgElement\r\n alt=\"background\"\r\n [src]=\"imageUrl.value\"\r\n width=\"600\"\r\n (load)=\"onImageLoad(imgElement)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- timelines -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>animation</mat-icon> <span class=\"label\">animation</span>\r\n </ng-template>\r\n\r\n <gve-animation-timeline-set\r\n [tags]=\"opTags()\"\r\n [elementIds]=\"opElementIds()\"\r\n [timelines]=\"timelines.value\"\r\n (timelinesChange)=\"onTimelinesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- progress -->\r\n <div>\r\n @if (busy()) {\r\n <mat-progress-bar mode=\"indeterminate\" />\r\n }\r\n </div>\r\n\r\n <!-- result -->\r\n @if (result) {\r\n <fieldset id=\"result\">\r\n <legend>result</legend>\r\n <gve-chain-result-view\r\n [result]=\"result\"\r\n [initialStepIndex]=\"initialStepIndex\"\r\n [disabledRangePick]=\"editedOp() ? false : true\"\r\n (stepPick)=\"onStepPick($event)\"\r\n (rangePick)=\"onRangePick($event, true)\"\r\n />\r\n </fieldset>\r\n }\r\n\r\n <!-- snapshot view -->\r\n <fieldset id=\"preview\">\r\n <legend class=\"button-row\">\r\n <span>{{ viewTitle }}</span>\r\n </legend>\r\n <!-- snapshot view -->\r\n <gve-snapshot-view\r\n #snapshotView\r\n [debug]=\"debug()\"\r\n [data]=\"viewData\"\r\n (snapshotRender)=\"onSnapshotRender($any($event))\"\r\n (visualEvent)=\"onVisualEvent($any($event))\"\r\n />\r\n <div class=\"button-row\">\r\n <!-- toggle ruler -->\r\n <mat-button-toggle\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Toggle rulers\"\r\n [checked]=\"rulers\"\r\n (change)=\"toggleRulers()\"\r\n >\r\n <mat-icon class=\"mat-primary\">straighten</mat-icon>\r\n </mat-button-toggle>\r\n <!-- run to last -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"primary\"\r\n matTooltip=\"Refresh preview\"\r\n (click)=\"runToLast()\"\r\n >\r\n <mat-icon>refresh</mat-icon>\r\n </button>\r\n </div>\r\n @if (visualInfo) {\r\n <div id=\"visual-info\">{{ visualInfo }}</div>\r\n }\r\n </fieldset>\r\n\r\n <!-- chain view -->\r\n @if (chain()) {\r\n <div id=\"chain-view\">\r\n <gve-chain-view [chain]=\"chain()\" />\r\n </div>\r\n }\r\n\r\n <!--buttons -->\r\n <div class=\"form-row-center\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon>clear</mat-icon>\r\n close\r\n </button>\r\n @if (!noSave()) {\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save changes\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n }\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}.form-row .right{margin-left:auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}#text-range{margin:8px;border:1px solid silver;border-radius:6px;padding:6px}mat-expansion-panel{margin:8px 0}div#visual-info{font-size:95%;color:#909090;margin:8px}#list{margin:8px 0}#opStyle{margin-top:8px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#c8d9eb}tr.edited{background-color:#f6f6e4}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:#909090}.error{color:red}.input-nr{width:6em}.full-width{width:100%}.code{font-family:Courier New,Courier,monospace}.boxed{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}span.label{margin-left:8px}.feature,.fname{color:silver}.fvalue{color:#fff;background-color:#b5bdc9;padding:2px 4px;border-radius:4px}gve-animation-timeline-set{margin:8px 0}div#image{display:grid;gap:8px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"image-ctl image-view\"}div#image-ctl{grid-area:image-ctl}div#image-view{grid-area:image-view}div#chain-view{margin-bottom:8px}@media only screen and (max-width: 959px){div#image{grid-template-columns:1fr;grid-template-areas:\"image-ctl\" \"image-view\"}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "component", type: i7$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "component", type: i3$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i12.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatSliderModule }, { kind: "component", type: i13.MatSlider, selector: "mat-slider", inputs: ["disabled", "discrete", "showTickMarks", "min", "color", "disableRipple", "max", "step", "displayWith"], exportAs: ["matSlider"] }, { kind: "directive", type: i13.MatSliderThumb, selector: "input[matSliderThumb]", inputs: ["value"], outputs: ["valueChange", "dragStart", "dragEnd"], exportAs: ["matSliderThumb"] }, { kind: "component", type: MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i14.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i14.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i14.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i7.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ChainOperationEditorComponent, selector: "gve-chain-operation-editor", inputs: ["operation", "snapshot", "hidePreview", "featureDefs", "elementFeatureDefs", "diplomaticFeatureDefs", "rangePatch"], outputs: ["operationChange", "operationPreview", "operationCancel"] }, { kind: "component", type: ChainResultViewComponent, selector: "gve-chain-result-view", inputs: ["result", "initialStepIndex", "disabledRangePick"], outputs: ["stepPick", "rangePick"] }, { kind: "component", type: BaseTextViewComponent, selector: "gve-base-text-view", inputs: ["defaultColor", "defaultBorderColor", "selectionColor", "hasLineNumber", "text", "colorCallback", "borderColorCallback"], outputs: ["charPick", "rangePick"] }, { kind: "component", type: LnHeightsEditorComponent, selector: "gve-ln-heights-editor", inputs: ["lineCount", "heights"], outputs: ["heightsChange"] }, { kind: "component", type: AnimationTimelineSetComponent, selector: "gve-animation-timeline-set", inputs: ["timelines", "elementIds", "tags"], outputs: ["timelinesChange", "timelinesCancel"] }, { kind: "component", type: ChainViewComponent, selector: "gve-chain-view", inputs: ["chain", "direction", "selectedTags"], outputs: ["selectedTagsChange"] }, { kind: "pipe", type: i16.DecimalPipe, name: "number" }, { kind: "pipe", type: FlatLookupPipe, name: "flatLookup" }] }); }
3899
3983
  }
3900
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: SnapshotEditorComponent, decorators: [{
3984
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.7", ngImport: i0, type: SnapshotEditorComponent, decorators: [{
3901
3985
  type: Component,
3902
3986
  args: [{ selector: 'gve-snapshot-editor', imports: [
3903
3987
  CommonModule,
@@ -3912,6 +3996,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
3912
3996
  MatInputModule,
3913
3997
  MatProgressBarModule,
3914
3998
  MatSliderModule,
3999
+ MatSlideToggle,
3915
4000
  MatSnackBarModule,
3916
4001
  MatTabsModule,
3917
4002
  MatTooltipModule,
@@ -3922,11 +4007,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
3922
4007
  AnimationTimelineSetComponent,
3923
4008
  FlatLookupPipe,
3924
4009
  ChainViewComponent,
3925
- ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <mat-tab-group>\r\n <!-- text -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>article</mat-icon> <span class=\"label\">text</span>\r\n </ng-template>\r\n <mat-expansion-panel>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title> base text </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <!-- base text -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"mat-warn\"\r\n matTooltip=\"Enter base text\"\r\n (click)=\"inputBaseText()\"\r\n [disabled]=\"busy()\"\r\n >\r\n <mat-icon>edit</mat-icon> base text\r\n </button>\r\n\r\n @if (textRange()) {\r\n <span id=\"text-range\"\r\n >{{ textRange()!.at }}\u00D7{{ textRange()!.run }}</span\r\n >\r\n }\r\n </div>\r\n <!-- base text metadata -->\r\n <fieldset>\r\n <div class=\"form-row\">\r\n <!-- offsetX -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetX\"\r\n placeholder=\"X offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- offsetY -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetY\"\r\n placeholder=\"Y offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- lineHeightOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>ln h-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"lineHeightOffset\"\r\n placeholder=\"ln h-offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- charSpacingOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>char spacing</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"charSpacingOffset\"\r\n placeholder=\"char spacing\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- spcWidthOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>spc w-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"spcWidthOffset\"\r\n placeholder=\"spc w-offset\"\r\n />\r\n </mat-form-field>\r\n <!-- minLineHeights -->\r\n <div class=\"boxed\">\r\n <gve-ln-heights-editor\r\n [lineCount]=\"lineCount()\"\r\n [heights]=\"lnHeights.value || undefined\"\r\n (heightsChange)=\"onHeightsChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n <!-- textStyle -->\r\n <div>\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>text style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"textStyle\"\r\n placeholder=\"text style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </fieldset>\r\n <!-- text characters -->\r\n <gve-base-text-view\r\n [text]=\"baseText.value\"\r\n [hasLineNumber]=\"true\"\r\n (rangePick)=\"onRangePick($event)\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- operations -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>edit</mat-icon> <span class=\"label\">operations</span>\r\n </ng-template>\r\n\r\n <div id=\"snapshot-container\">\r\n <div id=\"general\">\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"width\"\r\n placeholder=\"width\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"height\"\r\n placeholder=\"height\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- chain -->\r\n <mat-checkbox [formControl]=\"showChain\">show chain</mat-checkbox>\r\n\r\n <div class=\"form-row right\">\r\n <!-- remove ops -->\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Remove all the operations\"\r\n [disabled]=\"!operations.value.length || busy()\"\r\n (click)=\"clearOperations()\"\r\n >\r\n <mat-icon>delete_forever</mat-icon> clear\r\n </button>\r\n\r\n <!-- batch add ops -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n matTooltip=\"Add a batch of operations\"\r\n class=\"mat-primary\"\r\n [disabled]=\"busy()\"\r\n (click)=\"parseOperations()\"\r\n >\r\n <mat-icon>post_add</mat-icon> batch\r\n </button>\r\n\r\n <!-- add op -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\r\n [disabled]=\"!baseText.value\"\r\n (click)=\"editNewOperation()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> operation\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- ops -->\r\n <div id=\"ops\">\r\n <!-- operations list -->\r\n @if (operations.value.length) {\r\n <table id=\"list\">\r\n <thead>\r\n <tr>\r\n <th>nr.</th>\r\n <th></th>\r\n <th>ID</th>\r\n <th>type</th>\r\n <th>at</th>\r\n <th>run</th>\r\n <th>value</th>\r\n <th>itag</th>\r\n <th>otag</th>\r\n <th>gid</th>\r\n <th>feats</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (operation of operations.value; track operation.id; let\r\n index=$index) {\r\n <tr\r\n [class.selected]=\"operation.id === resultOperationId\"\r\n [class.edited]=\"operation.id === editedOp()?.id\"\r\n >\r\n <td class=\"fit-width\">{{ index + 1 }}.</td>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-primary\"\r\n (click)=\"editOperation(index)\"\r\n matTooltip=\"Edit operation\"\r\n >\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-warn\"\r\n (click)=\"deleteOperation(index)\"\r\n matTooltip=\"Delete operation\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n <!-- run to -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-accent\"\r\n (click)=\"runTo(index)\"\r\n matTooltip=\"Run to this operation\"\r\n >\r\n <mat-icon class=\"mat-accent\">subscriptions</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ operation.id }}</td>\r\n <td>{{ operation.type | flatLookup : opTypeMap }}</td>\r\n <td>{{ operation.at }}</td>\r\n <td>{{ operation.run }}</td>\r\n <td>{{ operation.value }}</td>\r\n <td>{{ operation.inputTag }}</td>\r\n <td>{{ operation.outputTag }}</td>\r\n <td>{{ operation.groupId }}</td>\r\n <td>{{ operation.features?.length }}</td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- operation editor -->\r\n @if (editedOp()) {\r\n <mat-expansion-panel [expanded]=\"editedOp()\" [disabled]=\"!editedOp()\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>operation {{ editedOp()?.id }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <gve-chain-operation-editor\r\n [featureDefs]=\"featureDefs()\"\r\n [elementFeatureDefs]=\"elementFeatureDefs()\"\r\n [diplomaticFeatureDefs]=\"diplomaticFeatureDefs()\"\r\n [hidePreview]=\"editedOpIndex() === -1\"\r\n [operation]=\"editedOp()\"\r\n (operationCancel)=\"onOperationCancel()\"\r\n (operationChange)=\"onOperationChange($event)\"\r\n (operationPreview)=\"onOperationPreview($event)\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n }\r\n\r\n <!-- opStyle -->\r\n <div id=\"opStyle\">\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>operations style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"opStyle\"\r\n placeholder=\"operations style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- image -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>image</mat-icon> <span class=\"label\">image</span>\r\n </ng-template>\r\n\r\n <div id=\"image\">\r\n <div id=\"image-ctl\">\r\n <!-- url -->\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>URL</mat-label>\r\n <input matInput [formControl]=\"imageUrl\" />\r\n </mat-form-field>\r\n <div class=\"form-row\">\r\n <!-- x -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageX\"\r\n placeholder=\"X\"\r\n />\r\n </mat-form-field>\r\n <!-- y -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageY\"\r\n placeholder=\"Y\"\r\n />\r\n </mat-form-field>\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageWidth\"\r\n />\r\n </mat-form-field>\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageHeight\"\r\n />\r\n </mat-form-field>\r\n <!-- reset -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Reset image metadata\"\r\n (click)=\"resetImgMetadata()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </div>\r\n <div>\r\n <!-- image opacity -->\r\n <mat-slider\r\n min=\"0\"\r\n max=\"1\"\r\n step=\"0.01\"\r\n thumbLabel\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n matTooltip=\"Image opacity\"\r\n >\r\n <input\r\n matSliderThumb\r\n [value]=\"imageOpacity.value\"\r\n (valueChange)=\"onImageOpacityChange($event)\"\r\n />\r\n </mat-slider>\r\n <span id=\"opacity-value\">{{\r\n imageOpacity.value | number : \"1.2-2\"\r\n }}</span>\r\n </div>\r\n <!-- defs -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>defs</mat-label>\r\n <textarea matInput [formControl]=\"defs\" rows=\"3\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div id=\"image-view\">\r\n @if (imageUrl.value) {\r\n <img\r\n #imgElement\r\n alt=\"background\"\r\n [src]=\"imageUrl.value\"\r\n width=\"600\"\r\n (load)=\"onImageLoad(imgElement)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- timelines -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>animation</mat-icon> <span class=\"label\">animation</span>\r\n </ng-template>\r\n\r\n <gve-animation-timeline-set\r\n [tags]=\"opTags()\"\r\n [elementIds]=\"opElementIds()\"\r\n [timelines]=\"timelines.value\"\r\n (timelinesChange)=\"onTimelinesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- progress -->\r\n <div>\r\n @if (busy()) {\r\n <mat-progress-bar mode=\"indeterminate\" />\r\n }\r\n </div>\r\n\r\n <!-- result -->\r\n @if (result) {\r\n <fieldset id=\"result\">\r\n <legend>result</legend>\r\n <gve-chain-result-view\r\n [result]=\"result\"\r\n [initialStepIndex]=\"initialStepIndex\"\r\n (stepPick)=\"onStepPick($event)\"\r\n />\r\n </fieldset>\r\n }\r\n\r\n <!-- snapshot view -->\r\n <fieldset id=\"preview\">\r\n <legend class=\"button-row\">\r\n <span>{{ viewTitle }}</span>\r\n </legend>\r\n <!-- snapshot view -->\r\n <gve-snapshot-view\r\n #snapshotView\r\n [debug]=\"debug()\"\r\n [data]=\"viewData\"\r\n (snapshotRender)=\"onSnapshotRender($any($event))\"\r\n (visualEvent)=\"onVisualEvent($any($event))\"\r\n />\r\n <div class=\"button-row\">\r\n <!-- toggle ruler -->\r\n <mat-button-toggle\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Toggle rulers\"\r\n [checked]=\"rulers\"\r\n (change)=\"toggleRulers()\"\r\n >\r\n <mat-icon class=\"mat-primary\">straighten</mat-icon>\r\n </mat-button-toggle>\r\n <!-- run to last -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"primary\"\r\n matTooltip=\"Refresh preview\"\r\n (click)=\"runToLast()\"\r\n >\r\n <mat-icon>refresh</mat-icon>\r\n </button>\r\n </div>\r\n @if (visualInfo) {\r\n <div id=\"visual-info\">{{ visualInfo }}</div>\r\n }\r\n </fieldset>\r\n\r\n <!-- chain view -->\r\n @if (chain()) {\r\n <div id=\"chain-view\">\r\n <gve-chain-view [chain]=\"chain()\" />\r\n </div>\r\n }\r\n\r\n <!--buttons -->\r\n <div class=\"form-row-center\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon>clear</mat-icon>\r\n close\r\n </button>\r\n @if (!noSave()) {\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save changes\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n }\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}.form-row .right{margin-left:auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}#text-range{margin:8px;border:1px solid silver;border-radius:6px;padding:6px}mat-expansion-panel{margin:8px 0}div#visual-info{font-size:95%;color:#909090;margin:8px}#list{margin:8px 0}#opStyle{margin-top:8px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#c8d9eb}tr.edited{background-color:#f6f6e4}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:#909090}.error{color:red}.input-nr{width:6em}.full-width{width:100%}.code{font-family:Courier New,Courier,monospace}.boxed{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}span.label{margin-left:8px}gve-animation-timeline-set{margin:8px 0}div#image{display:grid;gap:8px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"image-ctl image-view\"}div#image-ctl{grid-area:image-ctl}div#image-view{grid-area:image-view}div#chain-view{margin-bottom:8px}@media only screen and (max-width: 959px){div#image{grid-template-columns:1fr;grid-template-areas:\"image-ctl\" \"image-view\"}}\n"] }]
3926
- }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: GveApiService }, { type: i3$2.MatDialog }, { type: i4$2.DialogService }, { type: i4$3.MatSnackBar }], propDecorators: { snapshotView: [{
4010
+ ], schemas: [CUSTOM_ELEMENTS_SCHEMA], template: "<form [formGroup]=\"form\" (submit)=\"save()\">\r\n <mat-tab-group>\r\n <!-- text -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>article</mat-icon> <span class=\"label\">text</span>\r\n </ng-template>\r\n <mat-expansion-panel>\r\n <mat-expansion-panel-header>\r\n <mat-panel-title> base text </mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <!-- base text -->\r\n <div class=\"form-row\">\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n color=\"mat-warn\"\r\n matTooltip=\"Enter base text\"\r\n (click)=\"inputBaseText()\"\r\n [disabled]=\"busy()\"\r\n >\r\n <mat-icon>edit</mat-icon> base text\r\n </button>\r\n\r\n @if (textRange()) {\r\n <span id=\"text-range\"\r\n >{{ textRange()!.at }}\u00D7{{ textRange()!.run }}</span\r\n >\r\n }\r\n </div>\r\n <!-- base text metadata -->\r\n <fieldset>\r\n <div class=\"form-row\">\r\n <!-- offsetX -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetX\"\r\n placeholder=\"X offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- offsetY -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"offsetY\"\r\n placeholder=\"Y offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- lineHeightOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>ln h-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"lineHeightOffset\"\r\n placeholder=\"ln h-offset\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- charSpacingOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>char spacing</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"charSpacingOffset\"\r\n placeholder=\"char spacing\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- spcWidthOffset -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>spc w-offset</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"spcWidthOffset\"\r\n placeholder=\"spc w-offset\"\r\n />\r\n </mat-form-field>\r\n <!-- minLineHeights -->\r\n <div class=\"boxed\">\r\n <gve-ln-heights-editor\r\n [lineCount]=\"lineCount()\"\r\n [heights]=\"lnHeights.value || undefined\"\r\n (heightsChange)=\"onHeightsChange($event)\"\r\n />\r\n </div>\r\n </div>\r\n <!-- textStyle -->\r\n <div>\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>text style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"textStyle\"\r\n placeholder=\"text style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </fieldset>\r\n <!-- text characters -->\r\n <gve-base-text-view\r\n [text]=\"baseText.value\"\r\n [hasLineNumber]=\"true\"\r\n (rangePick)=\"onRangePick($event)\"\r\n />\r\n </mat-expansion-panel>\r\n </mat-tab>\r\n\r\n <!-- operations -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>edit</mat-icon> <span class=\"label\">operations</span>\r\n </ng-template>\r\n\r\n <div id=\"snapshot-container\">\r\n <div id=\"general\">\r\n <div class=\"form-row\">\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"width\"\r\n placeholder=\"width\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"height\"\r\n placeholder=\"height\"\r\n />\r\n </mat-form-field>\r\n\r\n <!-- chain -->\r\n <mat-slide-toggle [formControl]=\"showChain\"\r\n >show chain</mat-slide-toggle\r\n >\r\n\r\n <!-- feat details -->\r\n <mat-slide-toggle [formControl]=\"featDetails\"\r\n >feat. details</mat-slide-toggle\r\n >\r\n\r\n <div class=\"form-row right\">\r\n <!-- remove ops -->\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Remove all the operations\"\r\n [disabled]=\"!operations.value.length || busy()\"\r\n (click)=\"clearOperations()\"\r\n >\r\n <mat-icon>delete_forever</mat-icon> clear\r\n </button>\r\n\r\n <!-- batch add ops -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n matTooltip=\"Add a batch of operations\"\r\n class=\"mat-primary\"\r\n [disabled]=\"busy()\"\r\n (click)=\"parseOperations()\"\r\n >\r\n <mat-icon>post_add</mat-icon> batch\r\n </button>\r\n\r\n <!-- add op -->\r\n <button\r\n type=\"button\"\r\n mat-flat-button\r\n class=\"mat-primary\"\r\n [disabled]=\"!baseText.value\"\r\n (click)=\"editNewOperation()\"\r\n >\r\n <mat-icon>add_circle</mat-icon> operation\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- ops -->\r\n <div id=\"ops\">\r\n <!-- operations list -->\r\n @if (operations.value.length) {\r\n <table id=\"list\">\r\n <thead>\r\n <tr>\r\n <th>nr.</th>\r\n <th></th>\r\n <th>ID</th>\r\n <th>type</th>\r\n <th>at</th>\r\n <th>run</th>\r\n <th>value</th>\r\n <th>itag</th>\r\n <th>otag</th>\r\n <th>gid</th>\r\n <th>feats</th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (operation of operations.value; track operation.id; let\r\n index=$index) {\r\n <tr\r\n [class.selected]=\"operation.id === resultOperationId\"\r\n [class.edited]=\"operation.id === editedOp()?.id\"\r\n >\r\n <td class=\"fit-width\">{{ index + 1 }}.</td>\r\n <td class=\"fit-width\">\r\n <!-- edit -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-primary\"\r\n (click)=\"editOperation(index)\"\r\n matTooltip=\"Edit operation\"\r\n >\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <!-- delete -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-warn\"\r\n (click)=\"deleteOperation(index)\"\r\n matTooltip=\"Delete operation\"\r\n >\r\n <mat-icon class=\"mat-warn\">delete</mat-icon>\r\n </button>\r\n <!-- run to -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"mat-accent\"\r\n (click)=\"runTo(index)\"\r\n matTooltip=\"Run to this operation\"\r\n >\r\n <mat-icon class=\"mat-accent\">subscriptions</mat-icon>\r\n </button>\r\n </td>\r\n <td>{{ operation.id }}</td>\r\n <td>{{ operation.type | flatLookup : opTypeMap }}</td>\r\n <td>{{ operation.at }}</td>\r\n <td>{{ operation.run }}</td>\r\n <td>{{ operation.value }}</td>\r\n <td>{{ operation.inputTag }}</td>\r\n <td>{{ operation.outputTag }}</td>\r\n <td>{{ operation.groupId }}</td>\r\n <td>\r\n @if (operation.features?.length) { @if (featDetails.value) {\r\n <div class=\"form-row\">\r\n @for (feat of operation.features; track $index; let\r\n i=$index) {\r\n <div class=\"feature\">\r\n <span class=\"fname\">{{ feat.name }}</span\r\n >=<span class=\"fvalue\">{{ feat.value }}</span>\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n {{ operation.features?.length }}\r\n } }\r\n </td>\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n }\r\n\r\n <!-- operation editor -->\r\n @if (editedOp()) {\r\n <mat-expansion-panel [expanded]=\"editedOp()\" [disabled]=\"!editedOp()\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>operation {{ editedOp()?.id }}</mat-panel-title>\r\n </mat-expansion-panel-header>\r\n <fieldset>\r\n <gve-chain-operation-editor\r\n [featureDefs]=\"featureDefs()\"\r\n [elementFeatureDefs]=\"elementFeatureDefs()\"\r\n [diplomaticFeatureDefs]=\"diplomaticFeatureDefs()\"\r\n [hidePreview]=\"editedOpIndex() === -1\"\r\n [operation]=\"editedOp()\"\r\n [rangePatch]=\"editedOpRangePatch()\"\r\n (operationCancel)=\"onOperationCancel()\"\r\n (operationChange)=\"onOperationChange($event)\"\r\n (operationPreview)=\"onOperationPreview($event)\"\r\n />\r\n </fieldset>\r\n </mat-expansion-panel>\r\n }\r\n\r\n <!-- opStyle -->\r\n <div id=\"opStyle\">\r\n <mat-form-field class=\"long-text\" appearance=\"outline\">\r\n <mat-label>operations style</mat-label>\r\n <textarea\r\n matInput\r\n [formControl]=\"opStyle\"\r\n placeholder=\"operations style\"\r\n ></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- image -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>image</mat-icon> <span class=\"label\">image</span>\r\n </ng-template>\r\n\r\n <div id=\"image\">\r\n <div id=\"image-ctl\">\r\n <!-- url -->\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>URL</mat-label>\r\n <input matInput [formControl]=\"imageUrl\" />\r\n </mat-form-field>\r\n <div class=\"form-row\">\r\n <!-- x -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>X</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageX\"\r\n placeholder=\"X\"\r\n />\r\n </mat-form-field>\r\n <!-- y -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>Y</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n [formControl]=\"imageY\"\r\n placeholder=\"Y\"\r\n />\r\n </mat-form-field>\r\n <!-- width -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>width</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageWidth\"\r\n />\r\n </mat-form-field>\r\n <!-- height -->\r\n <mat-form-field class=\"input-nr\">\r\n <mat-label>height</mat-label>\r\n <input\r\n matInput\r\n type=\"number\"\r\n min=\"0\"\r\n [formControl]=\"imageHeight\"\r\n />\r\n </mat-form-field>\r\n <!-- reset -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n matTooltip=\"Reset image metadata\"\r\n (click)=\"resetImgMetadata()\"\r\n >\r\n <mat-icon class=\"mat-warn\">clear</mat-icon>\r\n </button>\r\n </div>\r\n <div>\r\n <!-- image opacity -->\r\n <mat-slider\r\n min=\"0\"\r\n max=\"1\"\r\n step=\"0.01\"\r\n thumbLabel\r\n color=\"primary\"\r\n class=\"mat-primary\"\r\n matTooltip=\"Image opacity\"\r\n >\r\n <input\r\n matSliderThumb\r\n [value]=\"imageOpacity.value\"\r\n (valueChange)=\"onImageOpacityChange($event)\"\r\n />\r\n </mat-slider>\r\n <span id=\"opacity-value\">{{\r\n imageOpacity.value | number : \"1.2-2\"\r\n }}</span>\r\n </div>\r\n <!-- defs -->\r\n <div>\r\n <mat-form-field class=\"long-text\">\r\n <mat-label>defs</mat-label>\r\n <textarea matInput [formControl]=\"defs\" rows=\"3\"></textarea>\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n <div id=\"image-view\">\r\n @if (imageUrl.value) {\r\n <img\r\n #imgElement\r\n alt=\"background\"\r\n [src]=\"imageUrl.value\"\r\n width=\"600\"\r\n (load)=\"onImageLoad(imgElement)\"\r\n />\r\n }\r\n </div>\r\n </div>\r\n </mat-tab>\r\n\r\n <!-- timelines -->\r\n <mat-tab>\r\n <ng-template mat-tab-label>\r\n <mat-icon>animation</mat-icon> <span class=\"label\">animation</span>\r\n </ng-template>\r\n\r\n <gve-animation-timeline-set\r\n [tags]=\"opTags()\"\r\n [elementIds]=\"opElementIds()\"\r\n [timelines]=\"timelines.value\"\r\n (timelinesChange)=\"onTimelinesChange($event)\"\r\n />\r\n </mat-tab>\r\n </mat-tab-group>\r\n\r\n <!-- progress -->\r\n <div>\r\n @if (busy()) {\r\n <mat-progress-bar mode=\"indeterminate\" />\r\n }\r\n </div>\r\n\r\n <!-- result -->\r\n @if (result) {\r\n <fieldset id=\"result\">\r\n <legend>result</legend>\r\n <gve-chain-result-view\r\n [result]=\"result\"\r\n [initialStepIndex]=\"initialStepIndex\"\r\n [disabledRangePick]=\"editedOp() ? false : true\"\r\n (stepPick)=\"onStepPick($event)\"\r\n (rangePick)=\"onRangePick($event, true)\"\r\n />\r\n </fieldset>\r\n }\r\n\r\n <!-- snapshot view -->\r\n <fieldset id=\"preview\">\r\n <legend class=\"button-row\">\r\n <span>{{ viewTitle }}</span>\r\n </legend>\r\n <!-- snapshot view -->\r\n <gve-snapshot-view\r\n #snapshotView\r\n [debug]=\"debug()\"\r\n [data]=\"viewData\"\r\n (snapshotRender)=\"onSnapshotRender($any($event))\"\r\n (visualEvent)=\"onVisualEvent($any($event))\"\r\n />\r\n <div class=\"button-row\">\r\n <!-- toggle ruler -->\r\n <mat-button-toggle\r\n type=\"button\"\r\n mat-icon-button\r\n color=\"primary\"\r\n matTooltip=\"Toggle rulers\"\r\n [checked]=\"rulers\"\r\n (change)=\"toggleRulers()\"\r\n >\r\n <mat-icon class=\"mat-primary\">straighten</mat-icon>\r\n </mat-button-toggle>\r\n <!-- run to last -->\r\n <button\r\n type=\"button\"\r\n mat-icon-button\r\n class=\"primary\"\r\n matTooltip=\"Refresh preview\"\r\n (click)=\"runToLast()\"\r\n >\r\n <mat-icon>refresh</mat-icon>\r\n </button>\r\n </div>\r\n @if (visualInfo) {\r\n <div id=\"visual-info\">{{ visualInfo }}</div>\r\n }\r\n </fieldset>\r\n\r\n <!-- chain view -->\r\n @if (chain()) {\r\n <div id=\"chain-view\">\r\n <gve-chain-view [chain]=\"chain()\" />\r\n </div>\r\n }\r\n\r\n <!--buttons -->\r\n <div class=\"form-row-center\">\r\n <button\r\n type=\"button\"\r\n class=\"mat-warn\"\r\n mat-flat-button\r\n matTooltip=\"Discard changes\"\r\n (click)=\"close()\"\r\n >\r\n <mat-icon>clear</mat-icon>\r\n close\r\n </button>\r\n @if (!noSave()) {\r\n <button\r\n type=\"submit\"\r\n class=\"mat-primary\"\r\n mat-flat-button\r\n matTooltip=\"Save changes\"\r\n [disabled]=\"form.invalid\"\r\n >\r\n <mat-icon>check_circle</mat-icon>\r\n save\r\n </button>\r\n }\r\n </div>\r\n</form>\r\n", styles: [".form-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.form-row-center{display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}.form-row,.form-row-center *{flex:0 0 auto}.form-row .right{margin-left:auto}.button-row{display:flex;align-items:center;flex-wrap:wrap}.button-row *{flex:0 0 auto}.long-text{width:100%;max-width:800px}#text-range{margin:8px;border:1px solid silver;border-radius:6px;padding:6px}mat-expansion-panel{margin:8px 0}div#visual-info{font-size:95%;color:#909090;margin:8px}#list{margin:8px 0}#opStyle{margin-top:8px}table{width:100%;border-collapse:collapse}th{color:#909090;font-weight:400;text-align:left;background-color:#e1e0e0}th,td{padding:4px;border-bottom:1px solid silver}tbody tr:nth-child(2n){background-color:#e8e8e8}td.fit-width{width:1px;white-space:nowrap}tr.selected{background-color:#c8d9eb}tr.edited{background-color:#f6f6e4}fieldset{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}legend{color:#909090}.error{color:red}.input-nr{width:6em}.full-width{width:100%}.code{font-family:Courier New,Courier,monospace}.boxed{border:1px solid silver;border-radius:6px;padding:8px;margin:8px 0}span.label{margin-left:8px}.feature,.fname{color:silver}.fvalue{color:#fff;background-color:#b5bdc9;padding:2px 4px;border-radius:4px}gve-animation-timeline-set{margin:8px 0}div#image{display:grid;gap:8px;grid-template-rows:auto;grid-template-columns:1fr auto;grid-template-areas:\"image-ctl image-view\"}div#image-ctl{grid-area:image-ctl}div#image-view{grid-area:image-view}div#chain-view{margin-bottom:8px}@media only screen and (max-width: 959px){div#image{grid-template-columns:1fr;grid-template-areas:\"image-ctl\" \"image-view\"}}\n"] }]
4011
+ }], ctorParameters: () => [{ type: i1.FormBuilder }, { type: GveApiService }, { type: i3$3.MatDialog }, { type: i4$1.DialogService }, { type: i4$2.MatSnackBar }], propDecorators: { snapshotView: [{
3927
4012
  type: ViewChild,
3928
4013
  args: ['snapshotView', { static: false }]
3929
- }] } });
4014
+ }], snapshot: [{ type: i0.Input, args: [{ isSignal: true, alias: "snapshot", required: false }] }, { type: i0.Output, args: ["snapshotChange"] }], batchOps: [{ type: i0.Input, args: [{ isSignal: true, alias: "batchOps", required: false }] }], noSave: [{ type: i0.Input, args: [{ isSignal: true, alias: "noSave", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], noDecoration: [{ type: i0.Input, args: [{ isSignal: true, alias: "noDecoration", required: false }] }], featureDefs: [{ type: i0.Input, args: [{ isSignal: true, alias: "featureDefs", required: false }] }], elementFeatureDefs: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementFeatureDefs", required: false }] }], diplomaticFeatureDefs: [{ type: i0.Input, args: [{ isSignal: true, alias: "diplomaticFeatureDefs", required: false }] }], snapshotCancel: [{ type: i0.Output, args: ["snapshotCancel"] }] } });
3930
4015
 
3931
4016
  /*
3932
4017
  * Public API Surface of gve-core