@rtsee/ngx 0.0.116 → 0.0.118

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.
@@ -38,22 +38,23 @@ import relativeTime from 'dayjs/plugin/relativeTime';
38
38
  import shave from 'shave';
39
39
  import { loadStripe } from '@stripe/stripe-js';
40
40
  import { Card } from 'primeng/card';
41
- import { Chip } from 'primeng/chip';
42
41
  import { CdkCopyToClipboard } from '@angular/cdk/clipboard';
43
42
  import { Toast } from 'primeng/toast';
44
43
  import * as i1$3 from 'primeng/api';
45
44
  import { MessageService } from 'primeng/api';
46
45
  import Swiper from 'swiper';
47
- import { EffectCards, Manipulation } from 'swiper/modules';
46
+ import { Virtual, EffectCards } from 'swiper/modules';
48
47
  import { OrderList } from 'primeng/orderlist';
49
48
  import { RTSeeIllustrator, RTSeeStoryEditorSection } from '@rtsee/presentation';
50
49
  import { Tabs, TabList, Tab, TabPanels, TabPanel } from 'primeng/tabs';
50
+ import { Chip } from 'primeng/chip';
51
51
  import { ColorPicker } from 'primeng/colorpicker';
52
52
  import { Slider } from 'primeng/slider';
53
53
  import { Tag } from 'primeng/tag';
54
+ import { SelectButton } from 'primeng/selectbutton';
54
55
  import { RTSeeRoutesList } from '@rtsee/factory';
55
56
  import { Avatar } from 'primeng/avatar';
56
- import { Image } from 'primeng/image';
57
+ import { Image as Image$1 } from 'primeng/image';
57
58
  import { PanelMenu } from 'primeng/panelmenu';
58
59
  import { Menu } from 'primeng/menu';
59
60
  import { Password } from 'primeng/password';
@@ -367,11 +368,11 @@ class ProfileComponent {
367
368
  }, 200);
368
369
  }
369
370
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ProfileComponent, deps: [{ token: RTSeeAuthService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Component }); }
370
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ProfileComponent, isStandalone: true, selector: "rtsee-profile", inputs: { messenger: "messenger", peer: "peer" }, ngImport: i0, template: "<div class=\"rts-chat-info rts-messenger-profile\">\n @if (displaySubtitle) {\n <div class=\"rts-chat-info-meta\">\n <p class=\"rts-chat-info-type\">{{ displaySubtitle }}</p>\n </div>\n }\n\n @if (isOtherUser) {\n <div class=\"rts-chat-info-call-actions\">\n <button class=\"rts-call-btn\"\n (click)=\"call()\"\n type=\"button\"\n title=\"Audio Call\"\n >\n <span class=\"material-icons-outlined\">call</span>\n <span>Audio</span>\n </button>\n <button class=\"rts-call-btn rts-call-btn-video\"\n (click)=\"videoCall()\"\n type=\"button\"\n title=\"Video Call\"\n >\n <span class=\"material-icons-outlined\">videocam</span>\n <span>Video</span>\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer && !messenger.myPeer.isAnonymous) {\n <div class=\"rts-chat-info-actions\">\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManageProfile()\"\n >\n <span class=\"material-icons-outlined\">edit</span>\n {{ messenger.labels[RTSeeTextLabelKeys.MESSENGER_EDIT_PROFILE_BTN_LABEL] || 'Edit Profile' }}\n </button>\n </div>\n\n <hr class=\"rts-divider\" />\n\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-field-row\">\n <label class=\"rts-profile-field-label\">Language</label>\n <p-select [options]=\"messenger.languageOptions\"\n [(ngModel)]=\"messenger.currentLanguage\"\n (ngModelChange)=\"setLanguage()\"\n optionLabel=\"key\"\n optionValue=\"key\"\n [placeholder]=\"messenger.labels[RTSeeTextLabelKeys.MESSANGER_LANGUAGE_PLACEHOLDER]\"\n class=\"rts-profile-language-select\"\n />\n </div>\n </div>\n\n @if (messenger.myPeer.plan) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-plan-badge\">\n <span class=\"material-icons-outlined\">verified</span>\n <span>{{ messenger.myPeer.plan.productName }}</span>\n </div>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManagePlanPage()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.MANAGE_PLAN] || 'Manage Plan' }}\n </button>\n </div>\n } @else {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n (click)=\"messenger.openPaymentPage()\"\n >\n <span class=\"material-icons-outlined\">stars</span>\n {{ messenger.labels[RTSeeTextLabelKeys.SUBSCRIBE_LABEL] || 'Upgrade Plan' }}\n </button>\n </div>\n }\n\n <hr class=\"rts-divider\" />\n <div class=\"rts-chat-info-danger-zone\">\n <button class=\"rts-btn rts-btn-danger rts-btn-block\"\n (click)=\"logOut()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.LOGOUT_BUTTON] || 'Logout' }}\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer?.isAnonymous) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-anon-box\">\n <div class=\"rts-profile-anon-icon\">\n <span class=\"material-icons-outlined\">shield</span>\n </div>\n <div class=\"rts-profile-anon-text\">\n @if (!messenger.currentLanguage || messenger.currentLanguage === 'eng') {\n <p>This is a temporary anonymous identity. Sign up or sign in to save your history permanently.</p>\n } @else if (messenger.currentLanguage === 'ua') {\n <p>\u0426\u0435 \u0442\u0438\u043C\u0447\u0430\u0441\u043E\u0432\u0430 \u0430\u043D\u043E\u043D\u0456\u043C\u043D\u0430 \u043E\u0441\u043E\u0431\u0438\u0441\u0442\u0456\u0441\u0442\u044C. \u0417\u0430\u0440\u0435\u0454\u0441\u0442\u0440\u0443\u0439\u0442\u0435\u0441\u044F, \u0430\u0431\u0438 \u0437\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0456\u0441\u0442\u043E\u0440\u0456\u044E \u0441\u043F\u0456\u043B\u043A\u0443\u0432\u0430\u043D\u043D\u044F.</p>\n }\n </div>\n </div>\n\n <div class=\"rts-profile-auth-actions\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_UP)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_UP_BUTTON] || 'Sign Up' }}\n </button>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_IN)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_IN_BUTTON] || 'Sign In' }}\n </button>\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
371
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: ProfileComponent, isStandalone: true, selector: "rtsee-profile", inputs: { messenger: "messenger", peer: "peer" }, ngImport: i0, template: "<div class=\"rts-chat-info rts-messenger-profile\">\n @if (displaySubtitle) {\n <div class=\"rts-chat-info-meta\">\n <p class=\"rts-chat-info-type\">{{ displaySubtitle }}</p>\n </div>\n }\n\n @if (isOtherUser) {\n <div class=\"rts-chat-info-call-actions\">\n <button class=\"rts-call-btn\"\n (click)=\"call()\"\n type=\"button\"\n title=\"Audio Call\"\n >\n <span class=\"material-icons-outlined\">call</span>\n <span>Audio</span>\n </button>\n <button class=\"rts-call-btn rts-call-btn-video\"\n (click)=\"videoCall()\"\n type=\"button\"\n title=\"Video Call\"\n >\n <span class=\"material-icons-outlined\">videocam</span>\n <span>Video</span>\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer && !messenger.myPeer.isAnonymous) {\n <div class=\"rts-chat-info-actions\">\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManageProfile()\"\n >\n <span class=\"material-icons-outlined\">edit</span>\n {{ messenger.labels[RTSeeTextLabelKeys.MESSENGER_EDIT_PROFILE_BTN_LABEL] || 'Edit Profile' }}\n </button>\n </div>\n\n <hr class=\"rts-divider\" />\n }\n\n <!-- language selection is available to every user (anonymous included) -->\n @if (!isOtherUser && messenger.myPeer) {\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-field-row\">\n <label class=\"rts-profile-field-label\" for=\"rts-profile-language-select\">\n {{ messenger.labels[RTSeeTextLabelKeys.LANGUAGE_LABEL] || 'Language' }}\n </label>\n <p-select [options]=\"messenger.languageOptions\"\n [(ngModel)]=\"messenger.currentLanguage\"\n (ngModelChange)=\"setLanguage()\"\n optionLabel=\"key\"\n optionValue=\"key\"\n inputId=\"rts-profile-language-select\"\n [placeholder]=\"messenger.labels[RTSeeTextLabelKeys.MESSANGER_LANGUAGE_PLACEHOLDER]\"\n class=\"rts-profile-language-select\"\n />\n </div>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer && !messenger.myPeer.isAnonymous) {\n @if (messenger.myPeer.plan) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-plan-badge\">\n <span class=\"material-icons-outlined\">verified</span>\n <span>{{ messenger.myPeer.plan.productName }}</span>\n </div>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManagePlanPage()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.MANAGE_PLAN] || 'Manage Plan' }}\n </button>\n </div>\n } @else {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n (click)=\"messenger.openPaymentPage()\"\n >\n <span class=\"material-icons-outlined\">stars</span>\n {{ messenger.labels[RTSeeTextLabelKeys.SUBSCRIBE_LABEL] || 'Upgrade Plan' }}\n </button>\n </div>\n }\n\n <hr class=\"rts-divider\" />\n <div class=\"rts-chat-info-danger-zone\">\n <button class=\"rts-btn rts-btn-danger rts-btn-block\"\n (click)=\"logOut()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.LOGOUT_BUTTON] || 'Logout' }}\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer?.isAnonymous) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-anon-box\">\n <div class=\"rts-profile-anon-icon\">\n <span class=\"material-icons-outlined\">shield</span>\n </div>\n <div class=\"rts-profile-anon-text\">\n @if (!messenger.currentLanguage || messenger.currentLanguage === 'eng') {\n <p>This is a temporary anonymous identity. Sign up or sign in to save your history permanently.</p>\n } @else if (messenger.currentLanguage === 'ua') {\n <p>\u0426\u0435 \u0442\u0438\u043C\u0447\u0430\u0441\u043E\u0432\u0430 \u0430\u043D\u043E\u043D\u0456\u043C\u043D\u0430 \u043E\u0441\u043E\u0431\u0438\u0441\u0442\u0456\u0441\u0442\u044C. \u0417\u0430\u0440\u0435\u0454\u0441\u0442\u0440\u0443\u0439\u0442\u0435\u0441\u044F, \u0430\u0431\u0438 \u0437\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0456\u0441\u0442\u043E\u0440\u0456\u044E \u0441\u043F\u0456\u043B\u043A\u0443\u0432\u0430\u043D\u043D\u044F.</p>\n }\n </div>\n </div>\n\n <div class=\"rts-profile-auth-actions\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_UP)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_UP_BUTTON] || 'Sign Up' }}\n </button>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_IN)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_IN_BUTTON] || 'Sign In' }}\n </button>\n </div>\n }\n</div>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
371
372
  }
372
373
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: ProfileComponent, decorators: [{
373
374
  type: Component,
374
- args: [{ selector: 'rtsee-profile', imports: [RouterLink, Select, FormsModule], standalone: true, template: "<div class=\"rts-chat-info rts-messenger-profile\">\n @if (displaySubtitle) {\n <div class=\"rts-chat-info-meta\">\n <p class=\"rts-chat-info-type\">{{ displaySubtitle }}</p>\n </div>\n }\n\n @if (isOtherUser) {\n <div class=\"rts-chat-info-call-actions\">\n <button class=\"rts-call-btn\"\n (click)=\"call()\"\n type=\"button\"\n title=\"Audio Call\"\n >\n <span class=\"material-icons-outlined\">call</span>\n <span>Audio</span>\n </button>\n <button class=\"rts-call-btn rts-call-btn-video\"\n (click)=\"videoCall()\"\n type=\"button\"\n title=\"Video Call\"\n >\n <span class=\"material-icons-outlined\">videocam</span>\n <span>Video</span>\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer && !messenger.myPeer.isAnonymous) {\n <div class=\"rts-chat-info-actions\">\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManageProfile()\"\n >\n <span class=\"material-icons-outlined\">edit</span>\n {{ messenger.labels[RTSeeTextLabelKeys.MESSENGER_EDIT_PROFILE_BTN_LABEL] || 'Edit Profile' }}\n </button>\n </div>\n\n <hr class=\"rts-divider\" />\n\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-field-row\">\n <label class=\"rts-profile-field-label\">Language</label>\n <p-select [options]=\"messenger.languageOptions\"\n [(ngModel)]=\"messenger.currentLanguage\"\n (ngModelChange)=\"setLanguage()\"\n optionLabel=\"key\"\n optionValue=\"key\"\n [placeholder]=\"messenger.labels[RTSeeTextLabelKeys.MESSANGER_LANGUAGE_PLACEHOLDER]\"\n class=\"rts-profile-language-select\"\n />\n </div>\n </div>\n\n @if (messenger.myPeer.plan) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-plan-badge\">\n <span class=\"material-icons-outlined\">verified</span>\n <span>{{ messenger.myPeer.plan.productName }}</span>\n </div>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManagePlanPage()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.MANAGE_PLAN] || 'Manage Plan' }}\n </button>\n </div>\n } @else {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n (click)=\"messenger.openPaymentPage()\"\n >\n <span class=\"material-icons-outlined\">stars</span>\n {{ messenger.labels[RTSeeTextLabelKeys.SUBSCRIBE_LABEL] || 'Upgrade Plan' }}\n </button>\n </div>\n }\n\n <hr class=\"rts-divider\" />\n <div class=\"rts-chat-info-danger-zone\">\n <button class=\"rts-btn rts-btn-danger rts-btn-block\"\n (click)=\"logOut()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.LOGOUT_BUTTON] || 'Logout' }}\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer?.isAnonymous) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-anon-box\">\n <div class=\"rts-profile-anon-icon\">\n <span class=\"material-icons-outlined\">shield</span>\n </div>\n <div class=\"rts-profile-anon-text\">\n @if (!messenger.currentLanguage || messenger.currentLanguage === 'eng') {\n <p>This is a temporary anonymous identity. Sign up or sign in to save your history permanently.</p>\n } @else if (messenger.currentLanguage === 'ua') {\n <p>\u0426\u0435 \u0442\u0438\u043C\u0447\u0430\u0441\u043E\u0432\u0430 \u0430\u043D\u043E\u043D\u0456\u043C\u043D\u0430 \u043E\u0441\u043E\u0431\u0438\u0441\u0442\u0456\u0441\u0442\u044C. \u0417\u0430\u0440\u0435\u0454\u0441\u0442\u0440\u0443\u0439\u0442\u0435\u0441\u044F, \u0430\u0431\u0438 \u0437\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0456\u0441\u0442\u043E\u0440\u0456\u044E \u0441\u043F\u0456\u043B\u043A\u0443\u0432\u0430\u043D\u043D\u044F.</p>\n }\n </div>\n </div>\n\n <div class=\"rts-profile-auth-actions\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_UP)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_UP_BUTTON] || 'Sign Up' }}\n </button>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_IN)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_IN_BUTTON] || 'Sign In' }}\n </button>\n </div>\n }\n</div>\n" }]
375
+ args: [{ selector: 'rtsee-profile', imports: [RouterLink, Select, FormsModule], standalone: true, template: "<div class=\"rts-chat-info rts-messenger-profile\">\n @if (displaySubtitle) {\n <div class=\"rts-chat-info-meta\">\n <p class=\"rts-chat-info-type\">{{ displaySubtitle }}</p>\n </div>\n }\n\n @if (isOtherUser) {\n <div class=\"rts-chat-info-call-actions\">\n <button class=\"rts-call-btn\"\n (click)=\"call()\"\n type=\"button\"\n title=\"Audio Call\"\n >\n <span class=\"material-icons-outlined\">call</span>\n <span>Audio</span>\n </button>\n <button class=\"rts-call-btn rts-call-btn-video\"\n (click)=\"videoCall()\"\n type=\"button\"\n title=\"Video Call\"\n >\n <span class=\"material-icons-outlined\">videocam</span>\n <span>Video</span>\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer && !messenger.myPeer.isAnonymous) {\n <div class=\"rts-chat-info-actions\">\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManageProfile()\"\n >\n <span class=\"material-icons-outlined\">edit</span>\n {{ messenger.labels[RTSeeTextLabelKeys.MESSENGER_EDIT_PROFILE_BTN_LABEL] || 'Edit Profile' }}\n </button>\n </div>\n\n <hr class=\"rts-divider\" />\n }\n\n <!-- language selection is available to every user (anonymous included) -->\n @if (!isOtherUser && messenger.myPeer) {\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-field-row\">\n <label class=\"rts-profile-field-label\" for=\"rts-profile-language-select\">\n {{ messenger.labels[RTSeeTextLabelKeys.LANGUAGE_LABEL] || 'Language' }}\n </label>\n <p-select [options]=\"messenger.languageOptions\"\n [(ngModel)]=\"messenger.currentLanguage\"\n (ngModelChange)=\"setLanguage()\"\n optionLabel=\"key\"\n optionValue=\"key\"\n inputId=\"rts-profile-language-select\"\n [placeholder]=\"messenger.labels[RTSeeTextLabelKeys.MESSANGER_LANGUAGE_PLACEHOLDER]\"\n class=\"rts-profile-language-select\"\n />\n </div>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer && !messenger.myPeer.isAnonymous) {\n @if (messenger.myPeer.plan) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <div class=\"rts-profile-plan-badge\">\n <span class=\"material-icons-outlined\">verified</span>\n <span>{{ messenger.myPeer.plan.productName }}</span>\n </div>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n (click)=\"messenger.openManagePlanPage()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.MANAGE_PLAN] || 'Manage Plan' }}\n </button>\n </div>\n } @else {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-section\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n (click)=\"messenger.openPaymentPage()\"\n >\n <span class=\"material-icons-outlined\">stars</span>\n {{ messenger.labels[RTSeeTextLabelKeys.SUBSCRIBE_LABEL] || 'Upgrade Plan' }}\n </button>\n </div>\n }\n\n <hr class=\"rts-divider\" />\n <div class=\"rts-chat-info-danger-zone\">\n <button class=\"rts-btn rts-btn-danger rts-btn-block\"\n (click)=\"logOut()\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.LOGOUT_BUTTON] || 'Logout' }}\n </button>\n </div>\n }\n\n @if (!isOtherUser && messenger.myPeer?.isAnonymous) {\n <hr class=\"rts-divider\" />\n <div class=\"rts-profile-anon-box\">\n <div class=\"rts-profile-anon-icon\">\n <span class=\"material-icons-outlined\">shield</span>\n </div>\n <div class=\"rts-profile-anon-text\">\n @if (!messenger.currentLanguage || messenger.currentLanguage === 'eng') {\n <p>This is a temporary anonymous identity. Sign up or sign in to save your history permanently.</p>\n } @else if (messenger.currentLanguage === 'ua') {\n <p>\u0426\u0435 \u0442\u0438\u043C\u0447\u0430\u0441\u043E\u0432\u0430 \u0430\u043D\u043E\u043D\u0456\u043C\u043D\u0430 \u043E\u0441\u043E\u0431\u0438\u0441\u0442\u0456\u0441\u0442\u044C. \u0417\u0430\u0440\u0435\u0454\u0441\u0442\u0440\u0443\u0439\u0442\u0435\u0441\u044F, \u0430\u0431\u0438 \u0437\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0456\u0441\u0442\u043E\u0440\u0456\u044E \u0441\u043F\u0456\u043B\u043A\u0443\u0432\u0430\u043D\u043D\u044F.</p>\n }\n </div>\n </div>\n\n <div class=\"rts-profile-auth-actions\">\n <button class=\"rts-btn rts-btn-primary rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_UP)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_UP_BUTTON] || 'Sign Up' }}\n </button>\n <button class=\"rts-btn rts-btn-ghost rts-btn-block\"\n [routerLink]=\"authConfiguration.getRoutePathSplit(AuthEndpointsKeys.SIGN_IN)\"\n >\n {{ messenger.labels[RTSeeTextLabelKeys.SIGN_IN_BUTTON] || 'Sign In' }}\n </button>\n </div>\n }\n</div>\n" }]
375
376
  }], ctorParameters: () => [{ type: RTSeeAuthService }, { type: i2.Router }], propDecorators: { messenger: [{
376
377
  type: Input
377
378
  }], peer: [{
@@ -1960,11 +1961,11 @@ class RatingComponent {
1960
1961
  this.init();
1961
1962
  }
1962
1963
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RatingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1963
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RatingComponent, isStandalone: true, selector: "rtsee-rating", inputs: { config: "config", rate: "rate" }, ngImport: i0, template: "<div class=\"rtsee-rating\">\n @for (star of stars; track $index) {\n <span class=\"rtsee-rating-star\"\n [ngClass]=\"{\n 'rtsee-rating-star-full': star === 'full',\n 'rtsee-rating-star-half': star === 'half',\n 'rtsee-rating-star-empty': star === 'empty',\n 'rtsee-rating-star-clickable': mutable\n }\"\n (click)=\"onStarClicked($index)\"\n >\u2605</span>\n }\n @if (rateValue) {\n <span class=\"rtsee-rating-number\">{{ rateValue | number:'1.1-1' }}</span>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: DecimalPipe, name: "number" }] }); }
1964
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RatingComponent, isStandalone: true, selector: "rtsee-rating", inputs: { config: "config", rate: "rate" }, ngImport: i0, template: "<div\n class=\"rtsee-rating\"\n data-ugc=\"true\"\n rel=\"ugc\"\n itemprop=\"reviewRating\"\n itemscope\n itemtype=\"https://schema.org/Rating\"\n>\n <meta itemprop=\"bestRating\" content=\"5\" />\n <meta itemprop=\"worstRating\" content=\"1\" />\n @for (star of stars; track $index) {\n <span class=\"rtsee-rating-star\"\n [ngClass]=\"{\n 'rtsee-rating-star-full': star === 'full',\n 'rtsee-rating-star-half': star === 'half',\n 'rtsee-rating-star-empty': star === 'empty',\n 'rtsee-rating-star-clickable': mutable\n }\"\n (click)=\"onStarClicked($index)\"\n >\u2605</span>\n }\n @if (rateValue) {\n <span class=\"rtsee-rating-number\" itemprop=\"ratingValue\">{{ rateValue | number:'1.1-1' }}</span>\n }\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: DecimalPipe, name: "number" }] }); }
1964
1965
  }
1965
1966
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RatingComponent, decorators: [{
1966
1967
  type: Component,
1967
- args: [{ selector: 'rtsee-rating', imports: [NgClass, DecimalPipe], template: "<div class=\"rtsee-rating\">\n @for (star of stars; track $index) {\n <span class=\"rtsee-rating-star\"\n [ngClass]=\"{\n 'rtsee-rating-star-full': star === 'full',\n 'rtsee-rating-star-half': star === 'half',\n 'rtsee-rating-star-empty': star === 'empty',\n 'rtsee-rating-star-clickable': mutable\n }\"\n (click)=\"onStarClicked($index)\"\n >\u2605</span>\n }\n @if (rateValue) {\n <span class=\"rtsee-rating-number\">{{ rateValue | number:'1.1-1' }}</span>\n }\n</div>\n" }]
1968
+ args: [{ selector: 'rtsee-rating', imports: [NgClass, DecimalPipe], template: "<div\n class=\"rtsee-rating\"\n data-ugc=\"true\"\n rel=\"ugc\"\n itemprop=\"reviewRating\"\n itemscope\n itemtype=\"https://schema.org/Rating\"\n>\n <meta itemprop=\"bestRating\" content=\"5\" />\n <meta itemprop=\"worstRating\" content=\"1\" />\n @for (star of stars; track $index) {\n <span class=\"rtsee-rating-star\"\n [ngClass]=\"{\n 'rtsee-rating-star-full': star === 'full',\n 'rtsee-rating-star-half': star === 'half',\n 'rtsee-rating-star-empty': star === 'empty',\n 'rtsee-rating-star-clickable': mutable\n }\"\n (click)=\"onStarClicked($index)\"\n >\u2605</span>\n }\n @if (rateValue) {\n <span class=\"rtsee-rating-number\" itemprop=\"ratingValue\">{{ rateValue | number:'1.1-1' }}</span>\n }\n</div>\n" }]
1968
1969
  }], propDecorators: { config: [{
1969
1970
  type: Input
1970
1971
  }], rate: [{
@@ -2121,7 +2122,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
2121
2122
  }] } });
2122
2123
 
