fmode-ng 0.0.85 → 0.0.86

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.
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/chat/chat-modal-input/modal-input.component.mjs
7
7
  */
8
- import{Component,Input,ViewChild}from"@angular/core";import{Router,RouterModule}from"@angular/router";import{AlertController,ToastController}from"@ionic/angular";import{FmodeChat}from"../../service-fmai/service-chat";import{ChatService}from"../../service-fmai/service-chat";import Parse from"parse";import{ImagineService}from"../../service-fmai/service-imagine/imagine.service";import{IonButton,IonContent,IonIcon,IonInput,IonItem,IonList,IonModal,IonPopover,IonTextarea,IonToolbar,ModalController}from"@ionic/angular/standalone";import{CommonModule}from"@angular/common";import{FormsModule,ReactiveFormsModule}from"@angular/forms";import{ModalAudioMessageComponent}from"./modal-audio-message/modal-audio-message.component";import{FmChatMessageCard}from"../chat-message-card/comp-message-card.component";import{addIcons}from"ionicons";import{imageOutline,chevronBackOutline,ellipsisHorizontalOutline,chevronDownOutline,chatboxEllipsesOutline,micOutline,paperPlaneOutline,shareSocialOutline,settingsOutline,alertOutline,colorWandOutline,peopleOutline}from"ionicons/icons";import{AccountService}from"../../../user/account/account.service";import*as i0 from"@angular/core";import*as i1 from"@ionic/angular";import*as i2 from"@ionic/angular/standalone";import*as i3 from"@angular/router";import*as i4 from"../../service-fmai/service-imagine/imagine.service";import*as i5 from"../../service-fmai/service-chat";import*as i6 from"../../../user/account/account.service";import*as i7 from"@angular/common";import*as i8 from"@angular/forms";addIcons({colorWandOutline:colorWandOutline,peopleOutline:peopleOutline,alertOutline:alertOutline,imageOutline:imageOutline,chevronBackOutline:chevronBackOutline,ellipsisHorizontalOutline:ellipsisHorizontalOutline,chevronDownOutline:chevronDownOutline,chatboxEllipsesOutline:chatboxEllipsesOutline,micOutline:micOutline,paperPlaneOutline:paperPlaneOutline,shareSocialOutline:shareSocialOutline,settingsOutline:settingsOutline});export class FmChatModalInput{closeAudio(){this.audioComp?.cancel(),this.isAudioModal=!1}async startTalk(){if(this.isSending)return!1;let e,n=document.body.clientHeight||960;this.audioModalHeightPoint=Number((165/n).toFixed(2)),this.chat.stopPlayingVoice(),e=await this.modalCtrl.create({component:ModalAudioMessageComponent,componentProps:{chat:this.chat,modal:e,onBreakPointSet:()=>{e?.setCurrentBreakpoint(this.audioModalHeightPoint)}},breakpoints:[this.audioModalHeightPoint],initialBreakpoint:this.audioModalHeightPoint}),e.present()}constructor(e,n,t,o,i,a,s){this.toastCtrl=e,this.alertCtrl=n,this.modalCtrl=t,this.router=o,this.imagineServ=i,this.chatServ=a,this.account=s,this.errorText="",this.isAudioModal=!1,this.audioModalHeightPoint=.35,this.isSending=!1,this.lastMessageTimestamp=0,this.replyTimeout=15e3,this.isShare=!1,this.user=Parse.User.current()}ngOnInit(){this.loadModel();let e=this;this.chat.focusUserInput=()=>{e.chat.isVoiceInputMode=!1,e.userInputComp.setFocus()}}async loadModel(){let e=this.chat?.role?.get("model");await this.chat.loadModelList(e)}async setMessageImage(){let e=await this.imagineServ.getimg();this.chat.userImage=e,console.log(this.chat?.userImage)}onInputFocus(){this.chat.isTexting=!0,this.chat.scrollToBottom&&this.chat.scrollToBottom()}onKeyDown(e){e.ctrlKey&&"Enter"===e.key&&(console.log("Ctrl+Enter 被按下"),this.sendMessage())}async sendMessage(){if(this.isSending)return!1;const e=Date.now();if(this.lastMessageTimestamp>0&&e-this.lastMessageTimestamp<this.replyTimeout){return this.errorText="请等待上一条消息的回复或稍后再试",(await this.toastCtrl.create({message:this.errorText,position:"top",icon:"alert",color:"warning",duration:1e3})).present(),!1}if(this.isSending=!0,this.lastMessageTimestamp=Date.now(),!await this.checkBalance())return this.isSending=!1,!1;if(!this.chat.userInput){return this.errorText="内容不能为空",(await this.toastCtrl.create({message:this.errorText,position:"top",icon:"alert",color:"warning-circle",duration:1e3})).present(),void(this.isSending=!1)}this.lastMessageTimeout&&clearTimeout(this.lastMessageTimeout),this.lastMessageTimeout=setTimeout((()=>{this.isSending=!1,this.lastMessageTimestamp=0}),this.replyTimeout),this.chat?.sendMessage(this.chat?.userInput,this.chat?.userImage,(e=>{}),{onMessageStart:e=>{clearTimeout(this.lastMessageTimeout),this.isSending=!1,this.lastMessageTimestamp=0},onSSMLComplete:e=>{console.log(e)}}),this.chat.userInput="",this.chat.userImage=""}async checkBalance(){let e=await this.account.getBilling();if(e?.credit?.balance>=10&&(this.chat.isDirect=!0),!this.chat?.currentModel?.get("payLimit"))return!0;if(e?.credit?.balance<10){return(await this.alertCtrl.create({header:"注意",subHeader:"您的余额不足,请充值后解锁高级模型",buttons:[{role:"cancel",text:"取消"},{role:"destructive",text:"充值",handler:()=>{this.router.navigateByUrl("/account/billing")}}]})).present(),!1}return!0}async getChatShare(){this.user=Parse.User.current();let e=new Parse.Query("ChatShare");e.equalTo("user",Parse.User.current().id),e.equalTo("session",this.chat?.sessionId);await e.first()}async toggleChatShare(){let e=new Parse.Query("ChatShare");e.equalTo("user",Parse.User.current().id),e.equalTo("role",this.chat?.role.id),e.equalTo("session",this.chat?.sessionId),e.select("objectId");let n=await e.first();if(n?.id)n.set("messageList",this.chat?.messageList);else{n=new(Parse.Object.extend("ChatShare")),n.set("user",{__type:"Pointer",className:"_User",objectId:Parse.User.current()?.id}),n.set("session",{__type:"Pointer",className:"ChatSession",objectId:this.chat?.sessionId}),n.set("role",{__type:"Pointer",className:"AvatarRole",objectId:this.chat?.role.id}),n.set("company",{__type:"Pointer",className:"Company",objectId:"E4KpGvTEto"}),n.set("messageList",this.chat?.messageList)}await n.save(),this.getChatShare()}async chatShareSuccessMessage(){(await this.toastCtrl.create({duration:1e3,message:"分享成功",color:"primary",icon:"information-circle",position:"top"})).present()}showShare(){this.isShare=!0}handleOkShare(){this.toggleChatShare(),this.chatShareSuccessMessage(),this.isShare=!1}handleCancelShare(){this.isShare=!1}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatModalInput,deps:[{token:i1.ToastController},{token:i1.AlertController},{token:i2.ModalController},{token:i3.Router},{token:i4.ImagineService},{token:i5.ChatService},{token:i6.AccountService}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"17.3.12",type:FmChatModalInput,isStandalone:!0,selector:"fm-chat-modal-input",inputs:{chat:"chat",message:"message",role:"role"},viewQueries:[{propertyName:"audioComp",first:!0,predicate:ModalAudioMessageComponent,descendants:!0},{propertyName:"userInputComp",first:!0,predicate:["userInput"],descendants:!0}],ngImport:i0,template:'<ion-toolbar>\n <ion-item class="button-item" lines="none">\n \x3c!-- 设置 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="settings-outline"></ion-icon> \n </ion-button> --\x3e\n <ng-container *ngFor="let button of chat?.leftButtons">\n <ion-button style="--padding-start:10px;--padding-end:10px;"\n shape="round" *ngIf="button.show()" fill="outline" [title]="button?.title" slot="start" (click)="button.onClick()">\n <ion-icon [name]="button?.icon" [slot]="button?.showTitle?\'start\':\'icon-only\'"></ion-icon>\n {{button?.showTitle&&button?.title}}\n </ion-button>\n </ng-container>\n\n <ng-container *ngFor="let button of chat?.role?.get(\'buttons\')">\n <ion-button shape="round" (click)="chatServ.doButtonAction(button)" fill="outline" slot="start">\n {{button?.name}}\n </ion-button>\n </ng-container>\n\n \x3c!--分享按钮--\x3e\n @if(!chat?.hideShare){\n <ion-button shape="round" *ngIf="chat?.messageList?.length>1" (click)="showShare()" fill="outline" title="分享" slot="end">\n <ion-icon name="share-social-outline"></ion-icon>\n </ion-button>\n }\n <ion-modal [isOpen]="isShare">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="handleCancelShare()">取消</ion-button>\n </ion-buttons>\n <ion-title>对话分享</ion-title>\n <ion-buttons slot="end">\n <ion-button (click)="handleOkShare()">分享</ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n <ng-container *ngFor="let message of chat?.messageList;let index=index;">\n \x3c!-- 内容格式化区域 --\x3e\n <fm-chat-message-card [chat]="chat" *ngIf="!message?.hidden" [index]="index" [message]="message" [role]="chat?.role"></fm-chat-message-card>\n </ng-container>\n\n <div *ngIf="false" class="popup-content">\n <div *ngFor="let message of chat?.messageList">\n \x3c!-- 头像 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'">\n <div>\n <img class="avatar" *ngIf="message?.role!=\'user\'" [src]="(chat?.role?.get(\'avatar\') || chat?.role?.get(\'thumb\') || \'https://file-cloud.fmode.cn/E4KpGvTEto/20230930/l413e6090731854.png\')+\'?\'+\'x-image-process=image/resize,m_fixed,w_100\'+\'&imageView2/1/w/32/h/32\'" >\n </div>\n <div class="user-question">\n <app-comp-user-avatar [user]="user" *ngIf="message?.role==\'user\'"></app-comp-user-avatar>\n </div>\n </div>\n \x3c!-- 内容 --\x3e\n <div class="message-wrapper">\n <div class="message-content-user">\n <div class="user-message" *ngIf="message?.role === \'user\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-role">\n <div class="role-message" *ngIf="message?.role !== \'user\' && message?.role !== \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-system">\n <div class="system-message" *ngIf="message?.role === \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n </div>\n <div class="chat-time" *ngIf="message?.createdAt" [ngClass]="{\'role-time\': message?.role !== \'user\'}">\n <span>{{message?.createdAt | date:"dd/MM/yyyy, HH/mm/ss a"}}</span>\n </div>\n </div>\n </div> \n </ion-content>\n </ng-template>\n </ion-modal>\n\n \x3c!-- 图片 --\x3e\n <ion-button shape="round" *ngIf="chat?.currentModel?.get(\'config\')?.imageEnabled" fill="outline" slot="end" (click)="setMessageImage()">\n <ion-icon name="image-outline"></ion-icon>\n </ion-button>\n \x3c!-- 模型 --\x3e\n @if(!chat?.hideModalSelect){\n <ion-button shape="round" fill="outline" slot="end" id="model-button">\n <ion-icon name="chevron-down-outline"></ion-icon>\n {{chat?.currentModel?.get&&chat?.currentModel?.get("name")||"Fmode-C1.0-128k"}}\n </ion-button>\n <ion-popover trigger="model-button" [dismissOnSelect]="true">\n <ng-template>\n <ion-content>\n <ion-list>\n <ng-container *ngFor="let model of chat.modelList">\n <ion-item (click)="chat.currentModel = model" [button]="true" [detail]="false">\n {{model?.get("name")}}\n <ion-note slot="end">{{model?.get("credit")}}/k</ion-note>\n </ion-item>\n </ng-container>\n </ion-list>\n </ion-content>\n </ng-template>\n </ion-popover>\n }\n\n </ion-item>\n\n <ion-item class="input-item" lines="none">\n \x3c!-- 语音消息输入 --\x3e\n <ng-container *ngIf="chat?.isVoiceInputMode">\n \x3c!-- 切换文本输入 --\x3e\n <ion-button class="btn-input-change" color="primary" (click)="chat.isVoiceInputMode=false" shape="round" size="large">\n <ion-icon name="chatbox-ellipses-outline" slot="icon-only"></ion-icon>\n </ion-button>\n \n <div class="btn-voice-start" (click)="startTalk()" [class.disabled]="isSending">\n <span>\n 点击讲话\n </span> \n </div>\n </ng-container>\n\n \x3c!-- 文本消息输入 --\x3e\n <ng-container *ngIf="!chat?.isVoiceInputMode">\n \x3c!-- 切换语音输入 --\x3e\n <ion-button [style.display]="chat.isTexting?\'none\':\'flex\'" class="btn-input-change" color="primary" *ngIf="chat?.role?.get(\'voiceConfig\')" (click)="chat.isVoiceInputMode=true" shape="round" size="large">\n <ion-icon name="mic-outline" slot="icon-only"></ion-icon>\n </ion-button>\n\n \x3c!-- 文本输入区域 --\x3e\n <ion-textarea\n #userInput\n *ngIf="chat" (keydown)="onKeyDown($event)"\n [errorText]="errorText"\n [(ngModel)]="chat.userInput"\n (ionFocus)="onInputFocus()"\n (ionBlur)="chat.isTexting=false"\n [autoGrow]="true" shape="round" fill="outline"\n label="Ctrl + Enter 发送消息" placeholder="请输入您的提示词"\n labelPlacement="floating"></ion-textarea>\n \n \x3c!-- 文本发送按钮 --\x3e\n <ion-button [disabled]="isSending"\n color="primary" shape="round" size="large" (click)="sendMessage()">\n <ion-icon name="paper-plane-outline" slot="icon-only"></ion-icon>\n </ion-button>\n </ng-container>\n </ion-item>\n</ion-toolbar>\n\n\n\x3c!-- 语音消息输入:弹出区域 --\x3e\n\x3c!-- <ion-modal #audioModal [isOpen]="isAudioModal" (willDismiss)="closeAudio()" [initialBreakpoint]="audioModalHeightPoint" [breakpoints]="[0, audioModalHeightPoint]">\n <ng-template>\n <fm-modal-audio-message #audioComp *ngIf="isAudioModal" [chat]="chat" [modal]="audioModal"></fm-modal-audio-message>\n </ng-template>\n</ion-modal> --\x3e',styles:['@charset "UTF-8";:host-context(body.dark) .btn-voice-start{background-color:#222428;color:#fff}:host-context(body.dark) ion-textarea{background-color:#222428;color:#fff}ion-toolbar{--background:none}ion-toolbar .button-item{--inner-padding-start:5px;--inner-padding-end:0px;--padding-start:5px;--padding-end:0px}ion-toolbar ion-item{--background:transparent}.disabled{opacity:.5;pointer-events:none}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}ion-textarea.custom{--background: #373737;--color: #fff;--padding-end: 10px;--padding-start: 10px;--placeholder-color: #ddd;--placeholder-opacity: .8}ion-textarea.custom textarea{width:calc(100% - 95px)}ion-textarea.custom ion-button{position:absolute;right:0}.input-item{display:flex;min-height:77px;align-items:center;border:none;--inner-padding-start:0px;--inner-padding-end:0px;--padding-start:0px;--padding-end:0px}.input-item ion-textarea{background-color:#fff;max-height:400px;padding:0 5px;margin:0 5px;border-radius:20px;overflow-y:auto}.input-item .btn-voice-start{display:flex;flex:1;justify-content:center;align-items:center;font-weight:700;background:#fff;border-radius:20px;min-height:50px}ion-textarea{transition:width .5s ease}ion-textarea:hover .btn-input-change,ion-textarea:focus-within .btn-input-change{display:none}.input-item:hover ion-textarea,.input-item:focus-within ion-textarea{border-color:var(--logo-color-primary)}::ng-deep .ant-modal-body{max-height:600px;overflow-y:auto}::ng-deep .ant-modal-footer{display:flex;justify-content:space-around}::ng-deep .ant-btn{width:40%}.popup-content{position:relative}.popup-content .message-content-user{display:flex;justify-content:flex-end}.popup-content .message-content-role{display:flex;justify-content:flex-start}.popup-content .message-content-system{display:flex;justify-content:center}.popup-content .user-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#e7f8ff}.popup-content .role-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#f6f6f6}.popup-content .user-question{margin-bottom:5px;display:flex;justify-content:flex-end}.popup-content .chat-time{margin-bottom:10px;display:flex;justify-content:flex-end;font-size:14px;color:#a3a3a3}.popup-content .role-time{justify-content:flex-start}\n'],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"directive",type:i7.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:i7.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:i7.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"pipe",type:i7.DatePipe,name:"date"},{kind:"ngmodule",type:FormsModule},{kind:"directive",type:i8.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:i8.NgModel,selector:"[ngModel]:not([formControlName]):not([formControl])",inputs:["name","disabled","ngModel","ngModelOptions"],outputs:["ngModelChange"],exportAs:["ngModel"]},{kind:"ngmodule",type:ReactiveFormsModule},{kind:"ngmodule",type:RouterModule},{kind:"component",type:IonToolbar,selector:"ion-toolbar",inputs:["color","mode"]},{kind:"component",type:IonItem,selector:"ion-item",inputs:["button","color","detail","detailIcon","disabled","download","href","lines","mode","rel","routerAnimation","routerDirection","target","type"]},{kind:"component",type:IonButton,selector:"ion-button",inputs:["buttonType","color","disabled","download","expand","fill","form","href","mode","rel","routerAnimation","routerDirection","shape","size","strong","target","type"]},{kind:"component",type:IonList,selector:"ion-list",inputs:["inset","lines","mode"]},{kind:"component",type:IonModal,selector:"ion-modal"},{kind:"component",type:IonIcon,selector:"ion-icon",inputs:["color","flipRtl","icon","ios","lazy","md","mode","name","sanitize","size","src"]},{kind:"component",type:IonTextarea,selector:"ion-textarea",inputs:["autoGrow","autocapitalize","autofocus","clearOnEdit","color","cols","counter","counterFormatter","debounce","disabled","enterkeyhint","errorText","fill","helperText","inputmode","label","labelPlacement","maxlength","minlength","mode","name","placeholder","readonly","required","rows","shape","spellcheck","value","wrap"]},{kind:"component",type:IonPopover,selector:"ion-popover"},{kind:"component",type:IonContent,selector:"ion-content",inputs:["color","fixedSlotPlacement","forceOverscroll","fullscreen","scrollEvents","scrollX","scrollY"]},{kind:"component",type:FmChatMessageCard,selector:"fm-chat-message-card",inputs:["index","message","role","chat"]}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatModalInput,decorators:[{type:Component,args:[{selector:"fm-chat-modal-input",standalone:!0,imports:[CommonModule,FormsModule,ReactiveFormsModule,RouterModule,IonToolbar,IonItem,IonButton,IonList,IonModal,IonInput,IonIcon,IonTextarea,IonPopover,IonContent,FmChatMessageCard,ModalAudioMessageComponent],template:'<ion-toolbar>\n <ion-item class="button-item" lines="none">\n \x3c!-- 设置 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="settings-outline"></ion-icon> \n </ion-button> --\x3e\n <ng-container *ngFor="let button of chat?.leftButtons">\n <ion-button style="--padding-start:10px;--padding-end:10px;"\n shape="round" *ngIf="button.show()" fill="outline" [title]="button?.title" slot="start" (click)="button.onClick()">\n <ion-icon [name]="button?.icon" [slot]="button?.showTitle?\'start\':\'icon-only\'"></ion-icon>\n {{button?.showTitle&&button?.title}}\n </ion-button>\n </ng-container>\n\n <ng-container *ngFor="let button of chat?.role?.get(\'buttons\')">\n <ion-button shape="round" (click)="chatServ.doButtonAction(button)" fill="outline" slot="start">\n {{button?.name}}\n </ion-button>\n </ng-container>\n\n \x3c!--分享按钮--\x3e\n @if(!chat?.hideShare){\n <ion-button shape="round" *ngIf="chat?.messageList?.length>1" (click)="showShare()" fill="outline" title="分享" slot="end">\n <ion-icon name="share-social-outline"></ion-icon>\n </ion-button>\n }\n <ion-modal [isOpen]="isShare">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="handleCancelShare()">取消</ion-button>\n </ion-buttons>\n <ion-title>对话分享</ion-title>\n <ion-buttons slot="end">\n <ion-button (click)="handleOkShare()">分享</ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n <ng-container *ngFor="let message of chat?.messageList;let index=index;">\n \x3c!-- 内容格式化区域 --\x3e\n <fm-chat-message-card [chat]="chat" *ngIf="!message?.hidden" [index]="index" [message]="message" [role]="chat?.role"></fm-chat-message-card>\n </ng-container>\n\n <div *ngIf="false" class="popup-content">\n <div *ngFor="let message of chat?.messageList">\n \x3c!-- 头像 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'">\n <div>\n <img class="avatar" *ngIf="message?.role!=\'user\'" [src]="(chat?.role?.get(\'avatar\') || chat?.role?.get(\'thumb\') || \'https://file-cloud.fmode.cn/E4KpGvTEto/20230930/l413e6090731854.png\')+\'?\'+\'x-image-process=image/resize,m_fixed,w_100\'+\'&imageView2/1/w/32/h/32\'" >\n </div>\n <div class="user-question">\n <app-comp-user-avatar [user]="user" *ngIf="message?.role==\'user\'"></app-comp-user-avatar>\n </div>\n </div>\n \x3c!-- 内容 --\x3e\n <div class="message-wrapper">\n <div class="message-content-user">\n <div class="user-message" *ngIf="message?.role === \'user\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-role">\n <div class="role-message" *ngIf="message?.role !== \'user\' && message?.role !== \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-system">\n <div class="system-message" *ngIf="message?.role === \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n </div>\n <div class="chat-time" *ngIf="message?.createdAt" [ngClass]="{\'role-time\': message?.role !== \'user\'}">\n <span>{{message?.createdAt | date:"dd/MM/yyyy, HH/mm/ss a"}}</span>\n </div>\n </div>\n </div> \n </ion-content>\n </ng-template>\n </ion-modal>\n\n \x3c!-- 图片 --\x3e\n <ion-button shape="round" *ngIf="chat?.currentModel?.get(\'config\')?.imageEnabled" fill="outline" slot="end" (click)="setMessageImage()">\n <ion-icon name="image-outline"></ion-icon>\n </ion-button>\n \x3c!-- 模型 --\x3e\n @if(!chat?.hideModalSelect){\n <ion-button shape="round" fill="outline" slot="end" id="model-button">\n <ion-icon name="chevron-down-outline"></ion-icon>\n {{chat?.currentModel?.get&&chat?.currentModel?.get("name")||"Fmode-C1.0-128k"}}\n </ion-button>\n <ion-popover trigger="model-button" [dismissOnSelect]="true">\n <ng-template>\n <ion-content>\n <ion-list>\n <ng-container *ngFor="let model of chat.modelList">\n <ion-item (click)="chat.currentModel = model" [button]="true" [detail]="false">\n {{model?.get("name")}}\n <ion-note slot="end">{{model?.get("credit")}}/k</ion-note>\n </ion-item>\n </ng-container>\n </ion-list>\n </ion-content>\n </ng-template>\n </ion-popover>\n }\n\n </ion-item>\n\n <ion-item class="input-item" lines="none">\n \x3c!-- 语音消息输入 --\x3e\n <ng-container *ngIf="chat?.isVoiceInputMode">\n \x3c!-- 切换文本输入 --\x3e\n <ion-button class="btn-input-change" color="primary" (click)="chat.isVoiceInputMode=false" shape="round" size="large">\n <ion-icon name="chatbox-ellipses-outline" slot="icon-only"></ion-icon>\n </ion-button>\n \n <div class="btn-voice-start" (click)="startTalk()" [class.disabled]="isSending">\n <span>\n 点击讲话\n </span> \n </div>\n </ng-container>\n\n \x3c!-- 文本消息输入 --\x3e\n <ng-container *ngIf="!chat?.isVoiceInputMode">\n \x3c!-- 切换语音输入 --\x3e\n <ion-button [style.display]="chat.isTexting?\'none\':\'flex\'" class="btn-input-change" color="primary" *ngIf="chat?.role?.get(\'voiceConfig\')" (click)="chat.isVoiceInputMode=true" shape="round" size="large">\n <ion-icon name="mic-outline" slot="icon-only"></ion-icon>\n </ion-button>\n\n \x3c!-- 文本输入区域 --\x3e\n <ion-textarea\n #userInput\n *ngIf="chat" (keydown)="onKeyDown($event)"\n [errorText]="errorText"\n [(ngModel)]="chat.userInput"\n (ionFocus)="onInputFocus()"\n (ionBlur)="chat.isTexting=false"\n [autoGrow]="true" shape="round" fill="outline"\n label="Ctrl + Enter 发送消息" placeholder="请输入您的提示词"\n labelPlacement="floating"></ion-textarea>\n \n \x3c!-- 文本发送按钮 --\x3e\n <ion-button [disabled]="isSending"\n color="primary" shape="round" size="large" (click)="sendMessage()">\n <ion-icon name="paper-plane-outline" slot="icon-only"></ion-icon>\n </ion-button>\n </ng-container>\n </ion-item>\n</ion-toolbar>\n\n\n\x3c!-- 语音消息输入:弹出区域 --\x3e\n\x3c!-- <ion-modal #audioModal [isOpen]="isAudioModal" (willDismiss)="closeAudio()" [initialBreakpoint]="audioModalHeightPoint" [breakpoints]="[0, audioModalHeightPoint]">\n <ng-template>\n <fm-modal-audio-message #audioComp *ngIf="isAudioModal" [chat]="chat" [modal]="audioModal"></fm-modal-audio-message>\n </ng-template>\n</ion-modal> --\x3e',styles:['@charset "UTF-8";:host-context(body.dark) .btn-voice-start{background-color:#222428;color:#fff}:host-context(body.dark) ion-textarea{background-color:#222428;color:#fff}ion-toolbar{--background:none}ion-toolbar .button-item{--inner-padding-start:5px;--inner-padding-end:0px;--padding-start:5px;--padding-end:0px}ion-toolbar ion-item{--background:transparent}.disabled{opacity:.5;pointer-events:none}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}ion-textarea.custom{--background: #373737;--color: #fff;--padding-end: 10px;--padding-start: 10px;--placeholder-color: #ddd;--placeholder-opacity: .8}ion-textarea.custom textarea{width:calc(100% - 95px)}ion-textarea.custom ion-button{position:absolute;right:0}.input-item{display:flex;min-height:77px;align-items:center;border:none;--inner-padding-start:0px;--inner-padding-end:0px;--padding-start:0px;--padding-end:0px}.input-item ion-textarea{background-color:#fff;max-height:400px;padding:0 5px;margin:0 5px;border-radius:20px;overflow-y:auto}.input-item .btn-voice-start{display:flex;flex:1;justify-content:center;align-items:center;font-weight:700;background:#fff;border-radius:20px;min-height:50px}ion-textarea{transition:width .5s ease}ion-textarea:hover .btn-input-change,ion-textarea:focus-within .btn-input-change{display:none}.input-item:hover ion-textarea,.input-item:focus-within ion-textarea{border-color:var(--logo-color-primary)}::ng-deep .ant-modal-body{max-height:600px;overflow-y:auto}::ng-deep .ant-modal-footer{display:flex;justify-content:space-around}::ng-deep .ant-btn{width:40%}.popup-content{position:relative}.popup-content .message-content-user{display:flex;justify-content:flex-end}.popup-content .message-content-role{display:flex;justify-content:flex-start}.popup-content .message-content-system{display:flex;justify-content:center}.popup-content .user-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#e7f8ff}.popup-content .role-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#f6f6f6}.popup-content .user-question{margin-bottom:5px;display:flex;justify-content:flex-end}.popup-content .chat-time{margin-bottom:10px;display:flex;justify-content:flex-end;font-size:14px;color:#a3a3a3}.popup-content .role-time{justify-content:flex-start}\n']}]}],ctorParameters:()=>[{type:i1.ToastController},{type:i1.AlertController},{type:i2.ModalController},{type:i3.Router},{type:i4.ImagineService},{type:i5.ChatService},{type:i6.AccountService}],propDecorators:{audioComp:[{type:ViewChild,args:[ModalAudioMessageComponent]}],userInputComp:[{type:ViewChild,args:["userInput"]}],chat:[{type:Input}],message:[{type:Input}],role:[{type:Input}]}});
8
+ import{Component,Input,ViewChild}from"@angular/core";import{Router,RouterModule}from"@angular/router";import{AlertController,ToastController}from"@ionic/angular";import{FmodeChat}from"../../service-fmai/service-chat";import{ChatService}from"../../service-fmai/service-chat";import Parse from"parse";import{ImagineService}from"../../service-fmai/service-imagine/imagine.service";import{IonButton,IonContent,IonIcon,IonInput,IonItem,IonList,IonModal,IonPopover,IonTextarea,IonToolbar,ModalController}from"@ionic/angular/standalone";import{CommonModule}from"@angular/common";import{FormsModule,ReactiveFormsModule}from"@angular/forms";import{ModalAudioMessageComponent}from"./modal-audio-message/modal-audio-message.component";import{FmChatMessageCard}from"../chat-message-card/comp-message-card.component";import{addIcons}from"ionicons";import{imageOutline,chevronBackOutline,ellipsisHorizontalOutline,chevronDownOutline,chatboxEllipsesOutline,micOutline,paperPlaneOutline,shareSocialOutline,settingsOutline,alertOutline,colorWandOutline,peopleOutline}from"ionicons/icons";import{AccountService}from"../../../user/account/account.service";import*as i0 from"@angular/core";import*as i1 from"@ionic/angular";import*as i2 from"@ionic/angular/standalone";import*as i3 from"@angular/router";import*as i4 from"../../service-fmai/service-imagine/imagine.service";import*as i5 from"../../service-fmai/service-chat";import*as i6 from"../../../user/account/account.service";import*as i7 from"@angular/common";import*as i8 from"@angular/forms";addIcons({colorWandOutline:colorWandOutline,peopleOutline:peopleOutline,alertOutline:alertOutline,imageOutline:imageOutline,chevronBackOutline:chevronBackOutline,ellipsisHorizontalOutline:ellipsisHorizontalOutline,chevronDownOutline:chevronDownOutline,chatboxEllipsesOutline:chatboxEllipsesOutline,micOutline:micOutline,paperPlaneOutline:paperPlaneOutline,shareSocialOutline:shareSocialOutline,settingsOutline:settingsOutline});export class FmChatModalInput{closeAudio(){this.audioComp?.cancel(),this.isAudioModal=!1}async startTalk(){if(this.isSending)return!1;let e,n=document.body.clientHeight||960;this.audioModalHeightPoint=Number((165/n).toFixed(2)),this.chat.stopPlayingVoice(),e=await this.modalCtrl.create({component:ModalAudioMessageComponent,componentProps:{chat:this.chat,modal:e,onBreakPointSet:()=>{e?.setCurrentBreakpoint(this.audioModalHeightPoint)}},breakpoints:[this.audioModalHeightPoint],initialBreakpoint:this.audioModalHeightPoint}),e.present()}constructor(e,n,t,o,i,a,s){this.toastCtrl=e,this.alertCtrl=n,this.modalCtrl=t,this.router=o,this.imagineServ=i,this.chatServ=a,this.account=s,this.errorText="",this.isAudioModal=!1,this.audioModalHeightPoint=.35,this.isSending=!1,this.lastMessageTimestamp=0,this.replyTimeout=15e3,this.isShare=!1,this.user=Parse.User.current()}ngOnInit(){this.loadModel();let e=this;this.chat.focusUserInput=()=>{e.chat.isVoiceInputMode=!1,e.userInputComp.setFocus()}}async loadModel(){let e=this.chat?.role?.get("model");await this.chat.loadModelList(e)}async setMessageImage(){let e=await this.imagineServ.getimg();this.chat.userImage=e,console.log(this.chat?.userImage)}onInputFocus(){this.chat.isTexting=!0,this.chat.scrollToBottom&&this.chat.scrollToBottom()}onKeyDown(e){e.ctrlKey&&"Enter"===e.key&&(console.log("Ctrl+Enter 被按下"),this.sendMessage())}async sendMessage(){if(this.isSending)return!1;const e=Date.now();if(this.lastMessageTimestamp>0&&e-this.lastMessageTimestamp<this.replyTimeout){return this.errorText="请等待上一条消息的回复或稍后再试",(await this.toastCtrl.create({message:this.errorText,position:"top",icon:"alert",color:"warning",duration:1e3})).present(),!1}if(this.isSending=!0,this.lastMessageTimestamp=Date.now(),!await this.checkBalance())return this.isSending=!1,!1;if(!this.chat.userInput){return this.errorText="内容不能为空",(await this.toastCtrl.create({message:this.errorText,position:"top",icon:"alert",color:"warning-circle",duration:1e3})).present(),void(this.isSending=!1)}this.lastMessageTimeout&&clearTimeout(this.lastMessageTimeout),this.lastMessageTimeout=setTimeout((()=>{this.isSending=!1,this.lastMessageTimestamp=0}),this.replyTimeout),this.chat?.sendMessage(this.chat?.userInput,this.chat?.userImage,(e=>{}),{onMessageStart:e=>{clearTimeout(this.lastMessageTimeout),this.isSending=!1,this.lastMessageTimestamp=0},onSSMLComplete:e=>{console.log(e)}}),this.chat.userInput="",this.chat.userImage=""}async checkBalance(){let e=await this.account.getBilling();if(e?.credit?.balance>=10&&(this.chat.isDirect=!0),!this.chat?.currentModel?.get("payLimit"))return!0;if(e?.credit?.balance<10){return(await this.alertCtrl.create({header:"注意",subHeader:"您的余额不足,请充值后解锁高级模型",buttons:[{role:"cancel",text:"取消"},{role:"destructive",text:"充值",handler:()=>{this.router.navigateByUrl("/account/billing")}}]})).present(),!1}return!0}async getChatShare(){this.user=Parse.User.current();let e=new Parse.Query("ChatShare");e.equalTo("user",Parse.User.current().id),e.equalTo("session",this.chat?.sessionId);await e.first()}async toggleChatShare(){let e=new Parse.Query("ChatShare");e.equalTo("user",Parse.User.current().id),e.equalTo("role",this.chat?.role.id),e.equalTo("session",this.chat?.sessionId),e.select("objectId");let n=await e.first();if(n?.id)n.set("messageList",this.chat?.messageList);else{n=new(Parse.Object.extend("ChatShare")),n.set("user",{__type:"Pointer",className:"_User",objectId:Parse.User.current()?.id}),n.set("session",{__type:"Pointer",className:"ChatSession",objectId:this.chat?.sessionId}),n.set("role",{__type:"Pointer",className:"AvatarRole",objectId:this.chat?.role.id}),n.set("company",{__type:"Pointer",className:"Company",objectId:"E4KpGvTEto"}),n.set("messageList",this.chat?.messageList)}await n.save(),this.getChatShare()}async chatShareSuccessMessage(){(await this.toastCtrl.create({duration:1e3,message:"分享成功",color:"primary",icon:"information-circle",position:"top"})).present()}showShare(){this.isShare=!0}handleOkShare(){this.toggleChatShare(),this.chatShareSuccessMessage(),this.isShare=!1}handleCancelShare(){this.isShare=!1}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatModalInput,deps:[{token:i1.ToastController},{token:i1.AlertController},{token:i2.ModalController},{token:i3.Router},{token:i4.ImagineService},{token:i5.ChatService},{token:i6.AccountService}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"17.0.0",version:"17.3.12",type:FmChatModalInput,isStandalone:!0,selector:"fm-chat-modal-input",inputs:{chat:"chat",message:"message",role:"role"},viewQueries:[{propertyName:"audioComp",first:!0,predicate:ModalAudioMessageComponent,descendants:!0},{propertyName:"userInputComp",first:!0,predicate:["userInput"],descendants:!0}],ngImport:i0,template:'<ion-toolbar>\n <ion-item class="button-item" lines="none">\n \x3c!-- 设置 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="settings-outline"></ion-icon> \n </ion-button> --\x3e\n <ng-container *ngFor="let button of chat?.leftButtons">\n <ion-button style="--padding-start:10px;--padding-end:10px;"\n shape="round" *ngIf="button?.show&&button?.show()" fill="outline" [title]="button?.title" slot="start" (click)="button.onClick()">\n <ion-icon [name]="button?.icon" [slot]="button?.showTitle?\'start\':\'icon-only\'"></ion-icon>\n {{button?.showTitle&&button?.title}}\n </ion-button>\n </ng-container>\n\n <ng-container *ngFor="let button of chat?.role?.get(\'buttons\')">\n <ion-button shape="round" (click)="chatServ.doButtonAction(button)" fill="outline" slot="start">\n {{button?.name}}\n </ion-button>\n </ng-container>\n\n \x3c!--分享按钮--\x3e\n @if(!chat?.hideShare){\n <ion-button shape="round" *ngIf="chat?.messageList?.length>1" (click)="showShare()" fill="outline" title="分享" slot="end">\n <ion-icon name="share-social-outline"></ion-icon>\n </ion-button>\n }\n <ion-modal [isOpen]="isShare">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="handleCancelShare()">取消</ion-button>\n </ion-buttons>\n <ion-title>对话分享</ion-title>\n <ion-buttons slot="end">\n <ion-button (click)="handleOkShare()">分享</ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n <ng-container *ngFor="let message of chat?.messageList;let index=index;">\n \x3c!-- 内容格式化区域 --\x3e\n <fm-chat-message-card [chat]="chat" *ngIf="!message?.hidden" [index]="index" [message]="message" [role]="chat?.role"></fm-chat-message-card>\n </ng-container>\n\n <div *ngIf="false" class="popup-content">\n <div *ngFor="let message of chat?.messageList">\n \x3c!-- 头像 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'">\n <div>\n <img class="avatar" *ngIf="message?.role!=\'user\'" [src]="(chat?.role?.get(\'avatar\') || chat?.role?.get(\'thumb\') || \'https://file-cloud.fmode.cn/E4KpGvTEto/20230930/l413e6090731854.png\')+\'?\'+\'x-image-process=image/resize,m_fixed,w_100\'+\'&imageView2/1/w/32/h/32\'" >\n </div>\n <div class="user-question">\n <app-comp-user-avatar [user]="user" *ngIf="message?.role==\'user\'"></app-comp-user-avatar>\n </div>\n </div>\n \x3c!-- 内容 --\x3e\n <div class="message-wrapper">\n <div class="message-content-user">\n <div class="user-message" *ngIf="message?.role === \'user\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-role">\n <div class="role-message" *ngIf="message?.role !== \'user\' && message?.role !== \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-system">\n <div class="system-message" *ngIf="message?.role === \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n </div>\n <div class="chat-time" *ngIf="message?.createdAt" [ngClass]="{\'role-time\': message?.role !== \'user\'}">\n <span>{{message?.createdAt | date:"dd/MM/yyyy, HH/mm/ss a"}}</span>\n </div>\n </div>\n </div> \n </ion-content>\n </ng-template>\n </ion-modal>\n\n \x3c!-- 图片 --\x3e\n <ion-button shape="round" *ngIf="chat?.currentModel?.get(\'config\')?.imageEnabled" fill="outline" slot="end" (click)="setMessageImage()">\n <ion-icon name="image-outline"></ion-icon>\n </ion-button>\n \x3c!-- 模型 --\x3e\n @if(!chat?.hideModalSelect){\n <ion-button shape="round" fill="outline" slot="end" id="model-button">\n <ion-icon name="chevron-down-outline"></ion-icon>\n {{chat?.currentModel?.get&&chat?.currentModel?.get("name")||"Fmode-C1.0-128k"}}\n </ion-button>\n <ion-popover trigger="model-button" [dismissOnSelect]="true">\n <ng-template>\n <ion-content>\n <ion-list>\n <ng-container *ngFor="let model of chat.modelList">\n <ion-item (click)="chat.currentModel = model" [button]="true" [detail]="false">\n {{model?.get("name")}}\n <ion-note slot="end">{{model?.get("credit")}}/k</ion-note>\n </ion-item>\n </ng-container>\n </ion-list>\n </ion-content>\n </ng-template>\n </ion-popover>\n }\n\n </ion-item>\n\n <ion-item class="input-item" lines="none">\n \x3c!-- 语音消息输入 --\x3e\n <ng-container *ngIf="chat?.isVoiceInputMode">\n \x3c!-- 切换文本输入 --\x3e\n <ion-button class="btn-input-change" color="primary" (click)="chat.isVoiceInputMode=false" shape="round" size="large">\n <ion-icon name="chatbox-ellipses-outline" slot="icon-only"></ion-icon>\n </ion-button>\n \n <div class="btn-voice-start" (click)="startTalk()" [class.disabled]="isSending">\n <span>\n 点击讲话\n </span> \n </div>\n </ng-container>\n\n \x3c!-- 文本消息输入 --\x3e\n <ng-container *ngIf="!chat?.isVoiceInputMode">\n \x3c!-- 切换语音输入 --\x3e\n <ion-button [style.display]="chat.isTexting?\'none\':\'flex\'" class="btn-input-change" color="primary" *ngIf="chat?.role?.get(\'voiceConfig\')" (click)="chat.isVoiceInputMode=true" shape="round" size="large">\n <ion-icon name="mic-outline" slot="icon-only"></ion-icon>\n </ion-button>\n\n \x3c!-- 文本输入区域 --\x3e\n <ion-textarea\n #userInput\n *ngIf="chat" (keydown)="onKeyDown($event)"\n [errorText]="errorText"\n [(ngModel)]="chat.userInput"\n (ionFocus)="onInputFocus()"\n (ionBlur)="chat.isTexting=false"\n [autoGrow]="true" shape="round" fill="outline"\n label="Ctrl + Enter 发送消息" placeholder="请输入您的提示词"\n labelPlacement="floating"></ion-textarea>\n \n \x3c!-- 文本发送按钮 --\x3e\n <ion-button [disabled]="isSending"\n color="primary" shape="round" size="large" (click)="sendMessage()">\n <ion-icon name="paper-plane-outline" slot="icon-only"></ion-icon>\n </ion-button>\n </ng-container>\n </ion-item>\n</ion-toolbar>\n\n\n\x3c!-- 语音消息输入:弹出区域 --\x3e\n\x3c!-- <ion-modal #audioModal [isOpen]="isAudioModal" (willDismiss)="closeAudio()" [initialBreakpoint]="audioModalHeightPoint" [breakpoints]="[0, audioModalHeightPoint]">\n <ng-template>\n <fm-modal-audio-message #audioComp *ngIf="isAudioModal" [chat]="chat" [modal]="audioModal"></fm-modal-audio-message>\n </ng-template>\n</ion-modal> --\x3e',styles:['@charset "UTF-8";:host-context(body.dark) .btn-voice-start{background-color:#222428;color:#fff}:host-context(body.dark) ion-textarea{background-color:#222428;color:#fff}ion-toolbar{--background:none}ion-toolbar .button-item{--inner-padding-start:5px;--inner-padding-end:0px;--padding-start:5px;--padding-end:0px}ion-toolbar ion-item{--background:transparent}.disabled{opacity:.5;pointer-events:none}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}ion-textarea.custom{--background: #373737;--color: #fff;--padding-end: 10px;--padding-start: 10px;--placeholder-color: #ddd;--placeholder-opacity: .8}ion-textarea.custom textarea{width:calc(100% - 95px)}ion-textarea.custom ion-button{position:absolute;right:0}.input-item{display:flex;min-height:77px;align-items:center;border:none;--inner-padding-start:0px;--inner-padding-end:0px;--padding-start:0px;--padding-end:0px}.input-item ion-textarea{background-color:#fff;max-height:400px;padding:0 5px;margin:0 5px;border-radius:20px;overflow-y:auto}.input-item .btn-voice-start{display:flex;flex:1;justify-content:center;align-items:center;font-weight:700;background:#fff;border-radius:20px;min-height:50px}ion-textarea{transition:width .5s ease}ion-textarea:hover .btn-input-change,ion-textarea:focus-within .btn-input-change{display:none}.input-item:hover ion-textarea,.input-item:focus-within ion-textarea{border-color:var(--logo-color-primary)}::ng-deep .ant-modal-body{max-height:600px;overflow-y:auto}::ng-deep .ant-modal-footer{display:flex;justify-content:space-around}::ng-deep .ant-btn{width:40%}.popup-content{position:relative}.popup-content .message-content-user{display:flex;justify-content:flex-end}.popup-content .message-content-role{display:flex;justify-content:flex-start}.popup-content .message-content-system{display:flex;justify-content:center}.popup-content .user-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#e7f8ff}.popup-content .role-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#f6f6f6}.popup-content .user-question{margin-bottom:5px;display:flex;justify-content:flex-end}.popup-content .chat-time{margin-bottom:10px;display:flex;justify-content:flex-end;font-size:14px;color:#a3a3a3}.popup-content .role-time{justify-content:flex-start}\n'],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"directive",type:i7.NgClass,selector:"[ngClass]",inputs:["class","ngClass"]},{kind:"directive",type:i7.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:i7.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"pipe",type:i7.DatePipe,name:"date"},{kind:"ngmodule",type:FormsModule},{kind:"directive",type:i8.NgControlStatus,selector:"[formControlName],[ngModel],[formControl]"},{kind:"directive",type:i8.NgModel,selector:"[ngModel]:not([formControlName]):not([formControl])",inputs:["name","disabled","ngModel","ngModelOptions"],outputs:["ngModelChange"],exportAs:["ngModel"]},{kind:"ngmodule",type:ReactiveFormsModule},{kind:"ngmodule",type:RouterModule},{kind:"component",type:IonToolbar,selector:"ion-toolbar",inputs:["color","mode"]},{kind:"component",type:IonItem,selector:"ion-item",inputs:["button","color","detail","detailIcon","disabled","download","href","lines","mode","rel","routerAnimation","routerDirection","target","type"]},{kind:"component",type:IonButton,selector:"ion-button",inputs:["buttonType","color","disabled","download","expand","fill","form","href","mode","rel","routerAnimation","routerDirection","shape","size","strong","target","type"]},{kind:"component",type:IonList,selector:"ion-list",inputs:["inset","lines","mode"]},{kind:"component",type:IonModal,selector:"ion-modal"},{kind:"component",type:IonIcon,selector:"ion-icon",inputs:["color","flipRtl","icon","ios","lazy","md","mode","name","sanitize","size","src"]},{kind:"component",type:IonTextarea,selector:"ion-textarea",inputs:["autoGrow","autocapitalize","autofocus","clearOnEdit","color","cols","counter","counterFormatter","debounce","disabled","enterkeyhint","errorText","fill","helperText","inputmode","label","labelPlacement","maxlength","minlength","mode","name","placeholder","readonly","required","rows","shape","spellcheck","value","wrap"]},{kind:"component",type:IonPopover,selector:"ion-popover"},{kind:"component",type:IonContent,selector:"ion-content",inputs:["color","fixedSlotPlacement","forceOverscroll","fullscreen","scrollEvents","scrollX","scrollY"]},{kind:"component",type:FmChatMessageCard,selector:"fm-chat-message-card",inputs:["index","message","role","chat"]}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatModalInput,decorators:[{type:Component,args:[{selector:"fm-chat-modal-input",standalone:!0,imports:[CommonModule,FormsModule,ReactiveFormsModule,RouterModule,IonToolbar,IonItem,IonButton,IonList,IonModal,IonInput,IonIcon,IonTextarea,IonPopover,IonContent,FmChatMessageCard,ModalAudioMessageComponent],template:'<ion-toolbar>\n <ion-item class="button-item" lines="none">\n \x3c!-- 设置 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="settings-outline"></ion-icon> \n </ion-button> --\x3e\n <ng-container *ngFor="let button of chat?.leftButtons">\n <ion-button style="--padding-start:10px;--padding-end:10px;"\n shape="round" *ngIf="button?.show&&button?.show()" fill="outline" [title]="button?.title" slot="start" (click)="button.onClick()">\n <ion-icon [name]="button?.icon" [slot]="button?.showTitle?\'start\':\'icon-only\'"></ion-icon>\n {{button?.showTitle&&button?.title}}\n </ion-button>\n </ng-container>\n\n <ng-container *ngFor="let button of chat?.role?.get(\'buttons\')">\n <ion-button shape="round" (click)="chatServ.doButtonAction(button)" fill="outline" slot="start">\n {{button?.name}}\n </ion-button>\n </ng-container>\n\n \x3c!--分享按钮--\x3e\n @if(!chat?.hideShare){\n <ion-button shape="round" *ngIf="chat?.messageList?.length>1" (click)="showShare()" fill="outline" title="分享" slot="end">\n <ion-icon name="share-social-outline"></ion-icon>\n </ion-button>\n }\n <ion-modal [isOpen]="isShare">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="handleCancelShare()">取消</ion-button>\n </ion-buttons>\n <ion-title>对话分享</ion-title>\n <ion-buttons slot="end">\n <ion-button (click)="handleOkShare()">分享</ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n <ng-container *ngFor="let message of chat?.messageList;let index=index;">\n \x3c!-- 内容格式化区域 --\x3e\n <fm-chat-message-card [chat]="chat" *ngIf="!message?.hidden" [index]="index" [message]="message" [role]="chat?.role"></fm-chat-message-card>\n </ng-container>\n\n <div *ngIf="false" class="popup-content">\n <div *ngFor="let message of chat?.messageList">\n \x3c!-- 头像 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'">\n <div>\n <img class="avatar" *ngIf="message?.role!=\'user\'" [src]="(chat?.role?.get(\'avatar\') || chat?.role?.get(\'thumb\') || \'https://file-cloud.fmode.cn/E4KpGvTEto/20230930/l413e6090731854.png\')+\'?\'+\'x-image-process=image/resize,m_fixed,w_100\'+\'&imageView2/1/w/32/h/32\'" >\n </div>\n <div class="user-question">\n <app-comp-user-avatar [user]="user" *ngIf="message?.role==\'user\'"></app-comp-user-avatar>\n </div>\n </div>\n \x3c!-- 内容 --\x3e\n <div class="message-wrapper">\n <div class="message-content-user">\n <div class="user-message" *ngIf="message?.role === \'user\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-role">\n <div class="role-message" *ngIf="message?.role !== \'user\' && message?.role !== \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n <div class="message-content-system">\n <div class="system-message" *ngIf="message?.role === \'system\'">\n <div class="item-content">\n \x3c!-- <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content" [render]="false"></fm-markdown-preview> --\x3e\n <fm-markdown-preview *ngIf="message?.complete" class="content-style" [content]="message?.content"></fm-markdown-preview>\n </div>\n </div>\n </div>\n </div>\n <div class="chat-time" *ngIf="message?.createdAt" [ngClass]="{\'role-time\': message?.role !== \'user\'}">\n <span>{{message?.createdAt | date:"dd/MM/yyyy, HH/mm/ss a"}}</span>\n </div>\n </div>\n </div> \n </ion-content>\n </ng-template>\n </ion-modal>\n\n \x3c!-- 图片 --\x3e\n <ion-button shape="round" *ngIf="chat?.currentModel?.get(\'config\')?.imageEnabled" fill="outline" slot="end" (click)="setMessageImage()">\n <ion-icon name="image-outline"></ion-icon>\n </ion-button>\n \x3c!-- 模型 --\x3e\n @if(!chat?.hideModalSelect){\n <ion-button shape="round" fill="outline" slot="end" id="model-button">\n <ion-icon name="chevron-down-outline"></ion-icon>\n {{chat?.currentModel?.get&&chat?.currentModel?.get("name")||"Fmode-C1.0-128k"}}\n </ion-button>\n <ion-popover trigger="model-button" [dismissOnSelect]="true">\n <ng-template>\n <ion-content>\n <ion-list>\n <ng-container *ngFor="let model of chat.modelList">\n <ion-item (click)="chat.currentModel = model" [button]="true" [detail]="false">\n {{model?.get("name")}}\n <ion-note slot="end">{{model?.get("credit")}}/k</ion-note>\n </ion-item>\n </ng-container>\n </ion-list>\n </ion-content>\n </ng-template>\n </ion-popover>\n }\n\n </ion-item>\n\n <ion-item class="input-item" lines="none">\n \x3c!-- 语音消息输入 --\x3e\n <ng-container *ngIf="chat?.isVoiceInputMode">\n \x3c!-- 切换文本输入 --\x3e\n <ion-button class="btn-input-change" color="primary" (click)="chat.isVoiceInputMode=false" shape="round" size="large">\n <ion-icon name="chatbox-ellipses-outline" slot="icon-only"></ion-icon>\n </ion-button>\n \n <div class="btn-voice-start" (click)="startTalk()" [class.disabled]="isSending">\n <span>\n 点击讲话\n </span> \n </div>\n </ng-container>\n\n \x3c!-- 文本消息输入 --\x3e\n <ng-container *ngIf="!chat?.isVoiceInputMode">\n \x3c!-- 切换语音输入 --\x3e\n <ion-button [style.display]="chat.isTexting?\'none\':\'flex\'" class="btn-input-change" color="primary" *ngIf="chat?.role?.get(\'voiceConfig\')" (click)="chat.isVoiceInputMode=true" shape="round" size="large">\n <ion-icon name="mic-outline" slot="icon-only"></ion-icon>\n </ion-button>\n\n \x3c!-- 文本输入区域 --\x3e\n <ion-textarea\n #userInput\n *ngIf="chat" (keydown)="onKeyDown($event)"\n [errorText]="errorText"\n [(ngModel)]="chat.userInput"\n (ionFocus)="onInputFocus()"\n (ionBlur)="chat.isTexting=false"\n [autoGrow]="true" shape="round" fill="outline"\n label="Ctrl + Enter 发送消息" placeholder="请输入您的提示词"\n labelPlacement="floating"></ion-textarea>\n \n \x3c!-- 文本发送按钮 --\x3e\n <ion-button [disabled]="isSending"\n color="primary" shape="round" size="large" (click)="sendMessage()">\n <ion-icon name="paper-plane-outline" slot="icon-only"></ion-icon>\n </ion-button>\n </ng-container>\n </ion-item>\n</ion-toolbar>\n\n\n\x3c!-- 语音消息输入:弹出区域 --\x3e\n\x3c!-- <ion-modal #audioModal [isOpen]="isAudioModal" (willDismiss)="closeAudio()" [initialBreakpoint]="audioModalHeightPoint" [breakpoints]="[0, audioModalHeightPoint]">\n <ng-template>\n <fm-modal-audio-message #audioComp *ngIf="isAudioModal" [chat]="chat" [modal]="audioModal"></fm-modal-audio-message>\n </ng-template>\n</ion-modal> --\x3e',styles:['@charset "UTF-8";:host-context(body.dark) .btn-voice-start{background-color:#222428;color:#fff}:host-context(body.dark) ion-textarea{background-color:#222428;color:#fff}ion-toolbar{--background:none}ion-toolbar .button-item{--inner-padding-start:5px;--inner-padding-end:0px;--padding-start:5px;--padding-end:0px}ion-toolbar ion-item{--background:transparent}.disabled{opacity:.5;pointer-events:none}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}ion-textarea.custom{--background: #373737;--color: #fff;--padding-end: 10px;--padding-start: 10px;--placeholder-color: #ddd;--placeholder-opacity: .8}ion-textarea.custom textarea{width:calc(100% - 95px)}ion-textarea.custom ion-button{position:absolute;right:0}.input-item{display:flex;min-height:77px;align-items:center;border:none;--inner-padding-start:0px;--inner-padding-end:0px;--padding-start:0px;--padding-end:0px}.input-item ion-textarea{background-color:#fff;max-height:400px;padding:0 5px;margin:0 5px;border-radius:20px;overflow-y:auto}.input-item .btn-voice-start{display:flex;flex:1;justify-content:center;align-items:center;font-weight:700;background:#fff;border-radius:20px;min-height:50px}ion-textarea{transition:width .5s ease}ion-textarea:hover .btn-input-change,ion-textarea:focus-within .btn-input-change{display:none}.input-item:hover ion-textarea,.input-item:focus-within ion-textarea{border-color:var(--logo-color-primary)}::ng-deep .ant-modal-body{max-height:600px;overflow-y:auto}::ng-deep .ant-modal-footer{display:flex;justify-content:space-around}::ng-deep .ant-btn{width:40%}.popup-content{position:relative}.popup-content .message-content-user{display:flex;justify-content:flex-end}.popup-content .message-content-role{display:flex;justify-content:flex-start}.popup-content .message-content-system{display:flex;justify-content:center}.popup-content .user-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#e7f8ff}.popup-content .role-message{padding:10px 10px 0;border-radius:10px;width:fit-content;max-width:100%;background-color:#f6f6f6}.popup-content .user-question{margin-bottom:5px;display:flex;justify-content:flex-end}.popup-content .chat-time{margin-bottom:10px;display:flex;justify-content:flex-end;font-size:14px;color:#a3a3a3}.popup-content .role-time{justify-content:flex-start}\n']}]}],ctorParameters:()=>[{type:i1.ToastController},{type:i1.AlertController},{type:i2.ModalController},{type:i3.Router},{type:i4.ImagineService},{type:i5.ChatService},{type:i6.AccountService}],propDecorators:{audioComp:[{type:ViewChild,args:[ModalAudioMessageComponent]}],userInputComp:[{type:ViewChild,args:["userInput"]}],chat:[{type:Input}],message:[{type:Input}],role:[{type:Input}]}});
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL2NoYXQvY2hhdC1tb2RhbC1pbnB1dC9tb2RhbC1pbnB1dC5jb21wb25lbnQubWpz`
10
10
 
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/chat/comp-role-prompt/comp-role-prompt.component.mjs
7
7
  */
8
- import{Component,Input}from"@angular/core";import{FmodeChat}from"../../service-fmai/service-chat";import*as Parse from"parse";import{IonButton,IonIcon,IonLabel,IonModal,IonSegment}from"@ionic/angular/standalone";import{CommonModule}from"@angular/common";import{FormsModule,ReactiveFormsModule}from"@angular/forms";import{CrossService}from"../../../platform/cross.service";import{addIcons}from"ionicons";import{chevronForward}from"ionicons/icons";import*as i0 from"@angular/core";import*as i1 from"../../../platform/cross.service";import*as i2 from"@angular/common";addIcons({chevronForward:chevronForward});export class CompRolePromptComponent{constructor(e){this.cross=e,this.role="",this.showModal=!1,this.cateIndex=0,this.isDarkMode=!1,this.company="E4KpGvTEto"}ngOnInit(){this.getChatPrompt(),document.body.classList.contains("dark")&&(this.isDarkMode=!0)}applyPrompt(e){this.chat.userInput=e,this.chat.isVoiceInputMode=!1,this.chat.focusUserInput(),"mobile"==this.cross?.navMenuType&&(this.chat.isPromptModalOpen=!1)}async getChatPrompt(){if(this.chat.promptList?.length)return;let e=new Parse.Query("ChatPrompt");e.notEqualTo("isDeleted",!0),e.equalTo("role",this.role),e.include("role");let o=await e.find();o?.length&&(this.chat.promptList=o,this.chat.promptList.forEach((e=>{let o=e.get("role").get("promptCates").filter((o=>o.name==e.get("cate")));e.img=o[0].img})),console.log(this.chat.promptList))}checkCate(e){this.cateIndex=e}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:CompRolePromptComponent,deps:[{token:i1.CrossService}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"17.3.12",type:CompRolePromptComponent,isStandalone:!0,selector:"app-comp-role-prompt",inputs:{chat:"chat",role:"role"},providers:[],ngImport:i0,template:'<ng-container *ngIf="chat?.messageList?.length<4 && chat?.isPromptMessageAreaShow && !chat?.userInput">\n \x3c!-- PC端样式 --\x3e\n <div class="prompt" *ngIf="cross?.navMenuType!=\'mobile\'">\n <ng-container *ngFor="let content of chat?.promptList">\n <div class="prompt_cate">\n <div class="cate_info">\n <div>\n <img [src]="content.img" alt="">\n </div>\n <div class="cate_name">\n {{content.get(\'cate\')}}\n </div>\n </div>\n <ng-container *ngFor="let message of content.get(\'messageList\')">\n <div class="message" (click)="applyPrompt(message)">\n {{message}}\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n \x3c!-- 手机端样式 --\x3e\n <ng-container *ngIf="cross?.navMenuType==\'mobile\'">\n <div class="prompt_mobile">\n <ng-container *ngFor="let content of chat?.promptList">\n <div class="prompt_cate">\n <div class="cate_info">\n <div>\n <img [src]="content.img" alt="">\n </div>\n <div class="cate_name">\n {{content.get(\'cate\')}}\n </div>\n </div>\n <ng-container *ngFor="let message of content.get(\'messageList\');let idx = index">\n <div *ngIf="idx<3" class="message" (click)="applyPrompt(message)">\n {{message}}\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n <div class="view_more" *ngIf="chat?.promptList?.length">\n <button (click)="chat.isPromptModalOpen=true" expand="block">查看更多<ion-icon name="chevron-forward"></ion-icon></button>\n </div>\n </ng-container>\n</ng-container>\n\n <ion-modal [isOpen]="chat.isPromptModalOpen" (willDismiss)="chat.isPromptModalOpen=false" [initialBreakpoint]="1" [breakpoints]="[0, 1]">\n <ng-template>\n <div class="block">\n <ion-label>话题灵感</ion-label>\n <div class="block_cate">\n \x3c!-- <ion-segment [(ngModel)]="cateIndex">\n <ng-container *ngFor="let prompt of chat?.promptList;let i = index">\n <ion-segment-button value="i">\n <ion-label>{{prompt?.get("cate")}}</ion-label>\n </ion-segment-button>\n </ng-container>\n </ion-segment> --\x3e\n <ng-container *ngFor="let prompt of chat?.promptList;let i = index">\n <ion-button [color]="i==cateIndex?\'secondary\':\'light\'" (click)="checkCate(i)">{{prompt?.get("cate")}}</ion-button>\n \x3c!-- <div [class]="i==cateIndex?\'active_cate\':\'\'" (click)="checkCate(i)">{{prompt?.get("cate")}}</div> --\x3e\n </ng-container>\n </div>\n <div class="message_mobile">\n <ng-container *ngFor="let message of chat?.promptList[cateIndex]?.get(\'messageList\')">\n <div class="message-box" [style.backgroundColor]="isDarkMode?\'rgba(255,255,255,0.2)\':\'white\'" [style.color]="isDarkMode?\'rgba(255,255,255,0.5)\':\'black\'" (click)="applyPrompt(message)">{{message}}</div>\n </ng-container>\n </div>\n </div>\n </ng-template>\n </ion-modal>',styles:[":host-context(body.dark) .prompt_cate .message{background:#fff3;border:1px solid #333;box-shadow:0 2px 8px #333;color:#ffffff80}:host-context(body.dark) .cate_info{color:#fff}:host-context(body.dark) ion-modal .block h4{color:#fff!important}:host-context(body.dark) ion-modal .block .message-box{color:#ccc!important;background-color:#333!important}:host-context(body.dark) .message-box{color:#ccc!important;background-color:#333!important}.prompt{color:#000;display:flex;justify-content:space-between;align-items:flex-start}.prompt .prompt_cate{flex:1;height:inherit;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-direction:column;display:flex;justify-content:center}.prompt .prompt_cate .cate_info{font-size:1.5em;font-weight:700}.prompt .prompt_cate .cate_info div img{width:58px;height:58px}.prompt .prompt_cate .cate_info .cate_name{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.prompt .prompt_cate .message{padding:1em;margin:1em;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}@media screen and (max-width: 390.4px){.prompt .prompt_cate .message{width:100px;height:64px;padding:unset;margin:.8em;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;overflow:hidden;white-space:pre-wrap;text-overflow:ellipsis}}.prompt_mobile{color:#000;display:flex;justify-content:space-between;align-items:flex-start}.prompt_mobile .prompt_cate{flex:1;height:inherit}.prompt_mobile .prompt_cate .cate_info{display:flex;justify-content:center;align-items:center;font-size:1.2em}.prompt_mobile .prompt_cate .cate_info div img{width:48px;height:48px}.prompt_mobile .prompt_cate .cate_info .cate_name{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;overflow:hidden;text-overflow:ellipsis;margin-top:2vw}.prompt_mobile .prompt_cate .message{min-height:48px;padding:0 2vw;margin:4vw 1vw;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;text-overflow:ellipsis}.view_more{text-align:center;color:#999;margin:4vw 0}.view_more>button{background-color:transparent}.block{width:100%;height:80vh;padding:2vw}.block .block_cate{display:flex;justify-content:space-evenly}.block .message_mobile{overflow-y:auto;height:65vh;color:#000}.message-box{background-color:#fff;text-align:center;padding:8px 0;margin:8px 0;border-radius:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}ion-modal{--height: auto}\n"],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"directive",type:i2.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:i2.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"ngmodule",type:FormsModule},{kind:"ngmodule",type:ReactiveFormsModule},{kind:"component",type:IonModal,selector:"ion-modal"},{kind:"component",type:IonButton,selector:"ion-button",inputs:["buttonType","color","disabled","download","expand","fill","form","href","mode","rel","routerAnimation","routerDirection","shape","size","strong","target","type"]},{kind:"component",type:IonLabel,selector:"ion-label",inputs:["color","mode","position"]},{kind:"component",type:IonIcon,selector:"ion-icon",inputs:["color","flipRtl","icon","ios","lazy","md","mode","name","sanitize","size","src"]}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:CompRolePromptComponent,decorators:[{type:Component,args:[{selector:"app-comp-role-prompt",standalone:!0,imports:[CommonModule,FormsModule,ReactiveFormsModule,IonModal,IonButton,IonSegment,IonLabel,IonIcon],providers:[],template:'<ng-container *ngIf="chat?.messageList?.length<4 && chat?.isPromptMessageAreaShow && !chat?.userInput">\n \x3c!-- PC端样式 --\x3e\n <div class="prompt" *ngIf="cross?.navMenuType!=\'mobile\'">\n <ng-container *ngFor="let content of chat?.promptList">\n <div class="prompt_cate">\n <div class="cate_info">\n <div>\n <img [src]="content.img" alt="">\n </div>\n <div class="cate_name">\n {{content.get(\'cate\')}}\n </div>\n </div>\n <ng-container *ngFor="let message of content.get(\'messageList\')">\n <div class="message" (click)="applyPrompt(message)">\n {{message}}\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n \x3c!-- 手机端样式 --\x3e\n <ng-container *ngIf="cross?.navMenuType==\'mobile\'">\n <div class="prompt_mobile">\n <ng-container *ngFor="let content of chat?.promptList">\n <div class="prompt_cate">\n <div class="cate_info">\n <div>\n <img [src]="content.img" alt="">\n </div>\n <div class="cate_name">\n {{content.get(\'cate\')}}\n </div>\n </div>\n <ng-container *ngFor="let message of content.get(\'messageList\');let idx = index">\n <div *ngIf="idx<3" class="message" (click)="applyPrompt(message)">\n {{message}}\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n <div class="view_more" *ngIf="chat?.promptList?.length">\n <button (click)="chat.isPromptModalOpen=true" expand="block">查看更多<ion-icon name="chevron-forward"></ion-icon></button>\n </div>\n </ng-container>\n</ng-container>\n\n <ion-modal [isOpen]="chat.isPromptModalOpen" (willDismiss)="chat.isPromptModalOpen=false" [initialBreakpoint]="1" [breakpoints]="[0, 1]">\n <ng-template>\n <div class="block">\n <ion-label>话题灵感</ion-label>\n <div class="block_cate">\n \x3c!-- <ion-segment [(ngModel)]="cateIndex">\n <ng-container *ngFor="let prompt of chat?.promptList;let i = index">\n <ion-segment-button value="i">\n <ion-label>{{prompt?.get("cate")}}</ion-label>\n </ion-segment-button>\n </ng-container>\n </ion-segment> --\x3e\n <ng-container *ngFor="let prompt of chat?.promptList;let i = index">\n <ion-button [color]="i==cateIndex?\'secondary\':\'light\'" (click)="checkCate(i)">{{prompt?.get("cate")}}</ion-button>\n \x3c!-- <div [class]="i==cateIndex?\'active_cate\':\'\'" (click)="checkCate(i)">{{prompt?.get("cate")}}</div> --\x3e\n </ng-container>\n </div>\n <div class="message_mobile">\n <ng-container *ngFor="let message of chat?.promptList[cateIndex]?.get(\'messageList\')">\n <div class="message-box" [style.backgroundColor]="isDarkMode?\'rgba(255,255,255,0.2)\':\'white\'" [style.color]="isDarkMode?\'rgba(255,255,255,0.5)\':\'black\'" (click)="applyPrompt(message)">{{message}}</div>\n </ng-container>\n </div>\n </div>\n </ng-template>\n </ion-modal>',styles:[":host-context(body.dark) .prompt_cate .message{background:#fff3;border:1px solid #333;box-shadow:0 2px 8px #333;color:#ffffff80}:host-context(body.dark) .cate_info{color:#fff}:host-context(body.dark) ion-modal .block h4{color:#fff!important}:host-context(body.dark) ion-modal .block .message-box{color:#ccc!important;background-color:#333!important}:host-context(body.dark) .message-box{color:#ccc!important;background-color:#333!important}.prompt{color:#000;display:flex;justify-content:space-between;align-items:flex-start}.prompt .prompt_cate{flex:1;height:inherit;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-direction:column;display:flex;justify-content:center}.prompt .prompt_cate .cate_info{font-size:1.5em;font-weight:700}.prompt .prompt_cate .cate_info div img{width:58px;height:58px}.prompt .prompt_cate .cate_info .cate_name{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.prompt .prompt_cate .message{padding:1em;margin:1em;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}@media screen and (max-width: 390.4px){.prompt .prompt_cate .message{width:100px;height:64px;padding:unset;margin:.8em;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;overflow:hidden;white-space:pre-wrap;text-overflow:ellipsis}}.prompt_mobile{color:#000;display:flex;justify-content:space-between;align-items:flex-start}.prompt_mobile .prompt_cate{flex:1;height:inherit}.prompt_mobile .prompt_cate .cate_info{display:flex;justify-content:center;align-items:center;font-size:1.2em}.prompt_mobile .prompt_cate .cate_info div img{width:48px;height:48px}.prompt_mobile .prompt_cate .cate_info .cate_name{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;overflow:hidden;text-overflow:ellipsis;margin-top:2vw}.prompt_mobile .prompt_cate .message{min-height:48px;padding:0 2vw;margin:4vw 1vw;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;text-overflow:ellipsis}.view_more{text-align:center;color:#999;margin:4vw 0}.view_more>button{background-color:transparent}.block{width:100%;height:80vh;padding:2vw}.block .block_cate{display:flex;justify-content:space-evenly}.block .message_mobile{overflow-y:auto;height:65vh;color:#000}.message-box{background-color:#fff;text-align:center;padding:8px 0;margin:8px 0;border-radius:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}ion-modal{--height: auto}\n"]}]}],ctorParameters:()=>[{type:i1.CrossService}],propDecorators:{chat:[{type:Input}],role:[{type:Input}]}});
8
+ import{Component,Input}from"@angular/core";import{FmodeChat}from"../../service-fmai/service-chat";import*as Parse from"parse";import{IonButton,IonIcon,IonLabel,IonModal,IonSegment}from"@ionic/angular/standalone";import{CommonModule}from"@angular/common";import{FormsModule,ReactiveFormsModule}from"@angular/forms";import{CrossService}from"../../../platform/cross.service";import{addIcons}from"ionicons";import{chevronForward}from"ionicons/icons";import*as i0 from"@angular/core";import*as i1 from"../../../platform/cross.service";import*as i2 from"@angular/common";addIcons({chevronForward:chevronForward});export class CompRolePromptComponent{constructor(e){this.cross=e,this.role="",this.showModal=!1,this.cateIndex=0,this.isDarkMode=!1,this.company="E4KpGvTEto"}ngOnInit(){this.getChatPrompt(),document.body.classList.contains("dark")&&(this.isDarkMode=!0)}applyPrompt(e){this.chat.userInput=e,this.chat.isVoiceInputMode=!1,this.chat.focusUserInput(),"mobile"==this.cross?.navMenuType&&(this.chat.isPromptModalOpen=!1)}async getChatPrompt(){if(this.chat.promptList?.length)return;let e=new Parse.Query("ChatPrompt");e.notEqualTo("isDeleted",!0),e.equalTo("role",this.role),e.include("role");let o=await e.find();o?.length&&(console.log("this.chat.promptList?.length",this.chat.promptList),this.chat.promptList?.length||(this.chat.promptList=o,this.chat.promptList.forEach((e=>{let o=e.get("role").get("promptCates").filter((o=>o.name==e.get("cate")));e.img=o[0].img})),console.log(this.chat.promptList)))}checkCate(e){this.cateIndex=e}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:CompRolePromptComponent,deps:[{token:i1.CrossService}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"17.3.12",type:CompRolePromptComponent,isStandalone:!0,selector:"app-comp-role-prompt",inputs:{chat:"chat",role:"role"},providers:[],ngImport:i0,template:'<ng-container *ngIf="chat?.messageList?.length<4 && chat?.isPromptMessageAreaShow && !chat?.userInput">\n \x3c!-- PC端样式 --\x3e\n <div class="prompt" *ngIf="cross?.navMenuType!=\'mobile\'">\n <ng-container *ngFor="let content of chat?.promptList">\n <div class="prompt_cate">\n <div class="cate_info">\n <div>\n <img [src]="content.img" alt="">\n </div>\n <div class="cate_name">\n {{content.get(\'cate\')}}\n </div>\n </div>\n <ng-container *ngFor="let message of content.get(\'messageList\')">\n <div class="message" (click)="applyPrompt(message)">\n {{message}}\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n \x3c!-- 手机端样式 --\x3e\n <ng-container *ngIf="cross?.navMenuType==\'mobile\'">\n <div class="prompt_mobile">\n <ng-container *ngFor="let content of chat?.promptList">\n <div class="prompt_cate">\n <div class="cate_info">\n <div>\n <img [src]="content.img" alt="">\n </div>\n <div class="cate_name">\n {{content.get(\'cate\')}}\n </div>\n </div>\n <ng-container *ngFor="let message of content.get(\'messageList\');let idx = index">\n <div *ngIf="idx<3" class="message" (click)="applyPrompt(message)">\n {{message}}\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n <div class="view_more" *ngIf="chat?.promptList?.length">\n <button (click)="chat.isPromptModalOpen=true" expand="block">查看更多<ion-icon name="chevron-forward"></ion-icon></button>\n </div>\n </ng-container>\n</ng-container>\n\n <ion-modal [isOpen]="chat.isPromptModalOpen" (willDismiss)="chat.isPromptModalOpen=false" [initialBreakpoint]="1" [breakpoints]="[0, 1]">\n <ng-template>\n <div class="block">\n <ion-label>话题灵感</ion-label>\n <div class="block_cate">\n \x3c!-- <ion-segment [(ngModel)]="cateIndex">\n <ng-container *ngFor="let prompt of chat?.promptList;let i = index">\n <ion-segment-button value="i">\n <ion-label>{{prompt?.get("cate")}}</ion-label>\n </ion-segment-button>\n </ng-container>\n </ion-segment> --\x3e\n <ng-container *ngFor="let prompt of chat?.promptList;let i = index">\n <ion-button [color]="i==cateIndex?\'secondary\':\'light\'" (click)="checkCate(i)">{{prompt?.get("cate")}}</ion-button>\n \x3c!-- <div [class]="i==cateIndex?\'active_cate\':\'\'" (click)="checkCate(i)">{{prompt?.get("cate")}}</div> --\x3e\n </ng-container>\n </div>\n <div class="message_mobile">\n <ng-container *ngFor="let message of chat?.promptList[cateIndex]?.get(\'messageList\')">\n <div class="message-box" [style.backgroundColor]="isDarkMode?\'rgba(255,255,255,0.2)\':\'white\'" [style.color]="isDarkMode?\'rgba(255,255,255,0.5)\':\'black\'" (click)="applyPrompt(message)">{{message}}</div>\n </ng-container>\n </div>\n </div>\n </ng-template>\n </ion-modal>',styles:[":host-context(body.dark) .prompt_cate .message{background:#fff3;border:1px solid #333;box-shadow:0 2px 8px #333;color:#ffffff80}:host-context(body.dark) .cate_info{color:#fff}:host-context(body.dark) ion-modal .block h4{color:#fff!important}:host-context(body.dark) ion-modal .block .message-box{color:#ccc!important;background-color:#333!important}:host-context(body.dark) .message-box{color:#ccc!important;background-color:#333!important}.prompt{color:#000;display:flex;justify-content:space-between;align-items:flex-start}.prompt .prompt_cate{flex:1;height:inherit;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-direction:column;display:flex;justify-content:center}.prompt .prompt_cate .cate_info{font-size:1.5em;font-weight:700;display:flex;justify-content:center;flex-direction:column;align-items:center}.prompt .prompt_cate .cate_info div img{border-radius:15px;width:58px;height:58px}.prompt .prompt_cate .cate_info .cate_name{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.prompt .prompt_cate .message{padding:1em;margin:1em;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}@media screen and (max-width: 390.4px){.prompt .prompt_cate .message{width:100px;height:64px;padding:unset;margin:.8em;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;overflow:hidden;white-space:pre-wrap;text-overflow:ellipsis}}.prompt_mobile{color:#000;display:flex;justify-content:space-between;align-items:flex-start}.prompt_mobile .prompt_cate{flex:1;height:inherit}.prompt_mobile .prompt_cate .cate_info{display:flex;justify-content:center;align-items:center;font-size:1.2em}.prompt_mobile .prompt_cate .cate_info div img{width:48px;height:48px}.prompt_mobile .prompt_cate .cate_info .cate_name{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;overflow:hidden;text-overflow:ellipsis;margin-top:2vw}.prompt_mobile .prompt_cate .message{min-height:48px;padding:0 2vw;margin:4vw 1vw;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;text-overflow:ellipsis}.view_more{text-align:center;color:#999;margin:4vw 0}.view_more>button{background-color:transparent}.block{width:100%;height:80vh;padding:2vw}.block .block_cate{display:flex;justify-content:space-evenly}.block .message_mobile{overflow-y:auto;height:65vh;color:#000}.message-box{background-color:#fff;text-align:center;padding:8px 0;margin:8px 0;border-radius:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}ion-modal{--height: auto}\n"],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"directive",type:i2.NgForOf,selector:"[ngFor][ngForOf]",inputs:["ngForOf","ngForTrackBy","ngForTemplate"]},{kind:"directive",type:i2.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"ngmodule",type:FormsModule},{kind:"ngmodule",type:ReactiveFormsModule},{kind:"component",type:IonModal,selector:"ion-modal"},{kind:"component",type:IonButton,selector:"ion-button",inputs:["buttonType","color","disabled","download","expand","fill","form","href","mode","rel","routerAnimation","routerDirection","shape","size","strong","target","type"]},{kind:"component",type:IonLabel,selector:"ion-label",inputs:["color","mode","position"]},{kind:"component",type:IonIcon,selector:"ion-icon",inputs:["color","flipRtl","icon","ios","lazy","md","mode","name","sanitize","size","src"]}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:CompRolePromptComponent,decorators:[{type:Component,args:[{selector:"app-comp-role-prompt",standalone:!0,imports:[CommonModule,FormsModule,ReactiveFormsModule,IonModal,IonButton,IonSegment,IonLabel,IonIcon],providers:[],template:'<ng-container *ngIf="chat?.messageList?.length<4 && chat?.isPromptMessageAreaShow && !chat?.userInput">\n \x3c!-- PC端样式 --\x3e\n <div class="prompt" *ngIf="cross?.navMenuType!=\'mobile\'">\n <ng-container *ngFor="let content of chat?.promptList">\n <div class="prompt_cate">\n <div class="cate_info">\n <div>\n <img [src]="content.img" alt="">\n </div>\n <div class="cate_name">\n {{content.get(\'cate\')}}\n </div>\n </div>\n <ng-container *ngFor="let message of content.get(\'messageList\')">\n <div class="message" (click)="applyPrompt(message)">\n {{message}}\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n \x3c!-- 手机端样式 --\x3e\n <ng-container *ngIf="cross?.navMenuType==\'mobile\'">\n <div class="prompt_mobile">\n <ng-container *ngFor="let content of chat?.promptList">\n <div class="prompt_cate">\n <div class="cate_info">\n <div>\n <img [src]="content.img" alt="">\n </div>\n <div class="cate_name">\n {{content.get(\'cate\')}}\n </div>\n </div>\n <ng-container *ngFor="let message of content.get(\'messageList\');let idx = index">\n <div *ngIf="idx<3" class="message" (click)="applyPrompt(message)">\n {{message}}\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n <div class="view_more" *ngIf="chat?.promptList?.length">\n <button (click)="chat.isPromptModalOpen=true" expand="block">查看更多<ion-icon name="chevron-forward"></ion-icon></button>\n </div>\n </ng-container>\n</ng-container>\n\n <ion-modal [isOpen]="chat.isPromptModalOpen" (willDismiss)="chat.isPromptModalOpen=false" [initialBreakpoint]="1" [breakpoints]="[0, 1]">\n <ng-template>\n <div class="block">\n <ion-label>话题灵感</ion-label>\n <div class="block_cate">\n \x3c!-- <ion-segment [(ngModel)]="cateIndex">\n <ng-container *ngFor="let prompt of chat?.promptList;let i = index">\n <ion-segment-button value="i">\n <ion-label>{{prompt?.get("cate")}}</ion-label>\n </ion-segment-button>\n </ng-container>\n </ion-segment> --\x3e\n <ng-container *ngFor="let prompt of chat?.promptList;let i = index">\n <ion-button [color]="i==cateIndex?\'secondary\':\'light\'" (click)="checkCate(i)">{{prompt?.get("cate")}}</ion-button>\n \x3c!-- <div [class]="i==cateIndex?\'active_cate\':\'\'" (click)="checkCate(i)">{{prompt?.get("cate")}}</div> --\x3e\n </ng-container>\n </div>\n <div class="message_mobile">\n <ng-container *ngFor="let message of chat?.promptList[cateIndex]?.get(\'messageList\')">\n <div class="message-box" [style.backgroundColor]="isDarkMode?\'rgba(255,255,255,0.2)\':\'white\'" [style.color]="isDarkMode?\'rgba(255,255,255,0.5)\':\'black\'" (click)="applyPrompt(message)">{{message}}</div>\n </ng-container>\n </div>\n </div>\n </ng-template>\n </ion-modal>',styles:[":host-context(body.dark) .prompt_cate .message{background:#fff3;border:1px solid #333;box-shadow:0 2px 8px #333;color:#ffffff80}:host-context(body.dark) .cate_info{color:#fff}:host-context(body.dark) ion-modal .block h4{color:#fff!important}:host-context(body.dark) ion-modal .block .message-box{color:#ccc!important;background-color:#333!important}:host-context(body.dark) .message-box{color:#ccc!important;background-color:#333!important}.prompt{color:#000;display:flex;justify-content:space-between;align-items:flex-start}.prompt .prompt_cate{flex:1;height:inherit;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-direction:column;display:flex;justify-content:center}.prompt .prompt_cate .cate_info{font-size:1.5em;font-weight:700;display:flex;justify-content:center;flex-direction:column;align-items:center}.prompt .prompt_cate .cate_info div img{border-radius:15px;width:58px;height:58px}.prompt .prompt_cate .cate_info .cate_name{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.prompt .prompt_cate .message{padding:1em;margin:1em;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}@media screen and (max-width: 390.4px){.prompt .prompt_cate .message{width:100px;height:64px;padding:unset;margin:.8em;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;overflow:hidden;white-space:pre-wrap;text-overflow:ellipsis}}.prompt_mobile{color:#000;display:flex;justify-content:space-between;align-items:flex-start}.prompt_mobile .prompt_cate{flex:1;height:inherit}.prompt_mobile .prompt_cate .cate_info{display:flex;justify-content:center;align-items:center;font-size:1.2em}.prompt_mobile .prompt_cate .cate_info div img{width:48px;height:48px}.prompt_mobile .prompt_cate .cate_info .cate_name{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;overflow:hidden;text-overflow:ellipsis;margin-top:2vw}.prompt_mobile .prompt_cate .message{min-height:48px;padding:0 2vw;margin:4vw 1vw;background:#fff;border:1px solid #eef2f2;border-radius:12px;box-shadow:0 2px 8px #f5f6f8;cursor:pointer;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;text-overflow:ellipsis}.view_more{text-align:center;color:#999;margin:4vw 0}.view_more>button{background-color:transparent}.block{width:100%;height:80vh;padding:2vw}.block .block_cate{display:flex;justify-content:space-evenly}.block .message_mobile{overflow-y:auto;height:65vh;color:#000}.message-box{background-color:#fff;text-align:center;padding:8px 0;margin:8px 0;border-radius:4px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}ion-modal{--height: auto}\n"]}]}],ctorParameters:()=>[{type:i1.CrossService}],propDecorators:{chat:[{type:Input}],role:[{type:Input}]}});
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL2NoYXQvY29tcC1yb2xlLXByb21wdC9jb21wLXJvbGUtcHJvbXB0LmNvbXBvbmVudC5tanM=`
10
10
 
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/service-fmai/service-imagine/imagine-func.mjs
7
7
  */
