@rtsee/ngx 0.0.109 → 0.0.110

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, Input, Component, ViewChild, EventEmitter, Output, forwardRef, Directive, DOCUMENT, HostListener, Inject, ChangeDetectionStrategy, output, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
2
+ import { Injectable, Input, Component, ViewChild, EventEmitter, Output, forwardRef, Directive, HostListener, reflectComponentType, DOCUMENT, Inject, ChangeDetectionStrategy, output, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
3
3
  import * as i2$1 from '@angular/common';
4
4
  import { NgClass, NgIf, NgForOf, DatePipe, NgComponentOutlet, SlicePipe, JsonPipe, DOCUMENT as DOCUMENT$1, NgStyle, CommonModule, NgOptimizedImage } from '@angular/common';
5
5
  import * as i1 from '@angular/forms';
@@ -17,8 +17,8 @@ import * as i2 from '@angular/router';
17
17
  import { RouterLink, RouterLinkActive, NavigationEnd } from '@angular/router';
18
18
  import { Divider } from 'primeng/divider';
19
19
  import * as i1$1 from '@angular/common/http';
20
- import { RTSeeChatMemberRoles, RTSeeIllustratorEvents, RTSeeChatTypes, OzzyTaskStatus } from '@rtsee/common';
21
- import { sanitizeImageUrl, ManageAgentForm, RTSeeForm, RTSeeFormFieldRadioGroup, RTSeeFormField, RTSeeFormFieldImageUploader, RTSeeRater } from '@rtsee/utilities-ui';
20
+ import { RTSeeChatMemberRoles, RTSeeIllustratorEvents, RTSeeChatTypes, OzzyTaskStatus, getStoryTranslatorLanguage, STORY_TRANSLATOR_LANGUAGES } from '@rtsee/common';
21
+ import { sanitizeImageUrl, ManageAgentForm, RTSeeForm, RTSeeFormFieldRadioGroup, RTSeeFormField, RTSeeFormFieldImageUploader, fetchImageBlob, RTSeeRater } from '@rtsee/utilities-ui';
22
22
  import { InputText } from 'primeng/inputtext';
23
23
  import { InputGroup } from 'primeng/inputgroup';
24
24
  import { Textarea } from 'primeng/textarea';
@@ -28,6 +28,7 @@ import { InputNumber } from 'primeng/inputnumber';
28
28
  import * as i1$2 from 'primeng/autocomplete';
29
29
  import { AutoCompleteModule } from 'primeng/autocomplete';
30
30
  import { ImageCropperComponent } from 'ngx-image-cropper';
31
+ import { Tooltip } from 'primeng/tooltip';
31
32
  import { Fieldset } from 'primeng/fieldset';
32
33
  import { DatePicker } from 'primeng/datepicker';
33
34
  import { RTSeeManageChatInfo, RTSeeChatEvents, OzzyClientTaskEvents, OzzyWorkflowClientEvents, RTSeeMentionAutocomplete, RTSeeWorkflowAutocomplete } from '@rtsee/messenger';
@@ -54,7 +55,6 @@ import { Listbox } from 'primeng/listbox';
54
55
  import { RTSeeRoutesList } from '@rtsee/factory';
55
56
  import { Avatar } from 'primeng/avatar';
56
57
  import { Image } from 'primeng/image';
57
- import { Tooltip } from 'primeng/tooltip';
58
58
  import { PanelMenu } from 'primeng/panelmenu';
59
59
  import { Menu } from 'primeng/menu';
60
60
  import { Password } from 'primeng/password';
@@ -675,15 +675,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
675
675
  }] } });
676
676
 