2123
2124
  class MediaWidgetComponent {
2124
- constructor() {
2125
+ constructor(cdr) {
2126
+ this.cdr = cdr;
2125
2127
  this.showCost = true;
2126
2128
  this.visibleImages = [];
2127
2129
  this.hiddenCount = 0;
@@ -2129,6 +2131,7 @@ class MediaWidgetComponent {
2129
2131
  this.downloadingUrl = null;
2130
2132
  this.loadedImageUrls = new Set();
2131
2133
  this.erroredImageUrls = new Set();
2134
+ this.preloadingImageUrls = new Set();
2132
2135
  this.MAX_GRID = 4;
2133
2136
  }
2134
2137
  ngOnInit() {
@@ -2141,26 +2144,102 @@ class MediaWidgetComponent {
2141
2144
  this.updateVisibleImages();
2142
2145
  }
2143
2146
  }
2144
- /** Downsized version for rendering, the original otherwise */
2147
+ get isAiGenerated() {
2148
+ return Boolean(this.message?.from?.isAgent ||
2149
+ this.message?.cost ||
2150
+ this.message?.data?.flowId ||
2151
+ this.message?.data?.requestId ||
2152
+ this.message?.data?.awaitingGeneration);
2153
+ }
2154
+ get isAwaitingGeneration() {
2155
+ return !this.hasImages && !this.hasVideo;
2156
+ }
2157
+ get awaitingPlaceholderText() {
2158
+ return this.message?.data?.placeholderText || 'Generating image...';
2159
+ }
2160
+ /** Downsized version for rendering/export, the original otherwise */
2145
2161
  previewUrl(img) {
2146
2162
  return img.smallVersionUrl || img.url;
2147
2163
  }
2164
+ /**
2165
+ * URL bound to the <img> element:
2166
+ * - While the full-size image is already loaded and `smallVersionUrl` is being preloaded,
2167
+ * keep displaying the loaded full-size `img.url` so there is no blank flicker,
2168
+ * then swap to `img.smallVersionUrl` as soon as it finishes preloading.
2169
+ */
2170
+ displayUrl(img) {
2171
+ if (img.smallVersionUrl) {
2172
+ if (this.loadedImageUrls.has(img.smallVersionUrl)) {
2173
+ return img.smallVersionUrl;
2174
+ }
2175
+ if (this.loadedImageUrls.has(img.url)) {
2176
+ this.preloadSmallVersion(img.smallVersionUrl);
2177
+ return img.url;
2178
+ }
2179
+ return img.smallVersionUrl;
2180
+ }
2181
+ return img.url;
2182
+ }
2183
+ /**
2184
+ * True while the full-size image is being compressed/uploaded by ImageCropper/Uploader
2185
+ * or while the resulting `smallVersionUrl` is preloading to replace the full-size image.
2186
+ */
2187
+ isProcessingImage(img) {
2188
+ const optimizingMessage = Boolean(this.message?.data?.isOptimizingImages ||
2189
+ this.messenger?.isOptimizingMessageImages?.(this.message?.id));
2190
+ if (optimizingMessage && !img.smallVersionUrl) {
2191
+ return true;
2192
+ }
2193
+ if (img.smallVersionUrl &&
2194
+ this.loadedImageUrls.has(img.url) &&
2195
+ !this.loadedImageUrls.has(img.smallVersionUrl) &&
2196
+ !this.erroredImageUrls.has(img.smallVersionUrl)) {
2197
+ return true;
2198
+ }
2199
+ return false;
2200
+ }
2148
2201
  isImageLoaded(img) {
2149
- const url = this.previewUrl(img);
2202
+ const url = this.displayUrl(img);
2150
2203
  return this.loadedImageUrls.has(url);
2151
2204
  }
2152
2205
  isImageError(img) {
2153
- const url = this.previewUrl(img);
2206
+ const url = this.displayUrl(img);
2154
2207
  return this.erroredImageUrls.has(url);
2155
2208
  }
2156
2209
  onImageLoad(img) {
2157
- const url = this.previewUrl(img);
2210
+ const url = this.displayUrl(img);
2158
2211
  this.loadedImageUrls.add(url);
2212
+ if (img.smallVersionUrl && url === img.url && !this.loadedImageUrls.has(img.smallVersionUrl)) {
2213
+ this.preloadSmallVersion(img.smallVersionUrl);
2214
+ }
2159
2215
  }
2160
2216
  onImageError(img) {
2161
- const url = this.previewUrl(img);
2217
+ const url = this.displayUrl(img);
2162
2218
  this.erroredImageUrls.add(url);
2163
2219
  }
2220
+ preloadSmallVersion(smallUrl) {
2221
+ if (typeof window === 'undefined' ||
2222
+ !smallUrl ||
2223
+ this.loadedImageUrls.has(smallUrl) ||
2224
+ this.preloadingImageUrls.has(smallUrl)) {
2225
+ return;
2226
+ }
2227
+ this.preloadingImageUrls.add(smallUrl);
2228
+ const preloader = new Image();
2229
+ preloader.onload = () => {
2230
+ this.preloadingImageUrls.delete(smallUrl);
2231
+ this.loadedImageUrls.add(smallUrl);
2232
+ this.cdr.markForCheck();
2233
+ this.cdr.detectChanges();
2234
+ };
2235
+ preloader.onerror = () => {
2236
+ this.preloadingImageUrls.delete(smallUrl);
2237
+ this.erroredImageUrls.add(smallUrl);
2238
+ this.cdr.markForCheck();
2239
+ this.cdr.detectChanges();
2240
+ };
2241
+ preloader.src = smallUrl;
2242
+ }
2164
2243
  async downloadOriginal(event, img) {
2165
2244
  event.stopPropagation();
2166
2245
  event.preventDefault();
@@ -2214,11 +2293,12 @@ class MediaWidgetComponent {
2214
2293
  sendToIllustrator(event, img) {
2215
2294
  event.stopPropagation();
2216
2295
  event.preventDefault();
2217
- if (typeof window !== 'undefined' && img?.url) {
2218
- // Retain AWS presigned URL so Illustrator can render the image preview!
2296
+ const targetUrl = this.previewUrl(img);
2297
+ if (typeof window !== 'undefined' && targetUrl) {
2298
+ // Use the web-optimized URL (smallVersionUrl when available) for Illustrator & Story Editor
2219
2299
  window.dispatchEvent(new CustomEvent(RTSeeIllustratorEvents.SET_IMAGE_FOR_REVIEW, {
2220
2300
  detail: {
2221
- imageUrl: img.url,
2301
+ imageUrl: targetUrl,
2222
2302
  prompt: img.name || (typeof this.message.text === 'string' ? this.message.text : '') || '',
2223
2303
  source: 'messenger',
2224
2304
  },
@@ -2228,11 +2308,12 @@ class MediaWidgetComponent {
2228
2308
  addToContext(event, img) {
2229
2309
  event.stopPropagation();
2230
2310
  event.preventDefault();
2231
- if (typeof window !== 'undefined' && img?.url) {
2311
+ const targetUrl = this.previewUrl(img);
2312
+ if (typeof window !== 'undefined' && targetUrl) {
2232
2313
  window.dispatchEvent(new CustomEvent(RTSeeIllustratorEvents.ADD_IMAGE_TO_CONTEXT, {
2233
2314
  detail: {
2234
2315
  id: 'chat-img-' + Math.random().toString(36).substring(2, 9),
2235
- imageUrl: img.url,
2316
+ imageUrl: targetUrl,
2236
2317
  title: img.name || 'Chat image',
2237
2318
  description: (typeof this.message.text === 'string' ? this.message.text : '') || '',
2238
2319
  visualDescription: '',
@@ -2243,7 +2324,11 @@ class MediaWidgetComponent {
2243
2324
  }
2244
2325
  }
2245
2326
  updateVisibleImages() {
2246
- const all = this.message.images || [];
2327
+ const rawImages = this.message?.images || [];
2328
+ // AI-generated messages only show a single image; user messages can show multiple images
2329
+ const all = this.isAiGenerated && rawImages.length > 1
2330
+ ? [rawImages[rawImages.length - 1]]
2331
+ : rawImages;
2247
2332
  if (this.showAll || all.length <= this.MAX_GRID) {
2248
2333
  this.visibleImages = all;
2249
2334
  this.hiddenCount = 0;
@@ -2252,27 +2337,33 @@ class MediaWidgetComponent {
2252
2337
  this.visibleImages = all.slice(0, this.MAX_GRID);
2253
2338
  this.hiddenCount = all.length - this.MAX_GRID;
2254
2339
  }
2340
+ for (const img of this.visibleImages) {
2341
+ if (img.smallVersionUrl && this.loadedImageUrls.has(img.url) && !this.loadedImageUrls.has(img.smallVersionUrl)) {
2342
+ this.preloadSmallVersion(img.smallVersionUrl);
2343
+ }
2344
+ }
2255
2345
  }
2256
2346
  notifyRequestedGeneration() {
2257
2347
  const reqId = this.message?.data?.requestId;
2258
- if (reqId && this.visibleImages.length > 0 && typeof window !== 'undefined') {
2348
+ const optimizedUrl = this.visibleImages[0]?.smallVersionUrl;
2349
+ if (reqId && optimizedUrl && typeof window !== 'undefined') {
2259
2350
  window.dispatchEvent(new CustomEvent(RTSeeIllustratorEvents.REQUESTED_GENERATION_SUCCEED, {
2260
2351
  detail: {
2261
2352
  requestId: reqId,
2262
- imageUrl: this.visibleImages[0].url,
2353
+ imageUrl: optimizedUrl,
2263
2354
  messageId: this.message.id,
2264
2355
  chatId: this.chat?.id,
2265
2356
  },
2266
2357
  }));
2267
2358
  }
2268
2359
  }
2269
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MediaWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2270
- 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\" [class.rts-image-loaded]=\"isImageLoaded(img)\">\n <!-- Media Placeholder (preview before image loads) -->\n @if (!isImageLoaded(img)) {\n <div class=\"rts-media-placeholder\" [class.rts-placeholder-error]=\"isImageError(img)\">\n <div class=\"rts-media-placeholder-shimmer\"></div>\n <div class=\"rts-media-placeholder-content\">\n @if (isImageError(img)) {\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">broken_image</span>\n <span class=\"rts-media-placeholder-text\">Failed to load</span>\n } @else {\n <div class=\"rts-media-placeholder-icon-wrap\">\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">image</span>\n </div>\n <span class=\"rts-media-placeholder-text\">{{ img.name || 'Loading media preview...' }}</span>\n }\n </div>\n </div>\n }\n\n <img\n [src]=\"previewUrl(img)\"\n [alt]=\"img.name\"\n class=\"rts-media-image\"\n [class.rts-media-image-loaded]=\"isImageLoaded(img)\"\n (load)=\"onImageLoad(img)\"\n (error)=\"onImageError(img)\"\n loading=\"lazy\"\n />\n\n @if (isImageLoaded(img)) {\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 }\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;aspect-ratio:16/9;background:#000}.rts-video-player{width:100%;height: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-1 .rts-media-image-wrapper{min-height:220px;aspect-ratio:16/10}.rts-media-grid-1 .rts-media-image-wrapper.rts-image-loaded{min-height:0;aspect-ratio:auto}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-2 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-grid-4 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e);display:flex;align-items:center;justify-content:center}.rts-media-placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#1a1a2af5,#12121cfa);border:1px solid rgba(255,255,255,.06);border-radius:6px;z-index:2;overflow:hidden;pointer-events:none}.rts-media-placeholder .rts-media-placeholder-shimmer{position:absolute;top:0;left:-150%;width:150%;height:100%;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.03) 30%,rgba(168,85,247,.12) 50%,rgba(255,255,255,.03) 70%,transparent 100%);animation:rts-media-shimmer 2.2s infinite ease-in-out}.rts-media-placeholder .rts-media-placeholder-content{position:relative;z-index:3;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:12px;text-align:center;max-width:90%}.rts-media-placeholder .rts-media-placeholder-icon-wrap{width:42px;height:42px;border-radius:50%;background:#6c5ce726;border:1px solid rgba(168,85,247,.35);display:flex;align-items:center;justify-content:center;animation:rts-media-pulse 2s infinite ease-in-out;box-shadow:0 4px 16px #6c5ce733}.rts-media-placeholder .rts-media-placeholder-icon{font-size:22px;color:var(--rts-accent-light, #c084fc)}.rts-media-placeholder .rts-media-placeholder-text{font-size:11px;font-weight:500;color:var(--rts-text-muted, #94a3b8);max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon-wrap{background:#ef444426;border-color:#ef444459;animation:none;box-shadow:none}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon{color:#ef4444}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-text{color:#f87171}@keyframes rts-media-shimmer{0%{transform:translate(0)}to{transform:translate(200%)}}@keyframes rts-media-pulse{0%,to{transform:scale(1);opacity:.85}50%{transform:scale(1.08);opacity:1;box-shadow:0 0 16px #a855f766}}.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:100%;max-height:280px;object-fit:cover;display:block;opacity:0;transition:opacity .25s ease-out,transform .2s ease;cursor:pointer}.rts-media-image.rts-media-image-loaded{opacity:1}.rts-media-image:hover{transform:scale(1.02)}.rts-media-grid-1 .rts-media-image{height:auto;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"] }] }); }
2360
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MediaWidgetComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
2361
+ 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 <!-- Awaiting AI image generation preloader -->\n @if (isAwaitingGeneration) {\n <div class=\"rts-media-images rts-media-grid-1\">\n <div class=\"rts-media-image-wrapper\">\n <div class=\"rts-media-placeholder\">\n <div class=\"rts-media-placeholder-shimmer\"></div>\n <div class=\"rts-media-placeholder-content\">\n <div class=\"rts-media-placeholder-icon-wrap\">\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">image</span>\n </div>\n <span class=\"rts-media-placeholder-text\">{{ awaitingPlaceholderText }}</span>\n </div>\n </div>\n </div>\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 || $index) {\n <div class=\"rts-media-image-wrapper\" [class.rts-image-loaded]=\"isImageLoaded(img)\">\n <!-- Media Placeholder (preview before image loads) -->\n @if (!isImageLoaded(img)) {\n <div class=\"rts-media-placeholder\" [class.rts-placeholder-error]=\"isImageError(img)\">\n <div class=\"rts-media-placeholder-shimmer\"></div>\n <div class=\"rts-media-placeholder-content\">\n @if (isImageError(img)) {\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">broken_image</span>\n <span class=\"rts-media-placeholder-text\">Failed to load</span>\n } @else {\n <div class=\"rts-media-placeholder-icon-wrap\">\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">image</span>\n </div>\n <span class=\"rts-media-placeholder-text\">{{ img.name || 'Loading media preview...' }}</span>\n }\n </div>\n </div>\n }\n\n <img\n [src]=\"displayUrl(img)\"\n [alt]=\"img.name\"\n class=\"rts-media-image\"\n [class.rts-media-image-loaded]=\"isImageLoaded(img)\"\n [class.rts-media-image-processing]=\"isImageLoaded(img) && isProcessingImage(img)\"\n (load)=\"onImageLoad(img)\"\n (error)=\"onImageError(img)\"\n loading=\"lazy\"\n />\n\n @if (isImageLoaded(img) && isProcessingImage(img)) {\n <div class=\"rts-media-processing-overlay\">\n <div class=\"rts-media-processing-badge\">\n <span class=\"rts-media-processing-spinner\"></span>\n <span class=\"rts-media-processing-text\">processing...</span>\n </div>\n </div>\n }\n\n @if (isImageLoaded(img) && !isProcessingImage(img)) {\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 }\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;aspect-ratio:16/9;background:#000}.rts-video-player{width:100%;height: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-1 .rts-media-image-wrapper{min-height:220px;aspect-ratio:16/10}.rts-media-grid-1 .rts-media-image-wrapper.rts-image-loaded{min-height:0;aspect-ratio:auto}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-2 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-grid-4 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e);display:flex;align-items:center;justify-content:center}.rts-media-placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#1a1a2af5,#12121cfa);border:1px solid rgba(255,255,255,.06);border-radius:6px;z-index:2;overflow:hidden;pointer-events:none}.rts-media-placeholder .rts-media-placeholder-shimmer{position:absolute;top:0;left:-150%;width:150%;height:100%;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.03) 30%,rgba(168,85,247,.12) 50%,rgba(255,255,255,.03) 70%,transparent 100%);animation:rts-media-shimmer 2.2s infinite ease-in-out}.rts-media-placeholder .rts-media-placeholder-content{position:relative;z-index:3;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:12px;text-align:center;max-width:90%}.rts-media-placeholder .rts-media-placeholder-icon-wrap{width:42px;height:42px;border-radius:50%;background:#6c5ce726;border:1px solid rgba(168,85,247,.35);display:flex;align-items:center;justify-content:center;animation:rts-media-pulse 2s infinite ease-in-out;box-shadow:0 4px 16px #6c5ce733}.rts-media-placeholder .rts-media-placeholder-icon{font-size:22px;color:var(--rts-accent-light, #c084fc)}.rts-media-placeholder .rts-media-placeholder-text{font-size:11px;font-weight:500;color:var(--rts-text-muted, #94a3b8);max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon-wrap{background:#ef444426;border-color:#ef444459;animation:none;box-shadow:none}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon{color:#ef4444}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-text{color:#f87171}@keyframes rts-media-shimmer{0%{transform:translate(0)}to{transform:translate(200%)}}@keyframes rts-media-pulse{0%,to{transform:scale(1);opacity:.85}50%{transform:scale(1.08);opacity:1;box-shadow:0 0 16px #a855f766}}.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:100%;max-height:280px;object-fit:cover;display:block;opacity:0;transition:opacity .25s ease-out,transform .2s ease;cursor:pointer}.rts-media-image.rts-media-image-loaded{opacity:1}.rts-media-image.rts-media-image-processing{filter:brightness(.75)}.rts-media-image:hover{transform:scale(1.02)}.rts-media-processing-overlay{position:absolute;inset:0;z-index:4;display:flex;align-items:center;justify-content:center;background:#0f0f1959;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);pointer-events:none}.rts-media-processing-badge{display:inline-flex;align-items:center;gap:8px;padding:6px 14px;border-radius:999px;background:#12121ce0;border:1px solid rgba(168,85,247,.45);box-shadow:0 4px 16px #00000073}.rts-media-processing-spinner{width:14px;height:14px;border-radius:50%;border:2px solid rgba(192,132,252,.28);border-top-color:#c084fc;animation:rts-media-spin .8s linear infinite}.rts-media-processing-text{font-size:12px;font-weight:600;color:#f3e8ff;letter-spacing:.02em}@keyframes rts-media-spin{to{transform:rotate(360deg)}}.rts-media-grid-1 .rts-media-image{height:auto;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"] }] }); }
2271
2362
  }
2272
2363
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MediaWidgetComponent, decorators: [{
2273
2364
  type: Component,
2274
- 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\" [class.rts-image-loaded]=\"isImageLoaded(img)\">\n <!-- Media Placeholder (preview before image loads) -->\n @if (!isImageLoaded(img)) {\n <div class=\"rts-media-placeholder\" [class.rts-placeholder-error]=\"isImageError(img)\">\n <div class=\"rts-media-placeholder-shimmer\"></div>\n <div class=\"rts-media-placeholder-content\">\n @if (isImageError(img)) {\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">broken_image</span>\n <span class=\"rts-media-placeholder-text\">Failed to load</span>\n } @else {\n <div class=\"rts-media-placeholder-icon-wrap\">\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">image</span>\n </div>\n <span class=\"rts-media-placeholder-text\">{{ img.name || 'Loading media preview...' }}</span>\n }\n </div>\n </div>\n }\n\n <img\n [src]=\"previewUrl(img)\"\n [alt]=\"img.name\"\n class=\"rts-media-image\"\n [class.rts-media-image-loaded]=\"isImageLoaded(img)\"\n (load)=\"onImageLoad(img)\"\n (error)=\"onImageError(img)\"\n loading=\"lazy\"\n />\n\n @if (isImageLoaded(img)) {\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 }\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;aspect-ratio:16/9;background:#000}.rts-video-player{width:100%;height: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-1 .rts-media-image-wrapper{min-height:220px;aspect-ratio:16/10}.rts-media-grid-1 .rts-media-image-wrapper.rts-image-loaded{min-height:0;aspect-ratio:auto}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-2 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-grid-4 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e);display:flex;align-items:center;justify-content:center}.rts-media-placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#1a1a2af5,#12121cfa);border:1px solid rgba(255,255,255,.06);border-radius:6px;z-index:2;overflow:hidden;pointer-events:none}.rts-media-placeholder .rts-media-placeholder-shimmer{position:absolute;top:0;left:-150%;width:150%;height:100%;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.03) 30%,rgba(168,85,247,.12) 50%,rgba(255,255,255,.03) 70%,transparent 100%);animation:rts-media-shimmer 2.2s infinite ease-in-out}.rts-media-placeholder .rts-media-placeholder-content{position:relative;z-index:3;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:12px;text-align:center;max-width:90%}.rts-media-placeholder .rts-media-placeholder-icon-wrap{width:42px;height:42px;border-radius:50%;background:#6c5ce726;border:1px solid rgba(168,85,247,.35);display:flex;align-items:center;justify-content:center;animation:rts-media-pulse 2s infinite ease-in-out;box-shadow:0 4px 16px #6c5ce733}.rts-media-placeholder .rts-media-placeholder-icon{font-size:22px;color:var(--rts-accent-light, #c084fc)}.rts-media-placeholder .rts-media-placeholder-text{font-size:11px;font-weight:500;color:var(--rts-text-muted, #94a3b8);max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon-wrap{background:#ef444426;border-color:#ef444459;animation:none;box-shadow:none}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon{color:#ef4444}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-text{color:#f87171}@keyframes rts-media-shimmer{0%{transform:translate(0)}to{transform:translate(200%)}}@keyframes rts-media-pulse{0%,to{transform:scale(1);opacity:.85}50%{transform:scale(1.08);opacity:1;box-shadow:0 0 16px #a855f766}}.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:100%;max-height:280px;object-fit:cover;display:block;opacity:0;transition:opacity .25s ease-out,transform .2s ease;cursor:pointer}.rts-media-image.rts-media-image-loaded{opacity:1}.rts-media-image:hover{transform:scale(1.02)}.rts-media-grid-1 .rts-media-image{height:auto;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"] }]
2275
- }], propDecorators: { message: [{
2365
+ 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 <!-- Awaiting AI image generation preloader -->\n @if (isAwaitingGeneration) {\n <div class=\"rts-media-images rts-media-grid-1\">\n <div class=\"rts-media-image-wrapper\">\n <div class=\"rts-media-placeholder\">\n <div class=\"rts-media-placeholder-shimmer\"></div>\n <div class=\"rts-media-placeholder-content\">\n <div class=\"rts-media-placeholder-icon-wrap\">\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">image</span>\n </div>\n <span class=\"rts-media-placeholder-text\">{{ awaitingPlaceholderText }}</span>\n </div>\n </div>\n </div>\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 || $index) {\n <div class=\"rts-media-image-wrapper\" [class.rts-image-loaded]=\"isImageLoaded(img)\">\n <!-- Media Placeholder (preview before image loads) -->\n @if (!isImageLoaded(img)) {\n <div class=\"rts-media-placeholder\" [class.rts-placeholder-error]=\"isImageError(img)\">\n <div class=\"rts-media-placeholder-shimmer\"></div>\n <div class=\"rts-media-placeholder-content\">\n @if (isImageError(img)) {\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">broken_image</span>\n <span class=\"rts-media-placeholder-text\">Failed to load</span>\n } @else {\n <div class=\"rts-media-placeholder-icon-wrap\">\n <span class=\"material-icons-outlined rts-media-placeholder-icon\">image</span>\n </div>\n <span class=\"rts-media-placeholder-text\">{{ img.name || 'Loading media preview...' }}</span>\n }\n </div>\n </div>\n }\n\n <img\n [src]=\"displayUrl(img)\"\n [alt]=\"img.name\"\n class=\"rts-media-image\"\n [class.rts-media-image-loaded]=\"isImageLoaded(img)\"\n [class.rts-media-image-processing]=\"isImageLoaded(img) && isProcessingImage(img)\"\n (load)=\"onImageLoad(img)\"\n (error)=\"onImageError(img)\"\n loading=\"lazy\"\n />\n\n @if (isImageLoaded(img) && isProcessingImage(img)) {\n <div class=\"rts-media-processing-overlay\">\n <div class=\"rts-media-processing-badge\">\n <span class=\"rts-media-processing-spinner\"></span>\n <span class=\"rts-media-processing-text\">processing...</span>\n </div>\n </div>\n }\n\n @if (isImageLoaded(img) && !isProcessingImage(img)) {\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 }\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;aspect-ratio:16/9;background:#000}.rts-video-player{width:100%;height: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-1 .rts-media-image-wrapper{min-height:220px;aspect-ratio:16/10}.rts-media-grid-1 .rts-media-image-wrapper.rts-image-loaded{min-height:0;aspect-ratio:auto}.rts-media-grid-2{grid-template-columns:1fr 1fr}.rts-media-grid-2 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-grid-4{grid-template-columns:1fr 1fr;grid-template-rows:auto auto}.rts-media-grid-4 .rts-media-image-wrapper{aspect-ratio:1/1;min-height:120px}.rts-media-image-wrapper{position:relative;overflow:hidden;border-radius:6px;background:var(--rts-bg-secondary, #1e1e2e);display:flex;align-items:center;justify-content:center}.rts-media-placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#1a1a2af5,#12121cfa);border:1px solid rgba(255,255,255,.06);border-radius:6px;z-index:2;overflow:hidden;pointer-events:none}.rts-media-placeholder .rts-media-placeholder-shimmer{position:absolute;top:0;left:-150%;width:150%;height:100%;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.03) 30%,rgba(168,85,247,.12) 50%,rgba(255,255,255,.03) 70%,transparent 100%);animation:rts-media-shimmer 2.2s infinite ease-in-out}.rts-media-placeholder .rts-media-placeholder-content{position:relative;z-index:3;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:12px;text-align:center;max-width:90%}.rts-media-placeholder .rts-media-placeholder-icon-wrap{width:42px;height:42px;border-radius:50%;background:#6c5ce726;border:1px solid rgba(168,85,247,.35);display:flex;align-items:center;justify-content:center;animation:rts-media-pulse 2s infinite ease-in-out;box-shadow:0 4px 16px #6c5ce733}.rts-media-placeholder .rts-media-placeholder-icon{font-size:22px;color:var(--rts-accent-light, #c084fc)}.rts-media-placeholder .rts-media-placeholder-text{font-size:11px;font-weight:500;color:var(--rts-text-muted, #94a3b8);max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.2}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon-wrap{background:#ef444426;border-color:#ef444459;animation:none;box-shadow:none}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-icon{color:#ef4444}.rts-media-placeholder.rts-placeholder-error .rts-media-placeholder-text{color:#f87171}@keyframes rts-media-shimmer{0%{transform:translate(0)}to{transform:translate(200%)}}@keyframes rts-media-pulse{0%,to{transform:scale(1);opacity:.85}50%{transform:scale(1.08);opacity:1;box-shadow:0 0 16px #a855f766}}.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:100%;max-height:280px;object-fit:cover;display:block;opacity:0;transition:opacity .25s ease-out,transform .2s ease;cursor:pointer}.rts-media-image.rts-media-image-loaded{opacity:1}.rts-media-image.rts-media-image-processing{filter:brightness(.75)}.rts-media-image:hover{transform:scale(1.02)}.rts-media-processing-overlay{position:absolute;inset:0;z-index:4;display:flex;align-items:center;justify-content:center;background:#0f0f1959;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);pointer-events:none}.rts-media-processing-badge{display:inline-flex;align-items:center;gap:8px;padding:6px 14px;border-radius:999px;background:#12121ce0;border:1px solid rgba(168,85,247,.45);box-shadow:0 4px 16px #00000073}.rts-media-processing-spinner{width:14px;height:14px;border-radius:50%;border:2px solid rgba(192,132,252,.28);border-top-color:#c084fc;animation:rts-media-spin .8s linear infinite}.rts-media-processing-text{font-size:12px;font-weight:600;color:#f3e8ff;letter-spacing:.02em}@keyframes rts-media-spin{to{transform:rotate(360deg)}}.rts-media-grid-1 .rts-media-image{height:auto;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"] }]
2366
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { message: [{
2276
2367
  type: Input
2277
2368
  }], messenger: [{
2278
2369
  type: Input
@@ -2638,6 +2729,9 @@ class StorytellerStoryLinkStepComponent {
2638
2729
  return this.story.storyId ? `/story/${this.story.storyId}` : '';
2639
2730
  }
2640
2731
  get kicker() {
2732
+ if (this.story.repairedGrammar !== undefined || this.story.updatedSlidesCount !== undefined) {
2733
+ return 'Story repaired';
2734
+ }
2641
2735
  const language = this.story.language ? getStoryTranslatorLanguage(this.story.language) : undefined;
2642
2736
  return language ? `Translated to ${language.nativeName}` : 'Your story is ready';
2643
2737
  }
@@ -2657,7 +2751,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
2657
2751
  type: Input
2658
2752
  }] } });
2659
2753
 
2660
- const SEARCH_DEBOUNCE_MS = 250;
2754
+ const SEARCH_DEBOUNCE_MS$1 = 250;
2661
2755
  /**
2662
2756
  * Story Translator step 1: pick a story (autocomplete by name) and the target language.
2663
2757
  */
@@ -2671,10 +2765,13 @@ class StoryTranslatorFormStepComponent {
2671
2765
  storyQuery: new FormControl(''),
2672
2766
  storyId: new FormControl('', Validators.required),
2673
2767
  language: new FormControl('', Validators.required),
2768
+ fromScratch: new FormControl(false),
2674
2769
  agentId: new FormControl('', Validators.required),
2675
2770
  /* eslint-enable */
2676
2771
  });
2677
2772
  this.selectedStory = null;
2773
+ this.hasExistingTranslation = false;
2774
+ this.checkingTranslations = false;
2678
2775
  this.suggestions = [];
2679
2776
  this.suggestionsOpened = false;
2680
2777
  this.highlightedIndex = 0;
@@ -2682,6 +2779,7 @@ class StoryTranslatorFormStepComponent {
2682
2779
  this.searchError = null;
2683
2780
  this.searchTimer = null;
2684
2781
  this.searchRequestId = 0;
2782
+ this.translationCheckRequestId = 0;
2685
2783
  this.onChangedBound = () => this.onWorkflowChanged();
2686
2784
  }
2687
2785
  get agents() {
@@ -2721,10 +2819,11 @@ class StoryTranslatorFormStepComponent {
2721
2819
  // typing invalidates the previous selection
2722
2820
  if (this.selectedStory) {
2723
2821
  this.selectedStory = null;
2724
- this.form.get('storyId')?.setValue('');
2822
+ this.hasExistingTranslation = false;
2823
+ this.form.patchValue({ storyId: '', fromScratch: false }, { emitEvent: false });
2725
2824
  }
2726
2825
  this.suggestionsOpened = true;
2727
- this.scheduleSearch(SEARCH_DEBOUNCE_MS);
2826
+ this.scheduleSearch(SEARCH_DEBOUNCE_MS$1);
2728
2827
  }
2729
2828
  onStoryKeydown(event) {
2730
2829
  if (!this.suggestionsOpened || !this.suggestions.length) {
@@ -2758,20 +2857,29 @@ class StoryTranslatorFormStepComponent {
2758
2857
  return;
2759
2858
  }
2760
2859
  this.selectedStory = story;
2761
- this.form.patchValue({ storyId: story.id, storyQuery: story.name }, { emitEvent: false });
2860
+ this.form.patchValue({ storyId: story.id, storyQuery: story.name, fromScratch: false }, { emitEvent: false });
2762
2861
  this.form.get('storyId')?.markAsTouched();
2763
2862
  this.suggestionsOpened = false;
2863
+ void this.checkExistingTranslation();
2764
2864
  }
2765
2865
  clearStory() {
2766
2866
  if (this.readonly) {
2767
2867
  return;
2768
2868
  }
2769
2869
  this.selectedStory = null;
2770
- this.form.patchValue({ storyId: '', storyQuery: '' }, { emitEvent: false });
2870
+ this.hasExistingTranslation = false;
2871
+ this.form.patchValue({ storyId: '', storyQuery: '', fromScratch: false }, { emitEvent: false });
2771
2872
  this.suggestionsOpened = true;
2772
2873
  this.scheduleSearch(0);
2773
2874
  setTimeout(() => this.storyInput?.nativeElement.focus());
2774
2875
  }
2876
+ onLanguageChange() {
2877
+ if (this.readonly) {
2878
+ return;
2879
+ }
2880
+ this.form.get('fromScratch')?.setValue(false, { emitEvent: false });
2881
+ void this.checkExistingTranslation();
2882
+ }
2775
2883
  onDocumentMouseDown(event) {
2776
2884
  if (this.suggestionsOpened && !this.host.nativeElement.contains(event.target)) {
2777
2885
  this.suggestionsOpened = false;
@@ -2790,6 +2898,7 @@ class StoryTranslatorFormStepComponent {
2790
2898
  storyName: this.selectedStory.name,
2791
2899
  language: this.form.get('language')?.value,
2792
2900
  agentId: this.form.get('agentId')?.value,
2901
+ fromScratch: this.hasExistingTranslation ? !!this.form.get('fromScratch')?.value : false,
2793
2902
  });
2794
2903
  }
2795
2904
  cancel() {
@@ -2851,7 +2960,19 @@ class StoryTranslatorFormStepComponent {
2851
2960
  const output = this.step?.output;
2852
2961
  if (output?.storyId) {
2853
2962
  this.selectedStory = { id: output.storyId, name: output.storyName || output.storyId };
2854
- this.form.patchValue({ storyId: output.storyId, storyQuery: output.storyName || '', language: output.language, agentId: output.agentId }, { emitEvent: false });
2963
+ this.form.patchValue({
2964
+ storyId: output.storyId,
2965
+ storyQuery: output.storyName || '',
2966
+ language: output.language,
2967
+ fromScratch: !!output.fromScratch,
2968
+ agentId: output.agentId,
2969
+ }, { emitEvent: false });
2970
+ if (output.fromScratch) {
2971
+ this.hasExistingTranslation = true;
2972
+ }
2973
+ else {
2974
+ void this.checkExistingTranslation(true);
2975
+ }
2855
2976
  }
2856
2977
  if (this.readonly) {
2857
2978
  this.suggestionsOpened = false;
@@ -2861,12 +2982,315 @@ class StoryTranslatorFormStepComponent {
2861
2982
  this.form.enable({ emitEvent: false });
2862
2983
  }
2863
2984
  }
2985
+ async checkExistingTranslation(preserveCheckboxValue = false) {
2986
+ const storyId = this.selectedStory?.id;
2987
+ const rawLang = this.form.get('language')?.value || '';
2988
+ const langInfo = getStoryTranslatorLanguage(rawLang);
2989
+ const languageKey = langInfo?.code || rawLang;
2990
+ if (!storyId || !languageKey) {
2991
+ this.hasExistingTranslation = false;
2992
+ this.checkingTranslations = false;
2993
+ if (!preserveCheckboxValue) {
2994
+ this.form.get('fromScratch')?.setValue(false, { emitEvent: false });
2995
+ }
2996
+ this.cdr.markForCheck();
2997
+ return;
2998
+ }
2999
+ const requestId = ++this.translationCheckRequestId;
3000
+ this.checkingTranslations = true;
3001
+ const aliases = this.selectedStory?.aliases || [];
3002
+ const hasAliasForLang = aliases.some((a) => {
3003
+ const norm = getStoryTranslatorLanguage(a.languageKey)?.code || a.languageKey;
3004
+ return norm === languageKey;
3005
+ });
3006
+ try {
3007
+ const translations = this.messenger?.ozzy
3008
+ ? await this.messenger.ozzy.getStoryTranslations(storyId)
3009
+ : [];
3010
+ if (requestId !== this.translationCheckRequestId) {
3011
+ return;
3012
+ }
3013
+ const hasStoryTranslation = translations.some((t) => {
3014
+ const norm = getStoryTranslatorLanguage(t.languageKey)?.code || t.languageKey;
3015
+ return norm === languageKey;
3016
+ });
3017
+ this.hasExistingTranslation = hasAliasForLang || hasStoryTranslation;
3018
+ if (!this.hasExistingTranslation && !preserveCheckboxValue) {
3019
+ this.form.get('fromScratch')?.setValue(false, { emitEvent: false });
3020
+ }
3021
+ }
3022
+ catch {
3023
+ if (requestId !== this.translationCheckRequestId) {
3024
+ return;
3025
+ }
3026
+ this.hasExistingTranslation = hasAliasForLang;
3027
+ }
3028
+ finally {
3029
+ if (requestId === this.translationCheckRequestId) {
3030
+ this.checkingTranslations = false;
3031
+ this.cdr.detectChanges();
3032
+ }
3033
+ }
3034
+ }
2864
3035
  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 }); }
2865
- 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"] }] }); }
3036
+ 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 (change)=\"onLanguageChange()\"\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 @if (hasExistingTranslation) {\n <label class=\"rts-st-checkbox-row\" for=\"story-translator-from-scratch-{{ step.id }}\">\n <input type=\"checkbox\"\n class=\"rts-st-checkbox-input\"\n formControlName=\"fromScratch\"\n id=\"story-translator-from-scratch-{{ step.id }}\"\n />\n <span class=\"rts-st-checkbox-body\">\n <span class=\"rts-st-checkbox-title\">From scratch</span>\n <span class=\"rts-st-checkbox-desc\">Overwrite existing {{ selectedLanguage?.nativeName || '' }} translations for all story elements</span>\n </span>\n </label>\n }\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-checkbox-row{display:flex;align-items:flex-start;gap:10px;padding:9px 11px;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));cursor:pointer;-webkit-user-select:none;user-select:none;transition:border-color .15s ease,background .15s ease}.rts-st-checkbox-row:hover{border-color:#8b5cf673;background:#8b5cf60f}.rts-st-checkbox-input{margin-top:2px;width:15px;height:15px;flex-shrink:0;accent-color:var(--rts-st-accent, #8b5cf6);cursor:pointer}.rts-st-checkbox-body{display:flex;flex-direction:column;gap:2px;min-width:0}.rts-st-checkbox-title{font-size:13px;font-weight:600;color:var(--rts-text-primary)}.rts-st-checkbox-desc{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.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.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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"] }] }); }
2866
3037
  }