8
- import Parse from"parse";import{apig}from"../../../nova-cloud/ncloud-api-func";export async function drawDalle(e){e.size=e?.size||"1024x1024",e.style=e?.style||"natural",e.quality=e?.quality||null;let t={model:"dall-e-3",prompt:e.prompt,n:1,quality:e.quality,response_format:"url",size:e.size,style:e.style};if(e.prompt?.length>3e3)throw"prompt maximum < 4000 characters";let a,l=await apig("aigc/gpt/v1/images/generations",t);if(console.log(l),l?.id){let e=new Parse.Query("ImagineWork");e.get(l?.id),a=await e.first(),console.log(a)}return a}
8
+ import Parse from"parse";import{apig}from"../../../nova-cloud/ncloud-api-func";export async function drawDalle(e){e.size=e?.size||"1024x1024",e.style=e?.style||"natural",e.quality=e?.quality||null;let t={model:"dall-e-3",prompt:e.prompt,n:1,quality:e.quality,response_format:"url",size:e.size,style:e.style};if(e.prompt?.length>3e3)throw"prompt maximum < 4000 characters";let a,r=await apig("aigc/gpt/v1/images/generations",t);if(console.log(r),r?.id){let e=new Parse.Query("ImagineWork");e.get(r?.id),a=await e.first(),console.log(a)}return a}export async function getJimengImg(e){let t,a,r=Parse.User.current(),i=await fetch("https://server.fmode.cn/api/volcengine/jimeng/getImg",{headers:{authorization:`Bearer ${r?.getSessionToken()}`,"Content-Type":"application/json"},mode:"cors",credentials:"omit",method:"POST",body:JSON.stringify(e)}),o=await i.json();t=o?.data?.workId;let n=new Parse.Query("ImagineWork");return a=await n.get(t),a}
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL3NlcnZpY2UtZm1haS9zZXJ2aWNlLWltYWdpbmUvaW1hZ2luZS1mdW5jLm1qcw==`
10
10
 
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/service-fmai/service-imagine/imagine.service.mjs
7
7
  */
8
- import{HttpClient}from"@angular/common/http";import{Injectable}from"@angular/core";import{NovaCloudService}from"../../../nova-cloud/nova-cloud.service";import Parse from"parse";import{NovaUploadService}from"../../../storage/service-upload/nova-upload.service";import{drawDalle}from"./imagine-func";import*as i0 from"@angular/core";import*as i1 from"@angular/common/http";import*as i2 from"../../../nova-cloud/nova-cloud.service";import*as i3 from"../../../storage/service-upload/nova-upload.service";export class ImagineService{constructor(e,t,i){this.http=e,this.ncloud=t,this.uploadServ=i,this.taskDetailMap={},this.newWorkList=[],this.myWorkList=[]}async drawDalle(e){let t=await drawDalle(e);return t?.id&&this.newWorkList.unshift(t),t}priceDalle(e){let t=[{model:"dall-e-3",quality:null,size:"1024x1024",credit:6.4},{model:"dall-e-3",quality:null,size:"1024x1792",credit:12.8},{model:"dall-e-3",quality:null,size:"1792x1024",credit:12.8},{model:"dall-e-3",quality:"hd",size:"1024x1024",credit:12.8},{model:"dall-e-3",quality:"hd",size:"1024x1792",credit:19.2},{model:"dall-e-3",quality:"hd",size:"1792x1024",credit:19.2},{model:"dall-e-2",quality:null,size:"1024x1024",credit:3.2},{model:"dall-e-2",quality:null,size:"512x512",credit:2.88},{model:"dall-e-2",quality:null,size:"256x256",credit:2.56}].find((t=>t.model==e.model&&t.quality==e.quality&&t.size==e.size));return t?.credit||19.2}priceStableDiffusion(e){let t=e.width*e.height,i=763e-9*t*e.steps+2278e-8*t*(e?.upscale||0)+(e?.hrSteps||0)*t*(e?.hrScale||0)*(e?.hrScale||0)*763e-9+(e?.faceFix?2:0)+(e?.imgOptions?.removeBackground?2:0)+(e?.imgOptions?.redrawBackground?2:0)+(e?.imgOptions?.facePreservation?2:0)+(e?.imgOptions?.genderDetect?1:0)+2*(e?.controlnet?.units?.length||0);return i=.3*i*e.batchSize,i}b64DataToBase64Image(e){let t=atob(e),i=new Blob([t],{type:"image/webp"});new Promise((e=>{let t=new FileReader;t.onloadend=function(){let i=t.result;console.log(i),e(i)},t.readAsDataURL(i)}))}async draw(e){let t=await this.ncloud.apig("aigc/sdapi/v1/draw",e),i=t?.paintingSign;return i&&setTimeout((async()=>{let e=new Parse.Query("ImagineWork");e.equalTo("taskId",i);let t=await e.first();console.log(t),t?.id&&this.newWorkList.unshift(t)}),1e3),t}async taskDetail(e){let t=await this.ncloud.apig("aigc/sdapi/v1/task/detail",{taskId:e});return console.log(t),this.taskDetailMap[e]=t,t}getMyWorkQuery(){let e=Parse.User.current();if(!e?.id)return;let t=this.getWorkQuery();return t.include("model","module","user"),t.equalTo("user",e.toPointer()),t}getWorkQuery(){let e=new Date((new Date).getTime()-6e4),t=Parse.Query.fromJSON("ImagineWork",{where:{$or:[{createdAt:{$lte:e},progress:{$ne:0}},{createdAt:{$gt:e}}]}});return t.include("model","module","user"),t.notEqualTo("isDeleted",!0),t.notEqualTo("isFailed",!0),t.doesNotExist("respData.error"),t.doesNotExist("respData.data.taskLimitCount"),t.addDescending("createdAt"),t}getimg(){return new Promise(((e,t)=>{let i=document.createElement("input");i.type="file",i.click();let handleChange=async()=>{if(i.removeEventListener("change",handleChange),i.files&&i.files.length>0){let t=i.files[0],a=await this.uploadServ.upload(t,(e=>{console.log(e),e.total.percent.toFixed(2)}));e(a.url)}else t("未选择文件")};i.addEventListener("change",handleChange)}))}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:ImagineService,deps:[{token:i1.HttpClient},{token:i2.NovaCloudService},{token:i3.NovaUploadService}],target:i0.ɵɵFactoryTarget.Injectable})}static{this.ɵprov=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:ImagineService,providedIn:"root"})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:ImagineService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}],ctorParameters:()=>[{type:i1.HttpClient},{type:i2.NovaCloudService},{type:i3.NovaUploadService}]});
8
+ import{HttpClient}from"@angular/common/http";import{Injectable}from"@angular/core";import{NovaCloudService}from"../../../nova-cloud/nova-cloud.service";import Parse from"parse";import{NovaUploadService}from"../../../storage/service-upload/nova-upload.service";import{drawDalle,getJimengImg}from"./imagine-func";import*as i0 from"@angular/core";import*as i1 from"@angular/common/http";import*as i2 from"../../../nova-cloud/nova-cloud.service";import*as i3 from"../../../storage/service-upload/nova-upload.service";export class ImagineService{constructor(e,t,i){this.http=e,this.ncloud=t,this.uploadServ=i,this.taskDetailMap={},this.newWorkList=[],this.myWorkList=[]}async drawDalle(e){let t=await drawDalle(e);return t?.id&&this.newWorkList.unshift(t),t}priceDalle(e){let t=[{model:"dall-e-3",quality:null,size:"1024x1024",credit:6.4},{model:"dall-e-3",quality:null,size:"1024x1792",credit:12.8},{model:"dall-e-3",quality:null,size:"1792x1024",credit:12.8},{model:"dall-e-3",quality:"hd",size:"1024x1024",credit:12.8},{model:"dall-e-3",quality:"hd",size:"1024x1792",credit:19.2},{model:"dall-e-3",quality:"hd",size:"1792x1024",credit:19.2},{model:"dall-e-2",quality:null,size:"1024x1024",credit:3.2},{model:"dall-e-2",quality:null,size:"512x512",credit:2.88},{model:"dall-e-2",quality:null,size:"256x256",credit:2.56}].find((t=>t.model==e.model&&t.quality==e.quality&&t.size==e.size));return t?.credit||19.2}priceStableDiffusion(e){let t=e.width*e.height,i=763e-9*t*e.steps+2278e-8*t*(e?.upscale||0)+(e?.hrSteps||0)*t*(e?.hrScale||0)*(e?.hrScale||0)*763e-9+(e?.faceFix?2:0)+(e?.imgOptions?.removeBackground?2:0)+(e?.imgOptions?.redrawBackground?2:0)+(e?.imgOptions?.facePreservation?2:0)+(e?.imgOptions?.genderDetect?1:0)+2*(e?.controlnet?.units?.length||0);return i=.3*i*e.batchSize,i}b64DataToBase64Image(e){let t=atob(e),i=new Blob([t],{type:"image/webp"});new Promise((e=>{let t=new FileReader;t.onloadend=function(){let i=t.result;console.log(i),e(i)},t.readAsDataURL(i)}))}async draw(e){let t=await this.ncloud.apig("aigc/sdapi/v1/draw",e),i=t?.paintingSign;return i&&setTimeout((async()=>{let e=new Parse.Query("ImagineWork");e.equalTo("taskId",i);let t=await e.first();console.log(t),t?.id&&this.newWorkList.unshift(t)}),1e3),t}async taskDetail(e){let t=await this.ncloud.apig("aigc/sdapi/v1/task/detail",{taskId:e});return console.log(t),this.taskDetailMap[e]=t,t}getMyWorkQuery(){let e=Parse.User.current();if(!e?.id)return;let t=this.getWorkQuery();return t.include("model","module","user"),t.equalTo("user",e.toPointer()),t}getWorkQuery(){let e=new Date((new Date).getTime()-6e4),t=Parse.Query.fromJSON("ImagineWork",{where:{$or:[{createdAt:{$lte:e},progress:{$ne:0}},{createdAt:{$gt:e}}]}});return t.include("model","module","user"),t.notEqualTo("isDeleted",!0),t.notEqualTo("isFailed",!0),t.doesNotExist("respData.error"),t.doesNotExist("respData.data.taskLimitCount"),t.addDescending("createdAt"),t}getimg(){return new Promise(((e,t)=>{let i=document.createElement("input");i.type="file",i.click();let handleChange=async()=>{if(i.removeEventListener("change",handleChange),i.files&&i.files.length>0){let t=i.files[0],o=await this.uploadServ.upload(t,(e=>{console.log(e),e.total.percent.toFixed(2)}));e(o.url)}else t("未选择文件")};i.addEventListener("change",handleChange)}))}async getJimengImg(e){let t=new Parse.Object("ImagineWork");t.set("__pending",!0),t.set("__id",Symbol()),this.newWorkList.unshift(t),console.log("111111",this.newWorkList);try{let i=await getJimengImg(e);console.log("222222",i);let o=this.newWorkList?.findIndex((e=>e?.get("__id")===t?.get("__id")));if(console.log("333333",o),-1==o)return;if(!i?.id)return console.log("444444","未查询出workId"),void this.newWorkList.splice(o,1);console.log("555555","执行替换操作"),this.newWorkList.splice(o,1,i)}catch(e){console.log("666666",e);let i=this.newWorkList?.findIndex((e=>e?.get("__id")===t?.get("__id")));if(-1==i)return;this.newWorkList.splice(i,1)}}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:ImagineService,deps:[{token:i1.HttpClient},{token:i2.NovaCloudService},{token:i3.NovaUploadService}],target:i0.ɵɵFactoryTarget.Injectable})}static{this.ɵprov=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:ImagineService,providedIn:"root"})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:ImagineService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}],ctorParameters:()=>[{type:i1.HttpClient},{type:i2.NovaCloudService},{type:i3.NovaUploadService}]});
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL3NlcnZpY2UtZm1haS9zZXJ2aWNlLWltYWdpbmUvaW1hZ2luZS5zZXJ2aWNlLm1qcw==`
10
10
 
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/voice/tts/fmode-tts-class.mjs
7
7
  */
