@firestitch/report 18.0.10 → 18.0.11

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.
@@ -245,6 +245,20 @@ class ReportData {
245
245
  ...config,
246
246
  });
247
247
  }
248
+ // The report module's General settings (semantic-build concurrency + its bounds).
249
+ settings(config = {}) {
250
+ return this._api.get(this._path('settings'), {}, {
251
+ key: 'settings',
252
+ ...config,
253
+ });
254
+ }
255
+ // Save the General settings; resolves to the same { settings } shape.
256
+ saveSettings(settings, config = {}) {
257
+ return this._api.put(this._path('settings'), settings, {
258
+ key: 'settings',
259
+ ...config,
260
+ });
261
+ }
248
262
  // Update a filter group — its exposure level ('component' | 'report' | 'both')
249
263
  // and/or its default value (the value seeding the report bar on open). Send
250
264
  // `default: null` (or an empty default) to clear it. Only the keys provided
@@ -2527,7 +2541,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
2527
2541
  // complete synchronously after setOption.
2528
2542
  async function renderChartImage(component, data, width, height, pixelRatio = 3) {
2529
2543
  // The same tree-shaken echarts build + 'report' theme the live canvas uses.
2530
- const echarts = (await import('./firestitch-report-echarts-Cel1iHUg.mjs')).default;
2544
+ const echarts = (await import('./firestitch-report-echarts-DaBOoR9Z.mjs')).default;
2531
2545
  const host = document.createElement('div');
2532
2546
  host.style.width = `${width}px`;
2533
2547
  host.style.height = `${height}px`;
@@ -3271,7 +3285,7 @@ class ReportComponent {
3271
3285
  ReportPdfService,
3272
3286
  // Tree-shaken ECharts core + the 'report' house theme, loaded lazily with
3273
3287
  // this route's chunk.
3274
- provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-Cel1iHUg.mjs').then((module) => module.default) }),
3288
+ provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-DaBOoR9Z.mjs').then((module) => module.default) }),
3275
3289
  ], viewQueries: [{ propertyName: "_split", first: true, predicate: ["split"], descendants: true, static: true }, { propertyName: "_chatPanel", first: true, predicate: ["chatPanel"], descendants: true, read: ElementRef, static: true }], ngImport: i0, template: "<div\n #split\n class=\"report fs-row.align-start\">\n <fs-ai-chat\n #chatPanel\n class=\"chat\"\n style=\"min-height: 500px;\"\n basePath=\"reports\"\n [requestData]=\"{ reportId: selected?.id ?? null }\"\n [introMessage]=\"introMessage\"\n (response)=\"onChatResponse($event)\">\n </fs-ai-chat>\n <div\n class=\"resizer\"\n (pointerdown)=\"onResizeStart($event)\">\n </div>\n <div class=\"viewer fs-flex fs-column\">\n <div class=\"fs-row.align-center.gap-sm\">\n <fs-autocomplete-chips\n class=\"fs-flex\"\n [fetch]=\"fetchReports\"\n [(ngModel)]=\"selected\"\n [disabled]=\"loadingReports\"\n [padless]=\"true\"\n [multiple]=\"false\"\n [fetchOnFocus]=\"true\"\n (ngModelChange)=\"reportChange($event)\"\n placeholder=\"Report\"\n name=\"report\">\n <ng-template\n fsAutocompleteChipsTemplate\n let-object=\"object\">\n {{ object.name }}\n </ng-template>\n <ng-template\n fsAutocompleteChipsStatic\n (click)=\"createReport()\">\n Create Report\n </ng-template>\n </fs-autocomplete-chips>\n @if (report) {\n <fs-menu>\n <ng-template\n fs-menu-item\n (click)=\"reportSettings()\">\n <mat-icon>\n tune\n </mat-icon>\n Report settings\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"toggleEditMode()\"\n [disabled]=\"editMode\">\n <mat-icon>\n dashboard_2_edit\n </mat-icon>\n {{ editMode ? 'Done editing layout' : 'Edit layout' }}\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportPowerpoint()\">\n <mat-icon>\n slideshow\n </mat-icon>\n Export PowerPoint\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportPdf()\">\n <mat-icon>\n picture_as_pdf\n </mat-icon>\n Export PDF\n </ng-template>\n </fs-menu>\n }\n </div>\n @if (report) {\n <!-- Report-level filters only (the report's actions live in the menu\n above). fs-filter reads its config once at init, so it's keyed on the\n filter signature: when the report-level filter set changes the block\n is recreated and re-reads the rebuilt config. -->\n @if (reportHasFilters) {\n @for (key of [reportFilterKey]; track key) {\n <fs-filter [config]=\"reportFilterConfig\"></fs-filter>\n }\n }\n <app-report-canvas\n [report]=\"report\"\n [editMode]=\"editMode\"\n (componentSettings)=\"componentSettings($event)\"\n (reportChanged)=\"onCanvasReportChanged()\"\n (editDone)=\"toggleEditMode()\">\n </app-report-canvas>\n }\n </div>\n</div>", styles: [".report{height:100%;min-height:500px}.report .chat{display:block;flex:0 0 25%;width:100%;height:100%;min-height:500px;min-width:0;border:none}.report .viewer{display:flex;flex-direction:column;min-width:0;height:100%;overflow:hidden}.report .viewer fs-filter{flex:0 0 auto;margin-top:10px;margin-bottom:0}.report .viewer ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.report .resizer{flex:0 0 11px;align-self:stretch;display:flex;justify-content:center;cursor:col-resize;touch-action:none;-webkit-user-select:none;user-select:none}.report .resizer:before{content:\"\";width:0px;background:#0000001f;transition:width .12s ease,background-color .12s ease}.report .resizer:hover:before{width:3px;background:var(--brand-primary-color)}.report.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.report.resizing .resizer:before{width:3px;background:var(--brand-primary-color)}.report.resizing .chat,.report.resizing .viewer{pointer-events:none}::ng-deep body.body-report-reports .mat-mdc-card-content{display:flex;flex-direction:column;box-sizing:border-box}::ng-deep body.body-report-reports .mat-mdc-card-content mat-tab-nav-panel{flex:1;min-height:0}::ng-deep body.body-report-reports .mat-mdc-card-content mat-tab-nav-panel router-outlet+ng-component{height:100%}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: FsAutocompleteChipsModule }, { kind: "component", type: i2$2.FsAutocompleteChipsComponent, selector: "fs-autocomplete-chips", inputs: ["fetch", "appearance", "floatLabel", "readonly", "size", "label", "placeholder", "chipImage", "chipBackground", "chipColor", "chipIcon", "chipIconColor", "chipClass", "chipPadding", "shape", "hint", "allowText", "allowObject", "delay", "minPanelWidth", "maxPanelHeight", "validateText", "removable", "allowClear", "color", "background", "orderable", "padless", "initOnClick", "fetchOnFocus", "multiple", "multipleAdd", "confirm", "disabled", "groupBy", "panelWidth", "panelClass", "compareWith"], outputs: ["selected", "removed", "reordered", "clear", "panelOpened", "panelClosed"] }, { kind: "directive", type: i2$2.FsAutocompleteObjectDirective, selector: "[fsAutocompleteObject],[fsAutocompleteChipsTemplate]" }, { kind: "directive", type: i2$2.FsAutocompleteChipsStaticDirective, selector: "[fsAutocompleteChipsStatic]", inputs: ["show", "disable"], outputs: ["click", "selected"] }, { kind: "ngmodule", type: FsFilterModule }, { kind: "component", type: i2.FilterComponent, selector: "fs-filter", inputs: ["config"], outputs: ["closed", "opened", "ready"] }, { kind: "ngmodule", type: FsMenuModule }, { kind: "component", type: i2$3.FsMenuComponent, selector: "fs-menu", inputs: ["class", "buttonClass", "buttonType", "buttonColor"], outputs: ["opened", "closed"] }, { kind: "directive", type: i2$3.FsMenuItemDirective, selector: "fs-menu-group,[fs-menu-item]" }, { kind: "component", type: FsAiChatComponent, selector: "fs-ai-chat", inputs: ["basePath", "requestData", "introMessage"], outputs: ["response"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: ReportCanvasComponent, selector: "app-report-canvas", inputs: ["report", "editMode"], outputs: ["componentSettings", "reportChanged", "editDone"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3276
3290
  }
3277
3291
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ReportComponent, decorators: [{
@@ -3285,7 +3299,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3285
3299
  ReportPdfService,
3286
3300
  // Tree-shaken ECharts core + the 'report' house theme, loaded lazily with
3287
3301
  // this route's chunk.
3288
- provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-Cel1iHUg.mjs').then((module) => module.default) }),
3302
+ provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-DaBOoR9Z.mjs').then((module) => module.default) }),
3289
3303
  ], imports: [
3290
3304
  FormsModule,
3291
3305
  FsAutocompleteChipsModule,
@@ -3327,6 +3341,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3327
3341
  ], template: "<div class=\"fs-column tab-body\">\n <p class=\"hint\">\n This document is given to the report AI on every chat message. Use it for\n terminology, business rules and special cases the AI can't work out from the\n data on its own \u2014 it takes effect immediately, no deploy needed.\n </p>\n <fs-markdown-editor\n [ngModel]=\"content()\"\n (ngModelChange)=\"content.set($event)\"\n [config]=\"config()\"\n name=\"content\">\n </fs-markdown-editor>\n</div>\n", styles: [".tab-body{padding-top:16px}.hint{margin:0 0 12px;color:#0009;font-size:13px;line-height:1.5}fs-markdown-editor{display:block;min-height:320px}\n"] }]