2867
3038
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryTranslatorFormStepComponent, decorators: [{
2868
3039
  type: Component,
2869
- 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"] }]
3040
+ 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 (change)=\"onLanguageChange()\"\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 @if (hasExistingTranslation) {\n <label class=\"rts-st-checkbox-row\" for=\"story-translator-from-scratch-{{ step.id }}\">\n <input type=\"checkbox\"\n class=\"rts-st-checkbox-input\"\n formControlName=\"fromScratch\"\n id=\"story-translator-from-scratch-{{ step.id }}\"\n />\n <span class=\"rts-st-checkbox-body\">\n <span class=\"rts-st-checkbox-title\">From scratch</span>\n <span class=\"rts-st-checkbox-desc\">Overwrite existing {{ selectedLanguage?.nativeName || '' }} translations for all story elements</span>\n </span>\n </label>\n }\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-checkbox-row{display:flex;align-items:flex-start;gap:10px;padding:9px 11px;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));cursor:pointer;-webkit-user-select:none;user-select:none;transition:border-color .15s ease,background .15s ease}.rts-st-checkbox-row:hover{border-color:#8b5cf673;background:#8b5cf60f}.rts-st-checkbox-input{margin-top:2px;width:15px;height:15px;flex-shrink:0;accent-color:var(--rts-st-accent, #8b5cf6);cursor:pointer}.rts-st-checkbox-body{display:flex;flex-direction:column;gap:2px;min-width:0}.rts-st-checkbox-title{font-size:13px;font-weight:600;color:var(--rts-text-primary)}.rts-st-checkbox-desc{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.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"] }]
3041
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }], propDecorators: { step: [{
3042
+ type: Input
3043
+ }], workflow: [{
3044
+ type: Input
3045
+ }], chat: [{
3046
+ type: Input
3047
+ }], messenger: [{
3048
+ type: Input
3049
+ }], storyInput: [{
3050
+ type: ViewChild,
3051
+ args: ['storyInput']
3052
+ }], onDocumentMouseDown: [{
3053
+ type: HostListener,
3054
+ args: ['document:mousedown', ['$event']]
3055
+ }] } });
3056
+
3057
+ const SEARCH_DEBOUNCE_MS = 250;
3058
+ /**
3059
+ * Story Repair step 1 (#RepairStory):
3060
+ * - Pick a story (autocomplete by name)
3061
+ * - Choose repair options via 4 checkboxes (Grammar & Flow, Characters, Artifacts, Locations), all checked by default
3062
+ * - Pick an agent
3063
+ */
3064
+ class StoryRepairFormStepComponent {
3065
+ constructor(cdr, host) {
3066
+ this.cdr = cdr;
3067
+ this.host = host;
3068
+ this.form = new FormGroup({
3069
+ /* eslint-disable */
3070
+ storyQuery: new FormControl(''),
3071
+ storyId: new FormControl('', Validators.required),
3072
+ grammarAndFlow: new FormControl(true),
3073
+ characters: new FormControl(true),
3074
+ artifacts: new FormControl(true),
3075
+ locations: new FormControl(true),
3076
+ agentId: new FormControl('', Validators.required),
3077
+ /* eslint-enable */
3078
+ });
3079
+ this.selectedStory = null;
3080
+ this.suggestions = [];
3081
+ this.suggestionsOpened = false;
3082
+ this.highlightedIndex = 0;
3083
+ this.searchInProgress = false;
3084
+ this.searchError = null;
3085
+ this.optionsTouched = false;
3086
+ this.searchTimer = null;
3087
+ this.searchRequestId = 0;
3088
+ this.onChangedBound = () => this.onWorkflowChanged();
3089
+ }
3090
+ get agents() {
3091
+ return (this.chat?.members || []).filter((m) => m.isAgent);
3092
+ }
3093
+ get readonly() {
3094
+ return !this.step?.isActive || !!this.workflow?.submitInProgress;
3095
+ }
3096
+ get canCancel() {
3097
+ return !!this.workflow?.isDraft && !this.workflow?.submitInProgress && !!this.messenger;
3098
+ }
3099
+ get hasAtLeastOneOption() {
3100
+ const opts = this.currentOptions;
3101
+ return opts.grammarAndFlow || opts.characters || opts.artifacts || opts.locations;
3102
+ }
3103
+ get currentOptions() {
3104
+ return {
3105
+ grammarAndFlow: !!this.form.get('grammarAndFlow')?.value,
3106
+ characters: !!this.form.get('characters')?.value,
3107
+ artifacts: !!this.form.get('artifacts')?.value,
3108
+ locations: !!this.form.get('locations')?.value,
3109
+ };
3110
+ }
3111
+ ngOnInit() {
3112
+ this.workflow?.on(OzzyWorkflowClientEvents.CHANGED, this.onChangedBound);
3113
+ const agents = this.agents;
3114
+ if (agents.length === 1) {
3115
+ this.form.get('agentId')?.setValue(agents[0].id);
3116
+ }
3117
+ this.syncWithStep();
3118
+ }
3119
+ ngOnDestroy() {
3120
+ this.workflow?.off(OzzyWorkflowClientEvents.CHANGED, this.onChangedBound);
3121
+ this.clearSearchTimer();
3122
+ }
3123
+ onStoryFocus() {
3124
+ if (this.readonly) {
3125
+ return;
3126
+ }
3127
+ this.suggestionsOpened = true;
3128
+ if (!this.suggestions.length) {
3129
+ this.scheduleSearch(0);
3130
+ }
3131
+ }
3132
+ onStoryInput() {
3133
+ if (this.selectedStory) {
3134
+ this.selectedStory = null;
3135
+ this.form.get('storyId')?.setValue('');
3136
+ }
3137
+ this.suggestionsOpened = true;
3138
+ this.scheduleSearch(SEARCH_DEBOUNCE_MS);
3139
+ }
3140
+ onStoryKeydown(event) {
3141
+ if (!this.suggestionsOpened || !this.suggestions.length) {
3142
+ if (event.key === 'ArrowDown') {
3143
+ this.suggestionsOpened = true;
3144
+ this.scheduleSearch(0);
3145
+ }
3146
+ return;
3147
+ }
3148
+ switch (event.key) {
3149
+ case 'ArrowDown':
3150
+ event.preventDefault();
3151
+ this.highlightedIndex = (this.highlightedIndex + 1) % this.suggestions.length;
3152
+ break;
3153
+ case 'ArrowUp':
3154
+ event.preventDefault();
3155
+ this.highlightedIndex =
3156
+ (this.highlightedIndex - 1 + this.suggestions.length) % this.suggestions.length;
3157
+ break;
3158
+ case 'Enter':
3159
+ event.preventDefault();
3160
+ this.selectStory(this.suggestions[this.highlightedIndex]);
3161
+ break;
3162
+ case 'Escape':
3163
+ event.preventDefault();
3164
+ this.suggestionsOpened = false;
3165
+ break;
3166
+ }
3167
+ }
3168
+ selectStory(story) {
3169
+ if (!story || this.readonly) {
3170
+ return;
3171
+ }
3172
+ this.selectedStory = story;
3173
+ this.form.patchValue({ storyId: story.id, storyQuery: story.name }, { emitEvent: false });
3174
+ this.form.get('storyId')?.markAsTouched();
3175
+ this.suggestionsOpened = false;
3176
+ }
3177
+ clearStory() {
3178
+ if (this.readonly) {
3179
+ return;
3180
+ }
3181
+ this.selectedStory = null;
3182
+ this.form.patchValue({ storyId: '', storyQuery: '' }, { emitEvent: false });
3183
+ this.suggestionsOpened = true;
3184
+ this.scheduleSearch(0);
3185
+ setTimeout(() => this.storyInput?.nativeElement.focus());
3186
+ }
3187
+ onOptionChanged() {
3188
+ this.optionsTouched = true;
3189
+ }
3190
+ onDocumentMouseDown(event) {
3191
+ if (this.suggestionsOpened && !this.host.nativeElement.contains(event.target)) {
3192
+ this.suggestionsOpened = false;
3193
+ }
3194
+ }
3195
+ submit() {
3196
+ if (this.readonly) {
3197
+ return;
3198
+ }
3199
+ this.optionsTouched = true;
3200
+ if (this.form.invalid || !this.selectedStory || !this.hasAtLeastOneOption) {
3201
+ this.form.markAllAsTouched();
3202
+ return;
3203
+ }
3204
+ void this.workflow.submitStep({
3205
+ storyId: this.selectedStory.id,
3206
+ storyName: this.selectedStory.name,
3207
+ agentId: this.form.get('agentId')?.value,
3208
+ options: this.currentOptions,
3209
+ });
3210
+ }
3211
+ cancel() {
3212
+ if (!this.canCancel) {
3213
+ return;
3214
+ }
3215
+ this.messenger?.discardDraftWorkflow(this.workflow);
3216
+ }
3217
+ isInvalid(name) {
3218
+ const control = this.form.get(name);
3219
+ return !!control && control.invalid && control.touched;
3220
+ }
3221
+ scheduleSearch(delay) {
3222
+ this.clearSearchTimer();
3223
+ this.searchTimer = setTimeout(() => void this.search(), delay);
3224
+ }
3225
+ clearSearchTimer() {
3226
+ if (this.searchTimer) {
3227
+ clearTimeout(this.searchTimer);
3228
+ this.searchTimer = null;
3229
+ }
3230
+ }
3231
+ async search() {
3232
+ if (!this.messenger) {
3233
+ return;
3234
+ }
3235
+ const requestId = ++this.searchRequestId;
3236
+ const query = this.form.get('storyQuery')?.value || '';
3237
+ this.searchInProgress = true;
3238
+ this.searchError = null;
3239
+ this.cdr.markForCheck();
3240
+ try {
3241
+ const stories = await this.messenger.ozzy.searchStories(query);
3242
+ if (requestId !== this.searchRequestId) {
3243
+ return;
3244
+ }
3245
+ this.suggestions = stories;
3246
+ this.highlightedIndex = 0;
3247
+ }
3248
+ catch (e) {
3249
+ if (requestId !== this.searchRequestId) {
3250
+ return;
3251
+ }
3252
+ this.suggestions = [];
3253
+ this.searchError = e?.friendlyMessage || e?.message || 'Failed to load stories.';
3254
+ }
3255
+ finally {
3256
+ if (requestId === this.searchRequestId) {
3257
+ this.searchInProgress = false;
3258
+ this.cdr.detectChanges();
3259
+ }
3260
+ }
3261
+ }
3262
+ onWorkflowChanged() {
3263
+ this.syncWithStep();
3264
+ this.cdr.detectChanges();
3265
+ }
3266
+ syncWithStep() {
3267
+ const output = this.step?.output;
3268
+ if (output?.storyId) {
3269
+ this.selectedStory = { id: output.storyId, name: output.storyName || output.storyId };
3270
+ this.form.patchValue({
3271
+ storyId: output.storyId,
3272
+ storyQuery: output.storyName || '',
3273
+ grammarAndFlow: output.options ? !!output.options.grammarAndFlow : true,
3274
+ characters: output.options ? !!output.options.characters : true,
3275
+ artifacts: output.options ? !!output.options.artifacts : true,
3276
+ locations: output.options ? !!output.options.locations : true,
3277
+ agentId: output.agentId,
3278
+ }, { emitEvent: false });
3279
+ }
3280
+ if (this.readonly) {
3281
+ this.suggestionsOpened = false;
3282
+ this.form.disable({ emitEvent: false });
3283
+ }
3284
+ else {
3285
+ this.form.enable({ emitEvent: false });
3286
+ }
3287
+ }
3288
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryRepairFormStepComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
3289
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StoryRepairFormStepComponent, isStandalone: true, selector: "rtsee-story-repair-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-repair-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-repair-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-repair-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 @if (selectedStory) {\n <div class=\"rts-storyteller-field\">\n <span class=\"rts-storyteller-label\">Repair options</span>\n <div class=\"rts-st-repair-options-grid\">\n <label class=\"rts-st-checkbox-row\" for=\"story-repair-grammar-{{ step.id }}\">\n <input type=\"checkbox\"\n class=\"rts-st-checkbox-input\"\n formControlName=\"grammarAndFlow\"\n id=\"story-repair-grammar-{{ step.id }}\"\n (change)=\"onOptionChanged()\"\n />\n <span class=\"rts-st-checkbox-body\">\n <span class=\"rts-st-checkbox-title\">Grammar &amp; Flow</span>\n <span class=\"rts-st-checkbox-desc\">Fix typos, punctuation &amp; improve readability across story, slides &amp; entities</span>\n </span>\n </label>\n\n <label class=\"rts-st-checkbox-row\" for=\"story-repair-characters-{{ step.id }}\">\n <input type=\"checkbox\"\n class=\"rts-st-checkbox-input\"\n formControlName=\"characters\"\n id=\"story-repair-characters-{{ step.id }}\"\n (change)=\"onOptionChanged()\"\n />\n <span class=\"rts-st-checkbox-body\">\n <span class=\"rts-st-checkbox-title\">Characters</span>\n <span class=\"rts-st-checkbox-desc\">Add missing important characters and link them to slides</span>\n </span>\n </label>\n\n <label class=\"rts-st-checkbox-row\" for=\"story-repair-artifacts-{{ step.id }}\">\n <input type=\"checkbox\"\n class=\"rts-st-checkbox-input\"\n formControlName=\"artifacts\"\n id=\"story-repair-artifacts-{{ step.id }}\"\n (change)=\"onOptionChanged()\"\n />\n <span class=\"rts-st-checkbox-body\">\n <span class=\"rts-st-checkbox-title\">Artifacts</span>\n <span class=\"rts-st-checkbox-desc\">Add missing memorable objects and link them to slides</span>\n </span>\n </label>\n\n <label class=\"rts-st-checkbox-row\" for=\"story-repair-locations-{{ step.id }}\">\n <input type=\"checkbox\"\n class=\"rts-st-checkbox-input\"\n formControlName=\"locations\"\n id=\"story-repair-locations-{{ step.id }}\"\n (change)=\"onOptionChanged()\"\n />\n <span class=\"rts-st-checkbox-body\">\n <span class=\"rts-st-checkbox-title\">Locations</span>\n <span class=\"rts-st-checkbox-desc\">Add missing story locations and link them to slides</span>\n </span>\n </label>\n </div>\n @if (!hasAtLeastOneOption) {\n <div class=\"rts-storyteller-error\">Please select at least one option to repair.</div>\n }\n </div>\n }\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-repair-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-repair-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 || !selectedStory || !hasAtLeastOneOption\"\n id=\"story-repair-submit-{{ step.id }}\"\n >\n @if (workflow.submitInProgress) {\n <span class=\"rts-storyteller-spinner\"></span> Repairing\u2026\n } @else {\n \uD83D\uDEE0\uFE0F Repair Story\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-checkbox-row{display:flex;align-items:flex-start;gap:10px;padding:9px 11px;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));cursor:pointer;-webkit-user-select:none;user-select:none;transition:border-color .15s ease,background .15s ease}.rts-st-checkbox-row:hover{border-color:#8b5cf673;background:#8b5cf60f}.rts-st-checkbox-input{margin-top:2px;width:15px;height:15px;flex-shrink:0;accent-color:var(--rts-st-accent, #8b5cf6);cursor:pointer}.rts-st-checkbox-body{display:flex;flex-direction:column;gap:2px;min-width:0}.rts-st-checkbox-title{font-size:13px;font-weight:600;color:var(--rts-text-primary)}.rts-st-checkbox-desc{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.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", ".rts-st-repair-options-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}@media (max-width: 480px){.rts-st-repair-options-grid{grid-template-columns:1fr}}\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.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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"] }] }); }
3290
+ }
3291
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryRepairFormStepComponent, decorators: [{
3292
+ type: Component,
3293
+ args: [{ selector: 'rtsee-story-repair-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-repair-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-repair-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-repair-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 @if (selectedStory) {\n <div class=\"rts-storyteller-field\">\n <span class=\"rts-storyteller-label\">Repair options</span>\n <div class=\"rts-st-repair-options-grid\">\n <label class=\"rts-st-checkbox-row\" for=\"story-repair-grammar-{{ step.id }}\">\n <input type=\"checkbox\"\n class=\"rts-st-checkbox-input\"\n formControlName=\"grammarAndFlow\"\n id=\"story-repair-grammar-{{ step.id }}\"\n (change)=\"onOptionChanged()\"\n />\n <span class=\"rts-st-checkbox-body\">\n <span class=\"rts-st-checkbox-title\">Grammar &amp; Flow</span>\n <span class=\"rts-st-checkbox-desc\">Fix typos, punctuation &amp; improve readability across story, slides &amp; entities</span>\n </span>\n </label>\n\n <label class=\"rts-st-checkbox-row\" for=\"story-repair-characters-{{ step.id }}\">\n <input type=\"checkbox\"\n class=\"rts-st-checkbox-input\"\n formControlName=\"characters\"\n id=\"story-repair-characters-{{ step.id }}\"\n (change)=\"onOptionChanged()\"\n />\n <span class=\"rts-st-checkbox-body\">\n <span class=\"rts-st-checkbox-title\">Characters</span>\n <span class=\"rts-st-checkbox-desc\">Add missing important characters and link them to slides</span>\n </span>\n </label>\n\n <label class=\"rts-st-checkbox-row\" for=\"story-repair-artifacts-{{ step.id }}\">\n <input type=\"checkbox\"\n class=\"rts-st-checkbox-input\"\n formControlName=\"artifacts\"\n id=\"story-repair-artifacts-{{ step.id }}\"\n (change)=\"onOptionChanged()\"\n />\n <span class=\"rts-st-checkbox-body\">\n <span class=\"rts-st-checkbox-title\">Artifacts</span>\n <span class=\"rts-st-checkbox-desc\">Add missing memorable objects and link them to slides</span>\n </span>\n </label>\n\n <label class=\"rts-st-checkbox-row\" for=\"story-repair-locations-{{ step.id }}\">\n <input type=\"checkbox\"\n class=\"rts-st-checkbox-input\"\n formControlName=\"locations\"\n id=\"story-repair-locations-{{ step.id }}\"\n (change)=\"onOptionChanged()\"\n />\n <span class=\"rts-st-checkbox-body\">\n <span class=\"rts-st-checkbox-title\">Locations</span>\n <span class=\"rts-st-checkbox-desc\">Add missing story locations and link them to slides</span>\n </span>\n </label>\n </div>\n @if (!hasAtLeastOneOption) {\n <div class=\"rts-storyteller-error\">Please select at least one option to repair.</div>\n }\n </div>\n }\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-repair-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-repair-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 || !selectedStory || !hasAtLeastOneOption\"\n id=\"story-repair-submit-{{ step.id }}\"\n >\n @if (workflow.submitInProgress) {\n <span class=\"rts-storyteller-spinner\"></span> Repairing\u2026\n } @else {\n \uD83D\uDEE0\uFE0F Repair Story\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-checkbox-row{display:flex;align-items:flex-start;gap:10px;padding:9px 11px;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));cursor:pointer;-webkit-user-select:none;user-select:none;transition:border-color .15s ease,background .15s ease}.rts-st-checkbox-row:hover{border-color:#8b5cf673;background:#8b5cf60f}.rts-st-checkbox-input{margin-top:2px;width:15px;height:15px;flex-shrink:0;accent-color:var(--rts-st-accent, #8b5cf6);cursor:pointer}.rts-st-checkbox-body{display:flex;flex-direction:column;gap:2px;min-width:0}.rts-st-checkbox-title{font-size:13px;font-weight:600;color:var(--rts-text-primary)}.rts-st-checkbox-desc{font-size:11px;color:var(--rts-text-secondary, #8a8fa3)}.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", ".rts-st-repair-options-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}@media (max-width: 480px){.rts-st-repair-options-grid{grid-template-columns:1fr}}\n"] }]
2870
3294
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }], propDecorators: { step: [{
2871
3295
  type: Input
2872
3296
  }], workflow: [{
@@ -2893,6 +3317,7 @@ class WorkflowStepsService {
2893
3317
  { name: 'storyteller-form-step', component: StorytellerFormStepComponent },
2894
3318
  { name: 'storyteller-story-link-step', component: StorytellerStoryLinkStepComponent },
2895
3319
  { name: 'story-translator-form-step', component: StoryTranslatorFormStepComponent },
3320
+ { name: 'story-repair-form-step', component: StoryRepairFormStepComponent },
2896
3321
  ];
2897
3322
  }
2898
3323
  register(widget) {
@@ -3070,29 +3495,47 @@ class MessageComponent {
3070
3495
  this.timeFormatService = timeFormatService;
3071
3496
  this.widgetRef = null;
3072
3497
  this.widgetHasMessageInput = false;
3498
+ this.currentWidgetName = null;
3073
3499
  }
3074
3500
  ngOnInit() { }
3075
3501
  ngOnChanges(changes) {
3076
- // The message instance can be replaced (e.g. images got downsized versions) - forward it to the widget
3077
- if (changes['message'] && !changes['message'].firstChange && this.widgetRef) {
3078
- if (this.widgetHasMessageInput) {
3079
- this.widgetRef.setInput('message', this.message);
3502
+ // The message instance or widget type can be replaced (e.g. stream start -> media widget -> downsized versions)
3503
+ if (changes['message'] && !changes['message'].firstChange) {
3504
+ if (this.widgetHost && this.message?.widget !== this.currentWidgetName) {
3505
+ this.renderWidget();
3506
+ return;
3080
3507
  }
3081
- else {
3082
- this.widgetRef.instance.message = this.message;
3083
- this.widgetRef.changeDetectorRef.markForCheck();
3508
+ if (this.widgetRef) {
3509
+ if (this.widgetHasMessageInput) {
3510
+ this.widgetRef.setInput('message', this.message);
3511
+ }
3512
+ else {
3513
+ this.widgetRef.instance.message = this.message;
3514
+ if (typeof this.widgetRef.instance.ngOnChanges === 'function') {
3515
+ this.widgetRef.instance.ngOnChanges(changes);
3516
+ }
3517
+ }
3518
+ this.widgetRef.changeDetectorRef.detectChanges();
3084
3519
  }
3085
3520
  }
3086
3521
  }
3087
3522
  ngAfterViewInit() {
3523
+ this.renderWidget();
3524
+ }
3525
+ renderWidget() {
3088
3526
  /* eslint-disable */
3089
- if (this.message.widget) {
3527
+ if (!this.widgetHost) {
3528
+ return;
3529
+ }
3530
+ const viewContainerRef = this.widgetHost.viewContainerRef;
3531
+ viewContainerRef.clear();
3532
+ this.widgetRef = null;
3533
+ this.currentWidgetName = this.message?.widget || null;
3534
+ if (this.message?.widget) {
3090
3535
  const widget = this.widgetsService.getWidgetComponentByName(this.message.widget);
3091
3536
  if (!widget) {
3092
3537
  return;
3093
3538
  }
3094
- const viewContainerRef = this.widgetHost.viewContainerRef;
3095
- viewContainerRef.clear();
3096
3539
  const componentToShow = this.componentFactoryResolver.resolveComponentFactory(widget);
3097
3540
  const componentRef = viewContainerRef.createComponent(componentToShow);
3098
3541
  componentRef.instance.message = this.message;
@@ -3105,7 +3548,7 @@ class MessageComponent {
3105
3548
  /* eslint-enable */
3106
3549
  }
3107
3550
  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 }); }
3108
- 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]=\"{\n 'rts-message-from-me': message.from.id === messenger.clientId,\n 'rts-message-is-widget': !!message.widget\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]" }] }); }
3551
+ 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 data-ugc=\"true\"\n rel=\"ugc\"\n itemprop=\"comment\"\n itemscope\n itemtype=\"https://schema.org/Comment\"\n [ngClass]=\"{\n 'rts-message-from-me': message.from.id === messenger.clientId,\n 'rts-message-is-widget': !!message.widget\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 itemprop=\"text\"\n data-ugc=\"true\"\n rel=\"ugc\"\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]" }] }); }
3109
3552
  }