8
- import Parse from"parse";import{AudioPlayer}from"../lib/audio/audio.player";import{FmPushAudioOutputStreamCallback}from"../lib/audio/streamer.microsoft";import{FmodeTTSProviderMicrosoft}from"./provider-microsoft";import{PCMStreamer}from"../lib/audio/streamer.pcm";export class FmodeTTS{static{this.activeInstance=null}stop(){FmodeTTS.activeInstance===this&&(FmodeTTS.activeInstance=null),this.audioPlayer?.stop(),this.audioStream?.stop(),this.provider?.stop(),this.isPlaying=!1}constructor(t,e){if(this.isPlaying=!1,this.uploadServ=e,this.config=t,this.audioPlayer=AudioPlayer.getInstance(),this.audioStream=new FmPushAudioOutputStreamCallback,this.provider=t.provider||new FmodeTTSProviderMicrosoft,console.log("初始化provider",t,this.provider,this.provider.constructor),this.provider.constructor.toString().indexOf("Doubao")>-1){const e=new PCMStreamer(24e3,1,16);t.audioStream=e}else t.audioStream=this.audioStream;this.audioStream=t?.audioStream||this.audioStream,this.provider.initialize(t),this.provider.eventMap=this.eventMap}extractTextFromXML(t){if(!t||"string"!=typeof t)return"";const e=/>([^<]+)</g,o=[];let i;for(;null!==(i=e.exec(t));){const t=i[1]?.trim();t&&o.push(t)}return o.join(" ")}extractSSMLContent(t){if(!t||"string"!=typeof t)return t||"";const e=t.match(/<speak.*?<\/speak>/s);return e?.[0]||t}async speakAsync(t,e,o){if(FmodeTTS.activeInstance&&FmodeTTS.activeInstance!==this&&FmodeTTS.activeInstance.stop(),FmodeTTS.activeInstance=this,!e){const o=Parse.Object.extend("ChatVoice");(e=new o).set("ssml",t),e.set("content",this.extractTextFromXML(t));const i=localStorage.getItem("company");i&&e.set("company",{__type:"Pointer",className:"Company",objectId:i}),Parse.User.current()?.id&&e.set("user",Parse.User.current().toPointer())}if(!e?.get("voiceFile")){const o=[];e?.get("content")&&o.push({content:e?.get("content")}),t&&o.push({ssml:t});const i=Parse.Query.fromJSON("ChatVoice",{include:"voiceFile",where:{$or:o}});i.exists("voiceFile"),i.addDescending("updatedAt");const s=await i.first();e.set("voiceFile",s?.get("voiceFile"))}return console.log("Exists playAudioData",e?.get("voiceFile")),e?.get("voiceFile")?(this.playAudioData(e?.get("voiceFile")?.get("url"),e,o),e.save(),e):(t=this.extractSSMLContent(t),this.isPlaying=!0,new Promise(((i,s)=>{const a=Date.now();this.provider.synthesize(t,(async t=>{const s=Date.now(),r=t?.audioData;let n=Number(t?.audioDuration);"microsoft"==this.config.provider&&(n/=1e4),e.set("duration",n),console.log("eventMap?.onResult",o,n),o?.onResult?.({duration:n});const c=new Blob([r],{type:"audio/wav"});await this.uploadAndSaveVoice(c,e),console.log(`Audio synthesis finished. Duration: ${s-a} ms`),setTimeout((()=>{this.isPlaying=!1,o?.onStop?.()}),2e3),i(e)}),(t=>{this.isPlaying=!1,s(`Error occurred during synthesis: ${t}`)}),(()=>{o?.onStart?.()}))})))}async playAudioData(t,e,o){let i;if(t?.indexOf?.("http")>=-1)i=t;else{const o=new Blob([t],{type:"audio/wav"});i=URL.createObjectURL(o),await this.uploadAndSaveVoice(o,e)}o?.onStart?.(e),this.isPlaying=!0;const s=this.audioPlayer;s.setAudioEvent("onloadeddata",(()=>{const t=1e3*s.duration;e?.get("duration")||(e.set("duration",t),e.save()),o?.onLoaded?.(s)})),["onabort","onerror","onpause","onended","onclose"].forEach((t=>{s.setAudioEvent(t,(()=>{this.isPlaying=!1,o?.onStop?.()}))}));const playAudio=()=>{s.play(i).catch((()=>{setTimeout(playAudio,200)}))};playAudio()}async uploadAndSaveVoice(t,e){if(!this.uploadServ)return null;const o=e?.id||this.uploadServ.genMd5(e?.get("content")||e?.get("ssml")),i=new Date,s=`${o}${i.getFullYear()}${i.getMonth()+1}${i.getDate()}${i.getHours()}${i.getMinutes()}${i.getSeconds()}.wav`,a=new File([t],s,{type:"audio/wav"}),r=await this.uploadServ.upload(a);return r?.id&&(e.set("voiceFile",{__type:"Pointer",className:"Attachment",objectId:r.id}),await e.save()),r?.url||null}}
8
+ import Parse from"parse";import{AudioPlayer}from"../lib/audio/audio.player";import{FmPushAudioOutputStreamCallback}from"../lib/audio/streamer.microsoft";import{FmodeTTSProviderMicrosoft}from"./provider-microsoft";import{PCMStreamer}from"../lib/audio/streamer.pcm";export class FmodeTTS{static{this.activeInstance=null}stop(){FmodeTTS.activeInstance===this&&(FmodeTTS.activeInstance=null),this.audioPlayer?.stop(),this.audioStream?.stop(),this.provider?.stop(),this.isPlaying=!1}constructor(e,t){if(this.isPlaying=!1,this.uploadServ=t,this.config=e,this.audioPlayer=AudioPlayer.getInstance(),this.audioStream=new FmPushAudioOutputStreamCallback,this.provider=e.provider||new FmodeTTSProviderMicrosoft,console.log("初始化provider",e,this.provider,this.provider.constructor),this.provider.constructor.toString().indexOf("Doubao")>-1){const t=new PCMStreamer(24e3,1,16);e.audioStream=t}else e.audioStream=this.audioStream;this.audioStream=e?.audioStream||this.audioStream,this.provider.initialize(e),this.provider.voiceConfig=e?.voiceConfig,this.provider.eventMap=this.eventMap}extractTextFromXML(e){if(!e||"string"!=typeof e)return"";const t=/>([^<]+)</g,o=[];let i;for(;null!==(i=t.exec(e));){const e=i[1]?.trim();e&&o.push(e)}return o.join(" ")}extractSSMLContent(e){if(!e||"string"!=typeof e)return e||"";const t=e.match(/<speak.*?<\/speak>/s);return t?.[0]||e}async speakAsync(e,t,o){if(FmodeTTS.activeInstance&&FmodeTTS.activeInstance!==this&&FmodeTTS.activeInstance.stop(),FmodeTTS.activeInstance=this,!t){const o=Parse.Object.extend("ChatVoice");(t=new o).set("ssml",e),t.set("content",this.extractTextFromXML(e));const i=localStorage.getItem("company");i&&t.set("company",{__type:"Pointer",className:"Company",objectId:i}),Parse.User.current()?.id&&t.set("user",Parse.User.current().toPointer())}if(!t?.get("voiceFile")){const o=[];e&&o.push({ssml:e});const i=Parse.Query.fromJSON("ChatVoice",{include:"voiceFile",where:{$or:o}});i.exists("voiceFile"),i.addDescending("updatedAt");const s=await i.first();t.set("voiceFile",s?.get("voiceFile"))}return console.log("Exists playAudioData",t?.get("voiceFile")),t?.get("voiceFile")?(this.playAudioData(t?.get("voiceFile")?.get("url"),t,o),t.save(),t):(e=this.extractSSMLContent(e),this.isPlaying=!0,new Promise(((i,s)=>{const a=Date.now();this.provider.synthesize(e,(async e=>{const s=Date.now(),r=e?.audioData;let n=Number(e?.audioDuration);"microsoft"==this.config.provider&&(n/=1e4),t.set("duration",n),console.log("eventMap?.onResult",o,n),o?.onResult?.({duration:n});const c=new Blob([r],{type:"audio/wav"});await this.uploadAndSaveVoice(c,t),console.log(`Audio synthesis finished. Duration: ${s-a} ms`),setTimeout((()=>{this.isPlaying=!1,o?.onStop?.()}),2e3),i(t)}),(e=>{this.isPlaying=!1,s(`Error occurred during synthesis: ${e}`)}),(()=>{o?.onStart?.()}))})))}async playAudioData(e,t,o){let i;if(e?.indexOf?.("http")>=-1)i=e;else{const o=new Blob([e],{type:"audio/wav"});i=URL.createObjectURL(o),await this.uploadAndSaveVoice(o,t)}o?.onStart?.(t),this.isPlaying=!0;const s=this.audioPlayer;s.setAudioEvent("onloadeddata",(()=>{const e=1e3*s.duration;t?.get("duration")||(t.set("duration",e),t.save()),o?.onLoaded?.(s)})),["onabort","onerror","onpause","onended","onclose"].forEach((e=>{s.setAudioEvent(e,(()=>{this.isPlaying=!1,o?.onStop?.()}))}));const playAudio=()=>{s.play(i).catch((()=>{setTimeout(playAudio,200)}))};playAudio()}async uploadAndSaveVoice(e,t){if(!this.uploadServ)return null;const o=t?.id||this.uploadServ.genMd5(t?.get("content")||t?.get("ssml")),i=new Date,s=`${o}${i.getFullYear()}${i.getMonth()+1}${i.getDate()}${i.getHours()}${i.getMinutes()}${i.getSeconds()}.wav`,a=new File([e],s,{type:"audio/wav"}),r=await this.uploadServ.upload(a);return r?.id&&(t.set("voiceFile",{__type:"Pointer",className:"Attachment",objectId:r.id}),await t.save()),r?.url||null}}
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL3ZvaWNlL3R0cy9mbW9kZS10dHMtY2xhc3MubWpz`
10
10
 
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/voice/tts/provider-doubao.mjs
7
7
  */
8
- import pako from"pako";import{v4 as uuidv4}from"uuid";export class FmodeTTSProviderDoubao{constructor(){this.ws=null,this.audioChunks=[],this.isSynthesizing=!1,this.isStarted=!1}initialize(e){if(!e.appId||!e.accessToken)throw new Error("appId, accessToken are required for Doubao TTS");e.cluster=e.cluster||"volcano_tts",this.config=e,this.audioStream=e.audioStream}synthesize(e,t,s,i){this.isSynthesizing&&this.stop(),this.isSynthesizing=!0,this.audioChunks=[],this.eventMap?.onSpeakBefore?.();const r=e?.indexOf("<speak")>-1,n=r?this.extractTextFromSSML(e):e;let o=uuidv4();const a=buildFullUrl("wss://openspeech.bytedance.com/api/v1/tts/ws_binary",{api_jwt:this.config.stsToken});this.ws=new WebSocket(a),this.ws.binaryType="arraybuffer",this.ws.onopen=()=>{this.eventMap?.onStreamStarted?.();const e={app:{appid:this.config.appId,token:this.config.accessToken,cluster:this.config.cluster},user:{uid:"user123"},audio:{voice_type:"zh_female_wenrouxiaoya_moon_bigtts",encoding:"pcm",rate:24e3,speed_ratio:1,volume_ratio:1,pitch_ratio:1,language:"cn"},request:{reqid:o,text:n,text_type:r?"ssml":"plain",operation:"submit"}},t=this.buildBinaryMessage(e);this.ws?.send(t)},this.ws.onmessage=async e=>{try{if(e.data instanceof ArrayBuffer){const r=new Uint8Array(e.data),n=this.parseResponse(r,i);if(n.error)return s(n.error),void this.stop();if(n.audio&&(this.audioChunks.push(n.audio),this.audioStream),n.isLast){this.eventMap?.onAudioCompleted?.();const{buffer:e,duration:s}=await this.mergePcmToWav(this.audioChunks);t({audioData:e,audioDuration:n.duration||s||0}),this.stop()}}else console.warn("Unexpected text message from TTS server:",e.data)}catch(e){s(`Error processing TTS response: ${e}`),this.stop()}},this.ws.onerror=e=>{s(`WebSocket error: ${JSON.stringify(e)}`),this.stop()},this.ws.onclose=()=>{this.isSynthesizing&&(s("WebSocket connection closed unexpectedly"),this.stop())}}buildBinaryMessage(e){const t=JSON.stringify(e),s=(new TextEncoder).encode(t),i=pako.gzip(s),r=new ArrayBuffer(8+i.length),n=new DataView(r);n.setUint8(0,17),n.setUint8(1,16),n.setUint8(2,17),n.setUint8(3,0),n.setUint32(4,i.length,!1);return new Uint8Array(r).set(i,8),r}parseResponse(e,t){const s=new DataView(e.buffer),i=(e[0],15&e[0]),r=e[1]>>4,n=15&e[1],o=(e[2],15&e[2]),a=(e[3],4*i);let u=e.slice(a);if(11==r){if(0===n)return{};{const e=s.getInt32(a,!1),i=(s.getInt32(a+4,!1),u.slice(8));return this.audioStream.write(i.buffer.slice(0)),this.isStarted||(t?.(),this.isStarted=!0),{audio:i.buffer,isLast:e<0}}}if(15===r){const e=s.getInt32(a,!1);s.getInt32(a+4,!1);let t=u.slice(8);return 1===o&&(t=pako.ungzip(t)),{error:`TTS error (code ${e}): ${(new TextDecoder).decode(t)}`}}if(12!==r)return{error:`Unknown message type: ${r}`};{s.getInt32(a,!1);let e=u.slice(4);1===o&&(e=pako.ungzip(e));try{return{duration:JSON.parse((new TextDecoder).decode(e)).duration||0}}catch(e){return console.warn("Failed to parse TTS metadata:",e),{}}}}stop(){this.isSynthesizing=!1,this.ws&&(this.ws.close(),this.ws=null),this.audioChunks=[]}dispose(){this.stop()}extractTextFromSSML(e){return e.replace(/<[^>]+>/g,"").trim()}async blobToArrayBuffer(e){return new Promise(((t,s)=>{const i=new FileReader;i.onload=()=>t(i.result),i.onerror=s,i.readAsArrayBuffer(e)}))}async mergePcmToWav(e){let t=0;e.forEach((e=>{t+=e.byteLength}));const s=1e3*t/48e3,i=new Uint8Array(t);let r=0;e.forEach((e=>{i.set(new Uint8Array(e),r),r+=e.byteLength}));const n=this.createWavHeader({sampleRate:24e3,numChannels:1,bitsPerSample:16,pcmDataSize:t}),o=new Uint8Array(n.byteLength+i.byteLength);return o.set(new Uint8Array(n),0),o.set(i,n.byteLength),{buffer:o.buffer,duration:s}}createWavHeader(e){const{sampleRate:t,numChannels:s,bitsPerSample:i,pcmDataSize:r}=e,n=t*s*i/8,o=s*i/8,a=new ArrayBuffer(44),u=new DataView(a);return this.writeString(u,0,"RIFF"),u.setUint32(4,36+r,!0),this.writeString(u,8,"WAVE"),this.writeString(u,12,"fmt "),u.setUint32(16,16,!0),u.setUint16(20,1,!0),u.setUint16(22,s,!0),u.setUint32(24,t,!0),u.setUint32(28,n,!0),u.setUint16(32,o,!0),u.setUint16(34,i,!0),this.writeString(u,36,"data"),u.setUint32(40,r,!0),a}writeString(e,t,s){for(let i=0;i<s.length;i++)e.setUint8(t+i,s.charCodeAt(i))}}export function buildFullUrl(e,t){const s=[];for(const e in t)s.push(`${e}=${encodeURIComponent(t[e])}`);return`${e}?${s.join("&")}`}
8
+ import pako from"pako";import{v4 as uuidv4}from"uuid";export class FmodeTTSProviderDoubao{constructor(){this.ws=null,this.audioChunks=[],this.isSynthesizing=!1,this.isStarted=!1}initialize(e){if(!e.appId||!e.accessToken)throw new Error("appId, accessToken are required for Doubao TTS");e.cluster=e.cluster||"volcano_tts",this.config=e,this.audioStream=e.audioStream}synthesize(e,t,s,i){this.isSynthesizing&&this.stop(),this.isSynthesizing=!0,this.audioChunks=[],this.eventMap?.onSpeakBefore?.();const r=e?.indexOf("<speak")>-1,n=r?this.extractTextFromSSML(e):e;let o=uuidv4();const a=buildFullUrl("wss://openspeech.bytedance.com/api/v1/tts/ws_binary",{api_jwt:this.config.stsToken});this.ws=new WebSocket(a),this.ws.binaryType="arraybuffer",this.ws.onopen=()=>{this.eventMap?.onStreamStarted?.();const e={app:{appid:this.config.appId,token:this.config.accessToken,cluster:this.config.cluster},user:{uid:"user123"},audio:{voice_type:this.voiceConfig?.voice||"zh_female_shuangkuaisisi_emo_v2_mars_bigtts",encoding:"pcm",rate:24e3,speed_ratio:1,volume_ratio:1,pitch_ratio:1,language:"cn"},request:{reqid:o,text:n,text_type:r?"ssml":"plain",operation:"submit"}},t=this.buildBinaryMessage(e);this.ws?.send(t)},this.ws.onmessage=async e=>{try{if(e.data instanceof ArrayBuffer){const r=new Uint8Array(e.data),n=this.parseResponse(r,i);if(n.error)return s(n.error),void this.stop();if(n.audio&&(this.audioChunks.push(n.audio),this.audioStream),n.isLast){this.eventMap?.onAudioCompleted?.();const{buffer:e,duration:s}=await this.mergePcmToWav(this.audioChunks);t({audioData:e,audioDuration:n.duration||s||0}),this.stop()}}else console.warn("Unexpected text message from TTS server:",e.data)}catch(e){s(`Error processing TTS response: ${e}`),this.stop()}},this.ws.onerror=e=>{s(`WebSocket error: ${JSON.stringify(e)}`),this.stop()},this.ws.onclose=()=>{this.isSynthesizing&&(s("WebSocket connection closed unexpectedly"),this.stop())}}buildBinaryMessage(e){const t=JSON.stringify(e),s=(new TextEncoder).encode(t),i=pako.gzip(s),r=new ArrayBuffer(8+i.length),n=new DataView(r);n.setUint8(0,17),n.setUint8(1,16),n.setUint8(2,17),n.setUint8(3,0),n.setUint32(4,i.length,!1);return new Uint8Array(r).set(i,8),r}parseResponse(e,t){const s=new DataView(e.buffer),i=(e[0],15&e[0]),r=e[1]>>4,n=15&e[1],o=(e[2],15&e[2]),a=(e[3],4*i);let u=e.slice(a);if(11==r){if(0===n)return{};{const e=s.getInt32(a,!1),i=(s.getInt32(a+4,!1),u.slice(8));return this.audioStream.write(i.buffer.slice(0)),this.isStarted||(t?.(),this.isStarted=!0),{audio:i.buffer,isLast:e<0}}}if(15===r){const e=s.getInt32(a,!1);s.getInt32(a+4,!1);let t=u.slice(8);return 1===o&&(t=pako.ungzip(t)),{error:`TTS error (code ${e}): ${(new TextDecoder).decode(t)}`}}if(12!==r)return{error:`Unknown message type: ${r}`};{s.getInt32(a,!1);let e=u.slice(4);1===o&&(e=pako.ungzip(e));try{return{duration:JSON.parse((new TextDecoder).decode(e)).duration||0}}catch(e){return console.warn("Failed to parse TTS metadata:",e),{}}}}stop(){this.isSynthesizing=!1,this.ws&&(this.ws.close(),this.ws=null),this.audioChunks=[]}dispose(){this.stop()}extractTextFromSSML(e){return e.replace(/<[^>]+>/g,"").trim()}async blobToArrayBuffer(e){return new Promise(((t,s)=>{const i=new FileReader;i.onload=()=>t(i.result),i.onerror=s,i.readAsArrayBuffer(e)}))}async mergePcmToWav(e){let t=0;e.forEach((e=>{t+=e.byteLength}));const s=1e3*t/48e3,i=new Uint8Array(t);let r=0;e.forEach((e=>{i.set(new Uint8Array(e),r),r+=e.byteLength}));const n=this.createWavHeader({sampleRate:24e3,numChannels:1,bitsPerSample:16,pcmDataSize:t}),o=new Uint8Array(n.byteLength+i.byteLength);return o.set(new Uint8Array(n),0),o.set(i,n.byteLength),{buffer:o.buffer,duration:s}}createWavHeader(e){const{sampleRate:t,numChannels:s,bitsPerSample:i,pcmDataSize:r}=e,n=t*s*i/8,o=s*i/8,a=new ArrayBuffer(44),u=new DataView(a);return this.writeString(u,0,"RIFF"),u.setUint32(4,36+r,!0),this.writeString(u,8,"WAVE"),this.writeString(u,12,"fmt "),u.setUint32(16,16,!0),u.setUint16(20,1,!0),u.setUint16(22,s,!0),u.setUint32(24,t,!0),u.setUint32(28,n,!0),u.setUint16(32,o,!0),u.setUint16(34,i,!0),this.writeString(u,36,"data"),u.setUint32(40,r,!0),a}writeString(e,t,s){for(let i=0;i<s.length;i++)e.setUint8(t+i,s.charCodeAt(i))}}export function buildFullUrl(e,t){const s=[];for(const e in t)s.push(`${e}=${encodeURIComponent(t[e])}`);return`${e}?${s.join("&")}`}
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL3ZvaWNlL3R0cy9wcm92aWRlci1kb3ViYW8ubWpz`
10
10
 