3328
3342
  }] });
3329
3343
 
3344
+ // The General tab: report-module settings that aren't the AI knowledge document.
3345
+ // Currently just the semantic-build concurrency — how many summary calls the
3346
+ // build runs at once. A field-level child of the settings dialog form (its input
3347
+ // registers with the parent <form fsForm> via the ControlContainer view
3348
+ // provider), so the dialog's Save button submits and validates it. The value
3349
+ // itself is held on the shell (bound through `concurrency`), and the safe band
3350
+ // (`min`/`max`) comes from the backend so the field can't be set out of range.
3351
+ class GeneralComponent {
3352
+ concurrency = model(0);
3353
+ min = input.required();
3354
+ max = input.required();
3355
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GeneralComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3356
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.14", type: GeneralComponent, isStandalone: true, selector: "fs-ai-report-global-settings-general", inputs: { concurrency: { classPropertyName: "concurrency", publicName: "concurrency", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: true, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { concurrency: "concurrencyChange" }, ngImport: i0, template: "<div class=\"fs-column tab-body\">\n <mat-form-field>\n <mat-label>Semantic build concurrency</mat-label>\n <input\n matInput\n type=\"number\"\n [ngModel]=\"concurrency()\"\n (ngModelChange)=\"concurrency.set($event)\"\n name=\"semanticBuildConcurrency\"\n [fsFormRequired]=\"true\"\n [fsFormInteger]=\"true\"\n [fsFormMin]=\"min()\"\n [fsFormMax]=\"max()\">\n <mat-hint>\n How many AI calls the semantic-database build runs at once ({{ min() }}\u2013{{ max() }}).\n Higher is faster but more likely to hit the AI provider's rate limit. Default is 8.\n </mat-hint>\n </mat-form-field>\n</div>\n", styles: [".tab-body{padding-top:16px}mat-form-field{display:block;max-width:340px}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.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$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: FsFormModule }, { kind: "directive", type: i2$1.FsFormRequiredDirective, selector: "[fsFormRequired],[ngModel][required]", inputs: ["fsFormRequired", "required", "fsFormRequiredMessage"] }, { kind: "directive", type: i2$1.FsFormMinDirective, selector: "[fsFormMin]", inputs: ["fsFormMin", "fsFormMinMessage"] }, { kind: "directive", type: i2$1.FsFormMaxDirective, selector: "[fsFormMax]", inputs: ["fsFormMax", "fsFormMaxMessage"] }, { kind: "directive", type: i2$1.FsFormIntegerDirective, selector: "[fsFormInteger]", inputs: ["fsFormInteger", "fsFormIntegerMessage"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3357
+ }
3358
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GeneralComponent, decorators: [{
3359
+ type: Component,
3360
+ args: [{ selector: 'fs-ai-report-global-settings-general', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
3361
+ FormsModule,
3362
+ FsFormModule,
3363
+ MatFormField,
3364
+ MatLabel,
3365
+ MatHint,
3366
+ MatInput,
3367
+ ], template: "<div class=\"fs-column tab-body\">\n <mat-form-field>\n <mat-label>Semantic build concurrency</mat-label>\n <input\n matInput\n type=\"number\"\n [ngModel]=\"concurrency()\"\n (ngModelChange)=\"concurrency.set($event)\"\n name=\"semanticBuildConcurrency\"\n [fsFormRequired]=\"true\"\n [fsFormInteger]=\"true\"\n [fsFormMin]=\"min()\"\n [fsFormMax]=\"max()\">\n <mat-hint>\n How many AI calls the semantic-database build runs at once ({{ min() }}\u2013{{ max() }}).\n Higher is faster but more likely to hit the AI provider's rate limit. Default is 8.\n </mat-hint>\n </mat-form-field>\n</div>\n", styles: [".tab-body{padding-top:16px}mat-form-field{display:block;max-width:340px}\n"] }]
3368
+ }] });
3369
+
3330
3370
  // The History tab: every saved version of the AI Knowledge document