3110
3553
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessageComponent, decorators: [{
3111
3554
  type: Component,
@@ -3114,7 +3557,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
3114
3557
  MessageTimeAndStatusComponent,
3115
3558
  WidgetDirective,
3116
3559
  RatingComponent,
3117
- ], standalone: true, template: "<div class=\"rts-message-container\" #rtsMessageContainer\n [ngClass]=\"{\n 'rts-message-from-me': message.from.id === messenger.clientId,\n 'rts-message-is-widget': !!message.widget\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" }]
3560
+ ], standalone: true, template: "<div class=\"rts-message-container\" #rtsMessageContainer\n data-ugc=\"true\"\n rel=\"ugc\"\n itemprop=\"comment\"\n itemscope\n itemtype=\"https://schema.org/Comment\"\n [ngClass]=\"{\n 'rts-message-from-me': message.from.id === messenger.clientId,\n 'rts-message-is-widget': !!message.widget\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 itemprop=\"text\"\n data-ugc=\"true\"\n rel=\"ugc\"\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" }]
3118
3561
  }], ctorParameters: () => [{ type: MessageWidgetsService }, { type: i0.ComponentFactoryResolver }, { type: TimeFormatHelperService }], propDecorators: { chat: [{
3119
3562
  type: Input
3120
3563
  }], message: [{
@@ -3383,11 +3826,11 @@ class MessagesListComponent {
3383
3826
  this.lastGroupedMessage = this.chat.messages[this.chat.messages.length - 1];
3384
3827
  }
3385
3828
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessagesListComponent, deps: [{ token: TimeFormatHelperService }, { token: i2$1.ViewportScroller }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
3386
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MessagesListComponent, isStandalone: true, selector: "rtsee-messages-list", inputs: { chat: "chat", messenger: "messenger" }, host: { listeners: { "window:scroll": "onWindowScroll($event)", "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollBlock"], descendants: true }, { propertyName: "contentBlock", first: true, predicate: ["contentBlock"], descendants: true }, { propertyName: "bottomAnchor", first: true, predicate: ["bottomAnchor"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"rts-messages-list\"\n #scrollBlock\n infiniteScroll\n [infiniteScrollUpDistance]=\"2\"\n [infiniteScrollDistance]=\"2\"\n [infiniteScrollThrottle]=\"50\"\n [scrollWindow]=\"false\"\n (scrolledUp)=\"onScrolledToTop()\"\n (scroll)=\"onContainerScroll($event)\"\n [style.padding-bottom]=\"messenger.chatInputHeight + 'px'\"\n>\n <div class=\"rts-messages-list-inner\" #contentBlock>\n @for (messageGroup of messagesGroups; track messageGroup.id) {\n <div class=\"rts-messenger-message-wrapper\" [ngClass]=\"{\n 'rts-messenger-message-wrapper-with-sender': messageGroup.senderVisible\n }\">\n <div class=\"rts-messenger-message-inset\">\n <img [src]=\"messageGroup.sender.imageUrl\"\n class=\"rts-messenger-message-sender-image\"\n >\n </div>\n\n <div class=\"rts-messenger-message-sender-name\">\n <p>{{messageGroup.sender.name}}</p>\n </div>\n\n @for (message of messageGroup.messages; track message.id) {\n <rtsee-message [message]=\"message\"\n [messenger]=\"messenger\"\n [chat]=\"chat\"\n ></rtsee-message>\n }\n </div>\n }\n <div class=\"rts-messages-list-bottom-anchor\" #bottomAnchor></div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: InfiniteScrollDirective, selector: "[infiniteScroll], [infinite-scroll], [data-infinite-scroll]", inputs: ["infiniteScrollDistance", "infiniteScrollUpDistance", "infiniteScrollThrottle", "infiniteScrollDisabled", "infiniteScrollContainer", "scrollWindow", "immediateCheck", "horizontal", "alwaysCallback", "fromRoot"], outputs: ["scrolled", "scrolledUp"] }, { kind: "component", type: MessageComponent, selector: "rtsee-message", inputs: ["chat", "message", "member", "messenger"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
3829
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: MessagesListComponent, isStandalone: true, selector: "rtsee-messages-list", inputs: { chat: "chat", messenger: "messenger" }, host: { listeners: { "window:scroll": "onWindowScroll($event)", "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollBlock"], descendants: true }, { propertyName: "contentBlock", first: true, predicate: ["contentBlock"], descendants: true }, { propertyName: "bottomAnchor", first: true, predicate: ["bottomAnchor"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"rts-messages-list\"\n #scrollBlock\n infiniteScroll\n [infiniteScrollUpDistance]=\"2\"\n [infiniteScrollDistance]=\"2\"\n [infiniteScrollThrottle]=\"50\"\n [scrollWindow]=\"false\"\n (scrolledUp)=\"onScrolledToTop()\"\n (scroll)=\"onContainerScroll($event)\"\n [style.padding-bottom]=\"messenger.chatInputHeight + 'px'\"\n>\n <div class=\"rts-messages-list-inner\" #contentBlock>\n @for (messageGroup of messagesGroups; track messageGroup.id) {\n <div\n class=\"rts-messenger-message-wrapper\"\n data-ugc=\"true\"\n rel=\"ugc\"\n [ngClass]=\"{\n 'rts-messenger-message-wrapper-with-sender': messageGroup.senderVisible\n }\"\n >\n <div class=\"rts-messenger-message-inset\">\n <img [src]=\"messageGroup.sender.imageUrl\"\n class=\"rts-messenger-message-sender-image\"\n >\n </div>\n\n <div\n class=\"rts-messenger-message-sender-name\"\n itemprop=\"author\"\n itemscope\n itemtype=\"https://schema.org/Person\"\n >\n <p itemprop=\"name\">{{messageGroup.sender.name}}</p>\n </div>\n\n @for (message of messageGroup.messages; track message.id) {\n <rtsee-message [message]=\"message\"\n [messenger]=\"messenger\"\n [chat]=\"chat\"\n ></rtsee-message>\n }\n </div>\n }\n <div class=\"rts-messages-list-bottom-anchor\" #bottomAnchor></div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: InfiniteScrollDirective, selector: "[infiniteScroll], [infinite-scroll], [data-infinite-scroll]", inputs: ["infiniteScrollDistance", "infiniteScrollUpDistance", "infiniteScrollThrottle", "infiniteScrollDisabled", "infiniteScrollContainer", "scrollWindow", "immediateCheck", "horizontal", "alwaysCallback", "fromRoot"], outputs: ["scrolled", "scrolledUp"] }, { kind: "component", type: MessageComponent, selector: "rtsee-message", inputs: ["chat", "message", "member", "messenger"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
3387
3830
  }
3388
3831
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: MessagesListComponent, decorators: [{
3389
3832
  type: Component,
3390
- args: [{ selector: 'rtsee-messages-list', imports: [InfiniteScrollDirective, MessageComponent, NgClass], standalone: true, template: "<div class=\"rts-messages-list\"\n #scrollBlock\n infiniteScroll\n [infiniteScrollUpDistance]=\"2\"\n [infiniteScrollDistance]=\"2\"\n [infiniteScrollThrottle]=\"50\"\n [scrollWindow]=\"false\"\n (scrolledUp)=\"onScrolledToTop()\"\n (scroll)=\"onContainerScroll($event)\"\n [style.padding-bottom]=\"messenger.chatInputHeight + 'px'\"\n>\n <div class=\"rts-messages-list-inner\" #contentBlock>\n @for (messageGroup of messagesGroups; track messageGroup.id) {\n <div class=\"rts-messenger-message-wrapper\" [ngClass]=\"{\n 'rts-messenger-message-wrapper-with-sender': messageGroup.senderVisible\n }\">\n <div class=\"rts-messenger-message-inset\">\n <img [src]=\"messageGroup.sender.imageUrl\"\n class=\"rts-messenger-message-sender-image\"\n >\n </div>\n\n <div class=\"rts-messenger-message-sender-name\">\n <p>{{messageGroup.sender.name}}</p>\n </div>\n\n @for (message of messageGroup.messages; track message.id) {\n <rtsee-message [message]=\"message\"\n [messenger]=\"messenger\"\n [chat]=\"chat\"\n ></rtsee-message>\n }\n </div>\n }\n <div class=\"rts-messages-list-bottom-anchor\" #bottomAnchor></div>\n </div>\n</div>\n" }]
3833
+ args: [{ selector: 'rtsee-messages-list', imports: [InfiniteScrollDirective, MessageComponent, NgClass], standalone: true, template: "<div class=\"rts-messages-list\"\n #scrollBlock\n infiniteScroll\n [infiniteScrollUpDistance]=\"2\"\n [infiniteScrollDistance]=\"2\"\n [infiniteScrollThrottle]=\"50\"\n [scrollWindow]=\"false\"\n (scrolledUp)=\"onScrolledToTop()\"\n (scroll)=\"onContainerScroll($event)\"\n [style.padding-bottom]=\"messenger.chatInputHeight + 'px'\"\n>\n <div class=\"rts-messages-list-inner\" #contentBlock>\n @for (messageGroup of messagesGroups; track messageGroup.id) {\n <div\n class=\"rts-messenger-message-wrapper\"\n data-ugc=\"true\"\n rel=\"ugc\"\n [ngClass]=\"{\n 'rts-messenger-message-wrapper-with-sender': messageGroup.senderVisible\n }\"\n >\n <div class=\"rts-messenger-message-inset\">\n <img [src]=\"messageGroup.sender.imageUrl\"\n class=\"rts-messenger-message-sender-image\"\n >\n </div>\n\n <div\n class=\"rts-messenger-message-sender-name\"\n itemprop=\"author\"\n itemscope\n itemtype=\"https://schema.org/Person\"\n >\n <p itemprop=\"name\">{{messageGroup.sender.name}}</p>\n </div>\n\n @for (message of messageGroup.messages; track message.id) {\n <rtsee-message [message]=\"message\"\n [messenger]=\"messenger\"\n [chat]=\"chat\"\n ></rtsee-message>\n }\n </div>\n }\n <div class=\"rts-messages-list-bottom-anchor\" #bottomAnchor></div>\n </div>\n</div>\n" }]
3391
3834
  }], ctorParameters: () => [{ type: TimeFormatHelperService }, { type: i2$1.ViewportScroller }, { type: Document, decorators: [{
3392
3835
  type: Inject,
3393
3836
  args: [DOCUMENT]
@@ -4902,11 +5345,11 @@ class PresentationHeaderComponent {
4902
5345
  this.RTSeeTextLabelKeys = RTSeeTextLabelKeys;
4903
5346
  }
4904
5347
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PresentationHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4905
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: PresentationHeaderComponent, isStandalone: true, selector: "rtsee-presentation-header", inputs: { presentation: "presentation" }, ngImport: i0, template: "<header class=\"rtsee-presentation-header\"\n [ngClass]=\"{\n 'rtsee-presentation-header-convertable': presentation.activeStory,\n 'rtsee-presentation-header-visible': presentation.activeStory?.headerVisible\n }\"\n>\n @if (!presentation.activeStory && !presentation.openedStoryDetails) {\n <div class=\"rtsee-presentations-header-title\">\n <div class=\"rtsee-presentations-header-title-overlay\"></div>\n <div class=\"rtsee-presentations-header-title-content\">\n <h1>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_TITLE_LABEL]}}</h1>\n <p class=\"rtsee-presentations-header-title-subtitle\">\n <span>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_SUBTITLE_LABEL]}}</span>\n </p>\n </div>\n </div>\n }\n\n @if (presentation.activeStory) {\n <div class=\"rtsee-presentation-header-active-story-info\">\n <div class=\"rtsee-presentation-header-active-story-info-close\"\n [routerLink]=\"[presentation.homePage]\"\n (click)=\"presentation.disableActiveStory()\"\n >\n <i class=\"pi pi-times rtsee-presentation-header-active-story-info-close-icon\"></i>\n </div>\n <div class=\"rtsee-presentation-header-active-story-info-details\">\n <p-chip [label]=\"presentation.activeStory.name\"\n [image]=\"presentation.activeStory.imageUrl || undefined\"\n class=\"rtsee-presentation-header-active-story-name\"\n alt=\"Story image\" />\n </div>\n <div class=\"rtsee-presentation-header-active-story-info-settings\"\n (click)=\"presentation.settings.expand()\"\n >\n <i class=\"pi pi-cog-h rtsee-presentation-header-active-story-info-settings-icon\"></i>\n </div>\n </div>\n }\n @if (presentation.dashboard && !presentation.activeStory && !presentation.isDashboardOpen && !presentation.openedStoryDetails) {\n <p-button [label]=\"presentation.labels[RTSeeTextLabelKeys.DASHBOARD_BUTTON]\"\n (click)=\"presentation.toggleDashboardState()\"\n ></p-button>\n }\n</header>\n", dependencies: [{ 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: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: Chip, selector: "p-chip", inputs: ["label", "icon", "image", "alt", "styleClass", "disabled", "removable", "removeIcon", "chipProps"], outputs: ["onRemove", "onImageError"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
5348
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: PresentationHeaderComponent, isStandalone: true, selector: "rtsee-presentation-header", inputs: { presentation: "presentation" }, ngImport: i0, template: "<header class=\"rtsee-presentation-header\"\n [ngClass]=\"{\n 'rtsee-presentation-header-convertable': presentation.activeStory,\n 'rtsee-presentation-header-visible': presentation.activeStory?.headerVisible,\n 'rtsee-presentation-header-theme-dark': presentation.activeStory && presentation.settings.isDarkReadingTheme,\n 'rtsee-presentation-header-theme-light': presentation.activeStory && !presentation.settings.isDarkReadingTheme\n }\"\n>\n @if (!presentation.activeStory && !presentation.openedStoryDetails) {\n <div class=\"rtsee-presentations-header-title\">\n <div class=\"rtsee-presentations-header-title-overlay\"></div>\n <div class=\"rtsee-presentations-header-title-content\">\n <h1>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_TITLE_LABEL]}}</h1>\n <p class=\"rtsee-presentations-header-title-subtitle\">\n <span>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_SUBTITLE_LABEL]}}</span>\n </p>\n </div>\n </div>\n }\n\n @if (presentation.activeStory; as story) {\n <div class=\"rtsee-presentation-header-active-story-info\">\n <button type=\"button\"\n class=\"rtsee-presentation-header-icon-btn rtsee-presentation-header-active-story-info-close\"\n [routerLink]=\"[presentation.homePage]\"\n (click)=\"presentation.disableActiveStory()\"\n [attr.aria-label]=\"presentation.labels[RTSeeTextLabelKeys.BACK_LABEL]\"\n [title]=\"presentation.labels[RTSeeTextLabelKeys.BACK_LABEL]\"\n >\n <i class=\"pi pi-arrow-left\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"rtsee-presentation-header-active-story-info-details\">\n @if (story.imageUrl) {\n <img class=\"rtsee-presentation-header-active-story-cover\"\n [src]=\"story.imageUrl\"\n alt=\"\"\n width=\"34\"\n height=\"34\"\n decoding=\"async\"\n />\n }\n <div class=\"rtsee-presentation-header-active-story-text\">\n <span class=\"rtsee-presentation-header-active-story-name\" [title]=\"story.name\">\n {{ story.name }}\n </span>\n @if (story.totalSlides) {\n <span class=\"rtsee-presentation-header-active-story-progress-label\">\n {{ story.activeSlideNumber || 1 }} / {{ story.totalSlides }}\n </span>\n }\n </div>\n </div>\n\n <button type=\"button\"\n class=\"rtsee-presentation-header-icon-btn rtsee-presentation-header-active-story-info-settings\"\n (click)=\"presentation.settings.expand()\"\n [attr.aria-label]=\"presentation.labels[RTSeeTextLabelKeys.READING_SETTINGS_LABEL]\"\n [title]=\"presentation.labels[RTSeeTextLabelKeys.READING_SETTINGS_LABEL]\"\n >\n <!-- \"Aa\": the well known reading/display settings glyph (text size, theme) -->\n <span class=\"rtsee-presentation-header-settings-glyph\" aria-hidden=\"true\">\n <span class=\"rtsee-presentation-header-settings-glyph-big\">A</span><span class=\"rtsee-presentation-header-settings-glyph-small\">a</span>\n </span>\n </button>\n\n @if (story.totalSlides) {\n <div class=\"rtsee-presentation-header-active-story-progress\" aria-hidden=\"true\">\n <div class=\"rtsee-presentation-header-active-story-progress-bar\"\n [style.width.%]=\"((story.activeSlideNumber || 1) / story.totalSlides) * 100\"\n ></div>\n </div>\n }\n </div>\n }\n @if (presentation.dashboard && !presentation.activeStory && !presentation.isDashboardOpen && !presentation.openedStoryDetails) {\n <p-button [label]=\"presentation.labels[RTSeeTextLabelKeys.DASHBOARD_BUTTON]\"\n (click)=\"presentation.toggleDashboardState()\"\n ></p-button>\n }\n</header>\n", dependencies: [{ 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: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
4906
5349
  }
4907
5350
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PresentationHeaderComponent, decorators: [{
4908
5351
  type: Component,
4909
- args: [{ selector: 'rtsee-presentation-header', imports: [Button, NgClass, Chip, JoinCallComponent, RouterLink], standalone: true, template: "<header class=\"rtsee-presentation-header\"\n [ngClass]=\"{\n 'rtsee-presentation-header-convertable': presentation.activeStory,\n 'rtsee-presentation-header-visible': presentation.activeStory?.headerVisible\n }\"\n>\n @if (!presentation.activeStory && !presentation.openedStoryDetails) {\n <div class=\"rtsee-presentations-header-title\">\n <div class=\"rtsee-presentations-header-title-overlay\"></div>\n <div class=\"rtsee-presentations-header-title-content\">\n <h1>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_TITLE_LABEL]}}</h1>\n <p class=\"rtsee-presentations-header-title-subtitle\">\n <span>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_SUBTITLE_LABEL]}}</span>\n </p>\n </div>\n </div>\n }\n\n @if (presentation.activeStory) {\n <div class=\"rtsee-presentation-header-active-story-info\">\n <div class=\"rtsee-presentation-header-active-story-info-close\"\n [routerLink]=\"[presentation.homePage]\"\n (click)=\"presentation.disableActiveStory()\"\n >\n <i class=\"pi pi-times rtsee-presentation-header-active-story-info-close-icon\"></i>\n </div>\n <div class=\"rtsee-presentation-header-active-story-info-details\">\n <p-chip [label]=\"presentation.activeStory.name\"\n [image]=\"presentation.activeStory.imageUrl || undefined\"\n class=\"rtsee-presentation-header-active-story-name\"\n alt=\"Story image\" />\n </div>\n <div class=\"rtsee-presentation-header-active-story-info-settings\"\n (click)=\"presentation.settings.expand()\"\n >\n <i class=\"pi pi-cog-h rtsee-presentation-header-active-story-info-settings-icon\"></i>\n </div>\n </div>\n }\n @if (presentation.dashboard && !presentation.activeStory && !presentation.isDashboardOpen && !presentation.openedStoryDetails) {\n <p-button [label]=\"presentation.labels[RTSeeTextLabelKeys.DASHBOARD_BUTTON]\"\n (click)=\"presentation.toggleDashboardState()\"\n ></p-button>\n }\n</header>\n" }]
5352
+ args: [{ selector: 'rtsee-presentation-header', imports: [Button, NgClass, JoinCallComponent, RouterLink], standalone: true, template: "<header class=\"rtsee-presentation-header\"\n [ngClass]=\"{\n 'rtsee-presentation-header-convertable': presentation.activeStory,\n 'rtsee-presentation-header-visible': presentation.activeStory?.headerVisible,\n 'rtsee-presentation-header-theme-dark': presentation.activeStory && presentation.settings.isDarkReadingTheme,\n 'rtsee-presentation-header-theme-light': presentation.activeStory && !presentation.settings.isDarkReadingTheme\n }\"\n>\n @if (!presentation.activeStory && !presentation.openedStoryDetails) {\n <div class=\"rtsee-presentations-header-title\">\n <div class=\"rtsee-presentations-header-title-overlay\"></div>\n <div class=\"rtsee-presentations-header-title-content\">\n <h1>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_TITLE_LABEL]}}</h1>\n <p class=\"rtsee-presentations-header-title-subtitle\">\n <span>{{presentation.labels[RTSeeTextLabelKeys.PRESENTATION_SUBTITLE_LABEL]}}</span>\n </p>\n </div>\n </div>\n }\n\n @if (presentation.activeStory; as story) {\n <div class=\"rtsee-presentation-header-active-story-info\">\n <button type=\"button\"\n class=\"rtsee-presentation-header-icon-btn rtsee-presentation-header-active-story-info-close\"\n [routerLink]=\"[presentation.homePage]\"\n (click)=\"presentation.disableActiveStory()\"\n [attr.aria-label]=\"presentation.labels[RTSeeTextLabelKeys.BACK_LABEL]\"\n [title]=\"presentation.labels[RTSeeTextLabelKeys.BACK_LABEL]\"\n >\n <i class=\"pi pi-arrow-left\" aria-hidden=\"true\"></i>\n </button>\n\n <div class=\"rtsee-presentation-header-active-story-info-details\">\n @if (story.imageUrl) {\n <img class=\"rtsee-presentation-header-active-story-cover\"\n [src]=\"story.imageUrl\"\n alt=\"\"\n width=\"34\"\n height=\"34\"\n decoding=\"async\"\n />\n }\n <div class=\"rtsee-presentation-header-active-story-text\">\n <span class=\"rtsee-presentation-header-active-story-name\" [title]=\"story.name\">\n {{ story.name }}\n </span>\n @if (story.totalSlides) {\n <span class=\"rtsee-presentation-header-active-story-progress-label\">\n {{ story.activeSlideNumber || 1 }} / {{ story.totalSlides }}\n </span>\n }\n </div>\n </div>\n\n <button type=\"button\"\n class=\"rtsee-presentation-header-icon-btn rtsee-presentation-header-active-story-info-settings\"\n (click)=\"presentation.settings.expand()\"\n [attr.aria-label]=\"presentation.labels[RTSeeTextLabelKeys.READING_SETTINGS_LABEL]\"\n [title]=\"presentation.labels[RTSeeTextLabelKeys.READING_SETTINGS_LABEL]\"\n >\n <!-- \"Aa\": the well known reading/display settings glyph (text size, theme) -->\n <span class=\"rtsee-presentation-header-settings-glyph\" aria-hidden=\"true\">\n <span class=\"rtsee-presentation-header-settings-glyph-big\">A</span><span class=\"rtsee-presentation-header-settings-glyph-small\">a</span>\n </span>\n </button>\n\n @if (story.totalSlides) {\n <div class=\"rtsee-presentation-header-active-story-progress\" aria-hidden=\"true\">\n <div class=\"rtsee-presentation-header-active-story-progress-bar\"\n [style.width.%]=\"((story.activeSlideNumber || 1) / story.totalSlides) * 100\"\n ></div>\n </div>\n }\n </div>\n }\n @if (presentation.dashboard && !presentation.activeStory && !presentation.isDashboardOpen && !presentation.openedStoryDetails) {\n <p-button [label]=\"presentation.labels[RTSeeTextLabelKeys.DASHBOARD_BUTTON]\"\n (click)=\"presentation.toggleDashboardState()\"\n ></p-button>\n }\n</header>\n" }]
4910
5353
  }], propDecorators: { presentation: [{
4911
5354
  type: Input
4912
5355
  }] } });
@@ -5006,27 +5449,100 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
5006
5449
  type: Input
5007
5450
  }] } });
5008
5451
 
5452
+ /**
5453
+ * Story slides player.
5454
+ *
5455
+ * Uses Swiper's Virtual module with `renderExternal`, so Angular only renders the few
5456
+ * slides around the active one (Safari crashed when animating 20+ card slides at once).
5457
+ * Swiper is aware of ALL the story slides (1..totalSlides) from the start, while the
5458
+ * slide contents are lazy loaded by the story; slides not loaded yet render a placeholder.
5459
+ */
5009
5460
  class StoryPlayerComponent {
5010
- constructor() {
5011
- Swiper.use([EffectCards, Manipulation]);
5461
+ constructor(cdr) {
5462
+ this.cdr = cdr;
5463
+ this.swiper = null;
5464
+ /** slide numbers currently rendered by the virtual swiper */
5465
+ this.virtualData = { offset: 0, from: 0, to: -1, slides: [] };
5012
5466
  }
5013
5467
  ngOnInit() {
5014
5468
  console.log('Story Player: Story = ', this.story);
5015
5469
  }
5016
5470
  ngAfterViewInit() {
5017
- const swiper = new Swiper(this.swiperEl.nativeElement, {
5471
+ this.swiper = new Swiper(this.swiperEl.nativeElement, {
5472
+ // Virtual must be registered before the effect: on "setTranslate" the virtual
5473
+ // slides are rendered first, then the cards effect transforms them
5474
+ modules: [Virtual, EffectCards],
5018
5475
  effect: 'cards',
5019
5476
  grabCursor: true,
5477
+ virtual: {
5478
+ enabled: true,
5479
+ slides: this.getVirtualSlides(),
5480
+ // keep a couple of neighbours rendered so the cards stack looks natural
5481
+ addSlidesBefore: 1,
5482
+ addSlidesAfter: 2,
5483
+ // DOM is rendered by Angular; Swiper is updated manually once it's done
5484
+ renderExternal: (data) => this.renderVirtualSlides(data),
5485
+ renderExternalUpdate: false,
5486
+ },
5020
5487
  });
5021
- this.story.initSwiper(swiper);
5488
+ this.story.initSwiper(this.swiper, { virtual: true });
5022
5489
  }
5023
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryPlayerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5024
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StoryPlayerComponent, isStandalone: true, selector: "rtsee-story-player", inputs: { story: "story", settings: "settings", presentation: "presentation" }, viewQueries: [{ propertyName: "swiperEl", first: true, predicate: ["swiperContainer"], descendants: true }], ngImport: i0, template: "<div class=\"rtsee-story-player\">\n <div class=\"rtsee-story-player-slider swiper\" #swiperContainer>\n <div class=\"rtsee-story-player-slider-inner swiper-wrapper\">\n @for (slide of story.slides; track (slide.id || slide.slideNumber)) {\n <div class=\"story-player-slide-container swiper-slide\"\n [ngClass]=\"{'story-player-slide-active': slide.isActive}\"\n >\n <rtsee-slide [slide]=\"slide\"\n [story]=\"story\"\n [presentation]=\"presentation\"\n [settings]=\"settings\"\n ></rtsee-slide>\n </div>\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: SlideComponent, selector: "rtsee-slide", inputs: ["presentation", "story", "slide", "settings"] }] }); }
5490
+ /** grows the virtual slides list when the slides count becomes known / changes */
5491
+ ngDoCheck() {
5492
+ if (!this.swiper || this.swiper.destroyed) {
5493
+ return;
5494
+ }
5495
+ const total = this.getSlidesCount();
5496
+ if (this.swiper.virtual.slides.length !== total) {
5497
+ this.swiper.virtual.slides = this.getVirtualSlides();
5498
+ this.swiper.virtual.update(true);
5499
+ }
5500
+ }
5501
+ ngOnDestroy() {
5502
+ if (!this.swiper) {
5503
+ return;
5504
+ }
5505
+ if (this.story.swiper === this.swiper) {
5506
+ this.story.swiper = null;
5507
+ }
5508
+ this.swiper.destroy(true, false);
5509
+ this.swiper = null;
5510
+ }
5511
+ getLoadedSlide(slideNumber) {
5512
+ return this.story.slides.find((s) => s.slideNumber === slideNumber) || null;
5513
+ }
5514
+ renderVirtualSlides(data) {
5515
+ this.virtualData = data;
5516
+ // render synchronously: Swiper measures the slides right after this callback
5517
+ this.cdr.detectChanges();
5518
+ const swiper = this.swiper;
5519
+ // during init (beforeInit) `this.swiper` is not assigned yet and Swiper runs these updates by itself
5520
+ if (!swiper || swiper.destroyed) {
5521
+ return;
5522
+ }
5523
+ swiper.updateSlides();
5524
+ swiper.updateProgress();
5525
+ swiper.updateSlidesClasses();
5526
+ // lets the cards effect re-apply transforms to the freshly rendered slides
5527
+ swiper.emit('_virtualUpdated');
5528
+ }
5529
+ getSlidesCount() {
5530
+ const lastLoaded = this.story.slides.length
5531
+ ? this.story.slides[this.story.slides.length - 1].slideNumber
5532
+ : 0;
5533
+ return Math.max(this.story.totalSlides || 0, lastLoaded);
5534
+ }
5535
+ /** virtual slides are just the slide numbers: 1..N */
5536
+ getVirtualSlides() {
5537
+ return Array.from({ length: this.getSlidesCount() }, (_, i) => i + 1);
5538
+ }
5539
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryPlayerComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
5540
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StoryPlayerComponent, isStandalone: true, selector: "rtsee-story-player", inputs: { story: "story", settings: "settings", presentation: "presentation" }, viewQueries: [{ propertyName: "swiperEl", first: true, predicate: ["swiperContainer"], descendants: true }], ngImport: i0, template: "<div class=\"rtsee-story-player\"\n [class.rtsee-story-player-theme-dark]=\"settings.isDarkReadingTheme\"\n [class.rtsee-story-player-theme-light]=\"!settings.isDarkReadingTheme\"\n>\n <div class=\"rtsee-story-player-slider swiper\" #swiperContainer>\n <div class=\"rtsee-story-player-slider-inner swiper-wrapper\">\n <!-- only the virtual window (a few slides around the active one) is rendered -->\n @for (slideNumber of virtualData.slides; track slideNumber; let i = $index) {\n @let slide = getLoadedSlide(slideNumber);\n <div class=\"story-player-slide-container swiper-slide\"\n [attr.data-swiper-slide-index]=\"virtualData.from + i\"\n [style.left.px]=\"virtualData.offset\"\n [ngClass]=\"{'story-player-slide-active': slide?.isActive}\"\n >\n @if (slide) {\n <rtsee-slide [slide]=\"slide\"\n [story]=\"story\"\n [presentation]=\"presentation\"\n [settings]=\"settings\"\n ></rtsee-slide>\n } @else {\n <div class=\"story-player-slide-placeholder\" aria-busy=\"true\">\n <span class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></span>\n </div>\n }\n </div>\n }\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: SlideComponent, selector: "rtsee-slide", inputs: ["presentation", "story", "slide", "settings"] }] }); }
5025
5541
  }
5026
5542
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryPlayerComponent, decorators: [{
5027
5543
  type: Component,
5028
- args: [{ selector: 'rtsee-story-player', imports: [NgClass, SlideComponent], standalone: true, template: "<div class=\"rtsee-story-player\">\n <div class=\"rtsee-story-player-slider swiper\" #swiperContainer>\n <div class=\"rtsee-story-player-slider-inner swiper-wrapper\">\n @for (slide of story.slides; track (slide.id || slide.slideNumber)) {\n <div class=\"story-player-slide-container swiper-slide\"\n [ngClass]=\"{'story-player-slide-active': slide.isActive}\"\n >\n <rtsee-slide [slide]=\"slide\"\n [story]=\"story\"\n [presentation]=\"presentation\"\n [settings]=\"settings\"\n ></rtsee-slide>\n </div>\n }\n </div>\n </div>\n</div>\n" }]
5029
- }], ctorParameters: () => [], propDecorators: { story: [{
5544
+ args: [{ selector: 'rtsee-story-player', imports: [NgClass, SlideComponent], standalone: true, template: "<div class=\"rtsee-story-player\"\n [class.rtsee-story-player-theme-dark]=\"settings.isDarkReadingTheme\"\n [class.rtsee-story-player-theme-light]=\"!settings.isDarkReadingTheme\"\n>\n <div class=\"rtsee-story-player-slider swiper\" #swiperContainer>\n <div class=\"rtsee-story-player-slider-inner swiper-wrapper\">\n <!-- only the virtual window (a few slides around the active one) is rendered -->\n @for (slideNumber of virtualData.slides; track slideNumber; let i = $index) {\n @let slide = getLoadedSlide(slideNumber);\n <div class=\"story-player-slide-container swiper-slide\"\n [attr.data-swiper-slide-index]=\"virtualData.from + i\"\n [style.left.px]=\"virtualData.offset\"\n [ngClass]=\"{'story-player-slide-active': slide?.isActive}\"\n >\n @if (slide) {\n <rtsee-slide [slide]=\"slide\"\n [story]=\"story\"\n [presentation]=\"presentation\"\n [settings]=\"settings\"\n ></rtsee-slide>\n } @else {\n <div class=\"story-player-slide-placeholder\" aria-busy=\"true\">\n <span class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></span>\n </div>\n }\n </div>\n }\n </div>\n </div>\n</div>\n" }]
5545
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { story: [{
5030
5546
  type: Input
5031
5547
  }], settings: [{
5032
5548
  type: Input
@@ -5136,8 +5652,9 @@ class StoryElementsSectionComponent {
5136
5652
  if (this._illustrator) {
5137
5653
  this._illustrator.off('changed', this.onIllustratorChanged);
5138
5654
  }
5139
- for (const [requestId, listener] of this.activeGenerationListeners.entries()) {
5140
- window.removeEventListener(RTSeeIllustrator.REQUESTED_GENERATION_SUCCEED, listener);
5655
+ for (const [, listeners] of this.activeGenerationListeners.entries()) {
5656
+ window.removeEventListener(RTSeeIllustrator.REQUESTED_GENERATION_SUCCEED, listeners.succeed);
5657
+ window.removeEventListener(RTSeeIllustratorEvents.GENERATION_COMPLETED, listeners.completed);
5141
5658
  }
5142
5659
  this.activeGenerationListeners.clear();
5143
5660
  }
@@ -5186,15 +5703,29 @@ class StoryElementsSectionComponent {
5186
5703
  this.batchMode = false;
5187
5704
  this.selectedItemIds.clear();
5188
5705
  this.batchAdditionalPrompt = '';
5189
- this.cdr.markForCheck();
5190
5706
  for (const item of selectedItems) {
5191
- const requestId = RTSeeCore.uuidv4();
5192
5707
  this.generatingItemIds.add(item.id);
5193
- const listener = async (e) => {
5708
+ }
5709
+ this.cdr.markForCheck();
5710
+ const batchSpacingMs = 1200;
5711
+ for (let i = 0; i < selectedItems.length; i++) {
5712
+ const item = selectedItems[i];
5713
+ if (i > 0) {
5714
+ await new Promise((resolve) => setTimeout(resolve, batchSpacingMs));
5715
+ }
5716
+ const requestId = RTSeeCore.uuidv4();
5717
+ const cleanupListeners = () => {
5718
+ const entry = this.activeGenerationListeners.get(requestId);
5719
+ if (entry) {
5720
+ window.removeEventListener(RTSeeIllustrator.REQUESTED_GENERATION_SUCCEED, entry.succeed);
5721
+ window.removeEventListener(RTSeeIllustratorEvents.GENERATION_COMPLETED, entry.completed);
5722
+ this.activeGenerationListeners.delete(requestId);
5723
+ }
5724
+ };
5725
+ const succeedListener = async (e) => {
5194
5726
  const customEvent = e;
5195
5727
  if (customEvent.detail?.requestId === requestId && customEvent.detail?.imageUrl) {
5196
- window.removeEventListener(RTSeeIllustrator.REQUESTED_GENERATION_SUCCEED, listener);
5197
- this.activeGenerationListeners.delete(requestId);
5728
+ cleanupListeners();
5198
5729
  this.generatingItemIds.delete(item.id);
5199
5730
  const cleanUrl = sanitizeImageUrl(customEvent.detail.imageUrl);
5200
5731
  try {
@@ -5219,8 +5750,20 @@ class StoryElementsSectionComponent {
5219
5750
  }
5220
5751
  }
5221
5752
  };
5222
- window.addEventListener(RTSeeIllustrator.REQUESTED_GENERATION_SUCCEED, listener);
5223
- this.activeGenerationListeners.set(requestId, listener);
5753
+ const completedListener = (e) => {
5754
+ const customEvent = e;
5755
+ if (customEvent.detail?.requestId === requestId && customEvent.detail?.success === false) {
5756
+ cleanupListeners();
5757
+ this.generatingItemIds.delete(item.id);
5758
+ this.cdr.markForCheck();
5759
+ }
5760
+ };
5761
+ window.addEventListener(RTSeeIllustrator.REQUESTED_GENERATION_SUCCEED, succeedListener);
5762
+ window.addEventListener(RTSeeIllustratorEvents.GENERATION_COMPLETED, completedListener);
5763
+ this.activeGenerationListeners.set(requestId, {
5764
+ succeed: succeedListener,
5765
+ completed: completedListener,
5766
+ });
5224
5767
  const promptParts = [];
5225
5768
  promptParts.push(`${this.editor.label}: ${item.name || ''}`);
5226
5769
  if (item.visualDescription) {
@@ -5245,7 +5788,7 @@ class StoryElementsSectionComponent {
5245
5788
  chatId: this.illustrator?.activeChatId || undefined,
5246
5789
  agentId: this.illustrator?.activeAgentId || '',
5247
5790
  prompt: entityPrompt,
5248
- config: { ...config },
5791
+ config: { ...config, numberOfImages: 1 },
5249
5792
  contextImages: [],
5250
5793
  requestId,
5251
5794
  };
@@ -5980,11 +6523,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
5980
6523
 
5981
6524
  class PresentationSettingsComponent {
5982
6525
  constructor() {
6526
+ this.readingThemeOptions = [
6527
+ { label: 'Light', value: 'light', icon: 'pi pi-sun' },
6528
+ { label: 'Dark', value: 'dark', icon: 'pi pi-moon' },
6529
+ ];
5983
6530
  this.COMMON_CONSTANTS = COMMON_CONSTANTS;
5984
6531
  this.window = window;
5985
6532
  }
5986
6533
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PresentationSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5987
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: PresentationSettingsComponent, isStandalone: true, selector: "rtsee-presentation-settings", inputs: { settings: "settings", presentation: "presentation" }, ngImport: i0, template: "<div class=\"rtsee-presentation-settings\">\n <div class=\"rtsee-presentation-settings-header\">\n <p-chip label=\"Settings\" />\n <p-button label=\"Close\"\n (click)=\"settings.collapse()\"\n class=\"rtsee-presentations-dashboard-close-btn\"\n ></p-button>\n </div>\n <div class=\"rtsee-presentation-sound-settings\">\n <p-fieldset legend=\"Volume\" class=\"rtsee-presentation-sound-settings-fieldset\">\n <div class=\"rtsee-presentation-sound-settings-inner\">\n <div class=\"rtsee-presentation-sound-settings-inner-left\">\n <p-slider [(ngModel)]=\"settings.volume\"\n (onChange)=\"settings.setVolume(settings.volume)\"\n />\n </div>\n <div class=\"rtsee-presentation-sound-settings-inner-right\">\n <p-tag severity=\"info\"\n class=\"rtsee-presentation-sound-value-label\"\n [icon]=\"settings.volume ? 'pi pi-volume-up' : 'pi pi-volume-off'\"\n [value]=\"settings.volume.toString()\"\n [rounded]=\"true\" />\n </div>\n </div>\n </p-fieldset>\n </div>\n <div class=\"rtsee-presentation-font-size-settings\">\n <p-fieldset legend=\"Font Size\">\n <div class=\"rtsee-presentation-font-size-range\">\n <p-slider [(ngModel)]=\"settings.fontSizeOption\"\n (onChange)=\"settings.setFontSize(settings.fontSizeOption)\"\n [min]=\"1\"\n [max]=\"10\"\n />\n </div>\n <p-message severity=\"secondary\" class=\"rtsee-presentation-font-size-example\">\n <span [style.font-size]=\"settings.fontSize + 'rem'\">A long time ago in a galaxy far, far away...</span>\n </p-message>\n </p-fieldset>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: Slider, selector: "p-slider", inputs: ["animate", "min", "max", "orientation", "step", "range", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "component", type: Fieldset, selector: "p-fieldset", inputs: ["legend", "toggleable", "collapsed", "style", "styleClass", "transitionOptions"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: "component", type: Chip, selector: "p-chip", inputs: ["label", "icon", "image", "alt", "styleClass", "disabled", "removable", "removeIcon", "chipProps"], outputs: ["onRemove", "onImageError"] }, { kind: "component", type: Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }, { kind: "component", type: Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }] }); }
6534
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: PresentationSettingsComponent, isStandalone: true, selector: "rtsee-presentation-settings", inputs: { settings: "settings", presentation: "presentation" }, ngImport: i0, template: "<div class=\"rtsee-presentation-settings\">\n <div class=\"rtsee-presentation-settings-header\">\n <p-chip label=\"Settings\" />\n <p-button label=\"Close\"\n (click)=\"settings.collapse()\"\n class=\"rtsee-presentations-dashboard-close-btn\"\n ></p-button>\n </div>\n <div class=\"rtsee-presentation-sound-settings\">\n <p-fieldset legend=\"Volume\" class=\"rtsee-presentation-sound-settings-fieldset\">\n <div class=\"rtsee-presentation-sound-settings-inner\">\n <div class=\"rtsee-presentation-sound-settings-inner-left\">\n <p-slider [(ngModel)]=\"settings.volume\"\n (onChange)=\"settings.setVolume(settings.volume)\"\n />\n </div>\n <div class=\"rtsee-presentation-sound-settings-inner-right\">\n <p-tag severity=\"info\"\n class=\"rtsee-presentation-sound-value-label\"\n [icon]=\"settings.volume ? 'pi pi-volume-up' : 'pi pi-volume-off'\"\n [value]=\"settings.volume.toString()\"\n [rounded]=\"true\" />\n </div>\n </div>\n </p-fieldset>\n </div>\n <div class=\"rtsee-presentation-font-size-settings\">\n <p-fieldset legend=\"Font Size\">\n <div class=\"rtsee-presentation-font-size-range\">\n <p-slider [(ngModel)]=\"settings.fontSizeOption\"\n (onChange)=\"settings.setFontSize(settings.fontSizeOption)\"\n [min]=\"1\"\n [max]=\"10\"\n />\n </div>\n <p-message severity=\"secondary\" class=\"rtsee-presentation-font-size-example\">\n <span [style.font-size]=\"settings.fontSize + 'rem'\">A long time ago in a galaxy far, far away...</span>\n </p-message>\n </p-fieldset>\n </div>\n <div class=\"rtsee-presentation-reading-theme-settings\">\n <p-fieldset legend=\"Reading Theme\">\n <p-selectbutton\n [options]=\"readingThemeOptions\"\n [ngModel]=\"settings.readingTheme\"\n (ngModelChange)=\"settings.setReadingTheme($event)\"\n [allowEmpty]=\"false\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"rtsee-presentation-reading-theme-toggle\"\n >\n <ng-template #item let-option>\n <span [class]=\"option.icon\" aria-hidden=\"true\"></span>\n <span>{{ option.label }}</span>\n </ng-template>\n </p-selectbutton>\n <div class=\"rtsee-presentation-reading-theme-example\"\n [class.rtsee-presentation-reading-theme-example-dark]=\"settings.isDarkReadingTheme\"\n [style.font-size]=\"settings.fontSize + 'rem'\"\n >\n Once upon a time, under a sky full of stars...\n </div>\n </p-fieldset>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: Slider, selector: "p-slider", inputs: ["animate", "min", "max", "orientation", "step", "range", "styleClass", "ariaLabel", "ariaLabelledBy", "tabindex", "autofocus"], outputs: ["onChange", "onSlideEnd"] }, { kind: "component", type: Fieldset, selector: "p-fieldset", inputs: ["legend", "toggleable", "collapsed", "style", "styleClass", "transitionOptions"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: "component", type: Chip, selector: "p-chip", inputs: ["label", "icon", "image", "alt", "styleClass", "disabled", "removable", "removeIcon", "chipProps"], outputs: ["onRemove", "onImageError"] }, { kind: "component", type: Message, selector: "p-message", inputs: ["severity", "text", "escape", "style", "styleClass", "closable", "icon", "closeIcon", "life", "showTransitionOptions", "hideTransitionOptions", "size", "variant"], outputs: ["onClose"] }, { kind: "component", type: Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }] }); }
5988
6535
  }
5989
6536
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PresentationSettingsComponent, decorators: [{
5990
6537
  type: Component,
@@ -5995,8 +6542,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
5995
6542
  Button,
5996
6543
  Chip,
5997
6544
  Message,
5998
- Tag
5999
- ], template: "<div class=\"rtsee-presentation-settings\">\n <div class=\"rtsee-presentation-settings-header\">\n <p-chip label=\"Settings\" />\n <p-button label=\"Close\"\n (click)=\"settings.collapse()\"\n class=\"rtsee-presentations-dashboard-close-btn\"\n ></p-button>\n </div>\n <div class=\"rtsee-presentation-sound-settings\">\n <p-fieldset legend=\"Volume\" class=\"rtsee-presentation-sound-settings-fieldset\">\n <div class=\"rtsee-presentation-sound-settings-inner\">\n <div class=\"rtsee-presentation-sound-settings-inner-left\">\n <p-slider [(ngModel)]=\"settings.volume\"\n (onChange)=\"settings.setVolume(settings.volume)\"\n />\n </div>\n <div class=\"rtsee-presentation-sound-settings-inner-right\">\n <p-tag severity=\"info\"\n class=\"rtsee-presentation-sound-value-label\"\n [icon]=\"settings.volume ? 'pi pi-volume-up' : 'pi pi-volume-off'\"\n [value]=\"settings.volume.toString()\"\n [rounded]=\"true\" />\n </div>\n </div>\n </p-fieldset>\n </div>\n <div class=\"rtsee-presentation-font-size-settings\">\n <p-fieldset legend=\"Font Size\">\n <div class=\"rtsee-presentation-font-size-range\">\n <p-slider [(ngModel)]=\"settings.fontSizeOption\"\n (onChange)=\"settings.setFontSize(settings.fontSizeOption)\"\n [min]=\"1\"\n [max]=\"10\"\n />\n </div>\n <p-message severity=\"secondary\" class=\"rtsee-presentation-font-size-example\">\n <span [style.font-size]=\"settings.fontSize + 'rem'\">A long time ago in a galaxy far, far away...</span>\n </p-message>\n </p-fieldset>\n </div>\n</div>\n" }]
6545
+ Tag,
6546
+ SelectButton,
6547
+ ], template: "<div class=\"rtsee-presentation-settings\">\n <div class=\"rtsee-presentation-settings-header\">\n <p-chip label=\"Settings\" />\n <p-button label=\"Close\"\n (click)=\"settings.collapse()\"\n class=\"rtsee-presentations-dashboard-close-btn\"\n ></p-button>\n </div>\n <div class=\"rtsee-presentation-sound-settings\">\n <p-fieldset legend=\"Volume\" class=\"rtsee-presentation-sound-settings-fieldset\">\n <div class=\"rtsee-presentation-sound-settings-inner\">\n <div class=\"rtsee-presentation-sound-settings-inner-left\">\n <p-slider [(ngModel)]=\"settings.volume\"\n (onChange)=\"settings.setVolume(settings.volume)\"\n />\n </div>\n <div class=\"rtsee-presentation-sound-settings-inner-right\">\n <p-tag severity=\"info\"\n class=\"rtsee-presentation-sound-value-label\"\n [icon]=\"settings.volume ? 'pi pi-volume-up' : 'pi pi-volume-off'\"\n [value]=\"settings.volume.toString()\"\n [rounded]=\"true\" />\n </div>\n </div>\n </p-fieldset>\n </div>\n <div class=\"rtsee-presentation-font-size-settings\">\n <p-fieldset legend=\"Font Size\">\n <div class=\"rtsee-presentation-font-size-range\">\n <p-slider [(ngModel)]=\"settings.fontSizeOption\"\n (onChange)=\"settings.setFontSize(settings.fontSizeOption)\"\n [min]=\"1\"\n [max]=\"10\"\n />\n </div>\n <p-message severity=\"secondary\" class=\"rtsee-presentation-font-size-example\">\n <span [style.font-size]=\"settings.fontSize + 'rem'\">A long time ago in a galaxy far, far away...</span>\n </p-message>\n </p-fieldset>\n </div>\n <div class=\"rtsee-presentation-reading-theme-settings\">\n <p-fieldset legend=\"Reading Theme\">\n <p-selectbutton\n [options]=\"readingThemeOptions\"\n [ngModel]=\"settings.readingTheme\"\n (ngModelChange)=\"settings.setReadingTheme($event)\"\n [allowEmpty]=\"false\"\n optionLabel=\"label\"\n optionValue=\"value\"\n class=\"rtsee-presentation-reading-theme-toggle\"\n >\n <ng-template #item let-option>\n <span [class]=\"option.icon\" aria-hidden=\"true\"></span>\n <span>{{ option.label }}</span>\n </ng-template>\n </p-selectbutton>\n <div class=\"rtsee-presentation-reading-theme-example\"\n [class.rtsee-presentation-reading-theme-example-dark]=\"settings.isDarkReadingTheme\"\n [style.font-size]=\"settings.fontSize + 'rem'\"\n >\n Once upon a time, under a sky full of stars...\n </div>\n </p-fieldset>\n </div>\n</div>\n" }]
6000
6548
  }], propDecorators: { settings: [{
6001
6549
  type: Input
6002
6550
  }], presentation: [{
@@ -6005,11 +6553,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
6005
6553
 
6006
6554
  class RtseeEntityCommentsComponent {
6007
6555
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RtseeEntityCommentsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6008
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RtseeEntityCommentsComponent, isStandalone: true, selector: "rtsee-entity-comments", inputs: { messenger: "messenger", chat: "chat", entity: "entity" }, ngImport: i0, template: "<div class=\"rtsee-entity-comments\">\n @if (messenger && chat) {\n <rtsee-chat [messenger]=\"messenger\"\n [chatData]=\"chat\"\n [entity]=\"entity\"\n ></rtsee-chat>\n }\n</div>\n", dependencies: [{ kind: "component", type: ChatComponent, selector: "rtsee-chat", inputs: ["chat", "chatData", "entity", "messenger"] }] }); }
6556
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RtseeEntityCommentsComponent, isStandalone: true, selector: "rtsee-entity-comments", inputs: { messenger: "messenger", chat: "chat", entity: "entity" }, ngImport: i0, template: "<div class=\"rtsee-entity-comments\" data-ugc=\"true\" rel=\"ugc\" aria-label=\"User comments\">\n @if (messenger && chat) {\n <rtsee-chat [messenger]=\"messenger\"\n [chatData]=\"chat\"\n [entity]=\"entity\"\n ></rtsee-chat>\n }\n</div>\n", dependencies: [{ kind: "component", type: ChatComponent, selector: "rtsee-chat", inputs: ["chat", "chatData", "entity", "messenger"] }] }); }
6009
6557
  }
6010
6558
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RtseeEntityCommentsComponent, decorators: [{
6011
6559
  type: Component,
6012
- args: [{ selector: 'rtsee-entity-comments', imports: [ChatComponent], template: "<div class=\"rtsee-entity-comments\">\n @if (messenger && chat) {\n <rtsee-chat [messenger]=\"messenger\"\n [chatData]=\"chat\"\n [entity]=\"entity\"\n ></rtsee-chat>\n }\n</div>\n" }]
6560
+ args: [{ selector: 'rtsee-entity-comments', imports: [ChatComponent], template: "<div class=\"rtsee-entity-comments\" data-ugc=\"true\" rel=\"ugc\" aria-label=\"User comments\">\n @if (messenger && chat) {\n <rtsee-chat [messenger]=\"messenger\"\n [chatData]=\"chat\"\n [entity]=\"entity\"\n ></rtsee-chat>\n }\n</div>\n" }]
6013
6561
  }], propDecorators: { messenger: [{
6014
6562
  type: Input
6015
6563
  }], chat: [{
@@ -6096,11 +6644,11 @@ class RateItemComponent {
6096
6644
  return config ? config.name : '';
6097
6645
  }
6098
6646
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RateItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6099
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RateItemComponent, isStandalone: true, selector: "rtsee-rate-item", inputs: { group: "group", rater: "rater", showActions: "showActions" }, outputs: { edit: "edit", delete: "delete" }, ngImport: i0, template: "<div class=\"rtsee-rates-group-item\">\n <div class=\"rtsee-rates-group-header\">\n <div class=\"rtsee-rates-group-user-info\">\n @if (group.peer?.imageUrl) {\n <img\n class=\"rtsee-rates-group-avatar\"\n [src]=\"group.peer.imageUrl\"\n alt=\"User avatar\"\n />\n } @else {\n <div class=\"rtsee-rates-group-avatar-fallback\">\n {{ (group.peer?.name || \"?\").substring(0, 1) }}\n </div>\n }\n <span class=\"rtsee-rates-group-peer-name\">{{\n group.peer?.name || group.peerId\n }}</span>\n </div>\n\n <div class=\"rtsee-rates-group-right-header\">\n <span class=\"rtsee-rates-group-date\">{{\n group.createdAt | date: \"short\"\n }}</span>\n\n @if (showActions) {\n <div class=\"rtsee-rates-group-actions\">\n <p-button\n (click)=\"onEdit($event)\"\n [severity]=\"'secondary'\"\n [text]=\"true\"\n class=\"rtsee-rates-group-btn-edit\"\n >\n <span class=\"pi pi-pencil\"></span>\n </p-button>\n <p-button\n (click)=\"onDelete($event)\"\n [severity]=\"'danger'\"\n [text]=\"true\"\n class=\"rtsee-rates-group-btn-delete\"\n >\n <span class=\"pi pi-trash\"></span>\n </p-button>\n </div>\n }\n </div>\n </div>\n <div class=\"rtsee-rates-group-body\">\n @for (rate of group.rates; track rate.id) {\n <div class=\"rtsee-rates-group-rate-item-container\">\n <div class=\"rtsee-rates-group-rate-item\">\n <span class=\"rtsee-rates-group-rate-config-name\">{{\n getRatingConfigName(rate.configurationId)\n }}</span>\n <rtsee-rating [rate]=\"rate\"></rtsee-rating>\n </div>\n @if (rate.text) {\n <p class=\"rtsee-rates-group-rate-comment\">\"{{ rate.text }}\"</p>\n }\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: RatingComponent, selector: "rtsee-rating", inputs: ["config", "rate"] }, { 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: "pipe", type: DatePipe, name: "date" }] }); }
6647
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RateItemComponent, isStandalone: true, selector: "rtsee-rate-item", inputs: { group: "group", rater: "rater", showActions: "showActions" }, outputs: { edit: "edit", delete: "delete" }, ngImport: i0, template: "<div\n class=\"rtsee-rates-group-item\"\n data-ugc=\"true\"\n rel=\"ugc\"\n itemprop=\"review\"\n itemscope\n itemtype=\"https://schema.org/Review\"\n>\n <div class=\"rtsee-rates-group-header\">\n <div\n class=\"rtsee-rates-group-user-info\"\n itemprop=\"author\"\n itemscope\n itemtype=\"https://schema.org/Person\"\n >\n @if (group.peer?.imageUrl) {\n <img\n class=\"rtsee-rates-group-avatar\"\n [src]=\"group.peer.imageUrl\"\n alt=\"User avatar\"\n itemprop=\"image\"\n />\n } @else {\n <div class=\"rtsee-rates-group-avatar-fallback\">\n {{ (group.peer?.name || \"?\").substring(0, 1) }}\n </div>\n }\n <span class=\"rtsee-rates-group-peer-name\" itemprop=\"name\">{{\n group.peer?.name || group.peerId\n }}</span>\n </div>\n\n <div class=\"rtsee-rates-group-right-header\">\n <span\n class=\"rtsee-rates-group-date\"\n itemprop=\"datePublished\"\n [attr.content]=\"group.createdAt\"\n >{{\n group.createdAt | date: \"short\"\n }}</span>\n\n @if (showActions) {\n <div class=\"rtsee-rates-group-actions\">\n <p-button\n (click)=\"onEdit($event)\"\n [severity]=\"'secondary'\"\n [text]=\"true\"\n class=\"rtsee-rates-group-btn-edit\"\n >\n <span class=\"pi pi-pencil\"></span>\n </p-button>\n <p-button\n (click)=\"onDelete($event)\"\n [severity]=\"'danger'\"\n [text]=\"true\"\n class=\"rtsee-rates-group-btn-delete\"\n >\n <span class=\"pi pi-trash\"></span>\n </p-button>\n </div>\n }\n </div>\n </div>\n <div class=\"rtsee-rates-group-body\">\n @for (rate of group.rates; track rate.id) {\n <div class=\"rtsee-rates-group-rate-item-container\">\n <div class=\"rtsee-rates-group-rate-item\">\n <span class=\"rtsee-rates-group-rate-config-name\">{{\n getRatingConfigName(rate.configurationId)\n }}</span>\n <rtsee-rating [rate]=\"rate\"></rtsee-rating>\n </div>\n @if (rate.text) {\n <p class=\"rtsee-rates-group-rate-comment\" itemprop=\"reviewBody\" data-ugc=\"true\" rel=\"ugc\">\"{{ rate.text }}\"</p>\n }\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: RatingComponent, selector: "rtsee-rating", inputs: ["config", "rate"] }, { 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: "pipe", type: DatePipe, name: "date" }] }); }
6100
6648
  }
6101
6649
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RateItemComponent, decorators: [{
6102
6650
  type: Component,
6103
- args: [{ selector: 'rtsee-rate-item', imports: [RatingComponent, DatePipe, Button], template: "<div class=\"rtsee-rates-group-item\">\n <div class=\"rtsee-rates-group-header\">\n <div class=\"rtsee-rates-group-user-info\">\n @if (group.peer?.imageUrl) {\n <img\n class=\"rtsee-rates-group-avatar\"\n [src]=\"group.peer.imageUrl\"\n alt=\"User avatar\"\n />\n } @else {\n <div class=\"rtsee-rates-group-avatar-fallback\">\n {{ (group.peer?.name || \"?\").substring(0, 1) }}\n </div>\n }\n <span class=\"rtsee-rates-group-peer-name\">{{\n group.peer?.name || group.peerId\n }}</span>\n </div>\n\n <div class=\"rtsee-rates-group-right-header\">\n <span class=\"rtsee-rates-group-date\">{{\n group.createdAt | date: \"short\"\n }}</span>\n\n @if (showActions) {\n <div class=\"rtsee-rates-group-actions\">\n <p-button\n (click)=\"onEdit($event)\"\n [severity]=\"'secondary'\"\n [text]=\"true\"\n class=\"rtsee-rates-group-btn-edit\"\n >\n <span class=\"pi pi-pencil\"></span>\n </p-button>\n <p-button\n (click)=\"onDelete($event)\"\n [severity]=\"'danger'\"\n [text]=\"true\"\n class=\"rtsee-rates-group-btn-delete\"\n >\n <span class=\"pi pi-trash\"></span>\n </p-button>\n </div>\n }\n </div>\n </div>\n <div class=\"rtsee-rates-group-body\">\n @for (rate of group.rates; track rate.id) {\n <div class=\"rtsee-rates-group-rate-item-container\">\n <div class=\"rtsee-rates-group-rate-item\">\n <span class=\"rtsee-rates-group-rate-config-name\">{{\n getRatingConfigName(rate.configurationId)\n }}</span>\n <rtsee-rating [rate]=\"rate\"></rtsee-rating>\n </div>\n @if (rate.text) {\n <p class=\"rtsee-rates-group-rate-comment\">\"{{ rate.text }}\"</p>\n }\n </div>\n }\n </div>\n</div>\n" }]
6651
+ args: [{ selector: 'rtsee-rate-item', imports: [RatingComponent, DatePipe, Button], template: "<div\n class=\"rtsee-rates-group-item\"\n data-ugc=\"true\"\n rel=\"ugc\"\n itemprop=\"review\"\n itemscope\n itemtype=\"https://schema.org/Review\"\n>\n <div class=\"rtsee-rates-group-header\">\n <div\n class=\"rtsee-rates-group-user-info\"\n itemprop=\"author\"\n itemscope\n itemtype=\"https://schema.org/Person\"\n >\n @if (group.peer?.imageUrl) {\n <img\n class=\"rtsee-rates-group-avatar\"\n [src]=\"group.peer.imageUrl\"\n alt=\"User avatar\"\n itemprop=\"image\"\n />\n } @else {\n <div class=\"rtsee-rates-group-avatar-fallback\">\n {{ (group.peer?.name || \"?\").substring(0, 1) }}\n </div>\n }\n <span class=\"rtsee-rates-group-peer-name\" itemprop=\"name\">{{\n group.peer?.name || group.peerId\n }}</span>\n </div>\n\n <div class=\"rtsee-rates-group-right-header\">\n <span\n class=\"rtsee-rates-group-date\"\n itemprop=\"datePublished\"\n [attr.content]=\"group.createdAt\"\n >{{\n group.createdAt | date: \"short\"\n }}</span>\n\n @if (showActions) {\n <div class=\"rtsee-rates-group-actions\">\n <p-button\n (click)=\"onEdit($event)\"\n [severity]=\"'secondary'\"\n [text]=\"true\"\n class=\"rtsee-rates-group-btn-edit\"\n >\n <span class=\"pi pi-pencil\"></span>\n </p-button>\n <p-button\n (click)=\"onDelete($event)\"\n [severity]=\"'danger'\"\n [text]=\"true\"\n class=\"rtsee-rates-group-btn-delete\"\n >\n <span class=\"pi pi-trash\"></span>\n </p-button>\n </div>\n }\n </div>\n </div>\n <div class=\"rtsee-rates-group-body\">\n @for (rate of group.rates; track rate.id) {\n <div class=\"rtsee-rates-group-rate-item-container\">\n <div class=\"rtsee-rates-group-rate-item\">\n <span class=\"rtsee-rates-group-rate-config-name\">{{\n getRatingConfigName(rate.configurationId)\n }}</span>\n <rtsee-rating [rate]=\"rate\"></rtsee-rating>\n </div>\n @if (rate.text) {\n <p class=\"rtsee-rates-group-rate-comment\" itemprop=\"reviewBody\" data-ugc=\"true\" rel=\"ugc\">\"{{ rate.text }}\"</p>\n }\n </div>\n }\n </div>\n</div>\n" }]
6104
6652
  }], propDecorators: { group: [{
6105
6653
  type: Input
6106
6654
  }], rater: [{
@@ -6217,11 +6765,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
6217
6765
 
6218
6766
  class RatingSectionComponent {
6219
6767
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RatingSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6220
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: RatingSectionComponent, isStandalone: true, selector: "rtsee-rating-section", inputs: { rater: "rater", entity: "entity" }, ngImport: i0, template: "<div class=\"rtsee-rating-section\">\n <rtsee-rater [rater]=\"rater\"\n [entity]=\"entity\"\n ></rtsee-rater>\n <rtsee-rates-list [rater]=\"rater\"\n [entity]=\"entity\"\n ></rtsee-rates-list>\n</div>\n", dependencies: [{ kind: "component", type: RaterComponent, selector: "rtsee-rater", inputs: ["entity", "rater"] }, { kind: "component", type: RatesListComponent, selector: "rtsee-rates-list", inputs: ["rater", "entity"] }] }); }
6768
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.4", type: RatingSectionComponent, isStandalone: true, selector: "rtsee-rating-section", inputs: { rater: "rater", entity: "entity" }, ngImport: i0, template: "<div class=\"rtsee-rating-section\" data-ugc=\"true\" rel=\"ugc\" aria-label=\"User ratings and reviews\">\n <rtsee-rater [rater]=\"rater\"\n [entity]=\"entity\"\n ></rtsee-rater>\n <rtsee-rates-list [rater]=\"rater\"\n [entity]=\"entity\"\n ></rtsee-rates-list>\n</div>\n", dependencies: [{ kind: "component", type: RaterComponent, selector: "rtsee-rater", inputs: ["entity", "rater"] }, { kind: "component", type: RatesListComponent, selector: "rtsee-rates-list", inputs: ["rater", "entity"] }] }); }
6221
6769
  }
6222
6770
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RatingSectionComponent, decorators: [{
6223
6771
  type: Component,
6224
- args: [{ selector: 'rtsee-rating-section', imports: [RaterComponent, RatesListComponent], template: "<div class=\"rtsee-rating-section\">\n <rtsee-rater [rater]=\"rater\"\n [entity]=\"entity\"\n ></rtsee-rater>\n <rtsee-rates-list [rater]=\"rater\"\n [entity]=\"entity\"\n ></rtsee-rates-list>\n</div>\n" }]
6772
+ args: [{ selector: 'rtsee-rating-section', imports: [RaterComponent, RatesListComponent], template: "<div class=\"rtsee-rating-section\" data-ugc=\"true\" rel=\"ugc\" aria-label=\"User ratings and reviews\">\n <rtsee-rater [rater]=\"rater\"\n [entity]=\"entity\"\n ></rtsee-rater>\n <rtsee-rates-list [rater]=\"rater\"\n [entity]=\"entity\"\n ></rtsee-rates-list>\n</div>\n" }]
6225
6773
  }], propDecorators: { rater: [{
6226
6774
  type: Input
6227
6775
  }], entity: [{
@@ -6243,7 +6791,7 @@ class RtseeEntityPreviewComponent {
6243
6791
  });
6244
6792
  }
6245
6793
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RtseeEntityPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6246
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RtseeEntityPreviewComponent, isStandalone: true, selector: "rtsee-entity-preview", inputs: { entity: "entity", messenger: "messenger" }, ngImport: i0, template: "<div class=\"rtsee-entity-preview\">\n <div class=\"rtsee-entity-preview-tags\">\n <rtsee-entity-tags [entity]=\"entity\"></rtsee-entity-tags>\n </div>\n\n @if (showRating && rater) {\n <rtsee-rating-section [entity]=\"entity\"\n [rater]=\"rater\"\n ></rtsee-rating-section>\n } @else if (!showRating && entity.comments && messenger) {\n <div class=\"rtsee-entity-preview-comments\">\n <rtsee-entity-comments [messenger]=\"messenger\"\n [chat]=\"entity.comments\"\n [entity]=\"entity\"\n ></rtsee-entity-comments>\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: RtseeEntityCommentsComponent, selector: "rtsee-entity-comments", inputs: ["messenger", "chat", "entity"] }, { kind: "component", type: RtseeEntityTagsComponent, selector: "rtsee-entity-tags", inputs: ["entity"] }, { kind: "component", type: RatingSectionComponent, selector: "rtsee-rating-section", inputs: ["rater", "entity"] }] }); }
6794
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RtseeEntityPreviewComponent, isStandalone: true, selector: "rtsee-entity-preview", inputs: { entity: "entity", messenger: "messenger" }, ngImport: i0, template: "<div class=\"rtsee-entity-preview\">\n @if (false) {\n <div class=\"rtsee-entity-preview-tags\">\n <rtsee-entity-tags [entity]=\"entity\"></rtsee-entity-tags>\n </div>\n }\n\n @if (showRating && rater) {\n <rtsee-rating-section [entity]=\"entity\"\n [rater]=\"rater\"\n ></rtsee-rating-section>\n } @else if (!showRating && entity.comments && messenger) {\n <div class=\"rtsee-entity-preview-comments\">\n <rtsee-entity-comments [messenger]=\"messenger\"\n [chat]=\"entity.comments\"\n [entity]=\"entity\"\n ></rtsee-entity-comments>\n </div>\n }\n</div>\n", dependencies: [{ kind: "component", type: RtseeEntityCommentsComponent, selector: "rtsee-entity-comments", inputs: ["messenger", "chat", "entity"] }, { kind: "component", type: RtseeEntityTagsComponent, selector: "rtsee-entity-tags", inputs: ["entity"] }, { kind: "component", type: RatingSectionComponent, selector: "rtsee-rating-section", inputs: ["rater", "entity"] }] }); }
6247
6795
  }
6248
6796
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RtseeEntityPreviewComponent, decorators: [{
6249
6797
  type: Component,
@@ -6251,13 +6799,193 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
6251
6799
  RtseeEntityCommentsComponent,
6252
6800
  RtseeEntityTagsComponent,
6253
6801
  RatingSectionComponent,
6254
- ], template: "<div class=\"rtsee-entity-preview\">\n <div class=\"rtsee-entity-preview-tags\">\n <rtsee-entity-tags [entity]=\"entity\"></rtsee-entity-tags>\n </div>\n\n @if (showRating && rater) {\n <rtsee-rating-section [entity]=\"entity\"\n [rater]=\"rater\"\n ></rtsee-rating-section>\n } @else if (!showRating && entity.comments && messenger) {\n <div class=\"rtsee-entity-preview-comments\">\n <rtsee-entity-comments [messenger]=\"messenger\"\n [chat]=\"entity.comments\"\n [entity]=\"entity\"\n ></rtsee-entity-comments>\n </div>\n }\n</div>\n" }]
6802
+ ], template: "<div class=\"rtsee-entity-preview\">\n @if (false) {\n <div class=\"rtsee-entity-preview-tags\">\n <rtsee-entity-tags [entity]=\"entity\"></rtsee-entity-tags>\n </div>\n }\n\n @if (showRating && rater) {\n <rtsee-rating-section [entity]=\"entity\"\n [rater]=\"rater\"\n ></rtsee-rating-section>\n } @else if (!showRating && entity.comments && messenger) {\n <div class=\"rtsee-entity-preview-comments\">\n <rtsee-entity-comments [messenger]=\"messenger\"\n [chat]=\"entity.comments\"\n [entity]=\"entity\"\n ></rtsee-entity-comments>\n </div>\n }\n</div>\n" }]
6255
6803
  }], propDecorators: { entity: [{
6256
6804
  type: Input
6257
6805
  }], messenger: [{
6258
6806
  type: Input
6259
6807
  }] } });
6260
6808
 
6809
+ let nextThumbnailId = 0;
6810
+ /**
6811
+ * SEO-friendly thumbnail of a story element (author, character, artifact, location).
6812
+ * Renders schema.org microdata so the element is attached to the parent CreativeWork
6813
+ * through the given `itemProp` (e.g. "author", "character", "contentLocation", "mentions").
6814
+ * Only exposes public fields: name, description & image.
6815
+ *
6816
+ * Layout: small image in the top-left corner, name & description to the right.
6817
+ * The description is only clamped visually (CSS line-clamp), the full text always stays
6818
+ * in the DOM so it's indexable; a "show more / less" toggle appears when it overflows.
6819
+ */
6820
+ class StoryElementThumbnailComponent {
6821
+ constructor(cdr) {
6822
+ this.cdr = cdr;
6823
+ this.name = '';
6824
+ this.schemaType = 'Thing';
6825
+ /** featured layout (bigger image), used for single items like the author */
6826
+ this.horizontal = false;
6827
+ this.showMoreLabel = 'Show more';
6828
+ this.showLessLabel = 'Show less';
6829
+ this.descriptionId = `rtsee-story-element-description-${nextThumbnailId++}`;
6830
+ this.imageFailed = false;
6831
+ this.expanded = false;
6832
+ /** the clamped description overflows (toggle is needed) */
6833
+ this.truncatable = false;
6834
+ this.resizeObserver = null;
6835
+ this.destroyed = false;
6836
+ }
6837
+ get schemaUrl() {
6838
+ return `https://schema.org/${this.schemaType}`;
6839
+ }
6840
+ get initial() {
6841
+ return (this.name || '?').trim().charAt(0).toUpperCase();
6842
+ }
6843
+ get showImage() {
6844
+ return !!this.imageUrl && !this.imageFailed;
6845
+ }
6846
+ ngOnChanges(changes) {
6847
+ if (changes['description'] && !changes['description'].firstChange) {
6848
+ this.expanded = false;
6849
+ this.scheduleMeasure();
6850
+ }
6851
+ if (changes['imageUrl']) {
6852
+ this.imageFailed = false;
6853
+ }
6854
+ }
6855
+ ngAfterViewInit() {
6856
+ this.scheduleMeasure();
6857
+ if (typeof ResizeObserver !== 'undefined' && this.descriptionEl) {
6858
+ this.resizeObserver = new ResizeObserver(() => this.measure());
6859
+ this.resizeObserver.observe(this.descriptionEl.nativeElement);
6860
+ }
6861
+ }
6862
+ ngOnDestroy() {
6863
+ this.destroyed = true;
6864
+ this.resizeObserver?.disconnect();
6865
+ this.resizeObserver = null;
6866
+ }
6867
+ toggle() {
6868
+ if (!this.truncatable) {
6869
+ return;
6870
+ }
6871
+ this.expanded = !this.expanded;
6872
+ if (!this.expanded) {
6873
+ // re-check after collapsing (the width may have changed meanwhile)
6874
+ this.scheduleMeasure();
6875
+ }
6876
+ }
6877
+ onImageError() {
6878
+ this.imageFailed = true;
6879
+ }
6880
+ scheduleMeasure() {
6881
+ if (typeof requestAnimationFrame === 'undefined') {
6882
+ return;
6883
+ }
6884
+ requestAnimationFrame(() => this.measure());
6885
+ }
6886
+ /** compares the clamped height with the full content height */
6887
+ measure() {
6888
+ if (this.destroyed || this.expanded || !this.descriptionEl) {
6889
+ return;
6890
+ }
6891
+ const el = this.descriptionEl.nativeElement;
6892
+ const truncatable = el.scrollHeight - el.clientHeight > 1;
6893
+ if (truncatable !== this.truncatable) {
6894
+ this.truncatable = truncatable;
6895
+ this.cdr.detectChanges();
6896
+ }
6897
+ }
6898
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryElementThumbnailComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
6899
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StoryElementThumbnailComponent, isStandalone: true, selector: "rtsee-story-element-thumbnail", inputs: { name: "name", description: "description", imageUrl: "imageUrl", itemProp: "itemProp", schemaType: "schemaType", horizontal: "horizontal", showMoreLabel: "showMoreLabel", showLessLabel: "showLessLabel" }, viewQueries: [{ propertyName: "descriptionEl", first: true, predicate: ["descriptionEl"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<article\n class=\"rtsee-story-element-thumbnail\"\n [class.rtsee-story-element-thumbnail-featured]=\"horizontal\"\n [class.rtsee-story-element-thumbnail-expanded]=\"expanded\"\n [attr.itemprop]=\"itemProp || null\"\n itemscope\n [attr.itemtype]=\"schemaUrl\"\n>\n <figure class=\"rtsee-story-element-thumbnail-media\">\n @if (showImage) {\n <img\n class=\"rtsee-story-element-thumbnail-image\"\n [src]=\"imageUrl\"\n [alt]=\"name\"\n itemprop=\"image\"\n loading=\"lazy\"\n decoding=\"async\"\n width=\"144\"\n height=\"144\"\n (error)=\"onImageError()\"\n />\n } @else {\n <span class=\"rtsee-story-element-thumbnail-placeholder\" aria-hidden=\"true\">\n {{ initial }}\n </span>\n }\n </figure>\n <div class=\"rtsee-story-element-thumbnail-body\">\n <h3 class=\"rtsee-story-element-thumbnail-name\" itemprop=\"name\">{{ name }}</h3>\n @if (description) {\n <!-- full text always stays in the DOM (indexable), it's only clamped visually -->\n <div class=\"rtsee-story-element-thumbnail-description-wrapper\">\n <p\n #descriptionEl\n class=\"rtsee-story-element-thumbnail-description\"\n [class.rtsee-story-element-thumbnail-description-truncatable]=\"truncatable\"\n [id]=\"descriptionId\"\n itemprop=\"description\"\n (click)=\"toggle()\"\n >{{ description }}</p>\n <!-- kept outside of the itemprop element so the label doesn't pollute the description -->\n @if (truncatable) {\n <button\n type=\"button\"\n class=\"rtsee-story-element-thumbnail-toggle\"\n [class.rtsee-story-element-thumbnail-toggle-more]=\"!expanded\"\n [class.rtsee-story-element-thumbnail-toggle-less]=\"expanded\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"descriptionId\"\n (click)=\"toggle()\"\n >{{ expanded ? showLessLabel : showMoreLabel }}</button>\n }\n </div>\n }\n </div>\n</article>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6900
+ }
6901
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryElementThumbnailComponent, decorators: [{
6902
+ type: Component,
6903
+ args: [{ selector: 'rtsee-story-element-thumbnail', changeDetection: ChangeDetectionStrategy.OnPush, template: "<article\n class=\"rtsee-story-element-thumbnail\"\n [class.rtsee-story-element-thumbnail-featured]=\"horizontal\"\n [class.rtsee-story-element-thumbnail-expanded]=\"expanded\"\n [attr.itemprop]=\"itemProp || null\"\n itemscope\n [attr.itemtype]=\"schemaUrl\"\n>\n <figure class=\"rtsee-story-element-thumbnail-media\">\n @if (showImage) {\n <img\n class=\"rtsee-story-element-thumbnail-image\"\n [src]=\"imageUrl\"\n [alt]=\"name\"\n itemprop=\"image\"\n loading=\"lazy\"\n decoding=\"async\"\n width=\"144\"\n height=\"144\"\n (error)=\"onImageError()\"\n />\n } @else {\n <span class=\"rtsee-story-element-thumbnail-placeholder\" aria-hidden=\"true\">\n {{ initial }}\n </span>\n }\n </figure>\n <div class=\"rtsee-story-element-thumbnail-body\">\n <h3 class=\"rtsee-story-element-thumbnail-name\" itemprop=\"name\">{{ name }}</h3>\n @if (description) {\n <!-- full text always stays in the DOM (indexable), it's only clamped visually -->\n <div class=\"rtsee-story-element-thumbnail-description-wrapper\">\n <p\n #descriptionEl\n class=\"rtsee-story-element-thumbnail-description\"\n [class.rtsee-story-element-thumbnail-description-truncatable]=\"truncatable\"\n [id]=\"descriptionId\"\n itemprop=\"description\"\n (click)=\"toggle()\"\n >{{ description }}</p>\n <!-- kept outside of the itemprop element so the label doesn't pollute the description -->\n @if (truncatable) {\n <button\n type=\"button\"\n class=\"rtsee-story-element-thumbnail-toggle\"\n [class.rtsee-story-element-thumbnail-toggle-more]=\"!expanded\"\n [class.rtsee-story-element-thumbnail-toggle-less]=\"expanded\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"descriptionId\"\n (click)=\"toggle()\"\n >{{ expanded ? showLessLabel : showMoreLabel }}</button>\n }\n </div>\n }\n </div>\n</article>\n" }]
6904
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { name: [{
6905
+ type: Input,
6906
+ args: [{ required: true }]
6907
+ }], description: [{
6908
+ type: Input
6909
+ }], imageUrl: [{
6910
+ type: Input
6911
+ }], itemProp: [{
6912
+ type: Input
6913
+ }], schemaType: [{
6914
+ type: Input
6915
+ }], horizontal: [{
6916
+ type: Input
6917
+ }], showMoreLabel: [{
6918
+ type: Input
6919
+ }], showLessLabel: [{
6920
+ type: Input
6921
+ }], descriptionEl: [{
6922
+ type: ViewChild,
6923
+ args: ['descriptionEl']
6924
+ }] } });
6925
+
6926
+ /**
6927
+ * "You are about to read N slides" teaser:
6928
+ * a stack of circular slide illustrations followed by the slides count
6929
+ * and the approximate reading time.
6930
+ */
6931
+ class StorySlidesPreviewComponent {
6932
+ constructor() {
6933
+ /** e.g. "slides" */
6934
+ this.slidesLabel = '';
6935
+ /** e.g. "min" */
6936
+ this.minuteLabel = '';
6937
+ /** number of circles to show */
6938
+ this.count = 3;
6939
+ this.skeletons = [0, 1, 2];
6940
+ this.cache = null;
6941
+ }
6942
+ ngOnChanges(changes) {
6943
+ if (changes['story'] && this.story) {
6944
+ void this.story.loadPreviewSlides(this.count);
6945
+ }
6946
+ }
6947
+ /** story.averageReadTime is in seconds */
6948
+ get readTimeMinutes() {
6949
+ return this.story.averageReadTime ? Math.ceil(this.story.averageReadTime / 60) : 0;
6950
+ }
6951
+ get previews() {
6952
+ const source = this.story.previewSlides;
6953
+ const loading = this.story.previewSlidesLoading;
6954
+ const cover = this.story.imageUrl || null;
6955
+ if (this.cache &&
6956
+ this.cache.source === source &&
6957
+ this.cache.loading === loading &&
6958
+ this.cache.cover === cover) {
6959
+ return this.cache.result;
6960
+ }
6961
+ let result = (source || [])
6962
+ .filter((s) => !!s.imageUrl)
6963
+ .slice(0, this.count)
6964
+ .map((s) => ({ imageUrl: s.imageUrl, slideNumber: s.slideNumber }));
6965
+ // no illustrated slides: fall back to the story cover
6966
+ if (!result.length && !loading && cover) {
6967
+ result = [{ imageUrl: cover, slideNumber: 1 }];
6968
+ }
6969
+ this.cache = { source, loading, cover, result };
6970
+ return result;
6971
+ }
6972
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StorySlidesPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6973
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StorySlidesPreviewComponent, isStandalone: true, selector: "rtsee-story-slides-preview", inputs: { story: "story", slidesLabel: "slidesLabel", minuteLabel: "minuteLabel", count: "count" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"rtsee-story-slides-preview\">\n <span class=\"rtsee-story-slides-preview-stack\" aria-hidden=\"true\">\n @if (story.previewSlidesLoading && !previews.length) {\n @for (s of skeletons; track s) {\n <span class=\"rtsee-story-slides-preview-circle rtsee-story-slides-preview-circle-skeleton\"></span>\n }\n } @else {\n @for (preview of previews; track preview.slideNumber) {\n <span class=\"rtsee-story-slides-preview-circle\">\n <img\n [src]=\"preview.imageUrl\"\n [alt]=\"story.name + ' #' + preview.slideNumber\"\n loading=\"lazy\"\n decoding=\"async\"\n width=\"52\"\n height=\"52\"\n />\n </span>\n }\n }\n </span>\n <span class=\"rtsee-story-slides-preview-text\">\n <span class=\"rtsee-story-slides-preview-total\">\n <span class=\"rtsee-story-slides-preview-count\">{{ story.totalSlides }}</span>\n <span class=\"rtsee-story-slides-preview-label\">{{ slidesLabel }}</span>\n </span>\n @if (readTimeMinutes) {\n <span class=\"rtsee-story-slides-preview-time\">\n <span class=\"pi pi-clock\" aria-hidden=\"true\"></span>\n <span>~{{ readTimeMinutes }} {{ minuteLabel }}</span>\n </span>\n }\n </span>\n</div>\n", changeDetection: i0.ChangeDetectionStrategy.Default }); }
6974
+ }
6975
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StorySlidesPreviewComponent, decorators: [{
6976
+ type: Component,
6977
+ args: [{ selector: 'rtsee-story-slides-preview', changeDetection: ChangeDetectionStrategy.Default, template: "<div class=\"rtsee-story-slides-preview\">\n <span class=\"rtsee-story-slides-preview-stack\" aria-hidden=\"true\">\n @if (story.previewSlidesLoading && !previews.length) {\n @for (s of skeletons; track s) {\n <span class=\"rtsee-story-slides-preview-circle rtsee-story-slides-preview-circle-skeleton\"></span>\n }\n } @else {\n @for (preview of previews; track preview.slideNumber) {\n <span class=\"rtsee-story-slides-preview-circle\">\n <img\n [src]=\"preview.imageUrl\"\n [alt]=\"story.name + ' #' + preview.slideNumber\"\n loading=\"lazy\"\n decoding=\"async\"\n width=\"52\"\n height=\"52\"\n />\n </span>\n }\n }\n </span>\n <span class=\"rtsee-story-slides-preview-text\">\n <span class=\"rtsee-story-slides-preview-total\">\n <span class=\"rtsee-story-slides-preview-count\">{{ story.totalSlides }}</span>\n <span class=\"rtsee-story-slides-preview-label\">{{ slidesLabel }}</span>\n </span>\n @if (readTimeMinutes) {\n <span class=\"rtsee-story-slides-preview-time\">\n <span class=\"pi pi-clock\" aria-hidden=\"true\"></span>\n <span>~{{ readTimeMinutes }} {{ minuteLabel }}</span>\n </span>\n }\n </span>\n</div>\n" }]
6978
+ }], propDecorators: { story: [{
6979
+ type: Input,
6980
+ args: [{ required: true }]
6981
+ }], slidesLabel: [{
6982
+ type: Input
6983
+ }], minuteLabel: [{
6984
+ type: Input
6985
+ }], count: [{
6986
+ type: Input
6987
+ }] } });
6988
+
6261
6989
  class StoryDetailsComponent {
6262
6990
  constructor() {
6263
6991
  this.RTSeeTextLabelKeys = RTSeeTextLabelKeys;
@@ -6280,11 +7008,11 @@ class StoryDetailsComponent {
6280
7008
  return stars;
6281
7009
  }
6282
7010
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6283
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StoryDetailsComponent, isStandalone: true, selector: "rtsee-story-details", inputs: { story: "story", presentation: "presentation", messenger: "messenger" }, ngImport: i0, template: "<div class=\"rtsee-story-details\">\n <section\n class=\"rtsee-story-details-cover\"\n [style.background-image]=\"'url(' + story.imageUrl + ')'\"\n >\n <button\n class=\"rtsee-story-details-btn-close\"\n [routerLink]=\"[presentation.homePage]\"\n (click)=\"presentation.closeStoryDetails()\"\n aria-label=\"Close\"\n >\n <span class=\"pi pi-times\"></span>\n </button>\n <div class=\"rtsee-story-details-cover-overlay\"></div>\n <div class=\"rtsee-story-details-cover-content\">\n <div class=\"rtsee-story-details-cover-badges\">\n @if (story.categories?.length) {\n @for (category of story.categories; track category.id) {\n <span class=\"rtsee-story-details-tag-pill\">\n {{ category.name }}\n </span>\n }\n }\n @if (story.tags?.length) {\n @for (tag of story.tags; track tag.id) {\n <span\n class=\"rtsee-story-details-tag-pill rtsee-story-details-tag-pill-label\"\n >\n @if (tag.symbol) {\n <span class=\"symbol\">{{ tag.symbol }}</span>\n }\n {{ tag.name }}\n </span>\n }\n }\n </div>\n\n <h1 class=\"rtsee-story-details-cover-title\">{{ story.name }}</h1>\n\n <div class=\"rtsee-story-details-cover-meta\">\n @if (story.rating !== null) {\n <div class=\"rtsee-story-details-cover-rating\">\n <span class=\"rtsee-story-details-stars\">\n @for (star of getStarArray(story.rating); track $index) {\n <span\n class=\"rtsee-story-details-star\"\n [class.rtsee-story-details-star-full]=\"star === 'full'\"\n [class.rtsee-story-details-star-half]=\"star === 'half'\"\n [class.rtsee-story-details-star-empty]=\"star === 'empty'\"\n >\n @if (star === \"full\") {\n \u2605\n } @else if (star === \"half\") {\n \u2605\n } @else {\n \u2606\n }\n </span>\n }\n </span>\n <span class=\"rtsee-story-details-rating-number\">{{\n story.rating\n }}</span>\n @if (story.ratesCount !== null) {\n <span class=\"rtsee-story-details-rating-count\">\n <span>(</span>\n <span>{{ presentation.labels[RTSeeTextLabelKeys.FEEDBACKS_LABEL] }}:</span>\n <span>&nbsp;</span>\n <span>{{ story.ratesCount }}</span>\n <span>)</span>\n </span\n >\n }\n </div>\n }\n\n <div class=\"rtsee-story-details-cover-info-chips\">\n @if (story.averageReadTime) {\n <span class=\"rtsee-story-details-info-chip\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <polyline points=\"12 6 12 12 16 14\" />\n </svg>\n <span>\n <span>{{ Math.ceil(story.averageReadTime / 60) }}</span>\n <span>&nbsp;</span>\n <span>{{ presentation.labels[RTSeeTextLabelKeys.MINUTE_LABEL] }}</span>\n </span>\n </span>\n }\n @if (story.totalSlides) {\n <span class=\"rtsee-story-details-info-chip\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z\" />\n <path d=\"M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z\" />\n </svg>\n <span>\n <span>{{ presentation.labels[RTSeeTextLabelKeys.STORY_PAGES_LABEL] }}:</span>\n <span>&nbsp;</span>\n <span>{{ story.totalSlides }}</span>\n </span>\n </span>\n }\n </div>\n </div>\n </div>\n </section>\n\n <!-- CONTENT AREA -->\n <div class=\"rtsee-story-details-content-wrapper\">\n <div class=\"rtsee-story-details-cover-actions\">\n <p-button\n (click)=\"presentation.setActiveStory(story)\"\n [severity]=\"'primary'\"\n [label]=\"story.preselectedSlideNumber ? presentation.labels[RTSeeTextLabelKeys.CONTINUE_READING_BUTTON] : presentation.labels[RTSeeTextLabelKeys.START_READING_BUTTON]\"\n class=\"rtsee-story-details-start-reading rts-action-button\"\n >\n </p-button>\n </div>\n <div class=\"rtsee-story-details-content-grid\">\n <!-- Description Card -->\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z\" />\n <path d=\"M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_ABOUT_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\">\n {{ story.description }}\n </p>\n </article>\n\n @if (story.history) {\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <polyline points=\"12 6 12 12 14 14\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_HISTORY_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\">\n {{ story.history }}\n </p>\n </article>\n }\n\n @if (story.moralMeaning) {\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_MORAL_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\">\n {{ story.moralMeaning }}\n </p>\n </article>\n }\n </div>\n\n @if (messenger) {\n <rtsee-entity-preview\n [entity]=\"story\"\n [messenger]=\"messenger\"\n ></rtsee-entity-preview>\n }\n\n @if (story.categories?.length && false) {\n <div class=\"rtsee-story-details-categories\">\n @for (category of story.categories; track category.id) {\n <p-tag [value]=\"category.name\"></p-tag>\n }\n </div>\n }\n\n @if (story.tags?.length && false) {\n <div class=\"rtsee-story-details-labels\">\n @for (tag of story.tags; track tag.id) {\n <p-chip class=\"rtsee-story-details-label\">\n @if (tag.symbol) {\n <span class=\"rtsee-story-details-label-left\">{{\n tag.symbol\n }}</span>\n }\n <span class=\"rtsee-story-details-label-right\">{{\n tag.name\n }}</span>\n </p-chip>\n }\n </div>\n }\n\n <div class=\"rtsee-story-details-description-history\"></div>\n @if (story.isPublished && false) {\n <div class=\"rtsee-story-details-feedback\">\n <p>feedback here</p>\n </div>\n } @else if (messenger && story.discussion && false) {\n <div class=\"rtsee-story-details-votes\">\n <p class=\"rtsee-story-details-comments-label\">Comments</p>\n <rtsee-chat\n [messenger]=\"messenger\"\n [chatData]=\"story.discussion\"\n ></rtsee-chat>\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: Chip, selector: "p-chip", inputs: ["label", "icon", "image", "alt", "styleClass", "disabled", "removable", "removeIcon", "chipProps"], outputs: ["onRemove", "onImageError"] }, { kind: "component", type: Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { 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: "component", type: ChatComponent, selector: "rtsee-chat", inputs: ["chat", "chatData", "entity", "messenger"] }, { kind: "component", type: RtseeEntityPreviewComponent, selector: "rtsee-entity-preview", inputs: ["entity", "messenger"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
7011
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StoryDetailsComponent, isStandalone: true, selector: "rtsee-story-details", inputs: { story: "story", presentation: "presentation", messenger: "messenger" }, ngImport: i0, template: "<div\n class=\"rtsee-story-details\"\n itemscope\n itemtype=\"https://schema.org/ShortStory\"\n>\n @if (story.imageUrl) {\n <meta itemprop=\"image\" [attr.content]=\"story.imageUrl\" />\n }\n @if (story.languageKey) {\n <meta itemprop=\"inLanguage\" [attr.content]=\"story.languageKey === 'ua' ? 'uk' : 'en'\" />\n }\n @if (story.totalSlides) {\n <meta itemprop=\"numberOfPages\" [attr.content]=\"story.totalSlides\" />\n }\n <section\n class=\"rtsee-story-details-cover\"\n [style.background-image]=\"'url(' + story.imageUrl + ')'\"\n >\n <button\n class=\"rtsee-story-details-btn-close\"\n [routerLink]=\"[presentation.homePage]\"\n (click)=\"presentation.closeStoryDetails()\"\n aria-label=\"Close\"\n >\n <span class=\"pi pi-times\"></span>\n </button>\n <div class=\"rtsee-story-details-cover-overlay\"></div>\n <div class=\"rtsee-story-details-cover-content\">\n <div class=\"rtsee-story-details-cover-badges\">\n @if (story.categories?.length && false) {\n @for (category of story.categories; track category.id) {\n <span class=\"rtsee-story-details-tag-pill\">\n {{ category.name }}\n </span>\n }\n }\n @if (story.tags?.length) {\n @for (tag of story.tags; track tag.id) {\n <span\n class=\"rtsee-story-details-tag-pill rtsee-story-details-tag-pill-label\"\n >\n @if (tag.symbol) {\n <span class=\"symbol\">{{ tag.symbol }}</span>\n }\n {{ tag.name }}\n </span>\n }\n }\n </div>\n\n <h1 class=\"rtsee-story-details-cover-title\" itemprop=\"name\">{{ story.name }}</h1>\n\n <div class=\"rtsee-story-details-cover-meta\">\n @if (story.rating !== null) {\n <div\n class=\"rtsee-story-details-cover-rating\"\n itemprop=\"aggregateRating\"\n itemscope\n itemtype=\"https://schema.org/AggregateRating\"\n >\n <meta itemprop=\"bestRating\" content=\"5\" />\n <meta itemprop=\"worstRating\" content=\"1\" />\n <span class=\"rtsee-story-details-stars\">\n @for (star of getStarArray(story.rating); track $index) {\n <span\n class=\"rtsee-story-details-star\"\n [class.rtsee-story-details-star-full]=\"star === 'full'\"\n [class.rtsee-story-details-star-half]=\"star === 'half'\"\n [class.rtsee-story-details-star-empty]=\"star === 'empty'\"\n >\n @if (star === \"full\") {\n \u2605\n } @else if (star === \"half\") {\n \u2605\n } @else {\n \u2606\n }\n </span>\n }\n </span>\n <span class=\"rtsee-story-details-rating-number\" itemprop=\"ratingValue\">{{\n story.rating\n }}</span>\n @if (story.ratesCount !== null) {\n <meta itemprop=\"ratingCount\" [attr.content]=\"story.ratesCount\" />\n <span class=\"rtsee-story-details-rating-count\">\n <span>(</span>\n <span>{{ presentation.labels[RTSeeTextLabelKeys.FEEDBACKS_LABEL] }}:</span>\n <span>&nbsp;</span>\n <span itemprop=\"reviewCount\">{{ story.ratesCount }}</span>\n <span>)</span>\n </span\n >\n }\n </div>\n }\n </div>\n </div>\n </section>\n\n <!-- CONTENT AREA -->\n <div class=\"rtsee-story-details-content-wrapper\">\n <!-- Reading panel: what the user is about to read + the main CTA -->\n <section class=\"rtsee-story-details-reading-panel\" aria-label=\"{{ story.name }}\">\n @if (story.totalSlides) {\n <rtsee-story-slides-preview\n [story]=\"story\"\n [slidesLabel]=\"presentation.labels[RTSeeTextLabelKeys.STORY_PAGES_LABEL]\"\n [minuteLabel]=\"presentation.labels[RTSeeTextLabelKeys.MINUTE_LABEL]\"\n ></rtsee-story-slides-preview>\n }\n <p-button\n (click)=\"presentation.setActiveStory(story)\"\n [severity]=\"'primary'\"\n [label]=\"story.preselectedSlideNumber ? presentation.labels[RTSeeTextLabelKeys.CONTINUE_READING_BUTTON] : presentation.labels[RTSeeTextLabelKeys.START_READING_BUTTON]\"\n icon=\"pi pi-book\"\n iconPos=\"left\"\n class=\"rtsee-story-details-start-reading rts-action-button\"\n >\n </p-button>\n </section>\n <div class=\"rtsee-story-details-content-grid\">\n <!-- Description Card -->\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z\" />\n <path d=\"M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_ABOUT_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\" itemprop=\"description\">\n {{ story.description }}\n </p>\n </article>\n\n @if (story.history) {\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <polyline points=\"12 6 12 12 14 14\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_HISTORY_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\" itemprop=\"backstory\">\n {{ story.history }}\n </p>\n </article>\n }\n\n @if (story.moralMeaning) {\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_MORAL_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\" itemprop=\"teaches\">\n {{ story.moralMeaning }}\n </p>\n </article>\n }\n </div>\n\n <!-- Author -->\n @if (story.author) {\n <section\n class=\"rtsee-story-details-elements\"\n aria-labelledby=\"rtsee-story-details-author-heading\"\n >\n <header class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M12 20h9\" />\n <path d=\"M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z\" />\n </svg>\n <h2 id=\"rtsee-story-details-author-heading\">\n {{ presentation.labels[RTSeeTextLabelKeys.STORY_AUTHOR_LABEL] }}\n </h2>\n </header>\n <div class=\"rtsee-story-details-elements-featured\">\n <rtsee-story-element-thumbnail\n [name]=\"story.author.name\"\n [description]=\"story.author.description\"\n [imageUrl]=\"story.author.imageUrl\"\n itemProp=\"author\"\n schemaType=\"Person\"\n [horizontal]=\"true\"\n [showMoreLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_MORE_LABEL]\"\n [showLessLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_LESS_LABEL]\"\n ></rtsee-story-element-thumbnail>\n </div>\n </section>\n }\n\n <!-- Characters -->\n @if (story.characters.length) {\n <section\n class=\"rtsee-story-details-elements\"\n aria-labelledby=\"rtsee-story-details-characters-heading\"\n >\n <header class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\" />\n <circle cx=\"9\" cy=\"7\" r=\"4\" />\n <path d=\"M23 21v-2a4 4 0 0 0-3-3.87\" />\n <path d=\"M16 3.13a4 4 0 0 1 0 7.75\" />\n </svg>\n <h2 id=\"rtsee-story-details-characters-heading\">\n {{ presentation.labels[RTSeeTextLabelKeys.CHARACTERS_LABEL] }}\n </h2>\n </header>\n <ul class=\"rtsee-story-details-elements-grid\" role=\"list\">\n @for (character of story.characters; track character.id) {\n <li class=\"rtsee-story-details-elements-item\">\n <rtsee-story-element-thumbnail\n [name]=\"character.name\"\n [description]=\"character.description\"\n [imageUrl]=\"character.imageUrl\"\n itemProp=\"character\"\n schemaType=\"Person\"\n [showMoreLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_MORE_LABEL]\"\n [showLessLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_LESS_LABEL]\"\n ></rtsee-story-element-thumbnail>\n </li>\n }\n </ul>\n </section>\n }\n\n <!-- Artifacts -->\n @if (story.artifacts.length) {\n <section\n class=\"rtsee-story-details-elements\"\n aria-labelledby=\"rtsee-story-details-artifacts-heading\"\n >\n <header class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M6 3h12l4 6-10 13L2 9z\" />\n <path d=\"M11 3 8 9l4 13 4-13-3-6\" />\n <path d=\"M2 9h20\" />\n </svg>\n <h2 id=\"rtsee-story-details-artifacts-heading\">\n {{ presentation.labels[RTSeeTextLabelKeys.ARTIFACTS_LABEL] }}\n </h2>\n </header>\n <ul class=\"rtsee-story-details-elements-grid\" role=\"list\">\n @for (artifact of story.artifacts; track artifact.id) {\n <li class=\"rtsee-story-details-elements-item\">\n <rtsee-story-element-thumbnail\n [name]=\"artifact.name\"\n [description]=\"artifact.description\"\n [imageUrl]=\"artifact.imageUrl\"\n itemProp=\"mentions\"\n schemaType=\"Thing\"\n [showMoreLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_MORE_LABEL]\"\n [showLessLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_LESS_LABEL]\"\n ></rtsee-story-element-thumbnail>\n </li>\n }\n </ul>\n </section>\n }\n\n <!-- Places -->\n @if (story.locations.length) {\n <section\n class=\"rtsee-story-details-elements\"\n aria-labelledby=\"rtsee-story-details-places-heading\"\n >\n <header class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z\" />\n <circle cx=\"12\" cy=\"10\" r=\"3\" />\n </svg>\n <h2 id=\"rtsee-story-details-places-heading\">\n {{ presentation.labels[RTSeeTextLabelKeys.LOCATIONS_LABEL] }}\n </h2>\n </header>\n <ul class=\"rtsee-story-details-elements-grid\" role=\"list\">\n @for (place of story.locations; track place.id) {\n <li class=\"rtsee-story-details-elements-item\">\n <rtsee-story-element-thumbnail\n [name]=\"place.name\"\n [description]=\"place.description\"\n [imageUrl]=\"place.imageUrl\"\n itemProp=\"contentLocation\"\n schemaType=\"Place\"\n [showMoreLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_MORE_LABEL]\"\n [showLessLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_LESS_LABEL]\"\n ></rtsee-story-element-thumbnail>\n </li>\n }\n </ul>\n </section>\n }\n\n @if (messenger) {\n <rtsee-entity-preview\n [entity]=\"story\"\n [messenger]=\"messenger\"\n ></rtsee-entity-preview>\n }\n\n @if (story.categories?.length && false) {\n <div class=\"rtsee-story-details-categories\">\n @for (category of story.categories; track category.id) {\n <p-tag [value]=\"category.name\"></p-tag>\n }\n </div>\n }\n\n @if (story.tags?.length && false) {\n <div class=\"rtsee-story-details-labels\">\n @for (tag of story.tags; track tag.id) {\n <p-chip class=\"rtsee-story-details-label\">\n @if (tag.symbol) {\n <span class=\"rtsee-story-details-label-left\">{{\n tag.symbol\n }}</span>\n }\n <span class=\"rtsee-story-details-label-right\">{{\n tag.name\n }}</span>\n </p-chip>\n }\n </div>\n }\n\n <div class=\"rtsee-story-details-description-history\"></div>\n @if (story.isPublished && false) {\n <div class=\"rtsee-story-details-feedback\">\n <p>feedback here</p>\n </div>\n } @else if (messenger && story.discussion && false) {\n <div class=\"rtsee-story-details-votes\">\n <p class=\"rtsee-story-details-comments-label\">Comments</p>\n <rtsee-chat\n [messenger]=\"messenger\"\n [chatData]=\"story.discussion\"\n ></rtsee-chat>\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "component", type: Chip, selector: "p-chip", inputs: ["label", "icon", "image", "alt", "styleClass", "disabled", "removable", "removeIcon", "chipProps"], outputs: ["onRemove", "onImageError"] }, { kind: "component", type: Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { 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: "component", type: ChatComponent, selector: "rtsee-chat", inputs: ["chat", "chatData", "entity", "messenger"] }, { kind: "component", type: RtseeEntityPreviewComponent, selector: "rtsee-entity-preview", inputs: ["entity", "messenger"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: StoryElementThumbnailComponent, selector: "rtsee-story-element-thumbnail", inputs: ["name", "description", "imageUrl", "itemProp", "schemaType", "horizontal", "showMoreLabel", "showLessLabel"] }, { kind: "component", type: StorySlidesPreviewComponent, selector: "rtsee-story-slides-preview", inputs: ["story", "slidesLabel", "minuteLabel", "count"] }] }); }
6284
7012
  }
6285
7013
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StoryDetailsComponent, decorators: [{
6286
7014
  type: Component,
6287
- args: [{ selector: 'rtsee-story-details', imports: [Chip, Tag, Button, ChatComponent, RtseeEntityPreviewComponent, RouterLink], template: "<div class=\"rtsee-story-details\">\n <section\n class=\"rtsee-story-details-cover\"\n [style.background-image]=\"'url(' + story.imageUrl + ')'\"\n >\n <button\n class=\"rtsee-story-details-btn-close\"\n [routerLink]=\"[presentation.homePage]\"\n (click)=\"presentation.closeStoryDetails()\"\n aria-label=\"Close\"\n >\n <span class=\"pi pi-times\"></span>\n </button>\n <div class=\"rtsee-story-details-cover-overlay\"></div>\n <div class=\"rtsee-story-details-cover-content\">\n <div class=\"rtsee-story-details-cover-badges\">\n @if (story.categories?.length) {\n @for (category of story.categories; track category.id) {\n <span class=\"rtsee-story-details-tag-pill\">\n {{ category.name }}\n </span>\n }\n }\n @if (story.tags?.length) {\n @for (tag of story.tags; track tag.id) {\n <span\n class=\"rtsee-story-details-tag-pill rtsee-story-details-tag-pill-label\"\n >\n @if (tag.symbol) {\n <span class=\"symbol\">{{ tag.symbol }}</span>\n }\n {{ tag.name }}\n </span>\n }\n }\n </div>\n\n <h1 class=\"rtsee-story-details-cover-title\">{{ story.name }}</h1>\n\n <div class=\"rtsee-story-details-cover-meta\">\n @if (story.rating !== null) {\n <div class=\"rtsee-story-details-cover-rating\">\n <span class=\"rtsee-story-details-stars\">\n @for (star of getStarArray(story.rating); track $index) {\n <span\n class=\"rtsee-story-details-star\"\n [class.rtsee-story-details-star-full]=\"star === 'full'\"\n [class.rtsee-story-details-star-half]=\"star === 'half'\"\n [class.rtsee-story-details-star-empty]=\"star === 'empty'\"\n >\n @if (star === \"full\") {\n \u2605\n } @else if (star === \"half\") {\n \u2605\n } @else {\n \u2606\n }\n </span>\n }\n </span>\n <span class=\"rtsee-story-details-rating-number\">{{\n story.rating\n }}</span>\n @if (story.ratesCount !== null) {\n <span class=\"rtsee-story-details-rating-count\">\n <span>(</span>\n <span>{{ presentation.labels[RTSeeTextLabelKeys.FEEDBACKS_LABEL] }}:</span>\n <span>&nbsp;</span>\n <span>{{ story.ratesCount }}</span>\n <span>)</span>\n </span\n >\n }\n </div>\n }\n\n <div class=\"rtsee-story-details-cover-info-chips\">\n @if (story.averageReadTime) {\n <span class=\"rtsee-story-details-info-chip\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <polyline points=\"12 6 12 12 16 14\" />\n </svg>\n <span>\n <span>{{ Math.ceil(story.averageReadTime / 60) }}</span>\n <span>&nbsp;</span>\n <span>{{ presentation.labels[RTSeeTextLabelKeys.MINUTE_LABEL] }}</span>\n </span>\n </span>\n }\n @if (story.totalSlides) {\n <span class=\"rtsee-story-details-info-chip\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z\" />\n <path d=\"M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z\" />\n </svg>\n <span>\n <span>{{ presentation.labels[RTSeeTextLabelKeys.STORY_PAGES_LABEL] }}:</span>\n <span>&nbsp;</span>\n <span>{{ story.totalSlides }}</span>\n </span>\n </span>\n }\n </div>\n </div>\n </div>\n </section>\n\n <!-- CONTENT AREA -->\n <div class=\"rtsee-story-details-content-wrapper\">\n <div class=\"rtsee-story-details-cover-actions\">\n <p-button\n (click)=\"presentation.setActiveStory(story)\"\n [severity]=\"'primary'\"\n [label]=\"story.preselectedSlideNumber ? presentation.labels[RTSeeTextLabelKeys.CONTINUE_READING_BUTTON] : presentation.labels[RTSeeTextLabelKeys.START_READING_BUTTON]\"\n class=\"rtsee-story-details-start-reading rts-action-button\"\n >\n </p-button>\n </div>\n <div class=\"rtsee-story-details-content-grid\">\n <!-- Description Card -->\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z\" />\n <path d=\"M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_ABOUT_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\">\n {{ story.description }}\n </p>\n </article>\n\n @if (story.history) {\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <polyline points=\"12 6 12 12 14 14\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_HISTORY_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\">\n {{ story.history }}\n </p>\n </article>\n }\n\n @if (story.moralMeaning) {\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_MORAL_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\">\n {{ story.moralMeaning }}\n </p>\n </article>\n }\n </div>\n\n @if (messenger) {\n <rtsee-entity-preview\n [entity]=\"story\"\n [messenger]=\"messenger\"\n ></rtsee-entity-preview>\n }\n\n @if (story.categories?.length && false) {\n <div class=\"rtsee-story-details-categories\">\n @for (category of story.categories; track category.id) {\n <p-tag [value]=\"category.name\"></p-tag>\n }\n </div>\n }\n\n @if (story.tags?.length && false) {\n <div class=\"rtsee-story-details-labels\">\n @for (tag of story.tags; track tag.id) {\n <p-chip class=\"rtsee-story-details-label\">\n @if (tag.symbol) {\n <span class=\"rtsee-story-details-label-left\">{{\n tag.symbol\n }}</span>\n }\n <span class=\"rtsee-story-details-label-right\">{{\n tag.name\n }}</span>\n </p-chip>\n }\n </div>\n }\n\n <div class=\"rtsee-story-details-description-history\"></div>\n @if (story.isPublished && false) {\n <div class=\"rtsee-story-details-feedback\">\n <p>feedback here</p>\n </div>\n } @else if (messenger && story.discussion && false) {\n <div class=\"rtsee-story-details-votes\">\n <p class=\"rtsee-story-details-comments-label\">Comments</p>\n <rtsee-chat\n [messenger]=\"messenger\"\n [chatData]=\"story.discussion\"\n ></rtsee-chat>\n </div>\n }\n </div>\n</div>\n" }]
7015
+ args: [{ selector: 'rtsee-story-details', imports: [Chip, Tag, Button, ChatComponent, RtseeEntityPreviewComponent, RouterLink, StoryElementThumbnailComponent, StorySlidesPreviewComponent], template: "<div\n class=\"rtsee-story-details\"\n itemscope\n itemtype=\"https://schema.org/ShortStory\"\n>\n @if (story.imageUrl) {\n <meta itemprop=\"image\" [attr.content]=\"story.imageUrl\" />\n }\n @if (story.languageKey) {\n <meta itemprop=\"inLanguage\" [attr.content]=\"story.languageKey === 'ua' ? 'uk' : 'en'\" />\n }\n @if (story.totalSlides) {\n <meta itemprop=\"numberOfPages\" [attr.content]=\"story.totalSlides\" />\n }\n <section\n class=\"rtsee-story-details-cover\"\n [style.background-image]=\"'url(' + story.imageUrl + ')'\"\n >\n <button\n class=\"rtsee-story-details-btn-close\"\n [routerLink]=\"[presentation.homePage]\"\n (click)=\"presentation.closeStoryDetails()\"\n aria-label=\"Close\"\n >\n <span class=\"pi pi-times\"></span>\n </button>\n <div class=\"rtsee-story-details-cover-overlay\"></div>\n <div class=\"rtsee-story-details-cover-content\">\n <div class=\"rtsee-story-details-cover-badges\">\n @if (story.categories?.length && false) {\n @for (category of story.categories; track category.id) {\n <span class=\"rtsee-story-details-tag-pill\">\n {{ category.name }}\n </span>\n }\n }\n @if (story.tags?.length) {\n @for (tag of story.tags; track tag.id) {\n <span\n class=\"rtsee-story-details-tag-pill rtsee-story-details-tag-pill-label\"\n >\n @if (tag.symbol) {\n <span class=\"symbol\">{{ tag.symbol }}</span>\n }\n {{ tag.name }}\n </span>\n }\n }\n </div>\n\n <h1 class=\"rtsee-story-details-cover-title\" itemprop=\"name\">{{ story.name }}</h1>\n\n <div class=\"rtsee-story-details-cover-meta\">\n @if (story.rating !== null) {\n <div\n class=\"rtsee-story-details-cover-rating\"\n itemprop=\"aggregateRating\"\n itemscope\n itemtype=\"https://schema.org/AggregateRating\"\n >\n <meta itemprop=\"bestRating\" content=\"5\" />\n <meta itemprop=\"worstRating\" content=\"1\" />\n <span class=\"rtsee-story-details-stars\">\n @for (star of getStarArray(story.rating); track $index) {\n <span\n class=\"rtsee-story-details-star\"\n [class.rtsee-story-details-star-full]=\"star === 'full'\"\n [class.rtsee-story-details-star-half]=\"star === 'half'\"\n [class.rtsee-story-details-star-empty]=\"star === 'empty'\"\n >\n @if (star === \"full\") {\n \u2605\n } @else if (star === \"half\") {\n \u2605\n } @else {\n \u2606\n }\n </span>\n }\n </span>\n <span class=\"rtsee-story-details-rating-number\" itemprop=\"ratingValue\">{{\n story.rating\n }}</span>\n @if (story.ratesCount !== null) {\n <meta itemprop=\"ratingCount\" [attr.content]=\"story.ratesCount\" />\n <span class=\"rtsee-story-details-rating-count\">\n <span>(</span>\n <span>{{ presentation.labels[RTSeeTextLabelKeys.FEEDBACKS_LABEL] }}:</span>\n <span>&nbsp;</span>\n <span itemprop=\"reviewCount\">{{ story.ratesCount }}</span>\n <span>)</span>\n </span\n >\n }\n </div>\n }\n </div>\n </div>\n </section>\n\n <!-- CONTENT AREA -->\n <div class=\"rtsee-story-details-content-wrapper\">\n <!-- Reading panel: what the user is about to read + the main CTA -->\n <section class=\"rtsee-story-details-reading-panel\" aria-label=\"{{ story.name }}\">\n @if (story.totalSlides) {\n <rtsee-story-slides-preview\n [story]=\"story\"\n [slidesLabel]=\"presentation.labels[RTSeeTextLabelKeys.STORY_PAGES_LABEL]\"\n [minuteLabel]=\"presentation.labels[RTSeeTextLabelKeys.MINUTE_LABEL]\"\n ></rtsee-story-slides-preview>\n }\n <p-button\n (click)=\"presentation.setActiveStory(story)\"\n [severity]=\"'primary'\"\n [label]=\"story.preselectedSlideNumber ? presentation.labels[RTSeeTextLabelKeys.CONTINUE_READING_BUTTON] : presentation.labels[RTSeeTextLabelKeys.START_READING_BUTTON]\"\n icon=\"pi pi-book\"\n iconPos=\"left\"\n class=\"rtsee-story-details-start-reading rts-action-button\"\n >\n </p-button>\n </section>\n <div class=\"rtsee-story-details-content-grid\">\n <!-- Description Card -->\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z\" />\n <path d=\"M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_ABOUT_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\" itemprop=\"description\">\n {{ story.description }}\n </p>\n </article>\n\n @if (story.history) {\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <polyline points=\"12 6 12 12 14 14\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_HISTORY_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\" itemprop=\"backstory\">\n {{ story.history }}\n </p>\n </article>\n }\n\n @if (story.moralMeaning) {\n <article class=\"rtsee-story-details-glass-card\">\n <div class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z\" />\n </svg>\n <h2>{{presentation.labels[RTSeeTextLabelKeys.STORY_MORAL_LABEL]}}</h2>\n </div>\n <p class=\"rtsee-story-details-glass-card-text\" itemprop=\"teaches\">\n {{ story.moralMeaning }}\n </p>\n </article>\n }\n </div>\n\n <!-- Author -->\n @if (story.author) {\n <section\n class=\"rtsee-story-details-elements\"\n aria-labelledby=\"rtsee-story-details-author-heading\"\n >\n <header class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M12 20h9\" />\n <path d=\"M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z\" />\n </svg>\n <h2 id=\"rtsee-story-details-author-heading\">\n {{ presentation.labels[RTSeeTextLabelKeys.STORY_AUTHOR_LABEL] }}\n </h2>\n </header>\n <div class=\"rtsee-story-details-elements-featured\">\n <rtsee-story-element-thumbnail\n [name]=\"story.author.name\"\n [description]=\"story.author.description\"\n [imageUrl]=\"story.author.imageUrl\"\n itemProp=\"author\"\n schemaType=\"Person\"\n [horizontal]=\"true\"\n [showMoreLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_MORE_LABEL]\"\n [showLessLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_LESS_LABEL]\"\n ></rtsee-story-element-thumbnail>\n </div>\n </section>\n }\n\n <!-- Characters -->\n @if (story.characters.length) {\n <section\n class=\"rtsee-story-details-elements\"\n aria-labelledby=\"rtsee-story-details-characters-heading\"\n >\n <header class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2\" />\n <circle cx=\"9\" cy=\"7\" r=\"4\" />\n <path d=\"M23 21v-2a4 4 0 0 0-3-3.87\" />\n <path d=\"M16 3.13a4 4 0 0 1 0 7.75\" />\n </svg>\n <h2 id=\"rtsee-story-details-characters-heading\">\n {{ presentation.labels[RTSeeTextLabelKeys.CHARACTERS_LABEL] }}\n </h2>\n </header>\n <ul class=\"rtsee-story-details-elements-grid\" role=\"list\">\n @for (character of story.characters; track character.id) {\n <li class=\"rtsee-story-details-elements-item\">\n <rtsee-story-element-thumbnail\n [name]=\"character.name\"\n [description]=\"character.description\"\n [imageUrl]=\"character.imageUrl\"\n itemProp=\"character\"\n schemaType=\"Person\"\n [showMoreLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_MORE_LABEL]\"\n [showLessLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_LESS_LABEL]\"\n ></rtsee-story-element-thumbnail>\n </li>\n }\n </ul>\n </section>\n }\n\n <!-- Artifacts -->\n @if (story.artifacts.length) {\n <section\n class=\"rtsee-story-details-elements\"\n aria-labelledby=\"rtsee-story-details-artifacts-heading\"\n >\n <header class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M6 3h12l4 6-10 13L2 9z\" />\n <path d=\"M11 3 8 9l4 13 4-13-3-6\" />\n <path d=\"M2 9h20\" />\n </svg>\n <h2 id=\"rtsee-story-details-artifacts-heading\">\n {{ presentation.labels[RTSeeTextLabelKeys.ARTIFACTS_LABEL] }}\n </h2>\n </header>\n <ul class=\"rtsee-story-details-elements-grid\" role=\"list\">\n @for (artifact of story.artifacts; track artifact.id) {\n <li class=\"rtsee-story-details-elements-item\">\n <rtsee-story-element-thumbnail\n [name]=\"artifact.name\"\n [description]=\"artifact.description\"\n [imageUrl]=\"artifact.imageUrl\"\n itemProp=\"mentions\"\n schemaType=\"Thing\"\n [showMoreLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_MORE_LABEL]\"\n [showLessLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_LESS_LABEL]\"\n ></rtsee-story-element-thumbnail>\n </li>\n }\n </ul>\n </section>\n }\n\n <!-- Places -->\n @if (story.locations.length) {\n <section\n class=\"rtsee-story-details-elements\"\n aria-labelledby=\"rtsee-story-details-places-heading\"\n >\n <header class=\"rtsee-story-details-glass-card-header\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z\" />\n <circle cx=\"12\" cy=\"10\" r=\"3\" />\n </svg>\n <h2 id=\"rtsee-story-details-places-heading\">\n {{ presentation.labels[RTSeeTextLabelKeys.LOCATIONS_LABEL] }}\n </h2>\n </header>\n <ul class=\"rtsee-story-details-elements-grid\" role=\"list\">\n @for (place of story.locations; track place.id) {\n <li class=\"rtsee-story-details-elements-item\">\n <rtsee-story-element-thumbnail\n [name]=\"place.name\"\n [description]=\"place.description\"\n [imageUrl]=\"place.imageUrl\"\n itemProp=\"contentLocation\"\n schemaType=\"Place\"\n [showMoreLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_MORE_LABEL]\"\n [showLessLabel]=\"presentation.labels[RTSeeTextLabelKeys.SHOW_LESS_LABEL]\"\n ></rtsee-story-element-thumbnail>\n </li>\n }\n </ul>\n </section>\n }\n\n @if (messenger) {\n <rtsee-entity-preview\n [entity]=\"story\"\n [messenger]=\"messenger\"\n ></rtsee-entity-preview>\n }\n\n @if (story.categories?.length && false) {\n <div class=\"rtsee-story-details-categories\">\n @for (category of story.categories; track category.id) {\n <p-tag [value]=\"category.name\"></p-tag>\n }\n </div>\n }\n\n @if (story.tags?.length && false) {\n <div class=\"rtsee-story-details-labels\">\n @for (tag of story.tags; track tag.id) {\n <p-chip class=\"rtsee-story-details-label\">\n @if (tag.symbol) {\n <span class=\"rtsee-story-details-label-left\">{{\n tag.symbol\n }}</span>\n }\n <span class=\"rtsee-story-details-label-right\">{{\n tag.name\n }}</span>\n </p-chip>\n }\n </div>\n }\n\n <div class=\"rtsee-story-details-description-history\"></div>\n @if (story.isPublished && false) {\n <div class=\"rtsee-story-details-feedback\">\n <p>feedback here</p>\n </div>\n } @else if (messenger && story.discussion && false) {\n <div class=\"rtsee-story-details-votes\">\n <p class=\"rtsee-story-details-comments-label\">Comments</p>\n <rtsee-chat\n [messenger]=\"messenger\"\n [chatData]=\"story.discussion\"\n ></rtsee-chat>\n </div>\n }\n </div>\n</div>\n" }]
6288
7016
  }], propDecorators: { story: [{
6289
7017
  type: Input
6290
7018
  }], presentation: [{
@@ -6303,7 +7031,7 @@ class PresentationComponent {
6303
7031
  }
6304
7032
  }
6305
7033
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PresentationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6306
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: PresentationComponent, isStandalone: true, selector: "rtsee-presentation", inputs: { presentation: "presentation", messenger: "messenger" }, ngImport: i0, template: "<div class=\"rtsee-presentation\">\n <div class=\"rtsee-presentation-header-block\">\n <rtsee-presentation-header [presentation]=\"presentation\"\n ></rtsee-presentation-header>\n </div>\n\n @if (presentation.storyLoadInProgress) {\n <rtsee-preloader [diameter]=\"15\"></rtsee-preloader>\n }\n\n @if(presentation.isDashboardOpen && presentation.dashboard) {\n <rtsee-presentations-dashboard [dashboard]=\"presentation.dashboard\"\n ></rtsee-presentations-dashboard>\n }\n\n @if (presentation.settings.isExpanded) {\n <rtsee-presentation-settings [settings]=\"presentation.settings\"\n [presentation]=\"presentation\"\n ></rtsee-presentation-settings>\n }\n\n @if (presentation.isCategoriesListVisible && !presentation.storyLoadInProgress) {\n <div class=\"rtsee-presentation-categories-list\">\n @if (presentation.categoriesLoadInProgress) {\n <rtsee-preloader [diameter]=\"15\"></rtsee-preloader>\n }\n\n @if (presentation.activeSessionsStories.length) {\n <div class=\"rtsee-presentation-category-header\">\n <h2 class=\"rtsee-presentation-category-name\">{{presentation.labels[RTSeeTextLabelKeys.CONTINUE_SESSIONS_LABEL]}}</h2>\n </div>\n <div class=\"rtsee-presentation-stories-list\">\n @for (story of presentation.activeSessionsStories; track story.id) {\n <div class=\"rtsee-presentation-story-thumbnail-container\"\n (click)=\"onStoryThumbnailClicked(story)\"\n >\n <rtsee-story-thumbnail\n [story]=\"story\"\n [presentation]=\"presentation\"\n ></rtsee-story-thumbnail>\n </div>\n }\n </div>\n }\n\n @for (category of presentation.categories; track category.id) {\n <div class=\"rtsee-presentation-category-header\">\n <h2 class=\"rtsee-presentation-category-name\">{{ category.name }}</h2>\n <span class=\"rtsee-presentation-category-description\">{{ category.description }}</span>\n </div>\n <div class=\"rtsee-presentation-stories-list\">\n @for (story of category.stories; track story.id) {\n <div class=\"rtsee-presentation-story-thumbnail-container\"\n (click)=\"onStoryThumbnailClicked(story)\"\n >\n <rtsee-story-thumbnail\n [story]=\"story\"\n [presentation]=\"presentation\"\n ></rtsee-story-thumbnail>\n </div>\n }\n </div>\n }\n </div>\n }\n\n @if (presentation.activeStory && !presentation.isDashboardOpen) {\n <div class=\"rtsee-presentation-story-viewer-container\">\n <rtsee-story-player [story]=\"presentation.activeStory\"\n [settings]=\"presentation.settings\"\n [presentation]=\"presentation\"\n ></rtsee-story-player>\n </div>\n } @else if (presentation.openedStoryDetails && !presentation.isDashboardOpen) {\n <rtsee-story-details [story]=\"presentation.openedStoryDetails\"\n [presentation]=\"presentation\"\n [messenger]=\"messenger\"\n ></rtsee-story-details>\n }\n</div>\n", dependencies: [{ kind: "component", type: PresentationHeaderComponent, selector: "rtsee-presentation-header", inputs: ["presentation"] }, { kind: "component", type: StoryThumbnailComponent, selector: "rtsee-story-thumbnail", inputs: ["story", "presentation"] }, { kind: "component", type: StoryPlayerComponent, selector: "rtsee-story-player", inputs: ["story", "settings", "presentation"] }, { kind: "component", type: PreloaderComponent, selector: "rtsee-preloader", inputs: ["diameter", "color"] }, { kind: "component", type: PresentationsDashboardComponent, selector: "rtsee-presentations-dashboard", inputs: ["dashboard"] }, { kind: "component", type: PresentationSettingsComponent, selector: "rtsee-presentation-settings", inputs: ["settings", "presentation"] }, { kind: "component", type: StoryDetailsComponent, selector: "rtsee-story-details", inputs: ["story", "presentation", "messenger"] }] }); }
7034
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: PresentationComponent, isStandalone: true, selector: "rtsee-presentation", inputs: { presentation: "presentation", messenger: "messenger" }, ngImport: i0, template: "<div class=\"rtsee-presentation\">\n <div class=\"rtsee-presentation-header-block\">\n <rtsee-presentation-header [presentation]=\"presentation\"\n ></rtsee-presentation-header>\n </div>\n\n @if (presentation.storyLoadInProgress) {\n <rtsee-preloader [diameter]=\"15\"></rtsee-preloader>\n }\n\n @if(presentation.isDashboardOpen && presentation.dashboard) {\n <rtsee-presentations-dashboard [dashboard]=\"presentation.dashboard\"\n ></rtsee-presentations-dashboard>\n }\n\n @if (presentation.settings.isExpanded) {\n <rtsee-presentation-settings [settings]=\"presentation.settings\"\n [presentation]=\"presentation\"\n ></rtsee-presentation-settings>\n }\n\n @if (presentation.isCategoriesListVisible && !presentation.storyLoadInProgress) {\n <div class=\"rtsee-presentation-categories-list\">\n @if (presentation.categoriesLoadInProgress) {\n <rtsee-preloader [diameter]=\"15\"></rtsee-preloader>\n }\n\n @if (presentation.activeSessionsStories.length) {\n <div class=\"rtsee-presentation-category-header\">\n <h2 class=\"rtsee-presentation-category-name\">{{presentation.labels[RTSeeTextLabelKeys.CONTINUE_SESSIONS_LABEL]}}</h2>\n </div>\n <div class=\"rtsee-presentation-stories-list\">\n @for (story of presentation.activeSessionsStories; track story.id) {\n <div class=\"rtsee-presentation-story-thumbnail-container\"\n (click)=\"onStoryThumbnailClicked(story)\"\n >\n <rtsee-story-thumbnail\n [story]=\"story\"\n [presentation]=\"presentation\"\n ></rtsee-story-thumbnail>\n </div>\n }\n </div>\n }\n\n @for (category of presentation.categories; track category.id) {\n <div class=\"rtsee-presentation-category-header\">\n <h2 class=\"rtsee-presentation-category-name\">{{ category.name }}</h2>\n <span class=\"rtsee-presentation-category-description\">{{ category.description }}</span>\n </div>\n <div class=\"rtsee-presentation-stories-list\">\n @for (story of category.stories; track story.id) {\n <div class=\"rtsee-presentation-story-thumbnail-container\"\n (click)=\"onStoryThumbnailClicked(story)\"\n >\n <rtsee-story-thumbnail\n [story]=\"story\"\n [presentation]=\"presentation\"\n ></rtsee-story-thumbnail>\n </div>\n }\n </div>\n }\n </div>\n }\n\n @if (presentation.activeStory && !presentation.isDashboardOpen) {\n <div class=\"rtsee-presentation-story-viewer-container\"\n [class.rtsee-presentation-story-viewer-container-dark]=\"presentation.settings.isDarkReadingTheme\"\n >\n <rtsee-story-player [story]=\"presentation.activeStory\"\n [settings]=\"presentation.settings\"\n [presentation]=\"presentation\"\n ></rtsee-story-player>\n </div>\n } @else if (presentation.openedStoryDetails && !presentation.isDashboardOpen) {\n <rtsee-story-details [story]=\"presentation.openedStoryDetails\"\n [presentation]=\"presentation\"\n [messenger]=\"messenger\"\n ></rtsee-story-details>\n }\n</div>\n", dependencies: [{ kind: "component", type: PresentationHeaderComponent, selector: "rtsee-presentation-header", inputs: ["presentation"] }, { kind: "component", type: StoryThumbnailComponent, selector: "rtsee-story-thumbnail", inputs: ["story", "presentation"] }, { kind: "component", type: StoryPlayerComponent, selector: "rtsee-story-player", inputs: ["story", "settings", "presentation"] }, { kind: "component", type: PreloaderComponent, selector: "rtsee-preloader", inputs: ["diameter", "color"] }, { kind: "component", type: PresentationsDashboardComponent, selector: "rtsee-presentations-dashboard", inputs: ["dashboard"] }, { kind: "component", type: PresentationSettingsComponent, selector: "rtsee-presentation-settings", inputs: ["settings", "presentation"] }, { kind: "component", type: StoryDetailsComponent, selector: "rtsee-story-details", inputs: ["story", "presentation", "messenger"] }] }); }
6307
7035
  }