@@ -5,6 +5,6 @@
5
5
  * 保留所有权利 All Rights Reserved.
6
6
  * /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/core/agent/chat/fmode-chat.mjs
7
7
  */
8
- import{finalize}from"rxjs";import Parse from"parse";import{FmodeTTS,FmodeTTSProviderDoubao,FmodeTTSProviderMicrosoft}from"../../voice/tts";import{PromptTemplate}from"@langchain/core/prompts";import{getFormatTpl}from"../prompt/prompt-util";import{FmodeChatCompletion}from"./completion";const PromptTplTalkSSMLOutputCode="talk-ssml-output-tpl",PromptTplTalkTextSSMLCode="talk-text-ssml-tpl";export function getMessageContentText(t){let e="";return"string"==typeof t&&(e=t),"object"==typeof t&&(e=t?.find((t=>t?.text))?.text||""),e}export function getMessageImageUrl(t){return"object"==typeof t?t?.find((t=>t?.image_url))?.image_url?.url||"":null}export class FmodeChat{async loadModelList(t){if(this.modelList?.length)return;let e=new Parse.Query("ChatModel");e.notEqualTo("isDeleted",!0),e.equalTo("isEnabled",!0),e.addAscending("index"),this.modelList=await e.find(),this.currentModel=t||this.modelList?.find((t=>"fmode-4.5-128k"==t.get("code")))}showAvatar(){this.avatarConfig=this.role?.get("avatarConfig"),this.avatarConfig&&(this.isAvatarShow=!0,this.avatarConfig?.image&&(this.avatarConfig.image.waiting=this.avatarConfig.image.waiting||this.role?.get("thumb")||this.role?.get("avatar"),this.avatarMode="image"),this.avatarConfig?.video&&(this.avatarConfig.video.waiting=this.avatarConfig.video.waiting,this.avatarMode="video"))}scrollToBottom(t){t=t||this.scrollComp,t?.nativeElement?.scrollHeight&&(t.nativeElement.scrollTop=t.nativeElement.scrollHeight)}constructor(t,e,i,s,o,a,n){this.ChatSession=Parse.Object.extend("ChatSession"),this.messageList=[{role:"system",content:"系统提示:AI仅供参考"}],this.latestAIResponse="",this.userInput="",this.userImage="",this.isDirect=!1,this.mode="page",this.hideShare=!1,this.hideModalSelect=!1,this.hideInputPreview=!1,this.isAvatarShow=!1,this.avatarMode="",this.isPromptModalOpen=!1,this.isPromptMessageAreaShow=!0,this.promptList=[],this.focusUserInput=()=>{},this.leftButtons=[{title:"灵感",icon:"color-wand-outline",onClick:()=>{this.isPromptModalOpen=!0},show:()=>this?.promptList?.length},{title:"角色",icon:"people-outline",onClick:()=>{this.navCtrl?.navigateRoot("/chat/pro/mask")},show:()=>!0},{title:"呼叫",icon:"call-outline",onClick:()=>{this.chatServ?.callRole(this.role)},show:()=>this?.role?.get("voiceConfig")}],this.isVoiceInputMode=!1,this.isTexting=!1,this.isTalkMode=!1,this.SSMLRoleVoice="zh-CN-XiaoxiaoNeural",this.playAnimation=t=>{console.log(t)},this.welcome=async()=>{let t=this.messageList?.filter((t=>"assistant"==t?.role));if(t?.length)return;let e=Parse.User.current(),i=await this.loadSelf("Person","userVerify"),s=await this.loadSelf("Profile","user"),o=i?.get("name")||i?.get("userVefiry")?.get("realname")||i?.get("userVefiry")?.get("nickname"),a=e?.get("nickname")||s?.get("name")||e?.get("realname")||e?.get("name");i?.get("userVerify")?.id==e?.id&&(o="您"),a||(a=o);let n=this.role.get("voiceConfig")?.welcome?.prompt;if(this.role.get("voiceConfig")?.welcome?.promptList?.length){let t=this.role.get("voiceConfig")?.welcome?.promptList;n=t[Math.floor(Math.random()*t.length)]}if(!n)return;let r=await PromptTemplate.fromTemplate(n,{templateFormat:"mustache"}).format({name:a,userName:a,personName:o,timeOfDay:this.getTimeOfDay()}),l=await this.getVoiceByContentText(r),h={role:"assistant",voice:l,content:r,complete:!0};this.voiceMap[l?.id],this.playChatVoice(this.voiceMap[l?.id]),this.messageList.push(h)},this.self={},this.voiceMap={},this.VoiceTTSMap={},this.chatServ=s,this.role=e,this.sessionId=t,this.navCtrl=o,this.ncloud=a,this.uploadServ=n,i?.id&&(this.chatSession=i,this.messageList=this.chatSession.get("messageList"),this.sessionId=i?.id),this.role?.id&&(this.voiceConfig=this.role?.get("voiceConfig"),this.voiceConfig?.autoTalk&&(this.isTalkMode=this.isTalkMode||this.voiceConfig?.autoTalk,this.isDirect=!0))}getTimeOfDay(){const t=(new Date).getHours();return t>=5&&t<12?"早上":t>=12&&t<14?"中午":t>=14&&t<18?"下午":"晚上"}async loadSelf(t,e){if(this.self[t])return this.self[t];let i=Parse.User.current(),s=new Parse.Query(t);return s.include(e),s.equalTo(e,i?.id),this.self[t]=await s.first(),this.self[t]}async loadTalkSystemPrompt(t){if(!this.isTalkMode)return;if(!t)return;let e=t?.get("voiceConfig");"男"==t?.get("gender")?(this.SSMLRoleVoice="zh-CN-YunyeNeural","doubao"==e.provider&&(this.SSMLRoleVoice="zh_male_yangguangqingnian_emo_v2_mars_bigtts")):(this.SSMLRoleVoice="zh-CN-XiaoxiaoNeural","doubao"==e.provider&&(this.SSMLRoleVoice="zh_female_shuangkuaisisi_emo_v2_mars_bigtts")),this.SSMLRoleVoice=t?.get("voiceConfig")?.voice||this.SSMLRoleVoice;let i=await getFormatTpl("talk-ssml-output-tpl",{SSMLRoleVoice:this.SSMLRoleVoice}),s=t.get("prompt")||"请你扮演飞码AI的人工智能专家。";s+=i;let o={role:"user",content:s,hidden:!0},a=this.messageList?.map((t=>t?.content)).join();if(a.indexOf(s)>-1)return;let n=this.messageList?.findIndex((t=>"system"==t?.role)),r=n+1;this.messageList.splice(r,0,o)}loadRolePrompt(){let t=this.role?.get("prompt"),e={role:"user",content:t,hidden:!0};if(!t)return;let i=this.messageList?.map((t=>t?.content)).join();if(i.indexOf(t)>-1)return;let s=this.messageList?.findIndex((t=>"system"==t?.role)),o=s+1;this.messageList.splice(o,0,e)}async sendMessage(t="FmodeAiTest测试问题",e,i,s,o){if(this.scrollToBottom&&this.scrollToBottom(),this.isPromptMessageAreaShow=!1,this.loadRolePrompt(),e){let i={role:"user",content:[{type:"image_url",image_url:{url:e}},{type:"text",text:t}],complete:!0,createdAt:new Date};o&&(i.voice={id:o?.id}),this.messageList.push({role:"user",content:[{type:"image_url",image_url:{url:e}},{type:"text",text:t}],complete:!0,createdAt:new Date})}else{let e={role:"user",content:t,complete:!0,createdAt:new Date};o&&(e.voice={id:o?.id,duration:o?.duration}),this.messageList.push(e)}let a=new FmodeChatCompletion(this.fixMessageList(this.messageList),{model:this.currentModel?.get("code")||"fmode-4.5-128k"});if(this.onUserSend){if(!await this.onUserSend(this,this.messageList[this.messageList?.length-1]))return}this.userInput="",this.userImage="";let n=this.isDirect||!1;this.isTalkMode&&(n=!0);let r=a.sendCompletion({isDirect:n,onComplete:i||null}).pipe(finalize((async()=>{if(this.isTalkMode){let t=this.messageList[a.indexOfList]?.content,e=await this.getVoiceByContentText(t,s);s?.onSSMLComplete&&s?.onSSMLComplete(e),this.messageList[a.indexOfList].voice=e,this.playChatVoice(this.voiceMap[e?.id],{onResult:t=>{t?.duration&&(this.messageList[a.indexOfList].voice.duration=t?.duration)}})}this.messageList[a.indexOfList].complete=!0}))).subscribe((t=>{this.messageList[a.indexOfList]||s?.onMessageStart?.(t),this.messageList[a.indexOfList]=t,this.latestAIResponse=this.getContentText(t?.content);let e=this.chatSession?.get("messageList")?.length;this.messageList?.length>e&&this.saveChatSession(),t?.complete&&(this.onMessage&&this.onMessage(this,this.messageList[this.messageList?.length-1]),this.saveChatSession(),r.unsubscribe()),this.scrollToBottom&&this.scrollToBottom()}))}getVoiceByContentText(t,e,i=!1){let s=this.getContentText(t),o=new(Parse.Object.extend("ChatVoice")),a="",n=this.voiceConfig||this.role?.get("voiceConfig");return this.SSMLRoleVoice=n?.voice||this.SSMLRoleVoice,new Promise((async(t,e)=>{let resolveChatVoice=async()=>{o.set("content",s),o.set("ssml",a),o.set("role","assistant");let e=localStorage.getItem("company");e&&o.set("company",{__type:"Pointer",className:"Company",objectId:e}),Parse.User.current()?.id&&o.set("user",Parse.User.current().toPointer()),this.chatSession?.id&&o.set("session",this.chatSession?.toPointer()),o=await o.save(),this.voiceMap[o?.id]=o,t({id:o?.id})};if(0==i){let t=n?.provider||"microsoft",e=n?.rate,i=e?"</prosody>":"";a=`<speak provider="${t}">${e?`<prosody rate="${e}">`:""}<voice name="${this.SSMLRoleVoice}">${s}</voice>${i}</speak>`,resolveChatVoice()}if(1==i){let t=await getFormatTpl("talk-text-ssml-tpl",{content:s,SSMLRoleVoice:this.SSMLRoleVoice});new FmodeChatCompletion(this.fixMessageList([{role:"user",content:t}]),{model:this.currentModel?.get("code")||"fmode-4.5-128k"}).sendCompletion({isDirect:!0}).subscribe((async t=>{t?.complete&&(a=this.getContentText(t?.content),resolveChatVoice())}))}}))}getContentText(t){return"string"==typeof t?t:t?.[0]?.text||""}async initTTS(){this.voiceConfig=this.voiceConfig||this.role?.get("voiceConfig");let t=await this.ncloud.apig("voice/tts/token",{company:localStorage.getItem("company"),provider:this.voiceConfig?.provider||"microsoft"});if(t.provider=this.voiceConfig?.provider,t?.provider&&"microsoft"!=t?.provider||(t.provider=new FmodeTTSProviderMicrosoft),"doubao"==t?.provider&&(t.provider=new FmodeTTSProviderDoubao),console.log("initTTS",t),t?.token||t?.stsToken){return new FmodeTTS(t,this.uploadServ)}return null}stopPlayingVoice(){Object.values(this.VoiceTTSMap).forEach((t=>{t?.isPlaying&&t?.stop()}))}async playChatVoice(t,e){let i=await this.initTTS();if(console.log("playChatVoice",i),i){try{this.playAnimation("talking"),i.speakAsync(t?.get("ssml"),t,{onStart:t=>{e?.onStart&&e?.onStart(t)},onLoaded:t=>{e?.onLoaded&&e?.onLoaded(t)},onResult:t=>{e?.onResult&&e?.onResult(t)},onStop:()=>{e?.onStop&&e?.onStop(),this.playAnimation("waiting")}})}catch(t){}return this.VoiceTTSMap[t.id]=i,i}return null}async saveChatSession(){if("new"==this.sessionId&&(this.chatSession=new this.ChatSession),this.chatSession.set("title",this.genTitle()),this.chatSession.set("role",this.role?.toPointer()),this.chatSession.set("messageList",this.messageList),this.chatSession.set("user",Parse.User.current()?.toPointer()),this.chatSession=await this.chatSession.save(),this.onChatSaved&&this.onChatSaved(this),this.sessionId=this.chatSession?.id,this.sessionId){let t=`${window.location.origin}/chat/pro/chat/${this.sessionId}`;window.location?.pathname?.indexOf("chat/session")>-1&&(t=`${window.location.origin}/chat/session/chat/${this.sessionId}`),"modal"==this.mode&&(t=window.location.href),t=this.getInviteUrl(t),window.history.replaceState(null,null,t+window.location.search);let e={sid:this.chatSession?.id,rid:this.role?.id,name:this.role?.get("name"),message:this.chatSession?.get("messageList")?.[this.chatSession?.get("messageList")?.length-1]?.content?.slice(0,20),latest:this.chatSession?.createdAt};this.chatServ&&!this.chatServ?.chatList?.length&&(this.chatServ.chatList=[]);let i=this.chatServ?.chatList?.find((t=>t?.sid==e?.sid));i>-1?this.chatServ.chatList[i]=e:this.chatServ?.chatList.unshift(e)}}getInviteUrl(t){let e=new URL(t),i=Parse.User?.current()?.id;return e.searchParams.set("invite",i),e.href}genTitle(){if(this.title)return this.title;let t=this.messageList.find((t=>"user"==t.role))?.content;return"string"==typeof t&&(this.title=t?.slice(0,15)||""),"object"==typeof t&&(this.title=t?.find((t=>t?.text))?.text||""),this.title}fixMessageList(t){return t.map((t=>({role:t.role,content:t.content})))}nowStr(){let t=new Date;return`${t.getFullYear()}/${t.getMonth()+1}/${t.getDate()} ${t.getHours()}:${t.getMinutes()}:${t.getSeconds()}`}}
8
+ import{finalize}from"rxjs";import Parse from"parse";import{FmodeTTS,FmodeTTSProviderDoubao,FmodeTTSProviderMicrosoft}from"../../voice/tts";import{PromptTemplate}from"@langchain/core/prompts";import{getFormatTpl}from"../prompt/prompt-util";import{FmodeChatCompletion}from"./completion";const PromptTplTalkSSMLOutputCode="talk-ssml-output-tpl",PromptTplTalkTextSSMLCode="talk-text-ssml-tpl";export function getMessageContentText(t){let e="";return"string"==typeof t&&(e=t),"object"==typeof t&&(e=t?.find((t=>t?.text))?.text||""),e}export function getMessageImageUrl(t){return"object"==typeof t?t?.find((t=>t?.image_url))?.image_url?.url||"":null}export class FmodeChat{async loadModelList(t){if(this.modelList?.length)return;let e=new Parse.Query("ChatModel");e.notEqualTo("isDeleted",!0),e.equalTo("isEnabled",!0),e.addAscending("index"),this.modelList=await e.find(),this.currentModel=t||this.modelList?.find((t=>"fmode-4.5-128k"==t.get("code")))}showAvatar(){this.avatarConfig=this.role?.get("avatarConfig"),this.avatarConfig&&(this.isAvatarShow=!0,this.avatarConfig?.image&&(this.avatarConfig.image.waiting=this.avatarConfig.image.waiting||this.role?.get("thumb")||this.role?.get("avatar"),this.avatarMode="image"),this.avatarConfig?.video&&(this.avatarConfig.video.waiting=this.avatarConfig.video.waiting,this.avatarMode="video"))}scrollToBottom(t){t=t||this.scrollComp,t?.nativeElement?.scrollHeight&&(t.nativeElement.scrollTop=t.nativeElement.scrollHeight)}constructor(t,e,i,s,o,a,n){this.ChatSession=Parse.Object.extend("ChatSession"),this.messageList=[{role:"system",content:"系统提示:AI仅供参考"}],this.latestAIResponse="",this.userInput="",this.userImage="",this.isDirect=!1,this.mode="page",this.hideShare=!1,this.hideModalSelect=!1,this.hideInputPreview=!1,this.isAvatarShow=!1,this.avatarMode="",this.isPromptModalOpen=!1,this.isPromptMessageAreaShow=!0,this.promptList=[],this.focusUserInput=()=>{},this.leftButtons=[{title:"灵感",icon:"color-wand-outline",onClick:()=>{this.isPromptModalOpen=!0},show:()=>this?.promptList?.length},{title:"角色",icon:"people-outline",onClick:()=>{this.navCtrl?.navigateRoot("/chat/pro/mask")},show:()=>!0},{title:"呼叫",icon:"call-outline",onClick:()=>{this.chatServ?.callRole(this.role)},show:()=>this?.role?.get("voiceConfig")}],this.isVoiceInputMode=!1,this.isTexting=!1,this.isTalkMode=!1,this.SSMLRoleVoice="zh-CN-XiaoxiaoNeural",this.playAnimation=t=>{console.log(t)},this.welcome=async()=>{let t=this.messageList?.filter((t=>"assistant"==t?.role));if(t?.length)return;let e=Parse.User.current(),i=await this.loadSelf("Person","userVerify"),s=await this.loadSelf("Profile","user"),o=i?.get("name")||i?.get("userVefiry")?.get("realname")||i?.get("userVefiry")?.get("nickname"),a=e?.get("nickname")||s?.get("name")||e?.get("realname")||e?.get("name");i?.get("userVerify")?.id==e?.id&&(o="您"),a||(a=o);let n=this.role.get("voiceConfig")?.welcome?.prompt;if(this.role.get("voiceConfig")?.welcome?.promptList?.length){let t=this.role.get("voiceConfig")?.welcome?.promptList;n=t[Math.floor(Math.random()*t.length)]}if(!n)return;let r=await PromptTemplate.fromTemplate(n,{templateFormat:"mustache"}).format({name:a,userName:a,personName:o,timeOfDay:this.getTimeOfDay()}),l=await this.getVoiceByContentText(r),h={role:"assistant",voice:l,content:r,complete:!0};this.voiceMap[l?.id],this.playChatVoice(this.voiceMap[l?.id]),this.messageList.push(h)},this.self={},this.voiceMap={},this.VoiceTTSMap={},this.chatServ=s,this.role=e,this.sessionId=t,this.navCtrl=o,this.ncloud=a,this.uploadServ=n,i?.id&&(this.chatSession=i,this.messageList=this.chatSession.get("messageList"),this.sessionId=i?.id),this.role?.id&&(this.voiceConfig=this.role?.get("voiceConfig"),this.voiceConfig?.autoTalk&&(this.isTalkMode=this.isTalkMode||this.voiceConfig?.autoTalk,this.isDirect=!0))}getTimeOfDay(){const t=(new Date).getHours();return t>=5&&t<12?"早上":t>=12&&t<14?"中午":t>=14&&t<18?"下午":"晚上"}async loadSelf(t,e){if(this.self[t])return this.self[t];let i=Parse.User.current(),s=new Parse.Query(t);return s.include(e),s.equalTo(e,i?.id),this.self[t]=await s.first(),this.self[t]}async loadTalkSystemPrompt(t){if(!this.isTalkMode)return;if(!t)return;let e=t?.get("voiceConfig");"男"==t?.get("gender")?(this.SSMLRoleVoice="zh-CN-YunyeNeural","doubao"==e.provider&&(this.SSMLRoleVoice="zh_male_yangguangqingnian_emo_v2_mars_bigtts")):(this.SSMLRoleVoice="zh-CN-XiaoxiaoNeural","doubao"==e.provider&&(this.SSMLRoleVoice="zh_female_shuangkuaisisi_emo_v2_mars_bigtts")),this.SSMLRoleVoice=t?.get("voiceConfig")?.voice||this.SSMLRoleVoice;let i=await getFormatTpl("talk-ssml-output-tpl",{SSMLRoleVoice:this.SSMLRoleVoice}),s=t.get("prompt")||"请你扮演飞码AI的人工智能专家。";s+=i;let o={role:"user",content:s,hidden:!0},a=this.messageList?.map((t=>t?.content)).join();if(a.indexOf(s)>-1)return;let n=this.messageList?.findIndex((t=>"system"==t?.role)),r=n+1;this.messageList.splice(r,0,o)}loadRolePrompt(){let t=this.role?.get("prompt"),e={role:"user",content:t,hidden:!0};if(!t)return;let i=this.messageList?.map((t=>t?.content)).join();if(i.indexOf(t)>-1)return;let s=this.messageList?.findIndex((t=>"system"==t?.role)),o=s+1;this.messageList.splice(o,0,e)}async sendMessage(t="FmodeAiTest测试问题",e,i,s,o){if(this.scrollToBottom&&this.scrollToBottom(),this.isPromptMessageAreaShow=!1,this.loadRolePrompt(),e){let i={role:"user",content:[{type:"image_url",image_url:{url:e}},{type:"text",text:t}],complete:!0,createdAt:new Date};o&&(i.voice={id:o?.id}),this.messageList.push({role:"user",content:[{type:"image_url",image_url:{url:e}},{type:"text",text:t}],complete:!0,createdAt:new Date})}else{let e={role:"user",content:t,complete:!0,createdAt:new Date};o&&(e.voice={id:o?.id,duration:o?.duration}),this.messageList.push(e)}let a=new FmodeChatCompletion(this.fixMessageList(this.messageList),{model:this.currentModel?.get("code")||"fmode-4.5-128k"});if(this.onUserSend){if(!await this.onUserSend(this,this.messageList[this.messageList?.length-1]))return}this.userInput="",this.userImage="";let n=this.isDirect||!1;this.isTalkMode&&(n=!0);let r=a.sendCompletion({isDirect:n,onComplete:i||null}).pipe(finalize((async()=>{if(this.isTalkMode){let t=this.messageList[a.indexOfList]?.content,e=await this.getVoiceByContentText(t,s);s?.onSSMLComplete&&s?.onSSMLComplete(e),this.messageList[a.indexOfList].voice=e,this.playChatVoice(this.voiceMap[e?.id],{onResult:t=>{t?.duration&&(this.messageList[a.indexOfList].voice.duration=t?.duration)}})}this.messageList[a.indexOfList].complete=!0}))).subscribe((t=>{this.messageList[a.indexOfList]||s?.onMessageStart?.(t),this.messageList[a.indexOfList]=t,this.latestAIResponse=this.getContentText(t?.content);let e=this.chatSession?.get("messageList")?.length;this.messageList?.length>e&&this.saveChatSession(),t?.complete&&(this.onMessage&&this.onMessage(this,this.messageList[this.messageList?.length-1]),this.saveChatSession(),r.unsubscribe()),this.scrollToBottom&&this.scrollToBottom()}))}getVoiceByContentText(t,e,i=!1){let s=this.getContentText(t),o=new(Parse.Object.extend("ChatVoice")),a="",n=this.voiceConfig||this.role?.get("voiceConfig");return this.SSMLRoleVoice=n?.voice||this.SSMLRoleVoice,new Promise((async(t,e)=>{let resolveChatVoice=async()=>{o.set("content",s),o.set("ssml",a),o.set("role","assistant");let e=localStorage.getItem("company");e&&o.set("company",{__type:"Pointer",className:"Company",objectId:e}),Parse.User.current()?.id&&o.set("user",Parse.User.current().toPointer()),this.chatSession?.id&&o.set("session",this.chatSession?.toPointer()),o=await o.save(),this.voiceMap[o?.id]=o,t({id:o?.id})};if(0==i){let t=n?.provider||"microsoft",e=n?.rate,i=e?"</prosody>":"";a=`<speak provider="${t}">${e?`<prosody rate="${e}">`:""}<voice name="${this.SSMLRoleVoice}">${s}</voice>${i}</speak>`,"doubao"==t&&(a=s),resolveChatVoice()}if(1==i){let t=await getFormatTpl("talk-text-ssml-tpl",{content:s,SSMLRoleVoice:this.SSMLRoleVoice});new FmodeChatCompletion(this.fixMessageList([{role:"user",content:t}]),{model:this.currentModel?.get("code")||"fmode-4.5-128k"}).sendCompletion({isDirect:!0}).subscribe((async t=>{t?.complete&&(a=this.getContentText(t?.content),resolveChatVoice())}))}}))}getContentText(t){return"string"==typeof t?t:t?.[0]?.text||""}async initTTS(){this.voiceConfig=this.voiceConfig||this.role?.get("voiceConfig");let t=await this.ncloud.apig("voice/tts/token",{company:localStorage.getItem("company"),provider:this.voiceConfig?.provider||"microsoft"});if(t.provider=this.voiceConfig?.provider,t?.provider&&"microsoft"!=t?.provider||(t.provider=new FmodeTTSProviderMicrosoft),"doubao"==t?.provider&&(t.provider=new FmodeTTSProviderDoubao),t.voiceConfig=this.voiceConfig,console.log("initTTS",t),t?.token||t?.stsToken){return new FmodeTTS(t,this.uploadServ)}return null}stopPlayingVoice(){Object.values(this.VoiceTTSMap).forEach((t=>{t?.isPlaying&&t?.stop()}))}async playChatVoice(t,e){let i=await this.initTTS();if(console.log("playChatVoice",i),i){try{i.voiceConfig=this.voiceConfig,this.playAnimation("talking"),i.speakAsync(t?.get("ssml"),t,{onStart:t=>{e?.onStart&&e?.onStart(t)},onLoaded:t=>{e?.onLoaded&&e?.onLoaded(t)},onResult:t=>{e?.onResult&&e?.onResult(t)},onStop:()=>{e?.onStop&&e?.onStop(),this.playAnimation("waiting")}})}catch(t){}return this.VoiceTTSMap[t.id]=i,i}return null}async saveChatSession(){if("new"==this.sessionId&&(this.chatSession=new this.ChatSession),this.chatSession.set("title",this.genTitle()),this.chatSession.set("role",this.role?.toPointer()),this.chatSession.set("messageList",this.messageList),this.chatSession.set("user",Parse.User.current()?.toPointer()),this.chatSession=await this.chatSession.save(),this.onChatSaved&&this.onChatSaved(this),this.sessionId=this.chatSession?.id,this.sessionId){let t=`${window.location.origin}/chat/pro/chat/${this.sessionId}`;window.location?.pathname?.indexOf("chat/session")>-1&&(t=`${window.location.origin}/chat/session/chat/${this.sessionId}`),"modal"==this.mode&&(t=window.location.href),t=this.getInviteUrl(t),window.history.replaceState(null,null,t+window.location.search);let e={sid:this.chatSession?.id,rid:this.role?.id,name:this.role?.get("name"),message:this.chatSession?.get("messageList")?.[this.chatSession?.get("messageList")?.length-1]?.content?.slice(0,20),latest:this.chatSession?.createdAt};this.chatServ&&!this.chatServ?.chatList?.length&&(this.chatServ.chatList=[]);let i=this.chatServ?.chatList?.find((t=>t?.sid==e?.sid));i>-1?this.chatServ.chatList[i]=e:this.chatServ?.chatList.unshift(e)}}getInviteUrl(t){let e=new URL(t),i=Parse.User?.current()?.id;return e.searchParams.set("invite",i),e.href}genTitle(){if(this.title)return this.title;let t=this.messageList.find((t=>"user"==t.role))?.content;return"string"==typeof t&&(this.title=t?.slice(0,15)||""),"object"==typeof t&&(this.title=t?.find((t=>t?.text))?.text||""),this.title}fixMessageList(t){return t.map((t=>({role:t.role,content:t.content})))}nowStr(){let t=new Date;return`${t.getFullYear()}/${t.getMonth()+1}/${t.getDate()} ${t.getHours()}:${t.getMinutes()}:${t.getSeconds()}`}}
9
9
  var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9jb3JlL2FnZW50L2NoYXQvZm1vZGUtY2hhdC5tanM=`
10
10