3331
3371
  // (append-only), each previewed and restorable. Restoring mutates the current
3332
3372
  // version and reseeds the editor — a parent concern — so this stays
@@ -3401,6 +3441,9 @@ class SemanticDatabaseComponent {
3401
3441
  // Per-entity function caches, loaded on first expand. undefined = not
3402
3442
  // requested yet, null = request in flight (the skeleton window), object = loaded.
3403
3443
  _entityFunctions = signal({});
3444
+ // Guards the outcome message to fire exactly once per run — whether the stream
3445
+ // completes (build finished, possibly failed) or errors outright.
3446
+ _buildFinalized = false;
3404
3447
  _reportData = inject(ReportData);
3405
3448
  _process = inject(FsProcess);
3406
3449
  _prompt = inject(FsPrompt);
@@ -3441,22 +3484,62 @@ class SemanticDatabaseComponent {
3441
3484
  // dismissing the dialog never interrupts a running build.
3442
3485
  _runBuild() {
3443
3486
  this.building.set(true);
3487
+ this._buildFinalized = false;
3444
3488
  const target$ = this._reportData.semanticBuild()
3445
- .pipe(filter((event) => event?.type === StreamEventType.Data
3489
+ .pipe(
3490
+ // A hard stream/transport failure (the build couldn't even start)
3491
+ // finalizes here — completed$ won't fire in that case.
3492
+ tap({ error: (error) => this._finalizeBuild(error) }), filter((event) => event?.type === StreamEventType.Data
3446
3493
  && event.data?.kind === 'log'
3447
3494
  && typeof event.data.line === 'string'), map((event) => event.data.line));
3448
3495
  this._process
3449
3496
  .run('Semantic database build', target$)
3450
3497
  .completed$
3451
3498
  .pipe(take(1), takeUntilDestroyed(this._destroyRef))
3452
- .subscribe(() => {
3453
- this.building.set(false);
3454
- // The build rewrote every entity's function cache — drop the loaded
3455
- // copies so the next expand refetches fresh data.
3456
- this._entityFunctions.set({});
3499
+ .subscribe(() => this._finalizeBuild(null));
3500
+ }
3501
+ /**
3502
+ * Wrap up one build run exactly once. On a hard stream error there is no
3503
+ * status to read, so say so directly; otherwise refresh the status and
3504
+ * announce how the build actually ended — a build that failed (e.g. the AI
3505
+ * couldn't be reached) reports 'Failed' with a reason, and that must surface
3506
+ * instead of the run silently doing nothing.
3507
+ */
3508
+ _finalizeBuild(streamError) {
3509
+ if (this._buildFinalized) {
3510
+ return;
3511
+ }
3512
+ this._buildFinalized = true;
3513
+ this.building.set(false);
3514
+ // The build rewrote every entity's function cache — drop the loaded copies
3515
+ // so the next expand refetches fresh data.
3516
+ this._entityFunctions.set({});
3517
+ if (streamError) {
3518
+ this._message.error('The semantic database build could not run — check the AI connection and try again.');
3457
3519
  this._refreshStatus();
3520
+ return;
3521
+ }
3522
+ this._reportData.semanticBuildStatus()
3523
+ .pipe(takeUntilDestroyed(this._destroyRef))
3524
+ .subscribe((status) => {
3525
+ this.status.set(status);
3526
+ this._announceOutcome(status);
3458
3527
  });
3459
3528
  }
3529
+ /**
3530
+ * Tell the operator how the build ended: a failed/stopped build surfaces its
3531
+ * reason (so an AI connection problem is never silent), a completed one
3532
+ * confirms success.
3533
+ */
3534
+ _announceOutcome(status) {
3535
+ const build = status?.build;
3536
+ if (build?.state === 'F' || build?.state === 'K') {
3537
+ this._message.error(build.message || 'The semantic database build did not finish.');
3538
+ }
3539
+ else if (build?.state === 'C') {
3540
+ this._message.success('Semantic database build complete.');
3541
+ }
3542
+ }
3460
3543
  stageColor(stage) {
3461
3544
  return stage === 'done'
3462
3545
  ? SemanticDatabaseComponent.DONE_COLOR
@@ -3563,8 +3646,10 @@ const SCAFFOLD = [
3563
3646
  // History — every saved version (append-only), any of which can be restored.
3564
3647
  // Semantic Database — the build scoreboard the AI is built from, with a
3565
3648
  // self-contained Build action.
3566
- // The shell owns only the instructions document shared by the first two tabs
3567
- // (the editor draft, save and restore); everything else lives in the children.
3649
+ // General — report-module settings (the semantic-build concurrency).
3650
+ // The shell owns the shared data (the instructions document for the first two
3651
+ // tabs, the settings for General) plus the tab-aware save; everything else lives
3652
+ // in the children.
3568
3653
  class GlobalSettingsComponent {
3569
3654
  selectedTab = 'knowledge';
3570
3655
  // The Semantic Database tab component, so the shared footer can drive its
@@ -3577,8 +3662,13 @@ class GlobalSettingsComponent {
3577
3662
  maxLength: MAX_LENGTH,
3578
3663
  placeholder: 'Terminology, business rules and special cases the report AI should know…',
3579
3664
  };
3580
- // null until loaded — gates the form behind the skeleton.
3665
+ // Both null until loaded — the form waits behind the skeleton until each is in.
3581
3666
  instructions = signal(null);
3667
+ settings = signal(null);
3668
+ // The General tab's concurrency draft (bound to its field), seeded from settings.
3669
+ concurrency = signal(0);
3670
+ // Gate the whole form on both loads, so every tab's fields are ready to render.
3671
+ loaded = computed(() => !!this.instructions() && !!this.settings());
3582
3672
  _dialogRef = inject(MatDialogRef);
3583
3673
  _reportData = inject(ReportData);
3584
3674
  _message = inject(FsMessage);
@@ -3587,8 +3677,17 @@ class GlobalSettingsComponent {
3587
3677
  constructor() {
3588
3678
  this._init();
3589
3679
  }
3590
- // Save the draft as a new version (the backend skips an unchanged save).
3680
+ // The footer Save is shared, so it saves whatever the current tab owns: the
3681
+ // General tab persists its settings (and stays open); AI Knowledge saves a new
3682
+ // document version and closes.
3591
3683
  save = () => {
3684
+ if (this.selectedTab === 'general') {
3685
+ return this._reportData.saveSettings({ semanticBuildConcurrency: this.concurrency() })
3686
+ .pipe(tap((settings) => {
3687
+ this._applySettings(settings);
3688
+ this._message.success('Settings saved');
3689
+ }));
3690
+ }
3592
3691
  return this._reportData.saveInstructions(this.content())
3593
3692
  .pipe(tap((instructions) => {
3594
3693
  this._message.success('AI knowledge saved');
@@ -3612,13 +3711,20 @@ class GlobalSettingsComponent {
3612
3711
  this._reportData.instructions()
3613
3712
  .pipe(takeUntilDestroyed(this._destroyRef))
3614
3713
  .subscribe((instructions) => this._applyInstructions(instructions));
3714
+ this._reportData.settings()
3715
+ .pipe(takeUntilDestroyed(this._destroyRef))
3716
+ .subscribe((settings) => this._applySettings(settings));
3615
3717
  }
3616
3718
  _applyInstructions(instructions) {
3617
3719
  this.instructions.set(instructions);
3618
3720
  this.content.set(instructions.instructions?.content ?? SCAFFOLD);
3619
3721
  }
3722
+ _applySettings(settings) {
3723
+ this.settings.set(settings);
3724
+ this.concurrency.set(settings.semanticBuildConcurrency);
3725
+ }
3620
3726
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GlobalSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3621
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: GlobalSettingsComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "semanticDatabase", first: true, predicate: SemanticDatabaseComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<form\n *fsSkeletonForm=\"instructions()\"\n fsForm\n [submit]=\"save\">\n <fs-dialog>\n <fs-dialog-title>\n Report Settings\n </fs-dialog-title>\n <mat-dialog-content>\n <mat-tab-group [(selected)]=\"selectedTab\">\n <mat-tab\n label=\"AI Knowledge\"\n name=\"knowledge\">\n <fs-ai-report-global-settings-ai-knowledge\n [content]=\"content()\"\n (contentChange)=\"content.set($event)\"\n [config]=\"editorConfig\">\n </fs-ai-report-global-settings-ai-knowledge>\n </mat-tab>\n <mat-tab\n label=\"History\"\n name=\"history\">\n <fs-ai-report-global-settings-history\n [versions]=\"instructions()?.versions ?? []\"\n (restore)=\"restore($event)\">\n </fs-ai-report-global-settings-history>\n </mat-tab>\n <mat-tab\n label=\"Semantic Database\"\n name=\"build\">\n <fs-ai-report-global-settings-semantic-database>\n </fs-ai-report-global-settings-semantic-database>\n </mat-tab>\n </mat-tab-group>\n </mat-dialog-content>\n <mat-dialog-actions>\n <!-- The footer follows the selected tab. AI Knowledge is the only editable\n tab, so it gets Save/Cancel; History and Semantic Database are\n read/run surfaces, so they get a primary Close. The Semantic Database\n tab also carries its Build/Rebuild/Stop action to the right of Close. -->\n <fs-form-dialog-actions [save]=\"selectedTab === 'knowledge'\">\n @if (selectedTab !== 'knowledge') {\n <button\n type=\"button\"\n mat-button\n color=\"primary\"\n [mat-dialog-close]=\"undefined\">\n Close\n </button>\n }\n @if (selectedTab === 'build' && semanticDatabase(); as db) {\n @if (db.buildActive()) {\n <button\n type=\"button\"\n mat-button\n color=\"warn\"\n (click)=\"db.stopBuild()\">\n Stop Build\n </button>\n } @else {\n @if (db.status(); as buildStatus) {\n <button\n type=\"button\"\n mat-button\n (click)=\"db.build()\">\n {{ buildStatus.detail.generatedAt ? 'Rebuild' : 'Build' }}\n </button>\n }\n }\n }\n </fs-form-dialog-actions>\n </mat-dialog-actions>\n </fs-dialog>\n</form>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FsDialogModule }, { kind: "component", type: i3.FsDialogComponent, selector: "fs-dialog", inputs: ["mobileMode", "mobileButtonPlacement", "mobileWidth", "mode", "buttonLayout"] }, { kind: "component", type: i3.FsDialogTitleComponent, selector: "fs-dialog-title", inputs: ["close", "dockToggle", "back"] }, { kind: "ngmodule", type: FsFormModule }, { kind: "directive", type: i2$1.FsFormDirective, selector: "[fsForm]", inputs: ["wrapperSelector", "messageSelector", "hintSelector", "labelSelector", "autocomplete", "shortcuts", "confirm", "confirmDialog", "confirmDrawer", "confirmBrowser", "dirtySubmitButton", "submit", "successDelay", "errorDelay", "deactivationGuard"], outputs: ["fsForm", "invalid", "valid", "submitted", "reseted", "cleared"], exportAs: ["fsForm"] }, { kind: "component", type: i2$1.FsFormDialogActionsComponent, selector: "fs-form-dialog-actions", inputs: ["save", "create", "close", "done", "closeData", "name"] }, { kind: "directive", type: i2$1.FsButtonDirective, selector: "[mat-raised-button],[mat-button],[mat-flat-button],[mat-stroked-button]", inputs: ["name", "dirtySubmit"] }, { kind: "ngmodule", type: FsSkeletonModule }, { kind: "directive", type: i6$1.FsSkeletonFormDirective, selector: "[fsSkeletonForm]", inputs: ["fsSkeletonForm", "fsSkeletonFormLines"] }, { kind: "ngmodule", type: FsTabsModule }, { kind: "directive", type: i5.FsTabsHeaderTabGroupDirective, selector: "mat-tab-group, matTabGroup, [matTabGroup]", inputs: ["orientation", "selected", "selectedData"], outputs: ["selectedChange", "selectedDataChange"], exportAs: ["fsTabsHeaderTabGroup"] }, { kind: "directive", type: i5.FsTabsTabDirective, selector: "mat-tab,matTab", inputs: ["name", "data"], exportAs: ["fsTabsTab"] }, { kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i5$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i5$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: AiKnowledgeComponent, selector: "fs-ai-report-global-settings-ai-knowledge", inputs: ["content", "config"], outputs: ["contentChange"] }, { kind: "component", type: HistoryComponent, selector: "fs-ai-report-global-settings-history", inputs: ["versions"], outputs: ["restore"] }, { kind: "component", type: SemanticDatabaseComponent, selector: "fs-ai-report-global-settings-semantic-database" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3727
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: GlobalSettingsComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "semanticDatabase", first: true, predicate: SemanticDatabaseComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<form\n *fsSkeletonForm=\"loaded()\"\n fsForm\n [submit]=\"save\">\n <fs-dialog>\n <fs-dialog-title>\n Report Settings\n </fs-dialog-title>\n <mat-dialog-content>\n <mat-tab-group [(selected)]=\"selectedTab\">\n <mat-tab\n label=\"AI Knowledge\"\n name=\"knowledge\">\n <fs-ai-report-global-settings-ai-knowledge\n [content]=\"content()\"\n (contentChange)=\"content.set($event)\"\n [config]=\"editorConfig\">\n </fs-ai-report-global-settings-ai-knowledge>\n </mat-tab>\n <mat-tab\n label=\"History\"\n name=\"history\">\n <fs-ai-report-global-settings-history\n [versions]=\"instructions()?.versions ?? []\"\n (restore)=\"restore($event)\">\n </fs-ai-report-global-settings-history>\n </mat-tab>\n <mat-tab\n label=\"Semantic Database\"\n name=\"build\">\n <fs-ai-report-global-settings-semantic-database>\n </fs-ai-report-global-settings-semantic-database>\n </mat-tab>\n <mat-tab\n label=\"General\"\n name=\"general\">\n <fs-ai-report-global-settings-general\n [concurrency]=\"concurrency()\"\n (concurrencyChange)=\"concurrency.set($event)\"\n [min]=\"settings()!.semanticBuildConcurrencyMin\"\n [max]=\"settings()!.semanticBuildConcurrencyMax\">\n </fs-ai-report-global-settings-general>\n </mat-tab>\n </mat-tab-group>\n </mat-dialog-content>\n <mat-dialog-actions>\n <!-- The footer follows the selected tab. AI Knowledge and General are the\n editable tabs, so they get Save/Cancel; History and Semantic Database\n are read/run surfaces, so they get a primary Close. The Semantic\n Database tab also carries its Build/Rebuild/Stop action beside Close. -->\n <fs-form-dialog-actions [save]=\"selectedTab === 'knowledge' || selectedTab === 'general'\">\n @if (selectedTab !== 'knowledge' && selectedTab !== 'general') {\n <button\n type=\"button\"\n mat-button\n color=\"primary\"\n [mat-dialog-close]=\"undefined\">\n Close\n </button>\n }\n @if (selectedTab === 'build' && semanticDatabase(); as db) {\n @if (db.buildActive()) {\n <button\n type=\"button\"\n mat-button\n color=\"warn\"\n (click)=\"db.stopBuild()\">\n Stop Build\n </button>\n } @else {\n @if (db.status(); as buildStatus) {\n <button\n type=\"button\"\n mat-button\n (click)=\"db.build()\">\n {{ buildStatus.detail.generatedAt ? 'Rebuild' : 'Build' }}\n </button>\n }\n }\n }\n </fs-form-dialog-actions>\n </mat-dialog-actions>\n </fs-dialog>\n</form>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FsDialogModule }, { kind: "component", type: i3.FsDialogComponent, selector: "fs-dialog", inputs: ["mobileMode", "mobileButtonPlacement", "mobileWidth", "mode", "buttonLayout"] }, { kind: "component", type: i3.FsDialogTitleComponent, selector: "fs-dialog-title", inputs: ["close", "dockToggle", "back"] }, { kind: "ngmodule", type: FsFormModule }, { kind: "directive", type: i2$1.FsFormDirective, selector: "[fsForm]", inputs: ["wrapperSelector", "messageSelector", "hintSelector", "labelSelector", "autocomplete", "shortcuts", "confirm", "confirmDialog", "confirmDrawer", "confirmBrowser", "dirtySubmitButton", "submit", "successDelay", "errorDelay", "deactivationGuard"], outputs: ["fsForm", "invalid", "valid", "submitted", "reseted", "cleared"], exportAs: ["fsForm"] }, { kind: "component", type: i2$1.FsFormDialogActionsComponent, selector: "fs-form-dialog-actions", inputs: ["save", "create", "close", "done", "closeData", "name"] }, { kind: "directive", type: i2$1.FsButtonDirective, selector: "[mat-raised-button],[mat-button],[mat-flat-button],[mat-stroked-button]", inputs: ["name", "dirtySubmit"] }, { kind: "ngmodule", type: FsSkeletonModule }, { kind: "directive", type: i6$1.FsSkeletonFormDirective, selector: "[fsSkeletonForm]", inputs: ["fsSkeletonForm", "fsSkeletonFormLines"] }, { kind: "ngmodule", type: FsTabsModule }, { kind: "directive", type: i5.FsTabsHeaderTabGroupDirective, selector: "mat-tab-group, matTabGroup, [matTabGroup]", inputs: ["orientation", "selected", "selectedData"], outputs: ["selectedChange", "selectedDataChange"], exportAs: ["fsTabsHeaderTabGroup"] }, { kind: "directive", type: i5.FsTabsTabDirective, selector: "mat-tab,matTab", inputs: ["name", "data"], exportAs: ["fsTabsTab"] }, { kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i5$1.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass"], exportAs: ["matTab"] }, { kind: "component", type: i5$1.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: AiKnowledgeComponent, selector: "fs-ai-report-global-settings-ai-knowledge", inputs: ["content", "config"], outputs: ["contentChange"] }, { kind: "component", type: GeneralComponent, selector: "fs-ai-report-global-settings-general", inputs: ["concurrency", "min", "max"], outputs: ["concurrencyChange"] }, { kind: "component", type: HistoryComponent, selector: "fs-ai-report-global-settings-history", inputs: ["versions"], outputs: ["restore"] }, { kind: "component", type: SemanticDatabaseComponent, selector: "fs-ai-report-global-settings-semantic-database" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3622
3728
  }
3623
3729
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: GlobalSettingsComponent, decorators: [{
3624
3730
  type: Component,
@@ -3634,9 +3740,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
3634
3740
  MatDialogContent,
3635
3741
  MatTabsModule,
3636
3742
  AiKnowledgeComponent,
3743
+ GeneralComponent,
3637
3744
  HistoryComponent,
3638
3745
  SemanticDatabaseComponent,
3639
- ], template: "<form\n *fsSkeletonForm=\"instructions()\"\n fsForm\n [submit]=\"save\">\n <fs-dialog>\n <fs-dialog-title>\n Report Settings\n </fs-dialog-title>\n <mat-dialog-content>\n <mat-tab-group [(selected)]=\"selectedTab\">\n <mat-tab\n label=\"AI Knowledge\"\n name=\"knowledge\">\n <fs-ai-report-global-settings-ai-knowledge\n [content]=\"content()\"\n (contentChange)=\"content.set($event)\"\n [config]=\"editorConfig\">\n </fs-ai-report-global-settings-ai-knowledge>\n </mat-tab>\n <mat-tab\n label=\"History\"\n name=\"history\">\n <fs-ai-report-global-settings-history\n [versions]=\"instructions()?.versions ?? []\"\n (restore)=\"restore($event)\">\n </fs-ai-report-global-settings-history>\n </mat-tab>\n <mat-tab\n label=\"Semantic Database\"\n name=\"build\">\n <fs-ai-report-global-settings-semantic-database>\n </fs-ai-report-global-settings-semantic-database>\n </mat-tab>\n </mat-tab-group>\n </mat-dialog-content>\n <mat-dialog-actions>\n <!-- The footer follows the selected tab. AI Knowledge is the only editable\n tab, so it gets Save/Cancel; History and Semantic Database are\n read/run surfaces, so they get a primary Close. The Semantic Database\n tab also carries its Build/Rebuild/Stop action to the right of Close. -->\n <fs-form-dialog-actions [save]=\"selectedTab === 'knowledge'\">\n @if (selectedTab !== 'knowledge') {\n <button\n type=\"button\"\n mat-button\n color=\"primary\"\n [mat-dialog-close]=\"undefined\">\n Close\n </button>\n }\n @if (selectedTab === 'build' && semanticDatabase(); as db) {\n @if (db.buildActive()) {\n <button\n type=\"button\"\n mat-button\n color=\"warn\"\n (click)=\"db.stopBuild()\">\n Stop Build\n </button>\n } @else {\n @if (db.status(); as buildStatus) {\n <button\n type=\"button\"\n mat-button\n (click)=\"db.build()\">\n {{ buildStatus.detail.generatedAt ? 'Rebuild' : 'Build' }}\n </button>\n }\n }\n }\n </fs-form-dialog-actions>\n </mat-dialog-actions>\n </fs-dialog>\n</form>\n", styles: [":host{display:block}\n"] }]
3746
+ ], template: "<form\n *fsSkeletonForm=\"loaded()\"\n fsForm\n [submit]=\"save\">\n <fs-dialog>\n <fs-dialog-title>\n Report Settings\n </fs-dialog-title>\n <mat-dialog-content>\n <mat-tab-group [(selected)]=\"selectedTab\">\n <mat-tab\n label=\"AI Knowledge\"\n name=\"knowledge\">\n <fs-ai-report-global-settings-ai-knowledge\n [content]=\"content()\"\n (contentChange)=\"content.set($event)\"\n [config]=\"editorConfig\">\n </fs-ai-report-global-settings-ai-knowledge>\n </mat-tab>\n <mat-tab\n label=\"History\"\n name=\"history\">\n <fs-ai-report-global-settings-history\n [versions]=\"instructions()?.versions ?? []\"\n (restore)=\"restore($event)\">\n </fs-ai-report-global-settings-history>\n </mat-tab>\n <mat-tab\n label=\"Semantic Database\"\n name=\"build\">\n <fs-ai-report-global-settings-semantic-database>\n </fs-ai-report-global-settings-semantic-database>\n </mat-tab>\n <mat-tab\n label=\"General\"\n name=\"general\">\n <fs-ai-report-global-settings-general\n [concurrency]=\"concurrency()\"\n (concurrencyChange)=\"concurrency.set($event)\"\n [min]=\"settings()!.semanticBuildConcurrencyMin\"\n [max]=\"settings()!.semanticBuildConcurrencyMax\">\n </fs-ai-report-global-settings-general>\n </mat-tab>\n </mat-tab-group>\n </mat-dialog-content>\n <mat-dialog-actions>\n <!-- The footer follows the selected tab. AI Knowledge and General are the\n editable tabs, so they get Save/Cancel; History and Semantic Database\n are read/run surfaces, so they get a primary Close. The Semantic\n Database tab also carries its Build/Rebuild/Stop action beside Close. -->\n <fs-form-dialog-actions [save]=\"selectedTab === 'knowledge' || selectedTab === 'general'\">\n @if (selectedTab !== 'knowledge' && selectedTab !== 'general') {\n <button\n type=\"button\"\n mat-button\n color=\"primary\"\n [mat-dialog-close]=\"undefined\">\n Close\n </button>\n }\n @if (selectedTab === 'build' && semanticDatabase(); as db) {\n @if (db.buildActive()) {\n <button\n type=\"button\"\n mat-button\n color=\"warn\"\n (click)=\"db.stopBuild()\">\n Stop Build\n </button>\n } @else {\n @if (db.status(); as buildStatus) {\n <button\n type=\"button\"\n mat-button\n (click)=\"db.build()\">\n {{ buildStatus.detail.generatedAt ? 'Rebuild' : 'Build' }}\n </button>\n }\n }\n }\n </fs-form-dialog-actions>\n </mat-dialog-actions>\n </fs-dialog>\n</form>\n", styles: [":host{display:block}\n"] }]
3640
3747
  }], ctorParameters: () => [] });
3641
3748
 
3642
3749
  // The reports listing — an fs-list of reports. Rows are links built from a URL
@@ -4002,7 +4109,7 @@ class FsAiReportComponent {
4002
4109
  ReportPptxService,
4003
4110
  ReportPdfService,
4004
4111
  // Tree-shaken ECharts core + the 'report' house theme, loaded lazily.
4005
- provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-Cel1iHUg.mjs').then((module) => module.default) }),
4112
+ provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-DaBOoR9Z.mjs').then((module) => module.default) }),
4006
4113
  ], viewQueries: [{ propertyName: "_split", first: true, predicate: ["split"], descendants: true, static: true }, { propertyName: "_chatPanel", first: true, predicate: ["chatPanel"], descendants: true, read: ElementRef, static: true }], ngImport: i0, template: "<div class=\"fs-ai-report fs-column\">\n\n <div class=\"fs-ai-report__header fs-row.align-center\">\n <h2 class=\"fs-ai-report__title fs-flex\">{{ report?.name }}</h2>\n\n @if (report) {\n <!-- Report-level filters, inline with the toolbar actions. fs-filter\n reads its config once at init, so it's keyed on the filter\n signature: when the report-level filter set changes the block is\n recreated and re-reads the rebuilt config. -->\n @if (reportHasFilters) {\n @for (key of [reportFilterKey]; track key) {\n <fs-filter\n class=\"fs-ai-report__filters\"\n [config]=\"reportFilterConfig\">\n </fs-filter>\n }\n }\n\n <button\n mat-icon-button\n type=\"button\"\n [matTooltip]=\"editMode ? 'Done' : 'Edit layout'\"\n (click)=\"toggleEditMode()\">\n <mat-icon>{{ editMode ? 'lock' : 'open_with' }}</mat-icon>\n </button>\n\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Export\"\n [fsMenuTriggerFor]=\"exportMenu\">\n <mat-icon>download</mat-icon>\n </button>\n <fs-menu #exportMenu>\n <ng-template\n fs-menu-item\n (click)=\"exportPowerpoint()\">\n Export PowerPoint\n </ng-template>\n <ng-template\n fs-menu-item\n (click)=\"exportPdf()\">\n Export PDF\n </ng-template>\n </fs-menu>\n\n <button\n mat-icon-button\n type=\"button\"\n matTooltip=\"Report settings\"\n (click)=\"reportSettings()\">\n <mat-icon>settings</mat-icon>\n </button>\n }\n </div>\n\n <div\n #split\n class=\"fs-ai-report__split fs-row.align-start\">\n <fs-ai-chat\n #chatPanel\n class=\"chat\"\n [basePath]=\"basePath()\"\n [requestData]=\"{ reportId: reportId() }\"\n (response)=\"onChatResponse($event)\">\n </fs-ai-chat>\n\n <div\n class=\"resizer\"\n (pointerdown)=\"onResizeStart($event)\">\n </div>\n\n <div class=\"viewer fs-flex fs-column\">\n @if (report) {\n <app-report-canvas\n [report]=\"report\"\n [editMode]=\"editMode\"\n (componentSettings)=\"componentSettings($event)\"\n (reportChanged)=\"onCanvasReportChanged()\"\n (editDone)=\"toggleEditMode()\">\n </app-report-canvas>\n }\n </div>\n </div>\n</div>\n", styles: [".fs-ai-report{height:100%;min-height:500px;display:flex;flex-direction:column}.fs-ai-report__header{flex:0 0 auto;margin-bottom:10px}.fs-ai-report__title{margin:0;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fs-ai-report__filters{flex:0 0 auto;display:block;margin:0 5px 0 0}.fs-ai-report__filters ::ng-deep .filter-bar-container{align-items:center!important}.fs-ai-report__filters ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.fs-ai-report__split{flex:1 1 auto;min-height:0}.fs-ai-report .chat{display:block;flex:0 0 25%;width:100%;height:100%;min-height:400px;min-width:0;border:none}.fs-ai-report .viewer{display:flex;flex-direction:column;min-width:0;height:100%;overflow:hidden}.fs-ai-report .resizer{flex:0 0 11px;align-self:stretch;display:flex;justify-content:center;cursor:col-resize;touch-action:none;-webkit-user-select:none;user-select:none}.fs-ai-report .resizer:before{content:\"\";width:3px;border-radius:3px;background:transparent;transition:background-color .12s ease}.fs-ai-report .resizer:hover:before{background:var(--brand-primary-color)}.fs-ai-report__split.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.fs-ai-report__split.resizing .resizer:before{background:var(--brand-primary-color)}.fs-ai-report__split.resizing .chat,.fs-ai-report__split.resizing .viewer{pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: FsFilterModule }, { kind: "component", type: i2.FilterComponent, selector: "fs-filter", inputs: ["config"], outputs: ["closed", "opened", "ready"] }, { kind: "ngmodule", type: FsMenuModule }, { kind: "component", type: i2$3.FsMenuComponent, selector: "fs-menu", inputs: ["class", "buttonClass", "buttonType", "buttonColor"], outputs: ["opened", "closed"] }, { kind: "directive", type: i2$3.FsMenuItemDirective, selector: "fs-menu-group,[fs-menu-item]" }, { kind: "directive", type: i2$3.FsMenuTriggerDirective, selector: "[fsMenuTriggerFor]", inputs: ["fsMenuTriggerFor"] }, { kind: "component", type: FsAiChatComponent, selector: "fs-ai-chat", inputs: ["basePath", "requestData", "introMessage"], outputs: ["response"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2$5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: ReportCanvasComponent, selector: "app-report-canvas", inputs: ["report", "editMode"], outputs: ["componentSettings", "reportChanged", "editDone"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4007
4114
  }
4008
4115
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FsAiReportComponent, decorators: [{
@@ -4015,7 +4122,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4015
4122
  ReportPptxService,
4016
4123
  ReportPdfService,
4017
4124
  // Tree-shaken ECharts core + the 'report' house theme, loaded lazily.
4018
- provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-Cel1iHUg.mjs').then((module) => module.default) }),
4125
+ provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-DaBOoR9Z.mjs').then((module) => module.default) }),
4019
4126
  ], imports: [
4020
4127
  FsFilterModule,
4021
4128
  FsMenuModule,
@@ -4046,4 +4153,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4046
4153
  */
4047
4154
 
4048
4155
  export { FsAiReportsComponent as F, REPORT_CHART_COLORS_CSS as R, ReportComponent as a, FsAiReportComponent as b, ReportData as c, ReportService as d, ReportFilterStateService as e, FREQUENCY_OPTIONS as f };
4049
- //# sourceMappingURL=firestitch-report-firestitch-report-Y1XG_Gms.mjs.map
4156
+ //# sourceMappingURL=firestitch-report-firestitch-report-Cwsww3Jz.mjs.map