6308
7036
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: PresentationComponent, decorators: [{
6309
7037
  type: Component,
@@ -6316,7 +7044,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
6316
7044
  PresentationsDashboardComponent,
6317
7045
  PresentationSettingsComponent,
6318
7046
  StoryDetailsComponent,
6319
- ], standalone: true, template: "<div class=\"rtsee-presentation\">\n <div class=\"rtsee-presentation-header-block\">\n <rtsee-presentation-header [presentation]=\"presentation\"\n ></rtsee-presentation-header>\n </div>\n\n @if (presentation.storyLoadInProgress) {\n <rtsee-preloader [diameter]=\"15\"></rtsee-preloader>\n }\n\n @if(presentation.isDashboardOpen && presentation.dashboard) {\n <rtsee-presentations-dashboard [dashboard]=\"presentation.dashboard\"\n ></rtsee-presentations-dashboard>\n }\n\n @if (presentation.settings.isExpanded) {\n <rtsee-presentation-settings [settings]=\"presentation.settings\"\n [presentation]=\"presentation\"\n ></rtsee-presentation-settings>\n }\n\n @if (presentation.isCategoriesListVisible && !presentation.storyLoadInProgress) {\n <div class=\"rtsee-presentation-categories-list\">\n @if (presentation.categoriesLoadInProgress) {\n <rtsee-preloader [diameter]=\"15\"></rtsee-preloader>\n }\n\n @if (presentation.activeSessionsStories.length) {\n <div class=\"rtsee-presentation-category-header\">\n <h2 class=\"rtsee-presentation-category-name\">{{presentation.labels[RTSeeTextLabelKeys.CONTINUE_SESSIONS_LABEL]}}</h2>\n </div>\n <div class=\"rtsee-presentation-stories-list\">\n @for (story of presentation.activeSessionsStories; track story.id) {\n <div class=\"rtsee-presentation-story-thumbnail-container\"\n (click)=\"onStoryThumbnailClicked(story)\"\n >\n <rtsee-story-thumbnail\n [story]=\"story\"\n [presentation]=\"presentation\"\n ></rtsee-story-thumbnail>\n </div>\n }\n </div>\n }\n\n @for (category of presentation.categories; track category.id) {\n <div class=\"rtsee-presentation-category-header\">\n <h2 class=\"rtsee-presentation-category-name\">{{ category.name }}</h2>\n <span class=\"rtsee-presentation-category-description\">{{ category.description }}</span>\n </div>\n <div class=\"rtsee-presentation-stories-list\">\n @for (story of category.stories; track story.id) {\n <div class=\"rtsee-presentation-story-thumbnail-container\"\n (click)=\"onStoryThumbnailClicked(story)\"\n >\n <rtsee-story-thumbnail\n [story]=\"story\"\n [presentation]=\"presentation\"\n ></rtsee-story-thumbnail>\n </div>\n }\n </div>\n }\n </div>\n }\n\n @if (presentation.activeStory && !presentation.isDashboardOpen) {\n <div class=\"rtsee-presentation-story-viewer-container\">\n <rtsee-story-player [story]=\"presentation.activeStory\"\n [settings]=\"presentation.settings\"\n [presentation]=\"presentation\"\n ></rtsee-story-player>\n </div>\n } @else if (presentation.openedStoryDetails && !presentation.isDashboardOpen) {\n <rtsee-story-details [story]=\"presentation.openedStoryDetails\"\n [presentation]=\"presentation\"\n [messenger]=\"messenger\"\n ></rtsee-story-details>\n }\n</div>\n" }]
7047
+ ], standalone: true, template: "<div class=\"rtsee-presentation\">\n <div class=\"rtsee-presentation-header-block\">\n <rtsee-presentation-header [presentation]=\"presentation\"\n ></rtsee-presentation-header>\n </div>\n\n @if (presentation.storyLoadInProgress) {\n <rtsee-preloader [diameter]=\"15\"></rtsee-preloader>\n }\n\n @if(presentation.isDashboardOpen && presentation.dashboard) {\n <rtsee-presentations-dashboard [dashboard]=\"presentation.dashboard\"\n ></rtsee-presentations-dashboard>\n }\n\n @if (presentation.settings.isExpanded) {\n <rtsee-presentation-settings [settings]=\"presentation.settings\"\n [presentation]=\"presentation\"\n ></rtsee-presentation-settings>\n }\n\n @if (presentation.isCategoriesListVisible && !presentation.storyLoadInProgress) {\n <div class=\"rtsee-presentation-categories-list\">\n @if (presentation.categoriesLoadInProgress) {\n <rtsee-preloader [diameter]=\"15\"></rtsee-preloader>\n }\n\n @if (presentation.activeSessionsStories.length) {\n <div class=\"rtsee-presentation-category-header\">\n <h2 class=\"rtsee-presentation-category-name\">{{presentation.labels[RTSeeTextLabelKeys.CONTINUE_SESSIONS_LABEL]}}</h2>\n </div>\n <div class=\"rtsee-presentation-stories-list\">\n @for (story of presentation.activeSessionsStories; track story.id) {\n <div class=\"rtsee-presentation-story-thumbnail-container\"\n (click)=\"onStoryThumbnailClicked(story)\"\n >\n <rtsee-story-thumbnail\n [story]=\"story\"\n [presentation]=\"presentation\"\n ></rtsee-story-thumbnail>\n </div>\n }\n </div>\n }\n\n @for (category of presentation.categories; track category.id) {\n <div class=\"rtsee-presentation-category-header\">\n <h2 class=\"rtsee-presentation-category-name\">{{ category.name }}</h2>\n <span class=\"rtsee-presentation-category-description\">{{ category.description }}</span>\n </div>\n <div class=\"rtsee-presentation-stories-list\">\n @for (story of category.stories; track story.id) {\n <div class=\"rtsee-presentation-story-thumbnail-container\"\n (click)=\"onStoryThumbnailClicked(story)\"\n >\n <rtsee-story-thumbnail\n [story]=\"story\"\n [presentation]=\"presentation\"\n ></rtsee-story-thumbnail>\n </div>\n }\n </div>\n }\n </div>\n }\n\n @if (presentation.activeStory && !presentation.isDashboardOpen) {\n <div class=\"rtsee-presentation-story-viewer-container\"\n [class.rtsee-presentation-story-viewer-container-dark]=\"presentation.settings.isDarkReadingTheme\"\n >\n <rtsee-story-player [story]=\"presentation.activeStory\"\n [settings]=\"presentation.settings\"\n [presentation]=\"presentation\"\n ></rtsee-story-player>\n </div>\n } @else if (presentation.openedStoryDetails && !presentation.isDashboardOpen) {\n <rtsee-story-details [story]=\"presentation.openedStoryDetails\"\n [presentation]=\"presentation\"\n [messenger]=\"messenger\"\n ></rtsee-story-details>\n }\n</div>\n" }]
6320
7048
  }], ctorParameters: () => [], propDecorators: { presentation: [{
6321
7049
  type: Input
6322
7050
  }], messenger: [{
@@ -6364,7 +7092,7 @@ class RtseeNavComponent {
6364
7092
  return false;
6365
7093
  }
6366
7094
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RtseeNavComponent, deps: [{ token: RTSeeAuthService }], target: i0.ɵɵFactoryTarget.Component }); }
6367
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RtseeNavComponent, isStandalone: true, selector: "rtsee-nav", inputs: { config: "config", rtsee: "rtsee" }, ngImport: i0, template: "<div class=\"rtsee-nav\">\n @if (config.siteLogo) {\n <div class=\"rtsee-site-logo\">\n <p-image [src]=\"config.siteLogo.src\" class=\"rtsee-site-logo-image\"></p-image>\n <p-divider class=\"rtsee-site-logo-divider\" />\n </div>\n }\n <div class=\"rtsee-nav-bar\">\n @for (route of config.routes; track route.name) {\n <a [routerLink]=\"route.link\"\n routerLinkActive=\"rtsee-nav-item-active\"\n [ngClass]=\"{'rtsee-nav-item-hidden': !route.isEnabled || !route.iconClassName}\"\n [routerLinkActiveOptions]=\"{ exact: route.exactActiveLinkMatcher || false }\"\n class=\"rtsee-nav-item\"\n [pTooltip]=\"route.name\"\n >\n <span [ngClass]=\"route.iconClassName\"\n class=\"rtsee-nav-item-icon\" >\n </span>\n </a>\n }\n <p-avatar [image]=\"rtsee.socketConnection?.myPeer?.imageUrl\"\n class=\"rtsee-nav-item rtsee-nav-image rtsee-nav-profile\"\n [routerLink]=\"profileLink\"\n routerLinkActive=\"rtsee-nav-item-active\"\n [routerLinkActiveOptions]=\"{exact: true}\"\n [queryParams]=\"queryParams\"\n ></p-avatar>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "component", type: Image, selector: "p-image", inputs: ["imageClass", "imageStyle", "styleClass", "src", "srcSet", "sizes", "previewImageSrc", "previewImageSrcSet", "previewImageSizes", "alt", "width", "height", "loading", "preview", "showTransitionOptions", "hideTransitionOptions", "appendTo"], outputs: ["onShow", "onHide", "onImageError"] }, { kind: "component", type: Divider, selector: "p-divider", inputs: ["styleClass", "layout", "type", "align"] }, { 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"] }] }); }
7095
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: RtseeNavComponent, isStandalone: true, selector: "rtsee-nav", inputs: { config: "config", rtsee: "rtsee" }, ngImport: i0, template: "<div class=\"rtsee-nav\">\n @if (config.siteLogo) {\n <div class=\"rtsee-site-logo\">\n <p-image [src]=\"config.siteLogo.src\" class=\"rtsee-site-logo-image\"></p-image>\n <p-divider class=\"rtsee-site-logo-divider\" />\n </div>\n }\n <div class=\"rtsee-nav-bar\">\n @for (route of config.routes; track route.name) {\n <a [routerLink]=\"route.link\"\n routerLinkActive=\"rtsee-nav-item-active\"\n [ngClass]=\"{'rtsee-nav-item-hidden': !route.isEnabled || !route.iconClassName}\"\n [routerLinkActiveOptions]=\"{ exact: route.exactActiveLinkMatcher || false }\"\n class=\"rtsee-nav-item\"\n [pTooltip]=\"route.name\"\n >\n <span [ngClass]=\"route.iconClassName\"\n class=\"rtsee-nav-item-icon\" >\n </span>\n </a>\n }\n <p-avatar [image]=\"rtsee.socketConnection?.myPeer?.imageUrl\"\n class=\"rtsee-nav-item rtsee-nav-image rtsee-nav-profile\"\n [routerLink]=\"profileLink\"\n routerLinkActive=\"rtsee-nav-item-active\"\n [routerLinkActiveOptions]=\"{exact: true}\"\n [queryParams]=\"queryParams\"\n ></p-avatar>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "component", type: Image$1, selector: "p-image", inputs: ["imageClass", "imageStyle", "styleClass", "src", "srcSet", "sizes", "previewImageSrc", "previewImageSrcSet", "previewImageSizes", "alt", "width", "height", "loading", "preview", "showTransitionOptions", "hideTransitionOptions", "appendTo"], outputs: ["onShow", "onHide", "onImageError"] }, { kind: "component", type: Divider, selector: "p-divider", inputs: ["styleClass", "layout", "type", "align"] }, { 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"] }] }); }
6368
7096
  }