677
677
  class RTSeeImageUploaderComponent {
678
- constructor(elementRef) {
678
+ constructor(elementRef, cdr) {
679
679
  this.elementRef = elementRef;
680
+ this.cdr = cdr;
681
+ this.onUploaderChanged = () => this.cdr.markForCheck();
680
682
  this.submittingFromPainting = false;
681
683
  }
682
684
  ngOnInit() {
683
685
  this.attachEventHandlers();
684
686
  }
687
+ ngOnDestroy() {
688
+ this.imageUploader?.off('changed', this.onUploaderChanged);
689
+ }
685
690
  attachEventHandlers() {
686
691
  this.imageUploader.on('completed', this.onImagesUploaded.bind(this));
692
+ this.imageUploader.on('changed', this.onUploaderChanged);
693
+ }
694
+ get optimizeTooltip() {
695
+ if (this.imageUploader.optimizing) {
696
+ return 'Optimizing…';
697
+ }
698
+ const base = `Optimize (curr size: ${this.imageUploader.currentImageSizeMb}mb)`;
699
+ return this.imageUploader.optimizationError
700
+ ? `${base} - last attempt failed: ${this.imageUploader.optimizationError}`
701
+ : base;
702
+ }
703
+ async onOptimizeClicked(event) {
704
+ // The preview container opens the uploader on click
705
+ event.stopPropagation();
706
+ event.preventDefault();
707
+ const optimizedUrl = await this.imageUploader.optimizeCurrentImage();
708
+ if (optimizedUrl) {
709
+ // The optimized url is already propagated to the form field (`completed`) - persist it
710
+ setTimeout(() => this.submitParentForm(), 50);
711
+ }
687
712
  }
688
713
  onNextButtonClicked() {
689
714
  this.imageUploader.onNextClicked();
@@ -764,8 +789,11 @@ class RTSeeImageUploaderComponent {
764
789
  return;
765
790
  }
766
791
  }
767
- // 2. Programmatic RTSeeForm submit
768
- if (this.imageUploader.form && typeof this.imageUploader.form.submit === 'function') {
792
+ // 2. Custom submit hook, then programmatic RTSeeForm submit
793
+ if (this.imageUploader.onSubmitForm) {
794
+ this.imageUploader.onSubmitForm();
795
+ }
796
+ else if (this.imageUploader.form && typeof this.imageUploader.form.submit === 'function') {
769
797
  this.imageUploader.form.submit();
770
798
  }
771
799
  }
@@ -773,13 +801,13 @@ class RTSeeImageUploaderComponent {
773
801
  console.warn('[RTSeeImageUploaderComponent] submitParentForm error:', e);
774
802
  }
775
803
  }
776
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RTSeeImageUploaderComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
777
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RTSeeImageUploaderComponent, isStandalone: true, selector: "rtsee-image-uploader", inputs: { imageUploader: "imageUploader" }, viewQueries: [{ propertyName: "cropperComponent", first: true, predicate: RTSeeCropperComponent, descendants: true }], ngImport: i0, template: "<div class=\"rtsee-image-uploader\">\n @if (!imageUploader.awaitsUploadOrCrop) {\n <div class=\"rtsee-image-uploader-image\"\n (click)=\"imageUploader.init()\"\n >\n @if (imageUploader.uploadedPhotoPreview) {\n <img [src]=\"imageUploader.uploadedPhotoPreview\"\n class=\"rtsee-image-uploader-preview-image\"\n alt=\"uploaded_photo\"\n >\n }\n @if (!imageUploader.uploadedPhotoPreview) {\n <div class=\"rtsee-preloader-image-placeholder\">\n <span class=\"material-icons-outlined rtsee-upload-icon\">cloud_upload</span>\n <span class=\"rtsee-upload-text\">Tap to upload image</span>\n </div>\n }\n <div class=\"rtsee-image-uploader-illustrator-btn\"\n (click)=\"onPaintingIconClicked($event)\"\n title=\"Apply illustration from NanoBanana Illustrator\"\n >\n <p-button icon=\"pi pi-palette\" severity=\"help\" [rounded]=\"true\"></p-button>\n </div>\n <div class=\"rtsee-image-uploader-image-change-btn\">\n <p-button icon=\"pi pi-pencil\" severity=\"info\"></p-button>\n </div>\n </div>\n }\n\n @if (imageUploader.awaitsUploadOrCrop) {\n <rtsee-uploader [uploader]=\"imageUploader.uploader\"\n [hidden]=\"imageUploader.cropper?.activeImage\"\n ></rtsee-uploader>\n\n @if (imageUploader.cropper?.activeImage) {\n <rtsee-cropper [cropper]=\"imageUploader.cropper\"\n (nextClicked)=\"onNextButtonClicked()\"\n ></rtsee-cropper>\n }\n }\n</div>\n", dependencies: [{ kind: "component", type: RTSeeUploaderComponent, selector: "rtsee-uploader", inputs: ["uploader"] }, { kind: "component", type: RTSeeCropperComponent, selector: "rtsee-cropper", inputs: ["cropper"], outputs: ["nextClicked"] }, { kind: "component", type: Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }] }); }
804
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RTSeeImageUploaderComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
805
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RTSeeImageUploaderComponent, isStandalone: true, selector: "rtsee-image-uploader", inputs: { imageUploader: "imageUploader" }, viewQueries: [{ propertyName: "cropperComponent", first: true, predicate: RTSeeCropperComponent, descendants: true }], ngImport: i0, template: "<div class=\"rtsee-image-uploader\">\n @if (!imageUploader.awaitsUploadOrCrop) {\n <div class=\"rtsee-image-uploader-image\"\n (click)=\"imageUploader.init()\"\n >\n @if (imageUploader.uploadedPhotoPreview) {\n <img [src]=\"imageUploader.uploadedPhotoPreview\"\n class=\"rtsee-image-uploader-preview-image\"\n alt=\"uploaded_photo\"\n >\n }\n @if (!imageUploader.uploadedPhotoPreview) {\n <div class=\"rtsee-preloader-image-placeholder\">\n <span class=\"material-icons-outlined rtsee-upload-icon\">cloud_upload</span>\n <span class=\"rtsee-upload-text\">Tap to upload image</span>\n </div>\n }\n <div class=\"rtsee-image-uploader-illustrator-btn\"\n (click)=\"onPaintingIconClicked($event)\"\n title=\"Apply illustration from NanoBanana Illustrator\"\n >\n <p-button icon=\"pi pi-palette\" severity=\"help\" [rounded]=\"true\"></p-button>\n </div>\n <div class=\"rtsee-image-uploader-image-change-btn\"\n [class.rtsee-shifted]=\"imageUploader.needsOptimization\"\n >\n <p-button icon=\"pi pi-pencil\" severity=\"info\"></p-button>\n </div>\n @if (imageUploader.needsOptimization) {\n <div class=\"rtsee-image-uploader-optimize-btn\"\n id=\"rtsee-image-uploader-optimize-btn\"\n [class.rtsee-optimizing]=\"imageUploader.optimizing\"\n [pTooltip]=\"optimizeTooltip\"\n tooltipPosition=\"top\"\n (click)=\"onOptimizeClicked($event)\"\n >\n <p-button [icon]=\"imageUploader.optimizing ? 'pi pi-spin pi-spinner' : 'pi pi-exclamation-triangle'\"\n severity=\"warn\"\n [rounded]=\"true\"\n [disabled]=\"imageUploader.optimizing\"\n [attr.aria-label]=\"optimizeTooltip\"\n ></p-button>\n </div>\n }\n </div>\n }\n\n @if (imageUploader.awaitsUploadOrCrop) {\n <rtsee-uploader [uploader]=\"imageUploader.uploader\"\n [hidden]=\"imageUploader.cropper?.activeImage\"\n ></rtsee-uploader>\n\n @if (imageUploader.cropper?.activeImage) {\n <rtsee-cropper [cropper]=\"imageUploader.cropper\"\n (nextClicked)=\"onNextButtonClicked()\"\n ></rtsee-cropper>\n }\n }\n</div>\n", dependencies: [{ kind: "component", type: RTSeeUploaderComponent, selector: "rtsee-uploader", inputs: ["uploader"] }, { kind: "component", type: RTSeeCropperComponent, selector: "rtsee-cropper", inputs: ["cropper"], outputs: ["nextClicked"] }, { kind: "component", type: Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "buttonProps", "autofocus", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo"] }] }); }
778
806
  }
779
807
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RTSeeImageUploaderComponent, decorators: [{
780
808
  type: Component,
781
- args: [{ selector: 'rtsee-image-uploader', imports: [RTSeeUploaderComponent, RTSeeCropperComponent, Button], template: "<div class=\"rtsee-image-uploader\">\n @if (!imageUploader.awaitsUploadOrCrop) {\n <div class=\"rtsee-image-uploader-image\"\n (click)=\"imageUploader.init()\"\n >\n @if (imageUploader.uploadedPhotoPreview) {\n <img [src]=\"imageUploader.uploadedPhotoPreview\"\n class=\"rtsee-image-uploader-preview-image\"\n alt=\"uploaded_photo\"\n >\n }\n @if (!imageUploader.uploadedPhotoPreview) {\n <div class=\"rtsee-preloader-image-placeholder\">\n <span class=\"material-icons-outlined rtsee-upload-icon\">cloud_upload</span>\n <span class=\"rtsee-upload-text\">Tap to upload image</span>\n </div>\n }\n <div class=\"rtsee-image-uploader-illustrator-btn\"\n (click)=\"onPaintingIconClicked($event)\"\n title=\"Apply illustration from NanoBanana Illustrator\"\n >\n <p-button icon=\"pi pi-palette\" severity=\"help\" [rounded]=\"true\"></p-button>\n </div>\n <div class=\"rtsee-image-uploader-image-change-btn\">\n <p-button icon=\"pi pi-pencil\" severity=\"info\"></p-button>\n </div>\n </div>\n }\n\n @if (imageUploader.awaitsUploadOrCrop) {\n <rtsee-uploader [uploader]=\"imageUploader.uploader\"\n [hidden]=\"imageUploader.cropper?.activeImage\"\n ></rtsee-uploader>\n\n @if (imageUploader.cropper?.activeImage) {\n <rtsee-cropper [cropper]=\"imageUploader.cropper\"\n (nextClicked)=\"onNextButtonClicked()\"\n ></rtsee-cropper>\n }\n }\n</div>\n" }]
782
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { cropperComponent: [{
809
+ args: [{ selector: 'rtsee-image-uploader', imports: [RTSeeUploaderComponent, RTSeeCropperComponent, Button, Tooltip], template: "<div class=\"rtsee-image-uploader\">\n @if (!imageUploader.awaitsUploadOrCrop) {\n <div class=\"rtsee-image-uploader-image\"\n (click)=\"imageUploader.init()\"\n >\n @if (imageUploader.uploadedPhotoPreview) {\n <img [src]=\"imageUploader.uploadedPhotoPreview\"\n class=\"rtsee-image-uploader-preview-image\"\n alt=\"uploaded_photo\"\n >\n }\n @if (!imageUploader.uploadedPhotoPreview) {\n <div class=\"rtsee-preloader-image-placeholder\">\n <span class=\"material-icons-outlined rtsee-upload-icon\">cloud_upload</span>\n <span class=\"rtsee-upload-text\">Tap to upload image</span>\n </div>\n }\n <div class=\"rtsee-image-uploader-illustrator-btn\"\n (click)=\"onPaintingIconClicked($event)\"\n title=\"Apply illustration from NanoBanana Illustrator\"\n >\n <p-button icon=\"pi pi-palette\" severity=\"help\" [rounded]=\"true\"></p-button>\n </div>\n <div class=\"rtsee-image-uploader-image-change-btn\"\n [class.rtsee-shifted]=\"imageUploader.needsOptimization\"\n >\n <p-button icon=\"pi pi-pencil\" severity=\"info\"></p-button>\n </div>\n @if (imageUploader.needsOptimization) {\n <div class=\"rtsee-image-uploader-optimize-btn\"\n id=\"rtsee-image-uploader-optimize-btn\"\n [class.rtsee-optimizing]=\"imageUploader.optimizing\"\n [pTooltip]=\"optimizeTooltip\"\n tooltipPosition=\"top\"\n (click)=\"onOptimizeClicked($event)\"\n >\n <p-button [icon]=\"imageUploader.optimizing ? 'pi pi-spin pi-spinner' : 'pi pi-exclamation-triangle'\"\n severity=\"warn\"\n [rounded]=\"true\"\n [disabled]=\"imageUploader.optimizing\"\n [attr.aria-label]=\"optimizeTooltip\"\n ></p-button>\n </div>\n }\n </div>\n }\n\n @if (imageUploader.awaitsUploadOrCrop) {\n <rtsee-uploader [uploader]=\"imageUploader.uploader\"\n [hidden]=\"imageUploader.cropper?.activeImage\"\n ></rtsee-uploader>\n\n @if (imageUploader.cropper?.activeImage) {\n <rtsee-cropper [cropper]=\"imageUploader.cropper\"\n (nextClicked)=\"onNextButtonClicked()\"\n ></rtsee-cropper>\n }\n }\n</div>\n" }]
810
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { cropperComponent: [{
783
811
  type: ViewChild,
784
812
  args: [RTSeeCropperComponent]
785
813
  }], imageUploader: [{
@@ -1861,10 +1889,54 @@ class MediaWidgetComponent {
1861
1889
  this.visibleImages = [];
1862
1890
  this.hiddenCount = 0;
1863
1891
  this.showAll = false;
1892
+ this.downloadingUrl = null;
1864
1893
  this.MAX_GRID = 4;
1865
1894
  }
1866
1895
  ngOnInit() {
1867
1896
  this.updateVisibleImages();
1897
+ this.notifyRequestedGeneration();
1898
+ }
1899
+ ngOnChanges(changes) {
1900
+ // e.g. downsized versions got attached to the message images
1901
+ if (changes['message'] && !changes['message'].firstChange) {
1902
+ this.updateVisibleImages();
1903
+ }
1904
+ }
1905
+ /** Downsized version for rendering, the original otherwise */
1906
+ previewUrl(img) {
1907
+ return img.smallVersionUrl || img.url;
1908
+ }
1909
+ async downloadOriginal(event, img) {
1910
+ event.stopPropagation();
1911
+ event.preventDefault();
1912
+ if (typeof window === 'undefined' || !img?.url || this.downloadingUrl) {
1913
+ return;
1914
+ }
1915
+ this.downloadingUrl = img.url;
1916
+ try {
1917
+ const blob = await fetchImageBlob(img.url);
1918
+ const objectUrl = URL.createObjectURL(blob);
1919
+ const link = document.createElement('a');
1920
+ link.href = objectUrl;
1921
+ link.download = this.getDownloadFileName(img, blob);
1922
+ document.body.appendChild(link);
1923
+ link.click();
1924
+ link.remove();
1925
+ setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
1926
+ }
1927
+ catch (e) {
1928
+ // CORS / network failure - let the browser open the original instead
1929
+ console.warn('[MediaWidget] Blob download failed, opening original image:', e);
1930
+ window.open(img.url, '_blank', 'noopener');
1931
+ }
1932
+ finally {
1933
+ this.downloadingUrl = null;
1934
+ }
1935
+ }
1936
+ getDownloadFileName(img, blob) {
1937
+ const extension = blob.type?.split('/')[1]?.split('+')[0] || 'png';
1938
+ const base = (img.name || img.id || 'image').replace(/[^\w.-]+/g, '-');
1939
+ return base.includes('.') ? base : `${base}.${extension}`;
1868
1940
  }
1869
1941
  get hasVideo() {
1870
1942
  return !!this.message.video;
@@ -1925,6 +1997,8 @@ class MediaWidgetComponent {
1925
1997
  this.visibleImages = all.slice(0, this.MAX_GRID);
1926
1998
  this.hiddenCount = all.length - this.MAX_GRID;
1927
1999
  }
2000
+ }
2001
+ notifyRequestedGeneration() {
1928
2002
  const reqId = this.message?.data?.requestId;
1929
2003
  if (reqId && this.visibleImages.length > 0 && typeof window !== 'undefined') {
1930
2004
  window.dispatchEvent(new CustomEvent(RTSeeIllustratorEvents.REQUESTED_GENERATION_SUCCEED, {
@@ -1938,11 +2012,11 @@ class MediaWidgetComponent {
1938
2012
  }
1939
2013
  }
1940
2014
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MediaWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1941
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MediaWidgetComponent, isStandalone: true, selector: "rtsee-media-widget", inputs: { message: "message", messenger: "messenger", chat: "chat", showCost: "showCost" }, ngImport: i0, template: "<div class=\"rts-media-widget\">\n <!-- Video player -->\n @if (message.video) {\n <div class=\"rts-media-video\">\n <video\n controls\n playsinline\n preload=\"metadata\"\n [src]=\"message.video.url\"\n class=\"rts-video-player\"\n >\n Your browser does not support video playback.\n </video>\n </div>\n }\n\n <!-- Image grid -->\n @if (hasImages) {\n <div class=\"rts-media-images\" [ngClass]=\"gridClass\">\n @for (img of visibleImages; track img.url) {\n <div class=\"rts-media-image-wrapper\">\n <img [src]=\"img.url\" [alt]=\"img.name\" class=\"rts-media-image\" loading=\"lazy\" />\n <div class=\"rts-media-image-actions\">\n <button\n type=\"button\"\n class=\"rts-media-btn-paint\"\n title=\"Send image to Illustrator (Review & Iterate)\"\n (click)=\"sendToIllustrator($event, img)\"\n >\n <i class=\"pi pi-palette\"></i>\n <span>+ paint</span>\n </button>\n <button\n type=\"button\"\n class=\"rts-media-btn-context\"\n title=\"Add image to Illustrator context references\"\n (click)=\"addToContext($event, img)\"\n >\n <i class=\"pi pi-plus\"></i>\n <span>+ context</span>\n </button>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Show more / less toggle -->\n @if (hiddenCount > 0 || showAll) {\n <button class=\"rts-media-toggle\" (click)=\"toggleShowAll()\">\n {{ showAll ? 'Show less' : 'Show ' + hiddenCount + ' more' }}\n </button>\n }\n\n <!-- Cost display (optional, disabled when nested inside agentic widget) -->\n @if (showCost && message.cost) {\n <div class=\"rts-media-cost\">\n <rtsee-cost-display [cost]=\"message.cost\"></rtsee-cost-display>\n </div>\n }\n</div>\n", styles: [".rts-media-widget{max-width:100%}.rts-media-video{border-radius:8px;overflow:hidden;margin-bottom:6px}.rts-video-player{width:100%;max-height:360px;border-radius:8px;background:#000;display:block}.rts-media-images{display:grid;gap:4px;border-radius:8px;overflow:hidden;margin-bottom:4px}.rts-media-grid-1{grid-template-columns:1fr}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e)}.rts-media-image-actions{position:absolute;top:8px;right:8px;z-index:5;display:inline-flex;align-items:center;gap:6px}.rts-media-btn-paint{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#e9d5ff;border:1px solid rgba(168,85,247,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-paint i{font-size:11px;color:#c084fc}.rts-media-btn-paint span{line-height:1}.rts-media-btn-paint:hover{background:#9333eae6;color:#fff;border-color:#a855f7;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #a855f773}.rts-media-btn-paint:hover i{color:#fff}.rts-media-btn-paint:active{transform:translateY(0) scale(.98)}.rts-media-btn-context{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#bae6fd;border:1px solid rgba(56,189,248,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-context i{font-size:11px;color:#38bdf8}.rts-media-btn-context span{line-height:1}.rts-media-btn-context:hover{background:#0ea5e9e6;color:#fff;border-color:#38bdf8;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #38bdf873}.rts-media-btn-context:hover i{color:#fff}.rts-media-btn-context:active{transform:translateY(0) scale(.98)}.rts-media-image{width:100%;height:auto;max-height:280px;object-fit:cover;display:block;transition:transform .2s ease;cursor:pointer}.rts-media-image:hover{transform:scale(1.02)}.rts-media-grid-1 .rts-media-image{max-height:400px;object-fit:contain}.rts-media-toggle{display:block;width:100%;padding:6px 0;border:none;background:none;color:var(--rts-agent-accent, #8b5cf6);font-size:12px;font-weight:500;cursor:pointer;text-align:center;transition:opacity .15s ease}.rts-media-toggle:hover{opacity:.8}.rts-media-cost{display:flex;justify-content:flex-end;padding-top:4px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: CostDisplayComponent, selector: "rtsee-cost-display", inputs: ["cost"] }] }); }
2015
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MediaWidgetComponent, isStandalone: true, selector: "rtsee-media-widget", inputs: { message: "message", messenger: "messenger", chat: "chat", showCost: "showCost" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"rts-media-widget\">\n <!-- Video player -->\n @if (message.video) {\n <div class=\"rts-media-video\">\n <video\n controls\n playsinline\n preload=\"metadata\"\n [src]=\"message.video.url\"\n class=\"rts-video-player\"\n >\n Your browser does not support video playback.\n </video>\n </div>\n }\n\n <!-- Image grid -->\n @if (hasImages) {\n <div class=\"rts-media-images\" [ngClass]=\"gridClass\">\n @for (img of visibleImages; track img.id || img.url) {\n <div class=\"rts-media-image-wrapper\">\n <img [src]=\"previewUrl(img)\" [alt]=\"img.name\" class=\"rts-media-image\" loading=\"lazy\" />\n <button\n type=\"button\"\n class=\"rts-media-btn-download\"\n [class.rts-downloading]=\"downloadingUrl === img.url\"\n title=\"Download original image\"\n aria-label=\"Download original image\"\n (click)=\"downloadOriginal($event, img)\"\n >\n <i class=\"pi\" [ngClass]=\"downloadingUrl === img.url ? 'pi-spin pi-spinner' : 'pi-download'\"></i>\n </button>\n <div class=\"rts-media-image-actions\">\n <button\n type=\"button\"\n class=\"rts-media-btn-paint\"\n title=\"Send image to Illustrator (Review & Iterate)\"\n (click)=\"sendToIllustrator($event, img)\"\n >\n <i class=\"pi pi-palette\"></i>\n <span>+ paint</span>\n </button>\n <button\n type=\"button\"\n class=\"rts-media-btn-context\"\n title=\"Add image to Illustrator context references\"\n (click)=\"addToContext($event, img)\"\n >\n <i class=\"pi pi-plus\"></i>\n <span>+ context</span>\n </button>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Show more / less toggle -->\n @if (hiddenCount > 0 || showAll) {\n <button class=\"rts-media-toggle\" (click)=\"toggleShowAll()\">\n {{ showAll ? 'Show less' : 'Show ' + hiddenCount + ' more' }}\n </button>\n }\n\n <!-- Cost display (optional, disabled when nested inside agentic widget) -->\n @if (showCost && message.cost) {\n <div class=\"rts-media-cost\">\n <rtsee-cost-display [cost]=\"message.cost\"></rtsee-cost-display>\n </div>\n }\n</div>\n", styles: [".rts-media-widget{max-width:100%}.rts-media-video{border-radius:8px;overflow:hidden;margin-bottom:6px}.rts-video-player{width:100%;max-height:360px;border-radius:8px;background:#000;display:block}.rts-media-images{display:grid;gap:4px;border-radius:8px;overflow:hidden;margin-bottom:4px}.rts-media-grid-1{grid-template-columns:1fr}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e)}.rts-media-image-actions{position:absolute;top:8px;right:8px;z-index:5;display:inline-flex;align-items:center;gap:6px}.rts-media-btn-paint{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#e9d5ff;border:1px solid rgba(168,85,247,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-paint i{font-size:11px;color:#c084fc}.rts-media-btn-paint span{line-height:1}.rts-media-btn-paint:hover{background:#9333eae6;color:#fff;border-color:#a855f7;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #a855f773}.rts-media-btn-paint:hover i{color:#fff}.rts-media-btn-paint:active{transform:translateY(0) scale(.98)}.rts-media-btn-context{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#bae6fd;border:1px solid rgba(56,189,248,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-context i{font-size:11px;color:#38bdf8}.rts-media-btn-context span{line-height:1}.rts-media-btn-context:hover{background:#0ea5e9e6;color:#fff;border-color:#38bdf8;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #38bdf873}.rts-media-btn-context:hover i{color:#fff}.rts-media-btn-context:active{transform:translateY(0) scale(.98)}.rts-media-btn-download{position:absolute;left:8px;bottom:8px;z-index:5;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;padding:0;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#d1fae5;border:1px solid rgba(52,211,153,.45);border-radius:50%;cursor:pointer;opacity:.85;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-download i{font-size:12px;color:#34d399}.rts-media-btn-download:hover{opacity:1;background:#10b981e6;border-color:#34d399;transform:translateY(-1px) scale(1.08);box-shadow:0 4px 12px #34d39973}.rts-media-btn-download:hover i{color:#fff}.rts-media-btn-download:active{transform:translateY(0) scale(.96)}.rts-media-btn-download.rts-downloading{cursor:progress;opacity:1}.rts-media-image{width:100%;height:auto;max-height:280px;object-fit:cover;display:block;transition:transform .2s ease;cursor:pointer}.rts-media-image:hover{transform:scale(1.02)}.rts-media-grid-1 .rts-media-image{max-height:400px;object-fit:contain}.rts-media-toggle{display:block;width:100%;padding:6px 0;border:none;background:none;color:var(--rts-agent-accent, #8b5cf6);font-size:12px;font-weight:500;cursor:pointer;text-align:center;transition:opacity .15s ease}.rts-media-toggle:hover{opacity:.8}.rts-media-cost{display:flex;justify-content:flex-end;padding-top:4px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: CostDisplayComponent, selector: "rtsee-cost-display", inputs: ["cost"] }] }); }
1942
2016
  }
1943
2017
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MediaWidgetComponent, decorators: [{
1944
2018
  type: Component,
1945
- args: [{ selector: 'rtsee-media-widget', standalone: true, imports: [NgClass, CostDisplayComponent], template: "<div class=\"rts-media-widget\">\n <!-- Video player -->\n @if (message.video) {\n <div class=\"rts-media-video\">\n <video\n controls\n playsinline\n preload=\"metadata\"\n [src]=\"message.video.url\"\n class=\"rts-video-player\"\n >\n Your browser does not support video playback.\n </video>\n </div>\n }\n\n <!-- Image grid -->\n @if (hasImages) {\n <div class=\"rts-media-images\" [ngClass]=\"gridClass\">\n @for (img of visibleImages; track img.url) {\n <div class=\"rts-media-image-wrapper\">\n <img [src]=\"img.url\" [alt]=\"img.name\" class=\"rts-media-image\" loading=\"lazy\" />\n <div class=\"rts-media-image-actions\">\n <button\n type=\"button\"\n class=\"rts-media-btn-paint\"\n title=\"Send image to Illustrator (Review & Iterate)\"\n (click)=\"sendToIllustrator($event, img)\"\n >\n <i class=\"pi pi-palette\"></i>\n <span>+ paint</span>\n </button>\n <button\n type=\"button\"\n class=\"rts-media-btn-context\"\n title=\"Add image to Illustrator context references\"\n (click)=\"addToContext($event, img)\"\n >\n <i class=\"pi pi-plus\"></i>\n <span>+ context</span>\n </button>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Show more / less toggle -->\n @if (hiddenCount > 0 || showAll) {\n <button class=\"rts-media-toggle\" (click)=\"toggleShowAll()\">\n {{ showAll ? 'Show less' : 'Show ' + hiddenCount + ' more' }}\n </button>\n }\n\n <!-- Cost display (optional, disabled when nested inside agentic widget) -->\n @if (showCost && message.cost) {\n <div class=\"rts-media-cost\">\n <rtsee-cost-display [cost]=\"message.cost\"></rtsee-cost-display>\n </div>\n }\n</div>\n", styles: [".rts-media-widget{max-width:100%}.rts-media-video{border-radius:8px;overflow:hidden;margin-bottom:6px}.rts-video-player{width:100%;max-height:360px;border-radius:8px;background:#000;display:block}.rts-media-images{display:grid;gap:4px;border-radius:8px;overflow:hidden;margin-bottom:4px}.rts-media-grid-1{grid-template-columns:1fr}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e)}.rts-media-image-actions{position:absolute;top:8px;right:8px;z-index:5;display:inline-flex;align-items:center;gap:6px}.rts-media-btn-paint{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#e9d5ff;border:1px solid rgba(168,85,247,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-paint i{font-size:11px;color:#c084fc}.rts-media-btn-paint span{line-height:1}.rts-media-btn-paint:hover{background:#9333eae6;color:#fff;border-color:#a855f7;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #a855f773}.rts-media-btn-paint:hover i{color:#fff}.rts-media-btn-paint:active{transform:translateY(0) scale(.98)}.rts-media-btn-context{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#bae6fd;border:1px solid rgba(56,189,248,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-context i{font-size:11px;color:#38bdf8}.rts-media-btn-context span{line-height:1}.rts-media-btn-context:hover{background:#0ea5e9e6;color:#fff;border-color:#38bdf8;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #38bdf873}.rts-media-btn-context:hover i{color:#fff}.rts-media-btn-context:active{transform:translateY(0) scale(.98)}.rts-media-image{width:100%;height:auto;max-height:280px;object-fit:cover;display:block;transition:transform .2s ease;cursor:pointer}.rts-media-image:hover{transform:scale(1.02)}.rts-media-grid-1 .rts-media-image{max-height:400px;object-fit:contain}.rts-media-toggle{display:block;width:100%;padding:6px 0;border:none;background:none;color:var(--rts-agent-accent, #8b5cf6);font-size:12px;font-weight:500;cursor:pointer;text-align:center;transition:opacity .15s ease}.rts-media-toggle:hover{opacity:.8}.rts-media-cost{display:flex;justify-content:flex-end;padding-top:4px}\n"] }]
2019
+ args: [{ selector: 'rtsee-media-widget', standalone: true, imports: [NgClass, CostDisplayComponent], template: "<div class=\"rts-media-widget\">\n <!-- Video player -->\n @if (message.video) {\n <div class=\"rts-media-video\">\n <video\n controls\n playsinline\n preload=\"metadata\"\n [src]=\"message.video.url\"\n class=\"rts-video-player\"\n >\n Your browser does not support video playback.\n </video>\n </div>\n }\n\n <!-- Image grid -->\n @if (hasImages) {\n <div class=\"rts-media-images\" [ngClass]=\"gridClass\">\n @for (img of visibleImages; track img.id || img.url) {\n <div class=\"rts-media-image-wrapper\">\n <img [src]=\"previewUrl(img)\" [alt]=\"img.name\" class=\"rts-media-image\" loading=\"lazy\" />\n <button\n type=\"button\"\n class=\"rts-media-btn-download\"\n [class.rts-downloading]=\"downloadingUrl === img.url\"\n title=\"Download original image\"\n aria-label=\"Download original image\"\n (click)=\"downloadOriginal($event, img)\"\n >\n <i class=\"pi\" [ngClass]=\"downloadingUrl === img.url ? 'pi-spin pi-spinner' : 'pi-download'\"></i>\n </button>\n <div class=\"rts-media-image-actions\">\n <button\n type=\"button\"\n class=\"rts-media-btn-paint\"\n title=\"Send image to Illustrator (Review & Iterate)\"\n (click)=\"sendToIllustrator($event, img)\"\n >\n <i class=\"pi pi-palette\"></i>\n <span>+ paint</span>\n </button>\n <button\n type=\"button\"\n class=\"rts-media-btn-context\"\n title=\"Add image to Illustrator context references\"\n (click)=\"addToContext($event, img)\"\n >\n <i class=\"pi pi-plus\"></i>\n <span>+ context</span>\n </button>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Show more / less toggle -->\n @if (hiddenCount > 0 || showAll) {\n <button class=\"rts-media-toggle\" (click)=\"toggleShowAll()\">\n {{ showAll ? 'Show less' : 'Show ' + hiddenCount + ' more' }}\n </button>\n }\n\n <!-- Cost display (optional, disabled when nested inside agentic widget) -->\n @if (showCost && message.cost) {\n <div class=\"rts-media-cost\">\n <rtsee-cost-display [cost]=\"message.cost\"></rtsee-cost-display>\n </div>\n }\n</div>\n", styles: [".rts-media-widget{max-width:100%}.rts-media-video{border-radius:8px;overflow:hidden;margin-bottom:6px}.rts-video-player{width:100%;max-height:360px;border-radius:8px;background:#000;display:block}.rts-media-images{display:grid;gap:4px;border-radius:8px;overflow:hidden;margin-bottom:4px}.rts-media-grid-1{grid-template-columns:1fr}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e)}.rts-media-image-actions{position:absolute;top:8px;right:8px;z-index:5;display:inline-flex;align-items:center;gap:6px}.rts-media-btn-paint{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#e9d5ff;border:1px solid rgba(168,85,247,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-paint i{font-size:11px;color:#c084fc}.rts-media-btn-paint span{line-height:1}.rts-media-btn-paint:hover{background:#9333eae6;color:#fff;border-color:#a855f7;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #a855f773}.rts-media-btn-paint:hover i{color:#fff}.rts-media-btn-paint:active{transform:translateY(0) scale(.98)}.rts-media-btn-context{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#bae6fd;border:1px solid rgba(56,189,248,.45);border-radius:6px;font-size:11px;font-weight:600;cursor:pointer;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-context i{font-size:11px;color:#38bdf8}.rts-media-btn-context span{line-height:1}.rts-media-btn-context:hover{background:#0ea5e9e6;color:#fff;border-color:#38bdf8;transform:translateY(-1px) scale(1.04);box-shadow:0 4px 12px #38bdf873}.rts-media-btn-context:hover i{color:#fff}.rts-media-btn-context:active{transform:translateY(0) scale(.98)}.rts-media-btn-download{position:absolute;left:8px;bottom:8px;z-index:5;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;padding:0;background:#12121cd9;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);color:#d1fae5;border:1px solid rgba(52,211,153,.45);border-radius:50%;cursor:pointer;opacity:.85;transition:all .2s cubic-bezier(.34,1.56,.64,1);box-shadow:0 2px 8px #00000080}.rts-media-btn-download i{font-size:12px;color:#34d399}.rts-media-btn-download:hover{opacity:1;background:#10b981e6;border-color:#34d399;transform:translateY(-1px) scale(1.08);box-shadow:0 4px 12px #34d39973}.rts-media-btn-download:hover i{color:#fff}.rts-media-btn-download:active{transform:translateY(0) scale(.96)}.rts-media-btn-download.rts-downloading{cursor:progress;opacity:1}.rts-media-image{width:100%;height:auto;max-height:280px;object-fit:cover;display:block;transition:transform .2s ease;cursor:pointer}.rts-media-image:hover{transform:scale(1.02)}.rts-media-grid-1 .rts-media-image{max-height:400px;object-fit:contain}.rts-media-toggle{display:block;width:100%;padding:6px 0;border:none;background:none;color:var(--rts-agent-accent, #8b5cf6);font-size:12px;font-weight:500;cursor:pointer;text-align:center;transition:opacity .15s ease}.rts-media-toggle:hover{opacity:.8}.rts-media-cost{display:flex;justify-content:flex-end;padding-top:4px}\n"] }]
1946
2020
  }], propDecorators: { message: [{
1947
2021
  type: Input
1948
2022
  }], messenger: [{
@@ -2308,12 +2382,16 @@ class StorytellerStoryLinkStepComponent {
2308
2382
  get storyUrl() {
2309
2383
  return this.story.storyId ? `/story/${this.story.storyId}` : '';
2310
2384
  }
2385
+ get kicker() {
2386
+ const language = this.story.language ? getStoryTranslatorLanguage(this.story.language) : undefined;
2387
+ return language ? `Translated to ${language.nativeName}` : 'Your story is ready';
2388
+ }
2311
2389
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StorytellerStoryLinkStepComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2312
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StorytellerStoryLinkStepComponent, isStandalone: true, selector: "rtsee-storyteller-story-link-step", inputs: { step: "step", workflow: "workflow", chat: "chat", messenger: "messenger" }, ngImport: i0, template: "@if (storyUrl) {\n <a class=\"rts-story-link-card\" [href]=\"storyUrl\" target=\"_blank\" rel=\"noopener\">\n <div class=\"rts-story-link-icon\">\uD83D\uDCD6</div>\n <div class=\"rts-story-link-body\">\n <span class=\"rts-story-link-kicker\">Your story is ready</span>\n <span class=\"rts-story-link-title\">{{ story.name || 'Open story' }}</span>\n @if (story.description) {\n <span class=\"rts-story-link-description\">{{ story.description }}</span>\n }\n @if (story.slidesCount) {\n <span class=\"rts-story-link-meta\">{{ story.slidesCount }} slides</span>\n }\n </div>\n <span class=\"rts-story-link-arrow pi pi-arrow-right\"></span>\n </a>\n} @else {\n <div class=\"rts-storyteller-hint\">The story link will appear here once the story is created.</div>\n}\n", styles: [":host{display:block;--rts-st-accent: #8b5cf6;--rts-st-accent-2: #6366f1}.rts-storyteller-form{display:flex;flex-direction:column;gap:12px}.rts-storyteller-field{display:flex;flex-direction:column;gap:5px}.rts-storyteller-label{display:flex;justify-content:space-between;align-items:baseline;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-counter{font-weight:500;letter-spacing:0;text-transform:none}.rts-storyteller-input{width:100%;box-sizing:border-box;padding:9px 12px;border-radius:9px;border:1px solid var(--rts-border-dim, rgba(120, 120, 160, .25));background:var(--rts-bg-primary, rgba(127, 127, 160, .06));color:var(--rts-text-primary);font:inherit;font-size:13px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.rts-storyteller-input:focus{border-color:var(--rts-st-accent);box-shadow:0 0 0 3px #8b5cf62e}.rts-storyteller-input:disabled{opacity:.75;cursor:default}.rts-storyteller-input.rts-storyteller-invalid{border-color:#f43f5e;box-shadow:0 0 0 3px #f43f5e1f}.rts-storyteller-textarea{min-height:80px;resize:vertical;line-height:1.5}.rts-storyteller-textarea-lg{min-height:160px;max-height:480px}.rts-storyteller-select-wrap{position:relative}.rts-storyteller-select{appearance:none;padding-right:32px;cursor:pointer}.rts-storyteller-select-caret{position:absolute;right:12px;top:50%;transform:translateY(-50%);font-size:10px;pointer-events:none;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-hint{font-size:12px;font-style:italic;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-error{padding:6px 10px;border-radius:6px;font-size:12px;color:#f43f5e;background:#f43f5e14;border:1px solid rgba(244,63,94,.25)}.rts-storyteller-actions{display:flex;justify-content:flex-end}.rts-storyteller-submit{display:inline-flex;align-items:center;gap:6px;padding:8px 18px;border:none;border-radius:9px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent) 60%,#a78bfa);background-size:160% 160%;box-shadow:0 4px 14px -4px #6366f199;transition:transform .15s ease,box-shadow .15s ease,background-position .4s ease}.rts-storyteller-submit:hover:not(:disabled){transform:translateY(-1px);background-position:100% 0;box-shadow:0 6px 18px -4px #6366f1bf}.rts-storyteller-submit:disabled{opacity:.6;cursor:default;box-shadow:none}.rts-storyteller-spinner{width:11px;height:11px;border-radius:50%;border:2px solid rgba(255,255,255,.35);border-top-color:#fff;animation:rts-st-spin .8s linear infinite}.rts-story-link-card{display:flex;align-items:center;gap:12px;padding:14px;border-radius:12px;text-decoration:none;color:inherit;background:linear-gradient(135deg,#6366f11a,#8b5cf60f);border:1px solid rgba(139,92,246,.3);transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease;animation:rts-st-pop .4s cubic-bezier(.2,.9,.3,1.3)}.rts-story-link-card:hover{transform:translateY(-2px);border-color:var(--rts-st-accent);box-shadow:0 10px 28px -12px #8b5cf699}.rts-story-link-card:hover .rts-story-link-arrow{transform:translate(3px)}.rts-story-link-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:42px;height:42px;border-radius:12px;font-size:20px;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent))}.rts-story-link-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.rts-story-link-kicker{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-st-accent)}.rts-story-link-title{font-size:15px;font-weight:600;color:var(--rts-text-primary)}.rts-story-link-description{font-size:12px;color:var(--rts-text-secondary, #8a8fa3);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.rts-story-link-meta{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.rts-story-link-arrow{color:var(--rts-st-accent);transition:transform .2s ease}@keyframes rts-st-spin{to{transform:rotate(360deg)}}@keyframes rts-st-pop{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:none}}\n"] }); }
2390
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StorytellerStoryLinkStepComponent, isStandalone: true, selector: "rtsee-storyteller-story-link-step", inputs: { step: "step", workflow: "workflow", chat: "chat", messenger: "messenger" }, ngImport: i0, template: "@if (storyUrl) {\n <a class=\"rts-story-link-card\" [href]=\"storyUrl\" target=\"_blank\" rel=\"noopener\">\n <div class=\"rts-story-link-icon\">\uD83D\uDCD6</div>\n <div class=\"rts-story-link-body\">\n <span class=\"rts-story-link-kicker\">{{ kicker }}</span>\n <span class=\"rts-story-link-title\">{{ story.name || 'Open story' }}</span>\n @if (story.description) {\n <span class=\"rts-story-link-description\">{{ story.description }}</span>\n }\n @if (story.slidesCount) {\n <span class=\"rts-story-link-meta\">{{ story.slidesCount }} slides</span>\n }\n </div>\n <span class=\"rts-story-link-arrow pi pi-arrow-right\"></span>\n </a>\n} @else {\n <div class=\"rts-storyteller-hint\">The story link will appear here once the story is created.</div>\n}\n", styles: [":host{display:block;--rts-st-accent: #8b5cf6;--rts-st-accent-2: #6366f1}.rts-storyteller-form{display:flex;flex-direction:column;gap:12px}.rts-storyteller-field{display:flex;flex-direction:column;gap:5px}.rts-storyteller-label{display:flex;justify-content:space-between;align-items:baseline;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-counter{font-weight:500;letter-spacing:0;text-transform:none}.rts-storyteller-input{width:100%;box-sizing:border-box;padding:9px 12px;border-radius:9px;border:1px solid var(--rts-border-dim, rgba(120, 120, 160, .25));background:var(--rts-bg-primary, rgba(127, 127, 160, .06));color:var(--rts-text-primary);font:inherit;font-size:13px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.rts-storyteller-input:focus{border-color:var(--rts-st-accent);box-shadow:0 0 0 3px #8b5cf62e}.rts-storyteller-input:disabled{opacity:.75;cursor:default}.rts-storyteller-input.rts-storyteller-invalid{border-color:#f43f5e;box-shadow:0 0 0 3px #f43f5e1f}.rts-storyteller-textarea{min-height:80px;resize:vertical;line-height:1.5}.rts-storyteller-textarea-lg{min-height:160px;max-height:480px}.rts-storyteller-select-wrap{position:relative}.rts-storyteller-select{appearance:none;padding-right:32px;cursor:pointer}.rts-storyteller-select-caret{position:absolute;right:12px;top:50%;transform:translateY(-50%);font-size:10px;pointer-events:none;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-hint{font-size:12px;font-style:italic;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-error{padding:6px 10px;border-radius:6px;font-size:12px;color:#f43f5e;background:#f43f5e14;border:1px solid rgba(244,63,94,.25)}.rts-storyteller-actions{display:flex;justify-content:flex-end}.rts-storyteller-submit{display:inline-flex;align-items:center;gap:6px;padding:8px 18px;border:none;border-radius:9px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent) 60%,#a78bfa);background-size:160% 160%;box-shadow:0 4px 14px -4px #6366f199;transition:transform .15s ease,box-shadow .15s ease,background-position .4s ease}.rts-storyteller-submit:hover:not(:disabled){transform:translateY(-1px);background-position:100% 0;box-shadow:0 6px 18px -4px #6366f1bf}.rts-storyteller-submit:disabled{opacity:.6;cursor:default;box-shadow:none}.rts-storyteller-spinner{width:11px;height:11px;border-radius:50%;border:2px solid rgba(255,255,255,.35);border-top-color:#fff;animation:rts-st-spin .8s linear infinite}.rts-story-link-card{display:flex;align-items:center;gap:12px;padding:14px;border-radius:12px;text-decoration:none;color:inherit;background:linear-gradient(135deg,#6366f11a,#8b5cf60f);border:1px solid rgba(139,92,246,.3);transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease;animation:rts-st-pop .4s cubic-bezier(.2,.9,.3,1.3)}.rts-story-link-card:hover{transform:translateY(-2px);border-color:var(--rts-st-accent);box-shadow:0 10px 28px -12px #8b5cf699}.rts-story-link-card:hover .rts-story-link-arrow{transform:translate(3px)}.rts-story-link-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:42px;height:42px;border-radius:12px;font-size:20px;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent))}.rts-story-link-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.rts-story-link-kicker{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-st-accent)}.rts-story-link-title{font-size:15px;font-weight:600;color:var(--rts-text-primary)}.rts-story-link-description{font-size:12px;color:var(--rts-text-secondary, #8a8fa3);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.rts-story-link-meta{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.rts-story-link-arrow{color:var(--rts-st-accent);transition:transform .2s ease}@keyframes rts-st-spin{to{transform:rotate(360deg)}}@keyframes rts-st-pop{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:none}}\n"] }); }
2313
2391
  }
2314
2392
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StorytellerStoryLinkStepComponent, decorators: [{
2315
2393
  type: Component,
2316
- args: [{ selector: 'rtsee-storyteller-story-link-step', standalone: true, template: "@if (storyUrl) {\n <a class=\"rts-story-link-card\" [href]=\"storyUrl\" target=\"_blank\" rel=\"noopener\">\n <div class=\"rts-story-link-icon\">\uD83D\uDCD6</div>\n <div class=\"rts-story-link-body\">\n <span class=\"rts-story-link-kicker\">Your story is ready</span>\n <span class=\"rts-story-link-title\">{{ story.name || 'Open story' }}</span>\n @if (story.description) {\n <span class=\"rts-story-link-description\">{{ story.description }}</span>\n }\n @if (story.slidesCount) {\n <span class=\"rts-story-link-meta\">{{ story.slidesCount }} slides</span>\n }\n </div>\n <span class=\"rts-story-link-arrow pi pi-arrow-right\"></span>\n </a>\n} @else {\n <div class=\"rts-storyteller-hint\">The story link will appear here once the story is created.</div>\n}\n", styles: [":host{display:block;--rts-st-accent: #8b5cf6;--rts-st-accent-2: #6366f1}.rts-storyteller-form{display:flex;flex-direction:column;gap:12px}.rts-storyteller-field{display:flex;flex-direction:column;gap:5px}.rts-storyteller-label{display:flex;justify-content:space-between;align-items:baseline;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-counter{font-weight:500;letter-spacing:0;text-transform:none}.rts-storyteller-input{width:100%;box-sizing:border-box;padding:9px 12px;border-radius:9px;border:1px solid var(--rts-border-dim, rgba(120, 120, 160, .25));background:var(--rts-bg-primary, rgba(127, 127, 160, .06));color:var(--rts-text-primary);font:inherit;font-size:13px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.rts-storyteller-input:focus{border-color:var(--rts-st-accent);box-shadow:0 0 0 3px #8b5cf62e}.rts-storyteller-input:disabled{opacity:.75;cursor:default}.rts-storyteller-input.rts-storyteller-invalid{border-color:#f43f5e;box-shadow:0 0 0 3px #f43f5e1f}.rts-storyteller-textarea{min-height:80px;resize:vertical;line-height:1.5}.rts-storyteller-textarea-lg{min-height:160px;max-height:480px}.rts-storyteller-select-wrap{position:relative}.rts-storyteller-select{appearance:none;padding-right:32px;cursor:pointer}.rts-storyteller-select-caret{position:absolute;right:12px;top:50%;transform:translateY(-50%);font-size:10px;pointer-events:none;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-hint{font-size:12px;font-style:italic;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-error{padding:6px 10px;border-radius:6px;font-size:12px;color:#f43f5e;background:#f43f5e14;border:1px solid rgba(244,63,94,.25)}.rts-storyteller-actions{display:flex;justify-content:flex-end}.rts-storyteller-submit{display:inline-flex;align-items:center;gap:6px;padding:8px 18px;border:none;border-radius:9px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent) 60%,#a78bfa);background-size:160% 160%;box-shadow:0 4px 14px -4px #6366f199;transition:transform .15s ease,box-shadow .15s ease,background-position .4s ease}.rts-storyteller-submit:hover:not(:disabled){transform:translateY(-1px);background-position:100% 0;box-shadow:0 6px 18px -4px #6366f1bf}.rts-storyteller-submit:disabled{opacity:.6;cursor:default;box-shadow:none}.rts-storyteller-spinner{width:11px;height:11px;border-radius:50%;border:2px solid rgba(255,255,255,.35);border-top-color:#fff;animation:rts-st-spin .8s linear infinite}.rts-story-link-card{display:flex;align-items:center;gap:12px;padding:14px;border-radius:12px;text-decoration:none;color:inherit;background:linear-gradient(135deg,#6366f11a,#8b5cf60f);border:1px solid rgba(139,92,246,.3);transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease;animation:rts-st-pop .4s cubic-bezier(.2,.9,.3,1.3)}.rts-story-link-card:hover{transform:translateY(-2px);border-color:var(--rts-st-accent);box-shadow:0 10px 28px -12px #8b5cf699}.rts-story-link-card:hover .rts-story-link-arrow{transform:translate(3px)}.rts-story-link-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:42px;height:42px;border-radius:12px;font-size:20px;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent))}.rts-story-link-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.rts-story-link-kicker{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-st-accent)}.rts-story-link-title{font-size:15px;font-weight:600;color:var(--rts-text-primary)}.rts-story-link-description{font-size:12px;color:var(--rts-text-secondary, #8a8fa3);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.rts-story-link-meta{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.rts-story-link-arrow{color:var(--rts-st-accent);transition:transform .2s ease}@keyframes rts-st-spin{to{transform:rotate(360deg)}}@keyframes rts-st-pop{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:none}}\n"] }]
2394
+ args: [{ selector: 'rtsee-storyteller-story-link-step', standalone: true, template: "@if (storyUrl) {\n <a class=\"rts-story-link-card\" [href]=\"storyUrl\" target=\"_blank\" rel=\"noopener\">\n <div class=\"rts-story-link-icon\">\uD83D\uDCD6</div>\n <div class=\"rts-story-link-body\">\n <span class=\"rts-story-link-kicker\">{{ kicker }}</span>\n <span class=\"rts-story-link-title\">{{ story.name || 'Open story' }}</span>\n @if (story.description) {\n <span class=\"rts-story-link-description\">{{ story.description }}</span>\n }\n @if (story.slidesCount) {\n <span class=\"rts-story-link-meta\">{{ story.slidesCount }} slides</span>\n }\n </div>\n <span class=\"rts-story-link-arrow pi pi-arrow-right\"></span>\n </a>\n} @else {\n <div class=\"rts-storyteller-hint\">The story link will appear here once the story is created.</div>\n}\n", styles: [":host{display:block;--rts-st-accent: #8b5cf6;--rts-st-accent-2: #6366f1}.rts-storyteller-form{display:flex;flex-direction:column;gap:12px}.rts-storyteller-field{display:flex;flex-direction:column;gap:5px}.rts-storyteller-label{display:flex;justify-content:space-between;align-items:baseline;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-counter{font-weight:500;letter-spacing:0;text-transform:none}.rts-storyteller-input{width:100%;box-sizing:border-box;padding:9px 12px;border-radius:9px;border:1px solid var(--rts-border-dim, rgba(120, 120, 160, .25));background:var(--rts-bg-primary, rgba(127, 127, 160, .06));color:var(--rts-text-primary);font:inherit;font-size:13px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.rts-storyteller-input:focus{border-color:var(--rts-st-accent);box-shadow:0 0 0 3px #8b5cf62e}.rts-storyteller-input:disabled{opacity:.75;cursor:default}.rts-storyteller-input.rts-storyteller-invalid{border-color:#f43f5e;box-shadow:0 0 0 3px #f43f5e1f}.rts-storyteller-textarea{min-height:80px;resize:vertical;line-height:1.5}.rts-storyteller-textarea-lg{min-height:160px;max-height:480px}.rts-storyteller-select-wrap{position:relative}.rts-storyteller-select{appearance:none;padding-right:32px;cursor:pointer}.rts-storyteller-select-caret{position:absolute;right:12px;top:50%;transform:translateY(-50%);font-size:10px;pointer-events:none;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-hint{font-size:12px;font-style:italic;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-error{padding:6px 10px;border-radius:6px;font-size:12px;color:#f43f5e;background:#f43f5e14;border:1px solid rgba(244,63,94,.25)}.rts-storyteller-actions{display:flex;justify-content:flex-end}.rts-storyteller-submit{display:inline-flex;align-items:center;gap:6px;padding:8px 18px;border:none;border-radius:9px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent) 60%,#a78bfa);background-size:160% 160%;box-shadow:0 4px 14px -4px #6366f199;transition:transform .15s ease,box-shadow .15s ease,background-position .4s ease}.rts-storyteller-submit:hover:not(:disabled){transform:translateY(-1px);background-position:100% 0;box-shadow:0 6px 18px -4px #6366f1bf}.rts-storyteller-submit:disabled{opacity:.6;cursor:default;box-shadow:none}.rts-storyteller-spinner{width:11px;height:11px;border-radius:50%;border:2px solid rgba(255,255,255,.35);border-top-color:#fff;animation:rts-st-spin .8s linear infinite}.rts-story-link-card{display:flex;align-items:center;gap:12px;padding:14px;border-radius:12px;text-decoration:none;color:inherit;background:linear-gradient(135deg,#6366f11a,#8b5cf60f);border:1px solid rgba(139,92,246,.3);transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease;animation:rts-st-pop .4s cubic-bezier(.2,.9,.3,1.3)}.rts-story-link-card:hover{transform:translateY(-2px);border-color:var(--rts-st-accent);box-shadow:0 10px 28px -12px #8b5cf699}.rts-story-link-card:hover .rts-story-link-arrow{transform:translate(3px)}.rts-story-link-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:42px;height:42px;border-radius:12px;font-size:20px;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent))}.rts-story-link-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.rts-story-link-kicker{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-st-accent)}.rts-story-link-title{font-size:15px;font-weight:600;color:var(--rts-text-primary)}.rts-story-link-description{font-size:12px;color:var(--rts-text-secondary, #8a8fa3);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.rts-story-link-meta{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.rts-story-link-arrow{color:var(--rts-st-accent);transition:transform .2s ease}@keyframes rts-st-spin{to{transform:rotate(360deg)}}@keyframes rts-st-pop{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:none}}\n"] }]
2317
2395
  }], propDecorators: { step: [{
2318
2396
  type: Input
2319
2397
  }], workflow: [{
@@ -2324,6 +2402,232 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
2324
2402
  type: Input
2325
2403
  }] } });
2326
2404
 
2405
+ const SEARCH_DEBOUNCE_MS = 250;
2406
+ /**
2407
+ * Story Translator step 1: pick a story (autocomplete by name) and the target language.
2408
+ */
2409
+ class StoryTranslatorFormStepComponent {
2410
+ constructor(cdr, host) {
2411
+ this.cdr = cdr;
2412
+ this.host = host;
2413
+ this.languages = STORY_TRANSLATOR_LANGUAGES;
2414
+ this.form = new FormGroup({
2415
+ /* eslint-disable */
2416
+ storyQuery: new FormControl(''),
2417
+ storyId: new FormControl('', Validators.required),
2418
+ language: new FormControl('', Validators.required),
2419
+ agentId: new FormControl('', Validators.required),
2420
+ /* eslint-enable */
2421
+ });
2422
+ this.selectedStory = null;
2423
+ this.suggestions = [];
2424
+ this.suggestionsOpened = false;
2425
+ this.highlightedIndex = 0;
2426
+ this.searchInProgress = false;
2427
+ this.searchError = null;
2428
+ this.searchTimer = null;
2429
+ this.searchRequestId = 0;
2430
+ this.onChangedBound = () => this.onWorkflowChanged();
2431
+ }
2432
+ get agents() {
2433
+ return (this.chat?.members || []).filter((m) => m.isAgent);
2434
+ }
2435
+ get readonly() {
2436
+ return !this.step?.isActive || !!this.workflow?.submitInProgress;
2437
+ }
2438
+ get canCancel() {
2439
+ return !!this.workflow?.isDraft && !this.workflow?.submitInProgress && !!this.messenger;
2440
+ }
2441
+ get selectedLanguage() {
2442
+ return this.languages.find((l) => l.code === this.form.get('language')?.value);
2443
+ }
2444
+ ngOnInit() {
2445
+ this.workflow?.on(OzzyWorkflowClientEvents.CHANGED, this.onChangedBound);
2446
+ const agents = this.agents;
2447
+ if (agents.length === 1) {
2448
+ this.form.get('agentId')?.setValue(agents[0].id);
2449
+ }
2450
+ this.syncWithStep();
2451
+ }
2452
+ ngOnDestroy() {
2453
+ this.workflow?.off(OzzyWorkflowClientEvents.CHANGED, this.onChangedBound);
2454
+ this.clearSearchTimer();
2455
+ }
2456
+ onStoryFocus() {
2457
+ if (this.readonly) {
2458
+ return;
2459
+ }
2460
+ this.suggestionsOpened = true;
2461
+ if (!this.suggestions.length) {
2462
+ this.scheduleSearch(0);
2463
+ }
2464
+ }
2465
+ onStoryInput() {
2466
+ // typing invalidates the previous selection
2467
+ if (this.selectedStory) {
2468
+ this.selectedStory = null;
2469
+ this.form.get('storyId')?.setValue('');
2470
+ }
2471
+ this.suggestionsOpened = true;
2472
+ this.scheduleSearch(SEARCH_DEBOUNCE_MS);
2473
+ }
2474
+ onStoryKeydown(event) {
2475
+ if (!this.suggestionsOpened || !this.suggestions.length) {
2476
+ if (event.key === 'ArrowDown') {
2477
+ this.suggestionsOpened = true;
2478
+ this.scheduleSearch(0);
2479
+ }
2480
+ return;
2481
+ }
2482
+ switch (event.key) {
2483
+ case 'ArrowDown':
2484
+ event.preventDefault();
2485
+ this.highlightedIndex = (this.highlightedIndex + 1) % this.suggestions.length;
2486
+ break;
2487
+ case 'ArrowUp':
2488
+ event.preventDefault();
2489
+ this.highlightedIndex = (this.highlightedIndex - 1 + this.suggestions.length) % this.suggestions.length;
2490
+ break;
2491
+ case 'Enter':
2492
+ event.preventDefault();
2493
+ this.selectStory(this.suggestions[this.highlightedIndex]);
2494
+ break;
2495
+ case 'Escape':
2496
+ event.preventDefault();
2497
+ this.suggestionsOpened = false;
2498
+ break;
2499
+ }
2500
+ }
2501
+ selectStory(story) {
2502
+ if (!story || this.readonly) {
2503
+ return;
2504
+ }
2505
+ this.selectedStory = story;
2506
+ this.form.patchValue({ storyId: story.id, storyQuery: story.name }, { emitEvent: false });
2507
+ this.form.get('storyId')?.markAsTouched();
2508
+ this.suggestionsOpened = false;
2509
+ }
2510
+ clearStory() {
2511
+ if (this.readonly) {
2512
+ return;
2513
+ }
2514
+ this.selectedStory = null;
2515
+ this.form.patchValue({ storyId: '', storyQuery: '' }, { emitEvent: false });
2516
+ this.suggestionsOpened = true;
2517
+ this.scheduleSearch(0);
2518
+ setTimeout(() => this.storyInput?.nativeElement.focus());
2519
+ }
2520
+ onDocumentMouseDown(event) {
2521
+ if (this.suggestionsOpened && !this.host.nativeElement.contains(event.target)) {
2522
+ this.suggestionsOpened = false;
2523
+ }
2524
+ }
2525
+ submit() {
2526
+ if (this.readonly) {
2527
+ return;
2528
+ }
2529
+ if (this.form.invalid || !this.selectedStory) {
2530
+ this.form.markAllAsTouched();
2531
+ return;
2532
+ }
2533
+ void this.workflow.submitStep({
2534
+ storyId: this.selectedStory.id,
2535
+ storyName: this.selectedStory.name,
2536
+ language: this.form.get('language')?.value,
2537
+ agentId: this.form.get('agentId')?.value,
2538
+ });
2539
+ }
2540
+ cancel() {
2541
+ if (!this.canCancel) {
2542
+ return;
2543
+ }
2544
+ this.messenger?.discardDraftWorkflow(this.workflow);
2545
+ }
2546
+ isInvalid(name) {
2547
+ const control = this.form.get(name);
2548
+ return !!control && control.invalid && control.touched;
2549
+ }
2550
+ scheduleSearch(delay) {
2551
+ this.clearSearchTimer();
2552
+ this.searchTimer = setTimeout(() => void this.search(), delay);
2553
+ }
2554
+ clearSearchTimer() {
2555
+ if (this.searchTimer) {
2556
+ clearTimeout(this.searchTimer);
2557
+ this.searchTimer = null;
2558
+ }
2559
+ }
2560
+ async search() {
2561
+ if (!this.messenger) {
2562
+ return;
2563
+ }
2564
+ const requestId = ++this.searchRequestId;
2565
+ const query = this.form.get('storyQuery')?.value || '';
2566
+ this.searchInProgress = true;
2567
+ this.searchError = null;
2568
+ this.cdr.markForCheck();
2569
+ try {
2570
+ const stories = await this.messenger.ozzy.searchStories(query);
2571
+ if (requestId !== this.searchRequestId) {
2572
+ return;
2573
+ }
2574
+ this.suggestions = stories;
2575
+ this.highlightedIndex = 0;
2576
+ }
2577
+ catch (e) {
2578
+ if (requestId !== this.searchRequestId) {
2579
+ return;
2580
+ }
2581
+ this.suggestions = [];
2582
+ this.searchError = e?.friendlyMessage || e?.message || 'Failed to load stories.';
2583
+ }
2584
+ finally {
2585
+ if (requestId === this.searchRequestId) {
2586
+ this.searchInProgress = false;
2587
+ this.cdr.detectChanges();
2588
+ }
2589
+ }
2590
+ }
2591
+ onWorkflowChanged() {
2592
+ this.syncWithStep();
2593
+ this.cdr.detectChanges();
2594
+ }
2595
+ syncWithStep() {
2596
+ const output = this.step?.output;
2597
+ if (output?.storyId) {
2598
+ this.selectedStory = { id: output.storyId, name: output.storyName || output.storyId };
2599
+ this.form.patchValue({ storyId: output.storyId, storyQuery: output.storyName || '', language: output.language, agentId: output.agentId }, { emitEvent: false });
2600
+ }
2601
+ if (this.readonly) {
2602
+ this.suggestionsOpened = false;
2603
+ this.form.disable({ emitEvent: false });
2604
+ }
2605
+ else {
2606
+ this.form.enable({ emitEvent: false });
2607
+ }
2608
+ }
2609
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryTranslatorFormStepComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
2610
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StoryTranslatorFormStepComponent, isStandalone: true, selector: "rtsee-story-translator-form-step", inputs: { step: "step", workflow: "workflow", chat: "chat", messenger: "messenger" }, host: { listeners: { "document:mousedown": "onDocumentMouseDown($event)" } }, viewQueries: [{ propertyName: "storyInput", first: true, predicate: ["storyInput"], descendants: true }], ngImport: i0, template: "<form class=\"rts-storyteller-form\" [formGroup]=\"form\" (ngSubmit)=\"submit()\" autocomplete=\"off\">\n <div class=\"rts-storyteller-field\">\n <label class=\"rts-storyteller-label\" for=\"story-translator-story-{{ step.id }}\">Story</label>\n <div class=\"rts-st-autocomplete\">\n <span class=\"rts-st-autocomplete-icon pi\" [class.pi-search]=\"!searchInProgress\" [class.pi-spin]=\"searchInProgress\" [class.pi-spinner]=\"searchInProgress\"></span>\n <input #storyInput\n class=\"rts-storyteller-input rts-st-autocomplete-input\"\n [class.rts-storyteller-invalid]=\"isInvalid('storyId')\"\n [class.rts-st-autocomplete-selected]=\"!!selectedStory\"\n formControlName=\"storyQuery\"\n type=\"text\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n [attr.aria-expanded]=\"suggestionsOpened\"\n placeholder=\"Search stories by name\u2026\"\n id=\"story-translator-story-{{ step.id }}\"\n (focus)=\"onStoryFocus()\"\n (input)=\"onStoryInput()\"\n (keydown)=\"onStoryKeydown($event)\"\n />\n @if (selectedStory && !readonly) {\n <button type=\"button\"\n class=\"rts-st-autocomplete-clear pi pi-times\"\n aria-label=\"Clear story\"\n id=\"story-translator-clear-{{ step.id }}\"\n (click)=\"clearStory()\"\n ></button>\n }\n\n @if (suggestionsOpened && !readonly) {\n <ul class=\"rts-st-autocomplete-panel\" role=\"listbox\">\n @if (searchError) {\n <li class=\"rts-st-autocomplete-empty rts-st-autocomplete-error\">{{ searchError }}</li>\n } @else if (!suggestions.length && !searchInProgress) {\n <li class=\"rts-st-autocomplete-empty\">No stories found</li>\n }\n @for (story of suggestions; track story.id; let i = $index) {\n <li class=\"rts-st-autocomplete-option\"\n role=\"option\"\n [class.rts-st-autocomplete-option-active]=\"i === highlightedIndex\"\n [attr.aria-selected]=\"story.id === selectedStory?.id\"\n (mouseenter)=\"highlightedIndex = i\"\n (mousedown)=\"$event.preventDefault(); selectStory(story)\"\n >\n @if (story.imageUrl) {\n <img class=\"rts-st-autocomplete-thumb\" [src]=\"story.imageUrl\" [alt]=\"story.name\" loading=\"lazy\" />\n } @else {\n <span class=\"rts-st-autocomplete-thumb rts-st-autocomplete-thumb-empty\">\uD83D\uDCD6</span>\n }\n <span class=\"rts-st-autocomplete-option-body\">\n <span class=\"rts-st-autocomplete-option-name\">{{ story.name }}</span>\n @if (story.totalSlides) {\n <span class=\"rts-st-autocomplete-option-meta\">{{ story.totalSlides }} slides</span>\n }\n </span>\n </li>\n }\n </ul>\n }\n </div>\n </div>\n\n <label class=\"rts-storyteller-field\">\n <span class=\"rts-storyteller-label\">Translate to</span>\n <div class=\"rts-storyteller-select-wrap\">\n <select class=\"rts-storyteller-input rts-storyteller-select\"\n [class.rts-storyteller-invalid]=\"isInvalid('language')\"\n formControlName=\"language\"\n id=\"story-translator-language-{{ step.id }}\"\n >\n <option value=\"\" disabled>Select a language</option>\n @for (language of languages; track language.code) {\n <option [value]=\"language.code\">{{ language.nativeName }}</option>\n }\n </select>\n <span class=\"rts-storyteller-select-caret pi pi-chevron-down\"></span>\n </div>\n </label>\n\n <label class=\"rts-storyteller-field\">\n <span class=\"rts-storyteller-label\">Agent</span>\n @if (agents.length) {\n <div class=\"rts-storyteller-select-wrap\">\n <select class=\"rts-storyteller-input rts-storyteller-select\"\n [class.rts-storyteller-invalid]=\"isInvalid('agentId')\"\n formControlName=\"agentId\"\n id=\"story-translator-agent-{{ step.id }}\"\n >\n <option value=\"\" disabled>Select an agent</option>\n @for (agent of agents; track agent.id) {\n <option [value]=\"agent.id\">\n {{ agent.name }}{{ agent.agentConfiguration?.model?.model ? ' \u00B7 ' + agent.agentConfiguration?.model?.model : '' }}\n </option>\n }\n </select>\n <span class=\"rts-storyteller-select-caret pi pi-chevron-down\"></span>\n </div>\n } @else {\n <div class=\"rts-storyteller-hint\">There are no agentic members in this chat. Add an agent to continue.</div>\n }\n </label>\n\n @if (workflow.submitError) {\n <div class=\"rts-storyteller-error\">{{ workflow.submitError }}</div>\n }\n\n @if (step.isActive) {\n <div class=\"rts-storyteller-actions rts-st-translator-actions\">\n @if (canCancel) {\n <button class=\"rts-st-cancel\"\n type=\"button\"\n id=\"story-translator-cancel-{{ step.id }}\"\n (click)=\"cancel()\"\n >Cancel</button>\n }\n <button class=\"rts-storyteller-submit\"\n type=\"submit\"\n [disabled]=\"workflow.submitInProgress || !agents.length\"\n id=\"story-translator-submit-{{ step.id }}\"\n >\n @if (workflow.submitInProgress) {\n <span class=\"rts-storyteller-spinner\"></span> Translating\u2026\n } @else {\n \uD83C\uDF10 Translate{{ selectedLanguage ? ' to ' + selectedLanguage.nativeName : '' }}\n }\n </button>\n </div>\n }\n</form>\n", styles: [":host{display:block;--rts-st-accent: #8b5cf6;--rts-st-accent-2: #6366f1}.rts-storyteller-form{display:flex;flex-direction:column;gap:12px}.rts-storyteller-field{display:flex;flex-direction:column;gap:5px}.rts-storyteller-label{display:flex;justify-content:space-between;align-items:baseline;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-counter{font-weight:500;letter-spacing:0;text-transform:none}.rts-storyteller-input{width:100%;box-sizing:border-box;padding:9px 12px;border-radius:9px;border:1px solid var(--rts-border-dim, rgba(120, 120, 160, .25));background:var(--rts-bg-primary, rgba(127, 127, 160, .06));color:var(--rts-text-primary);font:inherit;font-size:13px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.rts-storyteller-input:focus{border-color:var(--rts-st-accent);box-shadow:0 0 0 3px #8b5cf62e}.rts-storyteller-input:disabled{opacity:.75;cursor:default}.rts-storyteller-input.rts-storyteller-invalid{border-color:#f43f5e;box-shadow:0 0 0 3px #f43f5e1f}.rts-storyteller-textarea{min-height:80px;resize:vertical;line-height:1.5}.rts-storyteller-textarea-lg{min-height:160px;max-height:480px}.rts-storyteller-select-wrap{position:relative}.rts-storyteller-select{appearance:none;padding-right:32px;cursor:pointer}.rts-storyteller-select-caret{position:absolute;right:12px;top:50%;transform:translateY(-50%);font-size:10px;pointer-events:none;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-hint{font-size:12px;font-style:italic;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-error{padding:6px 10px;border-radius:6px;font-size:12px;color:#f43f5e;background:#f43f5e14;border:1px solid rgba(244,63,94,.25)}.rts-storyteller-actions{display:flex;justify-content:flex-end}.rts-storyteller-submit{display:inline-flex;align-items:center;gap:6px;padding:8px 18px;border:none;border-radius:9px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent) 60%,#a78bfa);background-size:160% 160%;box-shadow:0 4px 14px -4px #6366f199;transition:transform .15s ease,box-shadow .15s ease,background-position .4s ease}.rts-storyteller-submit:hover:not(:disabled){transform:translateY(-1px);background-position:100% 0;box-shadow:0 6px 18px -4px #6366f1bf}.rts-storyteller-submit:disabled{opacity:.6;cursor:default;box-shadow:none}.rts-storyteller-spinner{width:11px;height:11px;border-radius:50%;border:2px solid rgba(255,255,255,.35);border-top-color:#fff;animation:rts-st-spin .8s linear infinite}.rts-story-link-card{display:flex;align-items:center;gap:12px;padding:14px;border-radius:12px;text-decoration:none;color:inherit;background:linear-gradient(135deg,#6366f11a,#8b5cf60f);border:1px solid rgba(139,92,246,.3);transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease;animation:rts-st-pop .4s cubic-bezier(.2,.9,.3,1.3)}.rts-story-link-card:hover{transform:translateY(-2px);border-color:var(--rts-st-accent);box-shadow:0 10px 28px -12px #8b5cf699}.rts-story-link-card:hover .rts-story-link-arrow{transform:translate(3px)}.rts-story-link-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:42px;height:42px;border-radius:12px;font-size:20px;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent))}.rts-story-link-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.rts-story-link-kicker{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-st-accent)}.rts-story-link-title{font-size:15px;font-weight:600;color:var(--rts-text-primary)}.rts-story-link-description{font-size:12px;color:var(--rts-text-secondary, #8a8fa3);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.rts-story-link-meta{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.rts-story-link-arrow{color:var(--rts-st-accent);transition:transform .2s ease}@keyframes rts-st-spin{to{transform:rotate(360deg)}}@keyframes rts-st-pop{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:none}}\n", ".rts-st-autocomplete{position:relative}.rts-st-autocomplete-icon{position:absolute;left:12px;top:50%;transform:translateY(-50%);font-size:12px;pointer-events:none;color:var(--rts-text-secondary, #8a8fa3)}.rts-st-autocomplete-input{padding-left:32px;padding-right:32px}.rts-st-autocomplete-input.rts-st-autocomplete-selected{font-weight:600}.rts-st-autocomplete-clear{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:22px;height:22px;padding:0;border:none;border-radius:50%;font-size:10px;cursor:pointer;color:var(--rts-text-secondary, #8a8fa3);background:transparent;transition:background .15s ease,color .15s ease}.rts-st-autocomplete-clear:hover{color:var(--rts-text-primary);background:#8b5cf626}.rts-st-autocomplete-panel{position:absolute;z-index:20;left:0;right:0;top:calc(100% + 4px);max-height:240px;margin:0;padding:4px;overflow-y:auto;list-style:none;border-radius:10px;border:1px solid var(--rts-border-dim, rgba(120, 120, 160, .25));background:var(--rts-bg-secondary, var(--rts-bg-primary, #1d1f2b));-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 14px 32px -12px #00000073;animation:rts-st-panel-in .14s ease-out}.rts-st-autocomplete-option{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:7px;cursor:pointer;transition:background .12s ease}.rts-st-autocomplete-option.rts-st-autocomplete-option-active{background:linear-gradient(135deg,#6366f129,#8b5cf61a)}.rts-st-autocomplete-option[aria-selected=true] .rts-st-autocomplete-option-name{color:var(--rts-st-accent)}.rts-st-autocomplete-thumb{flex-shrink:0;width:30px;height:30px;border-radius:7px;object-fit:cover}.rts-st-autocomplete-thumb-empty{display:flex;align-items:center;justify-content:center;font-size:15px;background:#8b5cf624}.rts-st-autocomplete-option-body{display:flex;flex-direction:column;min-width:0}.rts-st-autocomplete-option-name{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:500;color:var(--rts-text-primary)}.rts-st-autocomplete-option-meta{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.rts-st-autocomplete-empty{padding:8px;font-size:12px;font-style:italic;color:var(--rts-text-secondary, #8a8fa3)}.rts-st-autocomplete-error{font-style:normal;color:#f43f5e}.rts-st-translator-actions{gap:8px}.rts-st-cancel{padding:8px 16px;border-radius:9px;border:1px solid var(--rts-border-dim, rgba(120, 120, 160, .25));background:transparent;color:var(--rts-text-secondary, #8a8fa3);font:inherit;font-size:13px;font-weight:600;cursor:pointer;transition:color .15s ease,border-color .15s ease,background .15s ease}.rts-st-cancel:hover{color:var(--rts-text-primary);border-color:#f43f5e73;background:#f43f5e0f}@keyframes rts-st-panel-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { 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.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] }); }
2611
+ }
2612
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryTranslatorFormStepComponent, decorators: [{
2613
+ type: Component,
2614
+ args: [{ selector: 'rtsee-story-translator-form-step', standalone: true, imports: [ReactiveFormsModule], template: "<form class=\"rts-storyteller-form\" [formGroup]=\"form\" (ngSubmit)=\"submit()\" autocomplete=\"off\">\n <div class=\"rts-storyteller-field\">\n <label class=\"rts-storyteller-label\" for=\"story-translator-story-{{ step.id }}\">Story</label>\n <div class=\"rts-st-autocomplete\">\n <span class=\"rts-st-autocomplete-icon pi\" [class.pi-search]=\"!searchInProgress\" [class.pi-spin]=\"searchInProgress\" [class.pi-spinner]=\"searchInProgress\"></span>\n <input #storyInput\n class=\"rts-storyteller-input rts-st-autocomplete-input\"\n [class.rts-storyteller-invalid]=\"isInvalid('storyId')\"\n [class.rts-st-autocomplete-selected]=\"!!selectedStory\"\n formControlName=\"storyQuery\"\n type=\"text\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n [attr.aria-expanded]=\"suggestionsOpened\"\n placeholder=\"Search stories by name\u2026\"\n id=\"story-translator-story-{{ step.id }}\"\n (focus)=\"onStoryFocus()\"\n (input)=\"onStoryInput()\"\n (keydown)=\"onStoryKeydown($event)\"\n />\n @if (selectedStory && !readonly) {\n <button type=\"button\"\n class=\"rts-st-autocomplete-clear pi pi-times\"\n aria-label=\"Clear story\"\n id=\"story-translator-clear-{{ step.id }}\"\n (click)=\"clearStory()\"\n ></button>\n }\n\n @if (suggestionsOpened && !readonly) {\n <ul class=\"rts-st-autocomplete-panel\" role=\"listbox\">\n @if (searchError) {\n <li class=\"rts-st-autocomplete-empty rts-st-autocomplete-error\">{{ searchError }}</li>\n } @else if (!suggestions.length && !searchInProgress) {\n <li class=\"rts-st-autocomplete-empty\">No stories found</li>\n }\n @for (story of suggestions; track story.id; let i = $index) {\n <li class=\"rts-st-autocomplete-option\"\n role=\"option\"\n [class.rts-st-autocomplete-option-active]=\"i === highlightedIndex\"\n [attr.aria-selected]=\"story.id === selectedStory?.id\"\n (mouseenter)=\"highlightedIndex = i\"\n (mousedown)=\"$event.preventDefault(); selectStory(story)\"\n >\n @if (story.imageUrl) {\n <img class=\"rts-st-autocomplete-thumb\" [src]=\"story.imageUrl\" [alt]=\"story.name\" loading=\"lazy\" />\n } @else {\n <span class=\"rts-st-autocomplete-thumb rts-st-autocomplete-thumb-empty\">\uD83D\uDCD6</span>\n }\n <span class=\"rts-st-autocomplete-option-body\">\n <span class=\"rts-st-autocomplete-option-name\">{{ story.name }}</span>\n @if (story.totalSlides) {\n <span class=\"rts-st-autocomplete-option-meta\">{{ story.totalSlides }} slides</span>\n }\n </span>\n </li>\n }\n </ul>\n }\n </div>\n </div>\n\n <label class=\"rts-storyteller-field\">\n <span class=\"rts-storyteller-label\">Translate to</span>\n <div class=\"rts-storyteller-select-wrap\">\n <select class=\"rts-storyteller-input rts-storyteller-select\"\n [class.rts-storyteller-invalid]=\"isInvalid('language')\"\n formControlName=\"language\"\n id=\"story-translator-language-{{ step.id }}\"\n >\n <option value=\"\" disabled>Select a language</option>\n @for (language of languages; track language.code) {\n <option [value]=\"language.code\">{{ language.nativeName }}</option>\n }\n </select>\n <span class=\"rts-storyteller-select-caret pi pi-chevron-down\"></span>\n </div>\n </label>\n\n <label class=\"rts-storyteller-field\">\n <span class=\"rts-storyteller-label\">Agent</span>\n @if (agents.length) {\n <div class=\"rts-storyteller-select-wrap\">\n <select class=\"rts-storyteller-input rts-storyteller-select\"\n [class.rts-storyteller-invalid]=\"isInvalid('agentId')\"\n formControlName=\"agentId\"\n id=\"story-translator-agent-{{ step.id }}\"\n >\n <option value=\"\" disabled>Select an agent</option>\n @for (agent of agents; track agent.id) {\n <option [value]=\"agent.id\">\n {{ agent.name }}{{ agent.agentConfiguration?.model?.model ? ' \u00B7 ' + agent.agentConfiguration?.model?.model : '' }}\n </option>\n }\n </select>\n <span class=\"rts-storyteller-select-caret pi pi-chevron-down\"></span>\n </div>\n } @else {\n <div class=\"rts-storyteller-hint\">There are no agentic members in this chat. Add an agent to continue.</div>\n }\n </label>\n\n @if (workflow.submitError) {\n <div class=\"rts-storyteller-error\">{{ workflow.submitError }}</div>\n }\n\n @if (step.isActive) {\n <div class=\"rts-storyteller-actions rts-st-translator-actions\">\n @if (canCancel) {\n <button class=\"rts-st-cancel\"\n type=\"button\"\n id=\"story-translator-cancel-{{ step.id }}\"\n (click)=\"cancel()\"\n >Cancel</button>\n }\n <button class=\"rts-storyteller-submit\"\n type=\"submit\"\n [disabled]=\"workflow.submitInProgress || !agents.length\"\n id=\"story-translator-submit-{{ step.id }}\"\n >\n @if (workflow.submitInProgress) {\n <span class=\"rts-storyteller-spinner\"></span> Translating\u2026\n } @else {\n \uD83C\uDF10 Translate{{ selectedLanguage ? ' to ' + selectedLanguage.nativeName : '' }}\n }\n </button>\n </div>\n }\n</form>\n", styles: [":host{display:block;--rts-st-accent: #8b5cf6;--rts-st-accent-2: #6366f1}.rts-storyteller-form{display:flex;flex-direction:column;gap:12px}.rts-storyteller-field{display:flex;flex-direction:column;gap:5px}.rts-storyteller-label{display:flex;justify-content:space-between;align-items:baseline;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-counter{font-weight:500;letter-spacing:0;text-transform:none}.rts-storyteller-input{width:100%;box-sizing:border-box;padding:9px 12px;border-radius:9px;border:1px solid var(--rts-border-dim, rgba(120, 120, 160, .25));background:var(--rts-bg-primary, rgba(127, 127, 160, .06));color:var(--rts-text-primary);font:inherit;font-size:13px;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background .15s ease}.rts-storyteller-input:focus{border-color:var(--rts-st-accent);box-shadow:0 0 0 3px #8b5cf62e}.rts-storyteller-input:disabled{opacity:.75;cursor:default}.rts-storyteller-input.rts-storyteller-invalid{border-color:#f43f5e;box-shadow:0 0 0 3px #f43f5e1f}.rts-storyteller-textarea{min-height:80px;resize:vertical;line-height:1.5}.rts-storyteller-textarea-lg{min-height:160px;max-height:480px}.rts-storyteller-select-wrap{position:relative}.rts-storyteller-select{appearance:none;padding-right:32px;cursor:pointer}.rts-storyteller-select-caret{position:absolute;right:12px;top:50%;transform:translateY(-50%);font-size:10px;pointer-events:none;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-hint{font-size:12px;font-style:italic;color:var(--rts-text-secondary, #8a8fa3)}.rts-storyteller-error{padding:6px 10px;border-radius:6px;font-size:12px;color:#f43f5e;background:#f43f5e14;border:1px solid rgba(244,63,94,.25)}.rts-storyteller-actions{display:flex;justify-content:flex-end}.rts-storyteller-submit{display:inline-flex;align-items:center;gap:6px;padding:8px 18px;border:none;border-radius:9px;color:#fff;font-weight:600;font-size:13px;cursor:pointer;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent) 60%,#a78bfa);background-size:160% 160%;box-shadow:0 4px 14px -4px #6366f199;transition:transform .15s ease,box-shadow .15s ease,background-position .4s ease}.rts-storyteller-submit:hover:not(:disabled){transform:translateY(-1px);background-position:100% 0;box-shadow:0 6px 18px -4px #6366f1bf}.rts-storyteller-submit:disabled{opacity:.6;cursor:default;box-shadow:none}.rts-storyteller-spinner{width:11px;height:11px;border-radius:50%;border:2px solid rgba(255,255,255,.35);border-top-color:#fff;animation:rts-st-spin .8s linear infinite}.rts-story-link-card{display:flex;align-items:center;gap:12px;padding:14px;border-radius:12px;text-decoration:none;color:inherit;background:linear-gradient(135deg,#6366f11a,#8b5cf60f);border:1px solid rgba(139,92,246,.3);transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease;animation:rts-st-pop .4s cubic-bezier(.2,.9,.3,1.3)}.rts-story-link-card:hover{transform:translateY(-2px);border-color:var(--rts-st-accent);box-shadow:0 10px 28px -12px #8b5cf699}.rts-story-link-card:hover .rts-story-link-arrow{transform:translate(3px)}.rts-story-link-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:42px;height:42px;border-radius:12px;font-size:20px;background:linear-gradient(135deg,var(--rts-st-accent-2),var(--rts-st-accent))}.rts-story-link-body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}.rts-story-link-kicker{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--rts-st-accent)}.rts-story-link-title{font-size:15px;font-weight:600;color:var(--rts-text-primary)}.rts-story-link-description{font-size:12px;color:var(--rts-text-secondary, #8a8fa3);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.rts-story-link-meta{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.rts-story-link-arrow{color:var(--rts-st-accent);transition:transform .2s ease}@keyframes rts-st-spin{to{transform:rotate(360deg)}}@keyframes rts-st-pop{0%{opacity:0;transform:scale(.96)}to{opacity:1;transform:none}}\n", ".rts-st-autocomplete{position:relative}.rts-st-autocomplete-icon{position:absolute;left:12px;top:50%;transform:translateY(-50%);font-size:12px;pointer-events:none;color:var(--rts-text-secondary, #8a8fa3)}.rts-st-autocomplete-input{padding-left:32px;padding-right:32px}.rts-st-autocomplete-input.rts-st-autocomplete-selected{font-weight:600}.rts-st-autocomplete-clear{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:22px;height:22px;padding:0;border:none;border-radius:50%;font-size:10px;cursor:pointer;color:var(--rts-text-secondary, #8a8fa3);background:transparent;transition:background .15s ease,color .15s ease}.rts-st-autocomplete-clear:hover{color:var(--rts-text-primary);background:#8b5cf626}.rts-st-autocomplete-panel{position:absolute;z-index:20;left:0;right:0;top:calc(100% + 4px);max-height:240px;margin:0;padding:4px;overflow-y:auto;list-style:none;border-radius:10px;border:1px solid var(--rts-border-dim, rgba(120, 120, 160, .25));background:var(--rts-bg-secondary, var(--rts-bg-primary, #1d1f2b));-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 14px 32px -12px #00000073;animation:rts-st-panel-in .14s ease-out}.rts-st-autocomplete-option{display:flex;align-items:center;gap:10px;padding:6px 8px;border-radius:7px;cursor:pointer;transition:background .12s ease}.rts-st-autocomplete-option.rts-st-autocomplete-option-active{background:linear-gradient(135deg,#6366f129,#8b5cf61a)}.rts-st-autocomplete-option[aria-selected=true] .rts-st-autocomplete-option-name{color:var(--rts-st-accent)}.rts-st-autocomplete-thumb{flex-shrink:0;width:30px;height:30px;border-radius:7px;object-fit:cover}.rts-st-autocomplete-thumb-empty{display:flex;align-items:center;justify-content:center;font-size:15px;background:#8b5cf624}.rts-st-autocomplete-option-body{display:flex;flex-direction:column;min-width:0}.rts-st-autocomplete-option-name{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:13px;font-weight:500;color:var(--rts-text-primary)}.rts-st-autocomplete-option-meta{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.rts-st-autocomplete-empty{padding:8px;font-size:12px;font-style:italic;color:var(--rts-text-secondary, #8a8fa3)}.rts-st-autocomplete-error{font-style:normal;color:#f43f5e}.rts-st-translator-actions{gap:8px}.rts-st-cancel{padding:8px 16px;border-radius:9px;border:1px solid var(--rts-border-dim, rgba(120, 120, 160, .25));background:transparent;color:var(--rts-text-secondary, #8a8fa3);font:inherit;font-size:13px;font-weight:600;cursor:pointer;transition:color .15s ease,border-color .15s ease,background .15s ease}.rts-st-cancel:hover{color:var(--rts-text-primary);border-color:#f43f5e73;background:#f43f5e0f}@keyframes rts-st-panel-in{0%{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}\n"] }]
2615
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }], propDecorators: { step: [{
2616
+ type: Input
2617
+ }], workflow: [{
2618
+ type: Input
2619
+ }], chat: [{
2620
+ type: Input
2621
+ }], messenger: [{
2622
+ type: Input
2623
+ }], storyInput: [{
2624
+ type: ViewChild,
2625
+ args: ['storyInput']
2626
+ }], onDocumentMouseDown: [{
2627
+ type: HostListener,
2628
+ args: ['document:mousedown', ['$event']]
2629
+ }] } });
2630
+
2327
2631
  /**
2328
2632
  * Registry of the components which render workflow steps inside the rts-workflow widget.
2329
2633
  */
@@ -2333,6 +2637,7 @@ class WorkflowStepsService {
2333
2637
  { name: 'workflow-task-step', component: WorkflowTaskStepComponent },
2334
2638
  { name: 'storyteller-form-step', component: StorytellerFormStepComponent },
2335
2639
  { name: 'storyteller-story-link-step', component: StorytellerStoryLinkStepComponent },
2640
+ { name: 'story-translator-form-step', component: StoryTranslatorFormStepComponent },
2336
2641
  ];
2337
2642
  }
2338
2643
  register(widget) {
@@ -2508,8 +2813,22 @@ class MessageComponent {
2508
2813
  this.widgetsService = widgetsService;
2509
2814
  this.componentFactoryResolver = componentFactoryResolver;
2510
2815
  this.timeFormatService = timeFormatService;
2816
+ this.widgetRef = null;
2817
+ this.widgetHasMessageInput = false;
2511
2818
  }
2512
2819
  ngOnInit() { }
2820
+ ngOnChanges(changes) {
2821
+ // The message instance can be replaced (e.g. images got downsized versions) - forward it to the widget
2822
+ if (changes['message'] && !changes['message'].firstChange && this.widgetRef) {
2823
+ if (this.widgetHasMessageInput) {
2824
+ this.widgetRef.setInput('message', this.message);
2825
+ }
2826
+ else {
2827
+ this.widgetRef.instance.message = this.message;
2828
+ this.widgetRef.changeDetectorRef.markForCheck();
2829
+ }
2830
+ }
2831
+ }
2513
2832
  ngAfterViewInit() {
2514
2833
  /* eslint-disable */
2515
2834
  if (this.message.widget) {
@@ -2525,11 +2844,13 @@ class MessageComponent {
2525
2844
  componentRef.instance.messenger = this.messenger;
2526
2845
  componentRef.instance.chat = this.chat;
2527
2846
  componentRef.changeDetectorRef.detectChanges();
2847
+ this.widgetRef = componentRef;
2848
+ this.widgetHasMessageInput = !!reflectComponentType(widget)?.inputs.some((input) => input.propName === 'message');
2528
2849
  }
2529
2850
  /* eslint-enable */
2530
2851
  }
2531
2852
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessageComponent, deps: [{ token: MessageWidgetsService }, { token: i0.ComponentFactoryResolver }, { token: TimeFormatHelperService }], target: i0.ɵɵFactoryTarget.Component }); }
2532
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MessageComponent, isStandalone: true, selector: "rtsee-message", inputs: { chat: "chat", message: "message", member: "member", messenger: "messenger" }, viewQueries: [{ propertyName: "widgetHost", first: true, predicate: WidgetDirective, descendants: true }], ngImport: i0, template: "<div class=\"rts-message-container\" #rtsMessageContainer\n [ngClass]=\"{'rts-message-from-me': message.from.id === messenger.clientId}\"\n>\n <div class=\"rts-messenger-message\">\n @if (message.widget) {\n <ng-template widgetHost></ng-template>\n }\n @if (!message.widget) {\n <p class=\"rts-messenger-message-text\"\n >{{ message.text }}</p>\n }\n <div class=\"rts-messenger-message-info\" #rtsMessageInfo>\n <rtsee-message-time-and-status [message]=\"message\"\n [inChat]=\"true\"\n [hideStatus]=\"message.from.id !== messenger.clientId\"\n ></rtsee-message-time-and-status>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MessageTimeAndStatusComponent, selector: "rtsee-message-time-and-status", inputs: ["message", "hideStatus", "inChat"] }, { kind: "directive", type: WidgetDirective, selector: "[widgetHost]" }] }); }
2853
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MessageComponent, isStandalone: true, selector: "rtsee-message", inputs: { chat: "chat", message: "message", member: "member", messenger: "messenger" }, viewQueries: [{ propertyName: "widgetHost", first: true, predicate: WidgetDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"rts-message-container\" #rtsMessageContainer\n [ngClass]=\"{'rts-message-from-me': message.from.id === messenger.clientId}\"\n>\n <div class=\"rts-messenger-message\">\n @if (message.widget) {\n <ng-template widgetHost></ng-template>\n }\n @if (!message.widget) {\n <p class=\"rts-messenger-message-text\"\n >{{ message.text }}</p>\n }\n <div class=\"rts-messenger-message-info\" #rtsMessageInfo>\n <rtsee-message-time-and-status [message]=\"message\"\n [inChat]=\"true\"\n [hideStatus]=\"message.from.id !== messenger.clientId\"\n ></rtsee-message-time-and-status>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MessageTimeAndStatusComponent, selector: "rtsee-message-time-and-status", inputs: ["message", "hideStatus", "inChat"] }, { kind: "directive", type: WidgetDirective, selector: "[widgetHost]" }] }); }
2533
2854
  }
2534
2855
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessageComponent, decorators: [{
2535
2856
  type: Component,