6369
7097
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: RtseeNavComponent, decorators: [{
6370
7098
  type: Component,
@@ -6373,7 +7101,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
6373
7101
  RouterLinkActive,
6374
7102
  NgClass,
6375
7103
  Avatar,
6376
- Image,
7104
+ Image$1,
6377
7105
  Divider,
6378
7106
  Tooltip,
6379
7107
  ], standalone: true, template: "<div class=\"rtsee-nav\">\n @if (config.siteLogo) {\n <div class=\"rtsee-site-logo\">\n <p-image [src]=\"config.siteLogo.src\" class=\"rtsee-site-logo-image\"></p-image>\n <p-divider class=\"rtsee-site-logo-divider\" />\n </div>\n }\n <div class=\"rtsee-nav-bar\">\n @for (route of config.routes; track route.name) {\n <a [routerLink]=\"route.link\"\n routerLinkActive=\"rtsee-nav-item-active\"\n [ngClass]=\"{'rtsee-nav-item-hidden': !route.isEnabled || !route.iconClassName}\"\n [routerLinkActiveOptions]=\"{ exact: route.exactActiveLinkMatcher || false }\"\n class=\"rtsee-nav-item\"\n [pTooltip]=\"route.name\"\n >\n <span [ngClass]=\"route.iconClassName\"\n class=\"rtsee-nav-item-icon\" >\n </span>\n </a>\n }\n <p-avatar [image]=\"rtsee.socketConnection?.myPeer?.imageUrl\"\n class=\"rtsee-nav-item rtsee-nav-image rtsee-nav-profile\"\n [routerLink]=\"profileLink\"\n routerLinkActive=\"rtsee-nav-item-active\"\n [routerLinkActiveOptions]=\"{exact: true}\"\n [queryParams]=\"queryParams\"\n ></p-avatar>\n </div>\n</div>\n" }]