fmode-ng 0.0.70 → 0.0.72
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.
- package/esm2022/lib/aigc/avatar/comp-avatar-role-image/comp-avatar-role-image.component.mjs +1 -1
- package/esm2022/lib/aigc/avatar/comp-avatar-role-video/comp-avatar-role-video.component.mjs +1 -1
- package/esm2022/lib/aigc/chat/chat-header-area/comp-header-area.component.mjs +1 -1
- package/esm2022/lib/aigc/chat/chat-message-card/comp-message-card.component.mjs +1 -1
- package/esm2022/lib/aigc/chat/chat-modal-input/modal-input.component.mjs +1 -1
- package/esm2022/lib/aigc/voice/fmode-voice.service.mjs +1 -1
- package/esm2022/lib/aigc/voice/lib/recorder/engine-pcm.mjs +10 -0
- package/esm2022/lib/aigc/voice/lib/recorder/engine-wav.mjs +10 -0
- package/esm2022/lib/aigc/voice/lib/recorder/extension-waveview.mjs +10 -0
- package/fesm2022/fmode-ng.mjs +1 -1
- package/fesm2022/fmode-ng.mjs.map +1 -1
- package/lib/aigc/avatar/comp-avatar-role-image/comp-avatar-role-image.component.d.ts +1 -1
- package/lib/aigc/avatar/comp-avatar-role-video/comp-avatar-role-video.component.d.ts +1 -1
- package/lib/aigc/voice/fmode-voice.service.d.ts +3 -3
- package/lib/aigc/voice/lib/recorder/engine-pcm.d.ts +1 -0
- package/lib/aigc/voice/lib/recorder/engine-wav.d.ts +1 -0
- package/lib/aigc/voice/lib/recorder/extension-waveview.d.ts +1 -0
- package/package.json +1 -1
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
* 保留所有权利 All Rights Reserved.
|
|
6
6
|
* /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/avatar/comp-avatar-role-image/comp-avatar-role-image.component.mjs
|
|
7
7
|
*/
|
|
8
|
-
import{Component,ElementRef,Input,ViewChild}from"@angular/core";import{CommonModule}from"@angular/common";import Recorder from"recorder-core";import"
|
|
8
|
+
import{Component,ElementRef,Input,ViewChild}from"@angular/core";import{CommonModule}from"@angular/common";import Recorder from"recorder-core";import"../../voice/lib/recorder/extension-waveview";import Parse from"parse";import{FormsModule}from"@angular/forms";import{FmodeChat}from"../../service-fmai/service-chat/chat-class";import*as i0 from"@angular/core";export class CompAvatarRoleImageComponent{constructor(){this.animClass="waiting",this.imageMap={}}playWave(){!this.wave&&Recorder.WaveView&&(this.wave=Recorder.WaveView({elem:".record-wave-avatar",keep:!1}));let a=function generatePowerLevel(){return 100*Math.random()}();this.waveInterval=setInterval((()=>{let t=function generateSampleRate(){const a=[44100,48e3,88200,96e3];return a[Math.floor(Math.random()*a.length)]}(),e=function generatePcmData(a){const t=[];for(let e=0;e<a;e++)t.push(Math.floor(65536*Math.random())-32768);return t}(1e3);this.wave.input(e,a,t)}),40)}stopWave(){clearInterval(this.waveInterval)}ngAfterViewInit(){}ngOnInit(){setTimeout((()=>{this.imageMap=this.fmodeChat.avatarConfig?.image,this.avatarImage.nativeElement.src=this.imageMap?.waiting,this.fmodeChat.playAnimation=this.playAnimation}),1500)}playAnimation(){let a=this;return t=>{let e=a.avatarImage.nativeElement;switch(a.animClass=t,a.stopWave(),t){case"thinking":case"waiting":e.style.animationPlayState="running";break;case"talking":e.style.animationPlayState="running",a.playWave();break;case"listening":e.style.animationPlayState="pause";break;default:e.style.animationPlayState="paused"}}}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:CompAvatarRoleImageComponent,deps:[],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"17.3.12",type:CompAvatarRoleImageComponent,isStandalone:!0,selector:"fm-avatar-role-image",inputs:{fmodeChat:"fmodeChat",role:"role"},viewQueries:[{propertyName:"avatarImage",first:!0,predicate:["avatarImage"],descendants:!0}],ngImport:i0,template:'<div class="page">\n <div class="avatar" [class]="animClass">\n <div class="avatar-photo">\n <img #avatarImage alt="">\n \x3c!-- 音频波动 --\x3e\n <div class="record-wave-avatar">\n </div>\n </div>\n </div>\n</div>',styles:[".page{position:relative;width:100vw;height:100%;top:0;left:0;display:flex;justify-content:center;align-items:center;background-color:#000}.avatar{width:100%;display:flex;justify-content:center;align-items:center}.avatar .avatar-photo{position:relative;margin-top:-10vh;width:80%}.avatar .avatar-photo img{border-radius:10px}.avatar .record-wave-avatar{height:50px;width:100%;position:absolute;float:left;left:0;bottom:0}.thinking,.listening,.waiting,.talking{animation-duration:2s;animation-play-state:running;animation-iteration-count:infinite;animation-timing-function:ease-in-out}@keyframes waitingAnimation{0%{transform:scale(1)}50%{transform:scale(1.05)}to{transform:scale(1)}}@keyframes thinkingAnimation{0%{transform:rotate(0)}30%{transform:rotate(10deg)}60%{transform:rotate(-10deg)}to{transform:rotate(0)}}.thinking{animation-name:thinkingAnimation}.waiting{animation-name:waitingAnimation}.listening{animation-name:listeningAnimation}.talking{animation-name:talkingAnimation}\n"],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"ngmodule",type:FormsModule}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:CompAvatarRoleImageComponent,decorators:[{type:Component,args:[{selector:"fm-avatar-role-image",standalone:!0,imports:[CommonModule,FormsModule],template:'<div class="page">\n <div class="avatar" [class]="animClass">\n <div class="avatar-photo">\n <img #avatarImage alt="">\n \x3c!-- 音频波动 --\x3e\n <div class="record-wave-avatar">\n </div>\n </div>\n </div>\n</div>',styles:[".page{position:relative;width:100vw;height:100%;top:0;left:0;display:flex;justify-content:center;align-items:center;background-color:#000}.avatar{width:100%;display:flex;justify-content:center;align-items:center}.avatar .avatar-photo{position:relative;margin-top:-10vh;width:80%}.avatar .avatar-photo img{border-radius:10px}.avatar .record-wave-avatar{height:50px;width:100%;position:absolute;float:left;left:0;bottom:0}.thinking,.listening,.waiting,.talking{animation-duration:2s;animation-play-state:running;animation-iteration-count:infinite;animation-timing-function:ease-in-out}@keyframes waitingAnimation{0%{transform:scale(1)}50%{transform:scale(1.05)}to{transform:scale(1)}}@keyframes thinkingAnimation{0%{transform:rotate(0)}30%{transform:rotate(10deg)}60%{transform:rotate(-10deg)}to{transform:rotate(0)}}.thinking{animation-name:thinkingAnimation}.waiting{animation-name:waitingAnimation}.listening{animation-name:listeningAnimation}.talking{animation-name:talkingAnimation}\n"]}]}],propDecorators:{avatarImage:[{type:ViewChild,args:["avatarImage"]}],fmodeChat:[{type:Input}],role:[{type:Input}]}});
|
|
9
9
|
var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL2F2YXRhci9jb21wLWF2YXRhci1yb2xlLWltYWdlL2NvbXAtYXZhdGFyLXJvbGUtaW1hZ2UuY29tcG9uZW50Lm1qcw==`
|
|
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/avatar/comp-avatar-role-video/comp-avatar-role-video.component.mjs
|
|
7
7
|
*/
|
|
8
|
-
import{Component,Input,ViewChild,Output,EventEmitter}from"@angular/core";import{CommonModule}from"@angular/common";import Recorder from"recorder-core";import"
|
|
8
|
+
import{Component,Input,ViewChild,Output,EventEmitter}from"@angular/core";import{CommonModule}from"@angular/common";import Recorder from"recorder-core";import"../../voice/lib/recorder/extension-waveview";import Parse from"parse";import{FormsModule}from"@angular/forms";import{FmodeChat}from"../../service-fmai/service-chat/chat-class";import{FmVideoComponent}from"../../../video";import{IonButton,IonButtons,IonIcon,IonToolbar,NavController}from"@ionic/angular/standalone";import*as i0 from"@angular/core";import*as i1 from"@ionic/angular/standalone";export class CompAvatarRoleVideoComponent{constructor(t){this.navCtrl=t,this.animClass="waiting",this.onClose=new EventEmitter,this.videoMap={}}close(){this.onClose.emit(!0),this.fmodeChat.isAvatarShow=!1}goBack(){this.navCtrl.back()}playWave(){!this.wave&&Recorder.WaveView&&(this.wave=Recorder.WaveView({elem:".record-wave-avatar",keep:!1}));let t=function generatePowerLevel(){return 100*Math.random()}();this.waveInterval=setInterval((()=>{let o=function generateSampleRate(){const t=[44100,48e3,88200,96e3];return t[Math.floor(Math.random()*t.length)]}(),n=function generatePcmData(t){const o=[];for(let n=0;n<t;n++)o.push(Math.floor(65536*Math.random())-32768);return o}(1e3);this.wave.input(n,t,o)}),40)}stopWave(){clearInterval(this.waveInterval)}ngAfterViewInit(){}ngOnInit(){setTimeout((()=>{this.videoMap=this.fmodeChat.avatarConfig?.video,this.playVideo(this.videoMap?.waiting),this.fmodeChat.playAnimation=this.playAnimation}),1500)}playAnimation(){let t=this;return o=>{t.animClass=o,t.stopWave(),t.playVideo(t.videoMap[o])}}playVideo(t){this.avatarVideo.play(t)}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:CompAvatarRoleVideoComponent,deps:[{token:i1.NavController}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"17.3.12",type:CompAvatarRoleVideoComponent,isStandalone:!0,selector:"fm-avatar-role-video",inputs:{fmodeChat:"fmodeChat",role:"role"},outputs:{onClose:"onClose"},viewQueries:[{propertyName:"avatarVideo",first:!0,predicate:FmVideoComponent,descendants:!0}],ngImport:i0,template:'<div class="page">\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="goBack()">\n <ion-icon name="chevron-back-outline"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-buttons slot="end">\n <ion-button (click)="close()"> <ion-icon name="chevron-collapse-outline"></ion-icon> </ion-button>\n </ion-buttons>\n </ion-toolbar>\n <div class="avatar" [class]="animClass">\n <div class="avatar-photo">\n <fm-video #avatarVideo alt=""></fm-video>\n \x3c!-- 音频波动 --\x3e\n <div class="record-wave-avatar">\n </div>\n </div>\n </div>\n</div>',styles:["ion-toolbar{position:fixed;top:0;left:0;width:100vw;--background:transparent}.page{position:relative;width:100vw;height:100%;top:0;left:0;display:flex;justify-content:center;align-items:center;background-color:#000}.avatar{height:100%;width:100%;display:flex;justify-content:center;align-items:center}.avatar .avatar-photo{position:relative;display:flex;justify-content:center;width:80%;max-height:100%}.avatar .avatar-photo fm-video{border-radius:50%;max-height:100%;max-width:100%}.avatar .record-wave-avatar{height:50px;width:100%;position:absolute;float:left;left:0;bottom:0}.thinking,.listening,.waiting,.talking{animation-duration:2s;animation-play-state:running;animation-iteration-count:infinite;animation-timing-function:ease-in-out}@keyframes waitingAnimation{0%{transform:scale(1)}50%{transform:scale(1.05)}to{transform:scale(1)}}@keyframes thinkingAnimation{0%{transform:rotate(0)}30%{transform:rotate(10deg)}60%{transform:rotate(-10deg)}to{transform:rotate(0)}}\n"],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"ngmodule",type:FormsModule},{kind:"component",type:FmVideoComponent,selector:"fm-video",inputs:["url","canvasStyle"]},{kind:"component",type:IonIcon,selector:"ion-icon",inputs:["color","flipRtl","icon","ios","lazy","md","mode","name","sanitize","size","src"]},{kind:"component",type:IonToolbar,selector:"ion-toolbar",inputs:["color","mode"]},{kind:"component",type:IonButtons,selector:"ion-buttons",inputs:["collapse"]},{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"]}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:CompAvatarRoleVideoComponent,decorators:[{type:Component,args:[{selector:"fm-avatar-role-video",standalone:!0,imports:[CommonModule,FormsModule,FmVideoComponent,IonIcon,IonToolbar,IonButtons,IonButton],template:'<div class="page">\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="goBack()">\n <ion-icon name="chevron-back-outline"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-buttons slot="end">\n <ion-button (click)="close()"> <ion-icon name="chevron-collapse-outline"></ion-icon> </ion-button>\n </ion-buttons>\n </ion-toolbar>\n <div class="avatar" [class]="animClass">\n <div class="avatar-photo">\n <fm-video #avatarVideo alt=""></fm-video>\n \x3c!-- 音频波动 --\x3e\n <div class="record-wave-avatar">\n </div>\n </div>\n </div>\n</div>',styles:["ion-toolbar{position:fixed;top:0;left:0;width:100vw;--background:transparent}.page{position:relative;width:100vw;height:100%;top:0;left:0;display:flex;justify-content:center;align-items:center;background-color:#000}.avatar{height:100%;width:100%;display:flex;justify-content:center;align-items:center}.avatar .avatar-photo{position:relative;display:flex;justify-content:center;width:80%;max-height:100%}.avatar .avatar-photo fm-video{border-radius:50%;max-height:100%;max-width:100%}.avatar .record-wave-avatar{height:50px;width:100%;position:absolute;float:left;left:0;bottom:0}.thinking,.listening,.waiting,.talking{animation-duration:2s;animation-play-state:running;animation-iteration-count:infinite;animation-timing-function:ease-in-out}@keyframes waitingAnimation{0%{transform:scale(1)}50%{transform:scale(1.05)}to{transform:scale(1)}}@keyframes thinkingAnimation{0%{transform:rotate(0)}30%{transform:rotate(10deg)}60%{transform:rotate(-10deg)}to{transform:rotate(0)}}\n"]}]}],ctorParameters:()=>[{type:i1.NavController}],propDecorators:{avatarVideo:[{type:ViewChild,args:[FmVideoComponent]}],fmodeChat:[{type:Input}],role:[{type:Input}],onClose:[{type:Output}]}});
|
|
9
9
|
var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL2F2YXRhci9jb21wLWF2YXRhci1yb2xlLXZpZGVvL2NvbXAtYXZhdGFyLXJvbGUtdmlkZW8uY29tcG9uZW50Lm1qcw==`
|
|
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/chat-header-area/comp-header-area.component.mjs
|
|
7
7
|
*/
|
|
8
|
-
import{CommonModule}from"@angular/common";import{Component,Input}from"@angular/core";import{IonButtons,IonCard,IonHeader,IonIcon,IonButton,IonItem,IonLabel,IonList,IonNote,IonTitle,IonToolbar,IonModal,NavController,IonAvatar}from"@ionic/angular/standalone";import{CompAvatarRoleImageComponent}from"../../avatar";import{CompAvatarRoleVideoComponent}from"../../avatar";import{FmodeChat}from"../../service-fmai/service-chat";import{addIcons}from"ionicons";import{chevronBackOutline,ellipsisHorizontalOutline}from"ionicons/icons";import*as i0 from"@angular/core";import*as i1 from"@ionic/angular/standalone";import*as i2 from"@angular/common";addIcons({chevronBackOutline:chevronBackOutline,ellipsisHorizontalOutline:ellipsisHorizontalOutline});export class FmChatHeaderArea{constructor(n){this.navCtrl=n,this.isModalOpen=!1,this.goBack=()=>{this.navCtrl.back()}}async back(){if(this.chat?.onClose){if(!await(this.chat?.onClose(this.chat)))return}this.goBack()}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatHeaderArea,deps:[{token:i1.NavController}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"17.3.12",type:FmChatHeaderArea,isStandalone:!0,selector:"fm-chat-header-area",inputs:{chat:"chat",goBack:"goBack"},ngImport:i0,template:'<ion-toolbar *ngIf="chat?.isAvatarShow==false">\n <ion-buttons slot="start">\n <ion-button (click)="back()">\n <ion-icon name="chevron-back-outline"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-title>\n <div class="title-avatar-area">\n <div class="avatar-img" (click)="chat.showAvatar()">\n <img *ngIf="chat?.role?.get(\'avatar\')||chat?.role?.get(\'thumb\')" [src]="chat?.role?.get(\'avatar\')||chat?.role?.get(\'thumb\')" />\n <ion-icon *ngIf="chat?.role?.get(\'avatarConfig\')" name="resize-outline"></ion-icon>\n </div>\n <span (click)="chat.showAvatar()">\n {{chat?.role?.get("name")}}\n </span>\n </div>\n </ion-title>\n\n <ion-buttons slot="end">\n <ion-button (click)="isModalOpen=true"> <ion-icon name="ellipsis-horizontal-outline"></ion-icon> </ion-button>\n </ion-buttons>\n</ion-toolbar>\n\n<div class="avatar-area" *ngIf="chat?.isAvatarShow==true">\n <fm-avatar-role-image *ngIf="chat?.avatarMode==\'image\'" [fmodeChat]="chat" [role]="avatarRole"></fm-avatar-role-image>\n <fm-avatar-role-video *ngIf="chat?.avatarMode==\'video\'" [fmodeChat]="chat" [role]="avatarRole"></fm-avatar-role-video>\n</div>\n\n\n<ion-modal [isOpen]="isModalOpen" (willDismiss)="isModalOpen=false">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="isModalOpen=false">返回</ion-button>\n </ion-buttons>\n <ion-title>简介</ion-title>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n\n <ion-card style="margin: 0px;">\n <img [src]="chat?.role?.get(\'thumb\')" alt="">\n <ion-card-header>\n <ion-card-subtitle>{{chat?.role?.get(\'tags\')}}</ion-card-subtitle>\n <ion-card-title>{{chat?.role?.get("name")}}</ion-card-title>\n </ion-card-header>\n \n <ion-card-content>\n {{chat?.role?.get("desc")}}\n \n <ion-list [inset]="true" style="margin:0px;">\n \x3c!-- <ion-item>\n <ion-avatar *ngIf="chat?.role?.get(\'thumb\')" aria-hidden="true" slot="start">\n <img [src]="chat?.role?.get(\'thumb\')" />\n </ion-avatar>\n <ion-label>{{chat?.role?.get("name")}}</ion-label>\n </ion-item> --\x3e\n <ion-item lines="none" *ngIf="chat?.role?.get(\'age\')">\n <ion-note slot="start">年龄</ion-note>\n <ion-label>{{chat?.role?.get("age")}}</ion-label>\n </ion-item>\n <ion-item lines="none" *ngIf="chat?.role?.get(\'gender\')">\n <ion-note slot="start">性别</ion-note>\n <ion-label>{{chat?.role?.get("gender")}}</ion-label>\n </ion-item>\n <ion-item lines="none">\n <ion-note slot="start">称号</ion-note>\n <ion-label>{{chat?.role?.get("title")}}</ion-label>\n </ion-item>\n \n <ion-item lines="none">\n <ion-note slot="start">擅长</ion-note>\n <ion-label>{{chat?.role?.get("tags")?.join(\',\')}}</ion-label>\n </ion-item>\n \n </ion-list>\n </ion-card-content>\n </ion-card>\n </ion-content>\n </ng-template>\n </ion-modal>',styles:[".title-avatar-area{width:100%;height:100%;display:flex;justify-content:center;align-items:center}.title-avatar-area .avatar-img img{width:32px;height:32px;border-radius:50%;margin-right:5px}.title-avatar-area .avatar-img ion-icon{background:#df76dfcc;border-radius:50%;padding:3px;position:absolute;color:#000;margin-left:-20px;font-size:10px;margin-top:18px}.avatar-area{height:100%}\n"],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"directive",type:i2.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:IonToolbar,selector:"ion-toolbar",inputs:["color","mode"]},{kind:"component",type:IonButtons,selector:"ion-buttons",inputs:["collapse"]},{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:IonIcon,selector:"ion-icon",inputs:["color","flipRtl","icon","ios","lazy","md","mode","name","sanitize","size","src"]},{kind:"component",type:IonModal,selector:"ion-modal"},{kind:"component",type:IonTitle,selector:"ion-title",inputs:["color","size"]},{kind:"component",type:IonHeader,selector:"ion-header",inputs:["collapse","mode","translucent"]},{kind:"component",type:IonList,selector:"ion-list",inputs:["inset","lines","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:IonCard,selector:"ion-card",inputs:["button","color","disabled","download","href","mode","rel","routerAnimation","routerDirection","target","type"]},{kind:"component",type:IonLabel,selector:"ion-label",inputs:["color","mode","position"]},{kind:"component",type:IonNote,selector:"ion-note",inputs:["color","mode"]},{kind:"component",type:CompAvatarRoleImageComponent,selector:"fm-avatar-role-image",inputs:["fmodeChat","role"]},{kind:"component",type:CompAvatarRoleVideoComponent,selector:"fm-avatar-role-video",inputs:["fmodeChat","role"],outputs:["onClose"]}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatHeaderArea,decorators:[{type:Component,args:[{selector:"fm-chat-header-area",standalone:!0,imports:[CommonModule,IonToolbar,IonButtons,IonButton,IonIcon,IonModal,IonAvatar,IonTitle,IonHeader,IonList,IonItem,IonCard,IonLabel,IonNote,CompAvatarRoleImageComponent,CompAvatarRoleVideoComponent],template:'<ion-toolbar *ngIf="chat?.isAvatarShow==false">\n <ion-buttons slot="start">\n <ion-button (click)="back()">\n <ion-icon name="chevron-back-outline"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-title>\n <div class="title-avatar-area">\n <div class="avatar-img" (click)="chat.showAvatar()">\n <img *ngIf="chat?.role?.get(\'avatar\')||chat?.role?.get(\'thumb\')" [src]="chat?.role?.get(\'avatar\')||chat?.role?.get(\'thumb\')" />\n <ion-icon *ngIf="chat?.role?.get(\'avatarConfig\')" name="resize-outline"></ion-icon>\n </div>\n <span (click)="chat.showAvatar()">\n {{chat?.role?.get("name")}}\n </span>\n </div>\n </ion-title>\n\n <ion-buttons slot="end">\n <ion-button (click)="isModalOpen=true"> <ion-icon name="ellipsis-horizontal-outline"></ion-icon> </ion-button>\n </ion-buttons>\n</ion-toolbar>\n\n<div class="avatar-area" *ngIf="chat?.isAvatarShow==true">\n <fm-avatar-role-image *ngIf="chat?.avatarMode==\'image\'" [fmodeChat]="chat" [role]="avatarRole"></fm-avatar-role-image>\n <fm-avatar-role-video *ngIf="chat?.avatarMode==\'video\'" [fmodeChat]="chat" [role]="avatarRole"></fm-avatar-role-video>\n</div>\n\n\n<ion-modal [isOpen]="isModalOpen" (willDismiss)="isModalOpen=false">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="isModalOpen=false">返回</ion-button>\n </ion-buttons>\n <ion-title>简介</ion-title>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n\n <ion-card style="margin: 0px;">\n <img [src]="chat?.role?.get(\'thumb\')" alt="">\n <ion-card-header>\n <ion-card-subtitle>{{chat?.role?.get(\'tags\')}}</ion-card-subtitle>\n <ion-card-title>{{chat?.role?.get("name")}}</ion-card-title>\n </ion-card-header>\n \n <ion-card-content>\n {{chat?.role?.get("desc")}}\n \n <ion-list [inset]="true" style="margin:0px;">\n \x3c!-- <ion-item>\n <ion-avatar *ngIf="chat?.role?.get(\'thumb\')" aria-hidden="true" slot="start">\n <img [src]="chat?.role?.get(\'thumb\')" />\n </ion-avatar>\n <ion-label>{{chat?.role?.get("name")}}</ion-label>\n </ion-item> --\x3e\n <ion-item lines="none" *ngIf="chat?.role?.get(\'age\')">\n <ion-note slot="start">年龄</ion-note>\n <ion-label>{{chat?.role?.get("age")}}</ion-label>\n </ion-item>\n <ion-item lines="none" *ngIf="chat?.role?.get(\'gender\')">\n <ion-note slot="start">性别</ion-note>\n <ion-label>{{chat?.role?.get("gender")}}</ion-label>\n </ion-item>\n <ion-item lines="none">\n <ion-note slot="start">称号</ion-note>\n <ion-label>{{chat?.role?.get("title")}}</ion-label>\n </ion-item>\n \n <ion-item lines="none">\n <ion-note slot="start">擅长</ion-note>\n <ion-label>{{chat?.role?.get("tags")?.join(\',\')}}</ion-label>\n </ion-item>\n \n </ion-list>\n </ion-card-content>\n </ion-card>\n </ion-content>\n </ng-template>\n </ion-modal>',styles:[".title-avatar-area{width:100%;height:100%;display:flex;justify-content:center;align-items:center}.title-avatar-area .avatar-img img{width:32px;height:32px;border-radius:50%;margin-right:5px}.title-avatar-area .avatar-img ion-icon{background:#df76dfcc;border-radius:50%;padding:3px;position:absolute;color:#000;margin-left:-20px;font-size:10px;margin-top:18px}.avatar-area{height:100%}\n"]}]}],ctorParameters:()=>[{type:i1.NavController}],propDecorators:{chat:[{type:Input}],goBack:[{type:Input}]}});
|
|
8
|
+
import{CommonModule}from"@angular/common";import{Component,Input}from"@angular/core";import{IonButtons,IonCard,IonHeader,IonIcon,IonButton,IonItem,IonLabel,IonList,IonNote,IonTitle,IonToolbar,IonModal,NavController,IonAvatar}from"@ionic/angular/standalone";import{CompAvatarRoleImageComponent}from"../../avatar";import{CompAvatarRoleVideoComponent}from"../../avatar";import{FmodeChat}from"../../service-fmai/service-chat";import{addIcons}from"ionicons";import{chevronBackOutline,ellipsisHorizontalOutline}from"ionicons/icons/index";import*as i0 from"@angular/core";import*as i1 from"@ionic/angular/standalone";import*as i2 from"@angular/common";addIcons({chevronBackOutline:chevronBackOutline,ellipsisHorizontalOutline:ellipsisHorizontalOutline});export class FmChatHeaderArea{constructor(n){this.navCtrl=n,this.isModalOpen=!1,this.goBack=()=>{this.navCtrl.back()}}async back(){if(this.chat?.onClose){if(!await(this.chat?.onClose(this.chat)))return}this.goBack()}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatHeaderArea,deps:[{token:i1.NavController}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"17.3.12",type:FmChatHeaderArea,isStandalone:!0,selector:"fm-chat-header-area",inputs:{chat:"chat",goBack:"goBack"},ngImport:i0,template:'<ion-toolbar *ngIf="chat?.isAvatarShow==false">\n <ion-buttons slot="start">\n <ion-button (click)="back()">\n <ion-icon name="chevron-back-outline"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-title>\n <div class="title-avatar-area">\n <div class="avatar-img" (click)="chat.showAvatar()">\n <img *ngIf="chat?.role?.get(\'avatar\')||chat?.role?.get(\'thumb\')" [src]="chat?.role?.get(\'avatar\')||chat?.role?.get(\'thumb\')" />\n <ion-icon *ngIf="chat?.role?.get(\'avatarConfig\')" name="resize-outline"></ion-icon>\n </div>\n <span (click)="chat.showAvatar()">\n {{chat?.role?.get("name")}}\n </span>\n </div>\n </ion-title>\n\n <ion-buttons slot="end">\n <ion-button (click)="isModalOpen=true"> <ion-icon name="ellipsis-horizontal-outline"></ion-icon> </ion-button>\n </ion-buttons>\n</ion-toolbar>\n\n<div class="avatar-area" *ngIf="chat?.isAvatarShow==true">\n <fm-avatar-role-image *ngIf="chat?.avatarMode==\'image\'" [fmodeChat]="chat" [role]="avatarRole"></fm-avatar-role-image>\n <fm-avatar-role-video *ngIf="chat?.avatarMode==\'video\'" [fmodeChat]="chat" [role]="avatarRole"></fm-avatar-role-video>\n</div>\n\n\n<ion-modal [isOpen]="isModalOpen" (willDismiss)="isModalOpen=false">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="isModalOpen=false">返回</ion-button>\n </ion-buttons>\n <ion-title>简介</ion-title>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n\n <ion-card style="margin: 0px;">\n <img [src]="chat?.role?.get(\'thumb\')" alt="">\n <ion-card-header>\n <ion-card-subtitle>{{chat?.role?.get(\'tags\')}}</ion-card-subtitle>\n <ion-card-title>{{chat?.role?.get("name")}}</ion-card-title>\n </ion-card-header>\n \n <ion-card-content>\n {{chat?.role?.get("desc")}}\n \n <ion-list [inset]="true" style="margin:0px;">\n \x3c!-- <ion-item>\n <ion-avatar *ngIf="chat?.role?.get(\'thumb\')" aria-hidden="true" slot="start">\n <img [src]="chat?.role?.get(\'thumb\')" />\n </ion-avatar>\n <ion-label>{{chat?.role?.get("name")}}</ion-label>\n </ion-item> --\x3e\n <ion-item lines="none" *ngIf="chat?.role?.get(\'age\')">\n <ion-note slot="start">年龄</ion-note>\n <ion-label>{{chat?.role?.get("age")}}</ion-label>\n </ion-item>\n <ion-item lines="none" *ngIf="chat?.role?.get(\'gender\')">\n <ion-note slot="start">性别</ion-note>\n <ion-label>{{chat?.role?.get("gender")}}</ion-label>\n </ion-item>\n <ion-item lines="none">\n <ion-note slot="start">称号</ion-note>\n <ion-label>{{chat?.role?.get("title")}}</ion-label>\n </ion-item>\n \n <ion-item lines="none">\n <ion-note slot="start">擅长</ion-note>\n <ion-label>{{chat?.role?.get("tags")?.join(\',\')}}</ion-label>\n </ion-item>\n \n </ion-list>\n </ion-card-content>\n </ion-card>\n </ion-content>\n </ng-template>\n </ion-modal>',styles:[".title-avatar-area{width:100%;height:100%;display:flex;justify-content:center;align-items:center}.title-avatar-area .avatar-img img{width:32px;height:32px;border-radius:50%;margin-right:5px}.title-avatar-area .avatar-img ion-icon{background:#df76dfcc;border-radius:50%;padding:3px;position:absolute;color:#000;margin-left:-20px;font-size:10px;margin-top:18px}.avatar-area{height:100%}\n"],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"directive",type:i2.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"component",type:IonToolbar,selector:"ion-toolbar",inputs:["color","mode"]},{kind:"component",type:IonButtons,selector:"ion-buttons",inputs:["collapse"]},{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:IonIcon,selector:"ion-icon",inputs:["color","flipRtl","icon","ios","lazy","md","mode","name","sanitize","size","src"]},{kind:"component",type:IonModal,selector:"ion-modal"},{kind:"component",type:IonTitle,selector:"ion-title",inputs:["color","size"]},{kind:"component",type:IonHeader,selector:"ion-header",inputs:["collapse","mode","translucent"]},{kind:"component",type:IonList,selector:"ion-list",inputs:["inset","lines","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:IonCard,selector:"ion-card",inputs:["button","color","disabled","download","href","mode","rel","routerAnimation","routerDirection","target","type"]},{kind:"component",type:IonLabel,selector:"ion-label",inputs:["color","mode","position"]},{kind:"component",type:IonNote,selector:"ion-note",inputs:["color","mode"]},{kind:"component",type:CompAvatarRoleImageComponent,selector:"fm-avatar-role-image",inputs:["fmodeChat","role"]},{kind:"component",type:CompAvatarRoleVideoComponent,selector:"fm-avatar-role-video",inputs:["fmodeChat","role"],outputs:["onClose"]}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatHeaderArea,decorators:[{type:Component,args:[{selector:"fm-chat-header-area",standalone:!0,imports:[CommonModule,IonToolbar,IonButtons,IonButton,IonIcon,IonModal,IonAvatar,IonTitle,IonHeader,IonList,IonItem,IonCard,IonLabel,IonNote,CompAvatarRoleImageComponent,CompAvatarRoleVideoComponent],template:'<ion-toolbar *ngIf="chat?.isAvatarShow==false">\n <ion-buttons slot="start">\n <ion-button (click)="back()">\n <ion-icon name="chevron-back-outline"></ion-icon>\n </ion-button>\n </ion-buttons>\n <ion-title>\n <div class="title-avatar-area">\n <div class="avatar-img" (click)="chat.showAvatar()">\n <img *ngIf="chat?.role?.get(\'avatar\')||chat?.role?.get(\'thumb\')" [src]="chat?.role?.get(\'avatar\')||chat?.role?.get(\'thumb\')" />\n <ion-icon *ngIf="chat?.role?.get(\'avatarConfig\')" name="resize-outline"></ion-icon>\n </div>\n <span (click)="chat.showAvatar()">\n {{chat?.role?.get("name")}}\n </span>\n </div>\n </ion-title>\n\n <ion-buttons slot="end">\n <ion-button (click)="isModalOpen=true"> <ion-icon name="ellipsis-horizontal-outline"></ion-icon> </ion-button>\n </ion-buttons>\n</ion-toolbar>\n\n<div class="avatar-area" *ngIf="chat?.isAvatarShow==true">\n <fm-avatar-role-image *ngIf="chat?.avatarMode==\'image\'" [fmodeChat]="chat" [role]="avatarRole"></fm-avatar-role-image>\n <fm-avatar-role-video *ngIf="chat?.avatarMode==\'video\'" [fmodeChat]="chat" [role]="avatarRole"></fm-avatar-role-video>\n</div>\n\n\n<ion-modal [isOpen]="isModalOpen" (willDismiss)="isModalOpen=false">\n <ng-template>\n <ion-header>\n <ion-toolbar>\n <ion-buttons slot="start">\n <ion-button (click)="isModalOpen=false">返回</ion-button>\n </ion-buttons>\n <ion-title>简介</ion-title>\n </ion-toolbar>\n </ion-header>\n <ion-content class="ion-padding">\n\n <ion-card style="margin: 0px;">\n <img [src]="chat?.role?.get(\'thumb\')" alt="">\n <ion-card-header>\n <ion-card-subtitle>{{chat?.role?.get(\'tags\')}}</ion-card-subtitle>\n <ion-card-title>{{chat?.role?.get("name")}}</ion-card-title>\n </ion-card-header>\n \n <ion-card-content>\n {{chat?.role?.get("desc")}}\n \n <ion-list [inset]="true" style="margin:0px;">\n \x3c!-- <ion-item>\n <ion-avatar *ngIf="chat?.role?.get(\'thumb\')" aria-hidden="true" slot="start">\n <img [src]="chat?.role?.get(\'thumb\')" />\n </ion-avatar>\n <ion-label>{{chat?.role?.get("name")}}</ion-label>\n </ion-item> --\x3e\n <ion-item lines="none" *ngIf="chat?.role?.get(\'age\')">\n <ion-note slot="start">年龄</ion-note>\n <ion-label>{{chat?.role?.get("age")}}</ion-label>\n </ion-item>\n <ion-item lines="none" *ngIf="chat?.role?.get(\'gender\')">\n <ion-note slot="start">性别</ion-note>\n <ion-label>{{chat?.role?.get("gender")}}</ion-label>\n </ion-item>\n <ion-item lines="none">\n <ion-note slot="start">称号</ion-note>\n <ion-label>{{chat?.role?.get("title")}}</ion-label>\n </ion-item>\n \n <ion-item lines="none">\n <ion-note slot="start">擅长</ion-note>\n <ion-label>{{chat?.role?.get("tags")?.join(\',\')}}</ion-label>\n </ion-item>\n \n </ion-list>\n </ion-card-content>\n </ion-card>\n </ion-content>\n </ng-template>\n </ion-modal>',styles:[".title-avatar-area{width:100%;height:100%;display:flex;justify-content:center;align-items:center}.title-avatar-area .avatar-img img{width:32px;height:32px;border-radius:50%;margin-right:5px}.title-avatar-area .avatar-img ion-icon{background:#df76dfcc;border-radius:50%;padding:3px;position:absolute;color:#000;margin-left:-20px;font-size:10px;margin-top:18px}.avatar-area{height:100%}\n"]}]}],ctorParameters:()=>[{type:i1.NavController}],propDecorators:{chat:[{type:Input}],goBack:[{type:Input}]}});
|
|
9
9
|
var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL2NoYXQvY2hhdC1oZWFkZXItYXJlYS9jb21wLWhlYWRlci1hcmVhLmNvbXBvbmVudC5tanM=`
|
|
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/chat-message-card/comp-message-card.component.mjs
|
|
7
7
|
*/
|
|
8
|
-
import{Component,Input}from"@angular/core";import{ChatContentPipe,FmodeChat,getMessageContentText}from"../../service-fmai/service-chat";import Parse from"parse";import{CommonModule}from"@angular/common";import{IonAvatar,IonItem}from"@ionic/angular/standalone";import{CompUserAvatarComponent}from"../../../user/comp-user-avatar/comp-user-avatar.component";import{ClipboardService}from"../../comp-markdown-preview/clipboard.service";import{MarkdownPreviewModule}from"../../comp-markdown-preview/markdown-preview.module";import{NzSanitizerPipe}from"ng-zorro-antd/pipes";import{DurationStrPipe}from"./duration-str.pipe";import{addIcons}from"ionicons";import{wifiOutline,copyOutline}from"ionicons/icons";import*as i0 from"@angular/core";import*as i1 from"../../comp-markdown-preview/clipboard.service";import*as i2 from"@angular/common";import*as i3 from"../../comp-markdown-preview/markdown-preview.component";addIcons({wifiOutline:wifiOutline,copyOutline:copyOutline});export class FmChatMessageCard{constructor(e){this.copyServ=e,this.user=Parse.User.current()}async toggleVoicePlay(){if(this.message?.voice?.id&&this.chat.VoiceTTSMap[this.message?.voice?.id]&&(this.tts=this.chat.VoiceTTSMap[this.message?.voice?.id]),this.tts?.isPlaying)return void this.tts?.stop();let e,t=!1;if(this.message?.voice?.id){let t=new Parse.Query("ChatVoice");t.include("voiceFile"),e=await t.get(this.message?.voice?.id)}if(!e?.id){let n=await this.chat.getVoiceByContentText(this.message?.content);e=this.chat.voiceMap[n?.id],this.message.voice={id:e?.id,duration:e?.get("duration")},t=!0}this.message?.voice?.duration||(this.message.voice.duration=e?.get("duration"),t=!0),t&&this.saveSession(),await this.chat.playChatVoice(e,{onStart:t=>{e?.id,t?.id},onLoaded:e=>{this.message.voice.duration=1e3*e.duration,this.updateVoiceDuration(1e3*e.duration)},onStop:t=>{e?.id,t?.id}}),this.tts=this.chat.VoiceTTSMap[e?.id]}updateVoiceDuration(e){this.message?.voice?.duration||(this.message.voice.duration=e,this.saveSession())}saveSession(){(this.index>=2||void 0===this.index)&&this.chat?.saveChatSession()}async copy(){this.copyServ.copyToClipboard(getMessageContentText(this.message?.content))}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatMessageCard,deps:[{token:i1.ClipboardService}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"17.3.12",type:FmChatMessageCard,isStandalone:!0,selector:"fm-chat-message-card",inputs:{index:"index",message:"message",role:"role",chat:"chat"},ngImport:i0,template:'<div class="message-card" [class.right]="message?.role==\'user\'" [class.center]="message?.role==\'system\'">\n \x3c!-- 用户及操作区 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'"> \x3c!-- 系统消息不显示头像 --\x3e\n <div class="avatar-row">\n <div class="actions">\n \x3c!-- 刷新 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="refresh-outline"></ion-icon> \n </ion-button> --\x3e\n \x3c!-- 复制 --\x3e\n <ion-button size="small" fill="outline" slot="start" (click)="copy()">\n <ion-icon name="copy-outline"></ion-icon>\n </ion-button>\n \x3c!-- 编辑 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="create-outline"></ion-icon>\n </ion-button> --\x3e\n </div>\n \x3c!-- 音频消息区域 --\x3e\n <div *ngIf="((message?.role==\'assistant\' && chat?.role?.get(\'voiceConfig\')?.voice) || (message?.role==\'user\'&&message?.voice))" class="play-voice" (click)="toggleVoicePlay()">\n <div class="voice-button">\n <ion-icon name="wifi-outline"\n [style.transform]="message?.role==\'user\'?\'rotate(-90deg)\':\'rotate(90deg)\'"\n class="audio-icon" [class.play-voice-playing]="tts?.isPlaying"></ion-icon>\n \x3c!-- <span nz-icon [nzRotate]="message?.role==\'user\'?-90:90" nzType="wifi" nzTheme="outline" [class.play-voice-playing]="tts?.isPlaying"></span> --\x3e\n </div>\n <div class="voice-info">\n <span *ngIf="message?.voice?.duration">\n {{((message?.voice?.duration||0)/1000) | durationStr}}\n </span>\n <span *ngIf="!message?.voice?.duration">\n \x3c!-- --\x3e\n </span>\n </div>\n </div>\n \x3c!-- 头像区域 --\x3e \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/100/h/100\'" >\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="item-row images" *ngIf="message?.content | chatContent:\'image_url\'">\n <img [src]="message?.content | chatContent:\'image_url\'" alt="">\n </div>\n \x3c!-- 聊天气泡 --\x3e\n <div class="item-row bubble" [style.fontSize]="role?.get(\'uiConfig\')?.message?.bubble?.fontSize || \'0.8rem\'">\n <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content | chatContent" [render]="false"></fm-markdown-preview>\n <fm-markdown-preview *ngIf="message?.complete" [content]="message?.content | chatContent"></fm-markdown-preview>\n </div>\n \x3c!-- 时间显示 --\x3e\n <div class="item-row loading" *ngIf="message?.role!=\'system\' && !message?.complete">\n 正在输入<ion-spinner name="dots"></ion-spinner>\n </div>\n\n <div class="item-row created" *ngIf="message?.createdAt">\n <span>{{message?.createdAt | date:"dd/MM/yy HH:mm"}}</span>\n </div>\n</div>',styles:['@charset "UTF-8";:host-context(body.dark) .message-card .actions .item-native{background:none!important}:host-context(body.dark) .message-card .bubble{color:#0e101d}:host-context(body.dark) .message-card .bubble .content-style{filter:invert(1)!important}:host-context(body.dark) .message-card .bubble fm-markdown-preview{filter:invert(1)!important}:host-context(body.dark) .message-card .play-voice{background-color:#0e101d}:host-context(body.dark) .message-card .play-voice .voice-info{color:#fff}:host-context(body.dark) .message-card .right .bubble{color:#921f8a!important;background:#921f8a!important}:host-context(body.dark) .message-card .right .play-voice{background:#921f8a!important}:host-context(body.dark) .message-card .created span{color:#fff}@media screen and (max-width: 800px){.message-card:focus .actions{opacity:1!important}}.message-card:hover .actions{opacity:1;transition:opacity .3s ease-in-out}.message-card{display:flex;flex-wrap:wrap;justify-content:start;align-items:flex-start}.message-card .avatar-row{width:300px;height:32px;display:flex;flex-direction:row-reverse;justify-content:start;align-items:center}.message-card .actions{display:flex;opacity:0;padding-left:10px;padding-right:10px}.message-card .item-row{display:flex;flex:100%;justify-content:start;margin-bottom:5px}.message-card .images img{max-width:300px}.message-card .bubble{display:flex;justify-content:center;max-width:100%;padding:.5rem .5rem 0rem;color:#333;flex:none;border-radius:0 1.5em 1.5em/0em 1.5em 1.5em;color:#fff;background-color:currentColor}.message-card .bubble .content-style{filter:grayscale(1) contrast(999) invert(1)}.message-card .loading{text-align:right;color:#101010}.message-card .created{display:flex}.message-card .created span{font-size:12px;opacity:.4;white-space:nowrap;transition:all .6s ease;color:var(--black);text-align:center;width:100%;box-sizing:border-box;padding-right:10px;pointer-events:none;z-index:1}.right{justify-content:end;align-items:flex-end}.right .avatar-row{flex-direction:row;justify-content:end;width:auto}.right .actions{position:relative;margin-left:0}.right .item-row{justify-content:end}.right .bubble{color:#bbdefb;border-top-left-radius:1.5em;border-top-right-radius:0}.right .play-voice{flex-direction:row-reverse;background-color:#bbdefb}.center{justify-content:center;align-items:center}.center .item-row{justify-content:center}.center .bubble{color:var(--gray-secondary);border-top-left-radius:1.5em;border-top-right-radius:1.5em;font-size:12px;font-weight:100;padding:5px 20px}.play-voice{min-width:100px;height:32px;display:flex;justify-content:space-around;align-items:center;background-color:#fff;border-radius:7px}.play-voice .voice-button{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.play-voice .voice-button span{overflow:hidden;font-size:18px;color:#ff69b4}.play-voice .voice-info{height:32px;display:flex;padding:0 10px;justify-content:end;align-items:center;color:#333}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}.audio-icon{color:#ff69b4;font-size:18px}.play-voice-playing{animation:play-voice-animation 1s infinite}@keyframes play-voice-animation{0%{width:0}to{width:32px}}\n'],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"directive",type:i2.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"pipe",type:i2.DatePipe,name:"date"},{kind:"component",type:CompUserAvatarComponent,selector:"app-comp-user-avatar",inputs:["user"]},{kind:"ngmodule",type:MarkdownPreviewModule},{kind:"component",type:i3.MarkdownPreviewComponent,selector:"fm-markdown-preview",inputs:["content","render"]},{kind:"pipe",type:ChatContentPipe,name:"chatContent"},{kind:"pipe",type:DurationStrPipe,name:"durationStr"}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatMessageCard,decorators:[{type:Component,args:[{selector:"fm-chat-message-card",standalone:!0,imports:[CommonModule,IonItem,CompUserAvatarComponent,MarkdownPreviewModule,IonAvatar,ChatContentPipe,NzSanitizerPipe,DurationStrPipe],template:'<div class="message-card" [class.right]="message?.role==\'user\'" [class.center]="message?.role==\'system\'">\n \x3c!-- 用户及操作区 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'"> \x3c!-- 系统消息不显示头像 --\x3e\n <div class="avatar-row">\n <div class="actions">\n \x3c!-- 刷新 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="refresh-outline"></ion-icon> \n </ion-button> --\x3e\n \x3c!-- 复制 --\x3e\n <ion-button size="small" fill="outline" slot="start" (click)="copy()">\n <ion-icon name="copy-outline"></ion-icon>\n </ion-button>\n \x3c!-- 编辑 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="create-outline"></ion-icon>\n </ion-button> --\x3e\n </div>\n \x3c!-- 音频消息区域 --\x3e\n <div *ngIf="((message?.role==\'assistant\' && chat?.role?.get(\'voiceConfig\')?.voice) || (message?.role==\'user\'&&message?.voice))" class="play-voice" (click)="toggleVoicePlay()">\n <div class="voice-button">\n <ion-icon name="wifi-outline"\n [style.transform]="message?.role==\'user\'?\'rotate(-90deg)\':\'rotate(90deg)\'"\n class="audio-icon" [class.play-voice-playing]="tts?.isPlaying"></ion-icon>\n \x3c!-- <span nz-icon [nzRotate]="message?.role==\'user\'?-90:90" nzType="wifi" nzTheme="outline" [class.play-voice-playing]="tts?.isPlaying"></span> --\x3e\n </div>\n <div class="voice-info">\n <span *ngIf="message?.voice?.duration">\n {{((message?.voice?.duration||0)/1000) | durationStr}}\n </span>\n <span *ngIf="!message?.voice?.duration">\n \x3c!-- --\x3e\n </span>\n </div>\n </div>\n \x3c!-- 头像区域 --\x3e \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/100/h/100\'" >\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="item-row images" *ngIf="message?.content | chatContent:\'image_url\'">\n <img [src]="message?.content | chatContent:\'image_url\'" alt="">\n </div>\n \x3c!-- 聊天气泡 --\x3e\n <div class="item-row bubble" [style.fontSize]="role?.get(\'uiConfig\')?.message?.bubble?.fontSize || \'0.8rem\'">\n <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content | chatContent" [render]="false"></fm-markdown-preview>\n <fm-markdown-preview *ngIf="message?.complete" [content]="message?.content | chatContent"></fm-markdown-preview>\n </div>\n \x3c!-- 时间显示 --\x3e\n <div class="item-row loading" *ngIf="message?.role!=\'system\' && !message?.complete">\n 正在输入<ion-spinner name="dots"></ion-spinner>\n </div>\n\n <div class="item-row created" *ngIf="message?.createdAt">\n <span>{{message?.createdAt | date:"dd/MM/yy HH:mm"}}</span>\n </div>\n</div>',styles:['@charset "UTF-8";:host-context(body.dark) .message-card .actions .item-native{background:none!important}:host-context(body.dark) .message-card .bubble{color:#0e101d}:host-context(body.dark) .message-card .bubble .content-style{filter:invert(1)!important}:host-context(body.dark) .message-card .bubble fm-markdown-preview{filter:invert(1)!important}:host-context(body.dark) .message-card .play-voice{background-color:#0e101d}:host-context(body.dark) .message-card .play-voice .voice-info{color:#fff}:host-context(body.dark) .message-card .right .bubble{color:#921f8a!important;background:#921f8a!important}:host-context(body.dark) .message-card .right .play-voice{background:#921f8a!important}:host-context(body.dark) .message-card .created span{color:#fff}@media screen and (max-width: 800px){.message-card:focus .actions{opacity:1!important}}.message-card:hover .actions{opacity:1;transition:opacity .3s ease-in-out}.message-card{display:flex;flex-wrap:wrap;justify-content:start;align-items:flex-start}.message-card .avatar-row{width:300px;height:32px;display:flex;flex-direction:row-reverse;justify-content:start;align-items:center}.message-card .actions{display:flex;opacity:0;padding-left:10px;padding-right:10px}.message-card .item-row{display:flex;flex:100%;justify-content:start;margin-bottom:5px}.message-card .images img{max-width:300px}.message-card .bubble{display:flex;justify-content:center;max-width:100%;padding:.5rem .5rem 0rem;color:#333;flex:none;border-radius:0 1.5em 1.5em/0em 1.5em 1.5em;color:#fff;background-color:currentColor}.message-card .bubble .content-style{filter:grayscale(1) contrast(999) invert(1)}.message-card .loading{text-align:right;color:#101010}.message-card .created{display:flex}.message-card .created span{font-size:12px;opacity:.4;white-space:nowrap;transition:all .6s ease;color:var(--black);text-align:center;width:100%;box-sizing:border-box;padding-right:10px;pointer-events:none;z-index:1}.right{justify-content:end;align-items:flex-end}.right .avatar-row{flex-direction:row;justify-content:end;width:auto}.right .actions{position:relative;margin-left:0}.right .item-row{justify-content:end}.right .bubble{color:#bbdefb;border-top-left-radius:1.5em;border-top-right-radius:0}.right .play-voice{flex-direction:row-reverse;background-color:#bbdefb}.center{justify-content:center;align-items:center}.center .item-row{justify-content:center}.center .bubble{color:var(--gray-secondary);border-top-left-radius:1.5em;border-top-right-radius:1.5em;font-size:12px;font-weight:100;padding:5px 20px}.play-voice{min-width:100px;height:32px;display:flex;justify-content:space-around;align-items:center;background-color:#fff;border-radius:7px}.play-voice .voice-button{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.play-voice .voice-button span{overflow:hidden;font-size:18px;color:#ff69b4}.play-voice .voice-info{height:32px;display:flex;padding:0 10px;justify-content:end;align-items:center;color:#333}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}.audio-icon{color:#ff69b4;font-size:18px}.play-voice-playing{animation:play-voice-animation 1s infinite}@keyframes play-voice-animation{0%{width:0}to{width:32px}}\n']}]}],ctorParameters:()=>[{type:i1.ClipboardService}],propDecorators:{index:[{type:Input}],message:[{type:Input}],role:[{type:Input}],chat:[{type:Input}]}});
|
|
8
|
+
import{Component,Input}from"@angular/core";import{ChatContentPipe,FmodeChat,getMessageContentText}from"../../service-fmai/service-chat";import Parse from"parse";import{CommonModule}from"@angular/common";import{IonAvatar,IonItem}from"@ionic/angular/standalone";import{CompUserAvatarComponent}from"../../../user/comp-user-avatar/comp-user-avatar.component";import{ClipboardService}from"../../comp-markdown-preview/clipboard.service";import{MarkdownPreviewModule}from"../../comp-markdown-preview/markdown-preview.module";import{NzSanitizerPipe}from"ng-zorro-antd/pipes";import{DurationStrPipe}from"./duration-str.pipe";import{addIcons}from"ionicons";import{wifiOutline,copyOutline}from"ionicons/icons/index";import*as i0 from"@angular/core";import*as i1 from"../../comp-markdown-preview/clipboard.service";import*as i2 from"@angular/common";import*as i3 from"../../comp-markdown-preview/markdown-preview.component";addIcons({wifiOutline:wifiOutline,copyOutline:copyOutline});export class FmChatMessageCard{constructor(e){this.copyServ=e,this.user=Parse.User.current()}async toggleVoicePlay(){if(this.message?.voice?.id&&this.chat.VoiceTTSMap[this.message?.voice?.id]&&(this.tts=this.chat.VoiceTTSMap[this.message?.voice?.id]),this.tts?.isPlaying)return void this.tts?.stop();let e,t=!1;if(this.message?.voice?.id){let t=new Parse.Query("ChatVoice");t.include("voiceFile"),e=await t.get(this.message?.voice?.id)}if(!e?.id){let n=await this.chat.getVoiceByContentText(this.message?.content);e=this.chat.voiceMap[n?.id],this.message.voice={id:e?.id,duration:e?.get("duration")},t=!0}this.message?.voice?.duration||(this.message.voice.duration=e?.get("duration"),t=!0),t&&this.saveSession(),await this.chat.playChatVoice(e,{onStart:t=>{e?.id,t?.id},onLoaded:e=>{this.message.voice.duration=1e3*e.duration,this.updateVoiceDuration(1e3*e.duration)},onStop:t=>{e?.id,t?.id}}),this.tts=this.chat.VoiceTTSMap[e?.id]}updateVoiceDuration(e){this.message?.voice?.duration||(this.message.voice.duration=e,this.saveSession())}saveSession(){(this.index>=2||void 0===this.index)&&this.chat?.saveChatSession()}async copy(){this.copyServ.copyToClipboard(getMessageContentText(this.message?.content))}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatMessageCard,deps:[{token:i1.ClipboardService}],target:i0.ɵɵFactoryTarget.Component})}static{this.ɵcmp=i0.ɵɵngDeclareComponent({minVersion:"14.0.0",version:"17.3.12",type:FmChatMessageCard,isStandalone:!0,selector:"fm-chat-message-card",inputs:{index:"index",message:"message",role:"role",chat:"chat"},ngImport:i0,template:'<div class="message-card" [class.right]="message?.role==\'user\'" [class.center]="message?.role==\'system\'">\n \x3c!-- 用户及操作区 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'"> \x3c!-- 系统消息不显示头像 --\x3e\n <div class="avatar-row">\n <div class="actions">\n \x3c!-- 刷新 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="refresh-outline"></ion-icon> \n </ion-button> --\x3e\n \x3c!-- 复制 --\x3e\n <ion-button size="small" fill="outline" slot="start" (click)="copy()">\n <ion-icon name="copy-outline"></ion-icon>\n </ion-button>\n \x3c!-- 编辑 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="create-outline"></ion-icon>\n </ion-button> --\x3e\n </div>\n \x3c!-- 音频消息区域 --\x3e\n <div *ngIf="((message?.role==\'assistant\' && chat?.role?.get(\'voiceConfig\')?.voice) || (message?.role==\'user\'&&message?.voice))" class="play-voice" (click)="toggleVoicePlay()">\n <div class="voice-button">\n <ion-icon name="wifi-outline"\n [style.transform]="message?.role==\'user\'?\'rotate(-90deg)\':\'rotate(90deg)\'"\n class="audio-icon" [class.play-voice-playing]="tts?.isPlaying"></ion-icon>\n \x3c!-- <span nz-icon [nzRotate]="message?.role==\'user\'?-90:90" nzType="wifi" nzTheme="outline" [class.play-voice-playing]="tts?.isPlaying"></span> --\x3e\n </div>\n <div class="voice-info">\n <span *ngIf="message?.voice?.duration">\n {{((message?.voice?.duration||0)/1000) | durationStr}}\n </span>\n <span *ngIf="!message?.voice?.duration">\n \x3c!-- --\x3e\n </span>\n </div>\n </div>\n \x3c!-- 头像区域 --\x3e \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/100/h/100\'" >\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="item-row images" *ngIf="message?.content | chatContent:\'image_url\'">\n <img [src]="message?.content | chatContent:\'image_url\'" alt="">\n </div>\n \x3c!-- 聊天气泡 --\x3e\n <div class="item-row bubble" [style.fontSize]="role?.get(\'uiConfig\')?.message?.bubble?.fontSize || \'0.8rem\'">\n <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content | chatContent" [render]="false"></fm-markdown-preview>\n <fm-markdown-preview *ngIf="message?.complete" [content]="message?.content | chatContent"></fm-markdown-preview>\n </div>\n \x3c!-- 时间显示 --\x3e\n <div class="item-row loading" *ngIf="message?.role!=\'system\' && !message?.complete">\n 正在输入<ion-spinner name="dots"></ion-spinner>\n </div>\n\n <div class="item-row created" *ngIf="message?.createdAt">\n <span>{{message?.createdAt | date:"dd/MM/yy HH:mm"}}</span>\n </div>\n</div>',styles:['@charset "UTF-8";:host-context(body.dark) .message-card .actions .item-native{background:none!important}:host-context(body.dark) .message-card .bubble{color:#0e101d}:host-context(body.dark) .message-card .bubble .content-style{filter:invert(1)!important}:host-context(body.dark) .message-card .bubble fm-markdown-preview{filter:invert(1)!important}:host-context(body.dark) .message-card .play-voice{background-color:#0e101d}:host-context(body.dark) .message-card .play-voice .voice-info{color:#fff}:host-context(body.dark) .message-card .right .bubble{color:#921f8a!important;background:#921f8a!important}:host-context(body.dark) .message-card .right .play-voice{background:#921f8a!important}:host-context(body.dark) .message-card .created span{color:#fff}@media screen and (max-width: 800px){.message-card:focus .actions{opacity:1!important}}.message-card:hover .actions{opacity:1;transition:opacity .3s ease-in-out}.message-card{display:flex;flex-wrap:wrap;justify-content:start;align-items:flex-start}.message-card .avatar-row{width:300px;height:32px;display:flex;flex-direction:row-reverse;justify-content:start;align-items:center}.message-card .actions{display:flex;opacity:0;padding-left:10px;padding-right:10px}.message-card .item-row{display:flex;flex:100%;justify-content:start;margin-bottom:5px}.message-card .images img{max-width:300px}.message-card .bubble{display:flex;justify-content:center;max-width:100%;padding:.5rem .5rem 0rem;color:#333;flex:none;border-radius:0 1.5em 1.5em/0em 1.5em 1.5em;color:#fff;background-color:currentColor}.message-card .bubble .content-style{filter:grayscale(1) contrast(999) invert(1)}.message-card .loading{text-align:right;color:#101010}.message-card .created{display:flex}.message-card .created span{font-size:12px;opacity:.4;white-space:nowrap;transition:all .6s ease;color:var(--black);text-align:center;width:100%;box-sizing:border-box;padding-right:10px;pointer-events:none;z-index:1}.right{justify-content:end;align-items:flex-end}.right .avatar-row{flex-direction:row;justify-content:end;width:auto}.right .actions{position:relative;margin-left:0}.right .item-row{justify-content:end}.right .bubble{color:#bbdefb;border-top-left-radius:1.5em;border-top-right-radius:0}.right .play-voice{flex-direction:row-reverse;background-color:#bbdefb}.center{justify-content:center;align-items:center}.center .item-row{justify-content:center}.center .bubble{color:var(--gray-secondary);border-top-left-radius:1.5em;border-top-right-radius:1.5em;font-size:12px;font-weight:100;padding:5px 20px}.play-voice{min-width:100px;height:32px;display:flex;justify-content:space-around;align-items:center;background-color:#fff;border-radius:7px}.play-voice .voice-button{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.play-voice .voice-button span{overflow:hidden;font-size:18px;color:#ff69b4}.play-voice .voice-info{height:32px;display:flex;padding:0 10px;justify-content:end;align-items:center;color:#333}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}.audio-icon{color:#ff69b4;font-size:18px}.play-voice-playing{animation:play-voice-animation 1s infinite}@keyframes play-voice-animation{0%{width:0}to{width:32px}}\n'],dependencies:[{kind:"ngmodule",type:CommonModule},{kind:"directive",type:i2.NgIf,selector:"[ngIf]",inputs:["ngIf","ngIfThen","ngIfElse"]},{kind:"pipe",type:i2.DatePipe,name:"date"},{kind:"component",type:CompUserAvatarComponent,selector:"app-comp-user-avatar",inputs:["user"]},{kind:"ngmodule",type:MarkdownPreviewModule},{kind:"component",type:i3.MarkdownPreviewComponent,selector:"fm-markdown-preview",inputs:["content","render"]},{kind:"pipe",type:ChatContentPipe,name:"chatContent"},{kind:"pipe",type:DurationStrPipe,name:"durationStr"}]})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmChatMessageCard,decorators:[{type:Component,args:[{selector:"fm-chat-message-card",standalone:!0,imports:[CommonModule,IonItem,CompUserAvatarComponent,MarkdownPreviewModule,IonAvatar,ChatContentPipe,NzSanitizerPipe,DurationStrPipe],template:'<div class="message-card" [class.right]="message?.role==\'user\'" [class.center]="message?.role==\'system\'">\n \x3c!-- 用户及操作区 --\x3e\n <div class="item-row user" *ngIf="message?.role!=\'system\'"> \x3c!-- 系统消息不显示头像 --\x3e\n <div class="avatar-row">\n <div class="actions">\n \x3c!-- 刷新 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="refresh-outline"></ion-icon> \n </ion-button> --\x3e\n \x3c!-- 复制 --\x3e\n <ion-button size="small" fill="outline" slot="start" (click)="copy()">\n <ion-icon name="copy-outline"></ion-icon>\n </ion-button>\n \x3c!-- 编辑 --\x3e\n \x3c!-- <ion-button fill="outline" slot="start">\n <ion-icon name="create-outline"></ion-icon>\n </ion-button> --\x3e\n </div>\n \x3c!-- 音频消息区域 --\x3e\n <div *ngIf="((message?.role==\'assistant\' && chat?.role?.get(\'voiceConfig\')?.voice) || (message?.role==\'user\'&&message?.voice))" class="play-voice" (click)="toggleVoicePlay()">\n <div class="voice-button">\n <ion-icon name="wifi-outline"\n [style.transform]="message?.role==\'user\'?\'rotate(-90deg)\':\'rotate(90deg)\'"\n class="audio-icon" [class.play-voice-playing]="tts?.isPlaying"></ion-icon>\n \x3c!-- <span nz-icon [nzRotate]="message?.role==\'user\'?-90:90" nzType="wifi" nzTheme="outline" [class.play-voice-playing]="tts?.isPlaying"></span> --\x3e\n </div>\n <div class="voice-info">\n <span *ngIf="message?.voice?.duration">\n {{((message?.voice?.duration||0)/1000) | durationStr}}\n </span>\n <span *ngIf="!message?.voice?.duration">\n \x3c!-- --\x3e\n </span>\n </div>\n </div>\n \x3c!-- 头像区域 --\x3e \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/100/h/100\'" >\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="item-row images" *ngIf="message?.content | chatContent:\'image_url\'">\n <img [src]="message?.content | chatContent:\'image_url\'" alt="">\n </div>\n \x3c!-- 聊天气泡 --\x3e\n <div class="item-row bubble" [style.fontSize]="role?.get(\'uiConfig\')?.message?.bubble?.fontSize || \'0.8rem\'">\n <fm-markdown-preview *ngIf="!message?.complete" class="content-style" [content]="message?.content | chatContent" [render]="false"></fm-markdown-preview>\n <fm-markdown-preview *ngIf="message?.complete" [content]="message?.content | chatContent"></fm-markdown-preview>\n </div>\n \x3c!-- 时间显示 --\x3e\n <div class="item-row loading" *ngIf="message?.role!=\'system\' && !message?.complete">\n 正在输入<ion-spinner name="dots"></ion-spinner>\n </div>\n\n <div class="item-row created" *ngIf="message?.createdAt">\n <span>{{message?.createdAt | date:"dd/MM/yy HH:mm"}}</span>\n </div>\n</div>',styles:['@charset "UTF-8";:host-context(body.dark) .message-card .actions .item-native{background:none!important}:host-context(body.dark) .message-card .bubble{color:#0e101d}:host-context(body.dark) .message-card .bubble .content-style{filter:invert(1)!important}:host-context(body.dark) .message-card .bubble fm-markdown-preview{filter:invert(1)!important}:host-context(body.dark) .message-card .play-voice{background-color:#0e101d}:host-context(body.dark) .message-card .play-voice .voice-info{color:#fff}:host-context(body.dark) .message-card .right .bubble{color:#921f8a!important;background:#921f8a!important}:host-context(body.dark) .message-card .right .play-voice{background:#921f8a!important}:host-context(body.dark) .message-card .created span{color:#fff}@media screen and (max-width: 800px){.message-card:focus .actions{opacity:1!important}}.message-card:hover .actions{opacity:1;transition:opacity .3s ease-in-out}.message-card{display:flex;flex-wrap:wrap;justify-content:start;align-items:flex-start}.message-card .avatar-row{width:300px;height:32px;display:flex;flex-direction:row-reverse;justify-content:start;align-items:center}.message-card .actions{display:flex;opacity:0;padding-left:10px;padding-right:10px}.message-card .item-row{display:flex;flex:100%;justify-content:start;margin-bottom:5px}.message-card .images img{max-width:300px}.message-card .bubble{display:flex;justify-content:center;max-width:100%;padding:.5rem .5rem 0rem;color:#333;flex:none;border-radius:0 1.5em 1.5em/0em 1.5em 1.5em;color:#fff;background-color:currentColor}.message-card .bubble .content-style{filter:grayscale(1) contrast(999) invert(1)}.message-card .loading{text-align:right;color:#101010}.message-card .created{display:flex}.message-card .created span{font-size:12px;opacity:.4;white-space:nowrap;transition:all .6s ease;color:var(--black);text-align:center;width:100%;box-sizing:border-box;padding-right:10px;pointer-events:none;z-index:1}.right{justify-content:end;align-items:flex-end}.right .avatar-row{flex-direction:row;justify-content:end;width:auto}.right .actions{position:relative;margin-left:0}.right .item-row{justify-content:end}.right .bubble{color:#bbdefb;border-top-left-radius:1.5em;border-top-right-radius:0}.right .play-voice{flex-direction:row-reverse;background-color:#bbdefb}.center{justify-content:center;align-items:center}.center .item-row{justify-content:center}.center .bubble{color:var(--gray-secondary);border-top-left-radius:1.5em;border-top-right-radius:1.5em;font-size:12px;font-weight:100;padding:5px 20px}.play-voice{min-width:100px;height:32px;display:flex;justify-content:space-around;align-items:center;background-color:#fff;border-radius:7px}.play-voice .voice-button{width:32px;height:32px;display:flex;justify-content:center;align-items:center}.play-voice .voice-button span{overflow:hidden;font-size:18px;color:#ff69b4}.play-voice .voice-info{height:32px;display:flex;padding:0 10px;justify-content:end;align-items:center;color:#333}.avatar{border-radius:50%;width:32px;height:32px;object-fit:cover}.audio-icon{color:#ff69b4;font-size:18px}.play-voice-playing{animation:play-voice-animation 1s infinite}@keyframes play-voice-animation{0%{width:0}to{width:32px}}\n']}]}],ctorParameters:()=>[{type:i1.ClipboardService}],propDecorators:{index:[{type:Input}],message:[{type:Input}],role:[{type:Input}],chat:[{type:Input}]}});
|
|
9
9
|
var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL2NoYXQvY2hhdC1tZXNzYWdlLWNhcmQvY29tcC1tZXNzYWdlLWNhcmQuY29tcG9uZW50Lm1qcw==`
|
|
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/chat-modal-input/modal-input.component.mjs
|
|
7
7
|
*/
|
|
8
|
-
import{Component,Input,ViewChild}from"@angular/core";import{ActivatedRoute,Router,RouterModule}from"@angular/router";import{AlertController,NavController,ToastController}from"@ionic/angular";import{FmodeChat}from"../../service-fmai/service-chat";import{ChatService}from"../../service-fmai/service-chat";import Parse from"parse";import{NzMessageService}from"ng-zorro-antd/message";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*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"ng-zorro-antd/message";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(){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,r,l){this.toastCtrl=e,this.alertCtrl=n,this.modalCtrl=t,this.navCtrl=o,this.router=i,this.imagineServ=a,this.chatServ=s,this.route=r,this.messages=l,this.errorText="",this.isAudioModal=!1,this.audioModalHeightPoint=.35,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.chat.userInput){return this.errorText="内容不能为空",void(await this.toastCtrl.create({message:this.errorText,position:"top",icon:"alert",color:"warning-circle",duration:1e3})).present()}this.chat?.sendMessage(this.chat?.userInput,this.chat?.userImage,(e=>{}),{onSSMLComplete:e=>{console.log(e)}}),this.chat.userInput="",this.chat.userImage=""}async checkBalance(){if(!this.chat?.currentModel?.get("payLimit"))return!0;let e=await this.account.getBilling();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()}chatShareSuccessMessage(){this.messages.success("分享成功")}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:i1.NavController},{token:i3.Router},{token:i4.ImagineService},{token:i5.ChatService},{token:i3.ActivatedRoute},{token:i6.NzMessageService}],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 \x3c!-- <button nz-button *ngIf="chat?.messageList?.length>1"\n nzType="primary" title="分享" (click)="showShare()" style="width: 31.188px;height: 22px;box-shadow: none;background: #fff;border: 1px solid #4588ff;border-radius: 5px;"> <span class="iconfont icon-fenxiang" style="color: #4588ff;"></span></button>\n --\x3e\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 \x3c!-- <nz-avatar *ngIf="message?.role!=\'user\'" [nzSrc]="(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\'"></nz-avatar> --\x3e\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")||"飞码4.0"}}\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()">\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 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}.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 \x3c!-- <button nz-button *ngIf="chat?.messageList?.length>1"\n nzType="primary" title="分享" (click)="showShare()" style="width: 31.188px;height: 22px;box-shadow: none;background: #fff;border: 1px solid #4588ff;border-radius: 5px;"> <span class="iconfont icon-fenxiang" style="color: #4588ff;"></span></button>\n --\x3e\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 \x3c!-- <nz-avatar *ngIf="message?.role!=\'user\'" [nzSrc]="(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\'"></nz-avatar> --\x3e\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")||"飞码4.0"}}\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()">\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 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}.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:i1.NavController},{type:i3.Router},{type:i4.ImagineService},{type:i5.ChatService},{type:i3.ActivatedRoute},{type:i6.NzMessageService}],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{ActivatedRoute,Router,RouterModule}from"@angular/router";import{AlertController,NavController,ToastController}from"@ionic/angular";import{FmodeChat}from"../../service-fmai/service-chat";import{ChatService}from"../../service-fmai/service-chat";import Parse from"parse";import{NzMessageService}from"ng-zorro-antd/message";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/index";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"ng-zorro-antd/message";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(){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,r,l){this.toastCtrl=e,this.alertCtrl=n,this.modalCtrl=t,this.navCtrl=o,this.router=i,this.imagineServ=a,this.chatServ=s,this.route=r,this.messages=l,this.errorText="",this.isAudioModal=!1,this.audioModalHeightPoint=.35,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.chat.userInput){return this.errorText="内容不能为空",void(await this.toastCtrl.create({message:this.errorText,position:"top",icon:"alert",color:"warning-circle",duration:1e3})).present()}this.chat?.sendMessage(this.chat?.userInput,this.chat?.userImage,(e=>{}),{onSSMLComplete:e=>{console.log(e)}}),this.chat.userInput="",this.chat.userImage=""}async checkBalance(){if(!this.chat?.currentModel?.get("payLimit"))return!0;let e=await this.account.getBilling();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()}chatShareSuccessMessage(){this.messages.success("分享成功")}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:i1.NavController},{token:i3.Router},{token:i4.ImagineService},{token:i5.ChatService},{token:i3.ActivatedRoute},{token:i6.NzMessageService}],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 \x3c!-- <button nz-button *ngIf="chat?.messageList?.length>1"\n nzType="primary" title="分享" (click)="showShare()" style="width: 31.188px;height: 22px;box-shadow: none;background: #fff;border: 1px solid #4588ff;border-radius: 5px;"> <span class="iconfont icon-fenxiang" style="color: #4588ff;"></span></button>\n --\x3e\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 \x3c!-- <nz-avatar *ngIf="message?.role!=\'user\'" [nzSrc]="(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\'"></nz-avatar> --\x3e\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")||"飞码4.0"}}\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()">\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 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}.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 \x3c!-- <button nz-button *ngIf="chat?.messageList?.length>1"\n nzType="primary" title="分享" (click)="showShare()" style="width: 31.188px;height: 22px;box-shadow: none;background: #fff;border: 1px solid #4588ff;border-radius: 5px;"> <span class="iconfont icon-fenxiang" style="color: #4588ff;"></span></button>\n --\x3e\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 \x3c!-- <nz-avatar *ngIf="message?.role!=\'user\'" [nzSrc]="(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\'"></nz-avatar> --\x3e\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")||"飞码4.0"}}\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()">\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 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}.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:i1.NavController},{type:i3.Router},{type:i4.ImagineService},{type:i5.ChatService},{type:i3.ActivatedRoute},{type:i6.NzMessageService}],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/voice/fmode-voice.service.mjs
|
|
7
7
|
*/
|
|
8
|
-
import{Injectable}from"@angular/core";import Recorder from"recorder-core";import"recorder
|
|
8
|
+
import{Injectable}from"@angular/core";import Recorder from"recorder-core";import"./lib/recorder/engine-pcm";import"./lib/recorder/engine-wav";import"./lib/recorder/extension-waveview";import CryptoJS from"crypto-js";import{pcmtoWav}from"./lib/pcm2wav";import{convertFrameBufferToBase64,resampleBuffer}from"./lib/resample";import{WebSpeech}from"./class-asr";import{Platform}from"@ionic/angular";import{DiagnosticOriginal}from"@awesome-cordova-plugins/diagnostic";import*as i0 from"@angular/core";import*as i1 from"@ionic/angular";import*as i2 from"@awesome-cordova-plugins/diagnostic";export class FmodeVoiceService{constructor(t,e){this.platform=t,this.diagnostic=e,this.webSpeech=WebSpeech,this.isUserFinish=!1,this.recordWavBlob=null,this.recordPcmBlob=null,this.recordDuration=0,this.recordType="pcm",this.encodingType="raw",this.connStatus="",this.btnStatus="UNDEFINED",this.resultText="",this.resultTextTemp="",this.APPID="50f4a46c",this.API_SECRET="NzFlNmFhZDJjMDNkZGM3NzI0Mzg2OGNm",this.API_KEY="106ddc40dfd4b9ca6d7b47c70fada749",this.durationStr="00:00",this.duration=0,this.requestPermission()}toggleRecord(){console.log(this.btnStatus),"UNDEFINED"===this.btnStatus||"CLOSED"===this.btnStatus?this.startTalk():"CONNECTING"!==this.btnStatus&&"OPEN"!==this.btnStatus||this.finishTalk()}finishTalk(){this.isUserFinish=!0,this.onBeforeFinishTalk&&this.onBeforeFinishTalk(),this.recordStop()}async startTalk(t){this.resultText="",this.resultTextTemp="",this.onBeforeStartTalk&&this.onBeforeStartTalk(),event?.preventDefault(),await this.openWithPriviledge(),setTimeout((()=>{this.connectWebSocket()}),100),this.startCountdown(),this.onAfterStartTalk&&this.onAfterStartTalk()}cancelTalk(){this.onBeforeCancelTalk&&this.onBeforeCancelTalk(),this.recordStop(),this.iatWS?.close(),this.resultText=null,this.onAfterCancelTalk&&this.onAfterCancelTalk()}async recordStart(){this.createRecorder(),await this.openWithPriviledge(),this.recorder.start(),this.changeBtnStatus("OPEN"),this.onAfterRecordStart&&this.onAfterRecordStart()}recordStop(){return new Promise((t=>{clearInterval(this.countdownInterval),this.changeBtnStatus("CLOSED"),this.recorder?.stop((async(e,i)=>{try{this.iatWS.send(JSON.stringify({data:{status:2,format:"audio/L16;rate=16000",encoding:this.encodingType,audio:""}}))}catch(t){}let s=(window.URL||webkitURL).createObjectURL(e);console.log(e,s,"时长:"+i+"ms"),this.recordPcmBlob=e,this.recordWavBlob=await this.pcmBlobToWavBlob(e,44100),console.log("this.recordWavBlob",this.recordWavBlob),setTimeout((()=>{this.isUserFinish&&(this.onAfterFinishTalk&&this.onAfterFinishTalk(),this.isUserFinish=!1)}),2e3),this.recorder?.close(),this.recorder=null,console.log("localUrl",s),t(!0)}),(e=>{console.log("录音失败:"+e),this.recorder.close(),this.recorder=null,t(null)}))}))}playRecord(){this.playPCM(this.recordPcmBlob,44100)}async pcmBlobToWavBlob(t,e){return new Promise((i=>{let s=new FileReader;s.onload=function(t){let s=t.target.result,o=pcmtoWav(s,e,1,16);i(o)},s.readAsArrayBuffer(t)}))}async playPCM(t,e){let i=await this.pcmBlobToWavBlob(t,e),s=window.URL.createObjectURL(i),o=new Audio;o.src=s,o.play()}async playBuffers(){let t=await this.BuffersToBlob(this.buffers);this.playPCM(t,44100)}BuffersToBlob(t){let e=[];return t.forEach((t=>{t.forEach((t=>{e.push(t)}))})),new Blob([e],{type:"audio/pcm"})}splitAudioData(t){const e=1280,i=Math.ceil(t.length/e),s=[];for(let o=0;o<i;o++){const i=o*e,r=i+e,a=t.slice(i,r);s.push(a)}return s}BufferToBlob(t){return new Blob([t],{type:"audio/pcm"})}createRecorder(){this.recorder||(this.recorder=Recorder({type:this.recordType,sampleRate:44100,bitRate:16,onProcess:(t,e,i,s,o,r)=>{let a=t.length&&t[t.length-1];if(this.buffers=t,a=resampleBuffer(a,44100,16e3),this.iatWS.readyState===this.iatWS.OPEN){if(this.disableASR)return;this.iatWS.send(JSON.stringify({data:{status:1,format:"audio/L16;rate=16000",encoding:this.encodingType,audio:convertFrameBufferToBase64(a)}}))}this.waveClient?.input(t[t.length-1],e,s)}}))}async openWithPriviledge(){return console.log(this.btnStatus),await this.requestPermission(),this.createRecorder(),!!Recorder.IsOpen()||new Promise((t=>{this.recorder.open((()=>{let e=document.querySelector(".record-wave");e&&(console.log(e),Recorder.WaveView&&(this.waveClient=Recorder.WaveView({elem:".record-wave"}))),t(!0)}),((t,e)=>{console.log((e?"UserNotAllow,":"")+"无法录音:"+t)}))}))}getWebSocketUrl(){let t="wss://iat-api.xfyun.cn/v2/iat",e="iat-api.xfyun.cn",i=this.API_KEY,s=this.API_SECRET,o=(new Date).toUTCString(),r=`host: ${e}\ndate: ${o}\nGET /v2/iat HTTP/1.1`,a=CryptoJS.HmacSHA256(r,s),n=CryptoJS.enc.Base64.stringify(a);return t=`${t}?authorization=${btoa(`api_key="${i}", algorithm="hmac-sha256", headers="host date request-line", signature="${n}"`)}&date=${o}&host=${e}`,t}toBase64(t){for(var e="",i=new Uint8Array(t),s=i.byteLength,o=0;o<s;o++)e+=String.fromCharCode(i[o]);return window.btoa(e)}countTimer(){this.duration++;let t=String(parseInt(String(this.duration/60))).padStart(2,"0"),e=String((this.duration%60).toFixed(0)).padStart(2,"0"),i=t+":"+e;this.durationStr=i,this.connStatus=`录音中(${this.durationStr})`,console.log(this.duration,t,e),console.log(this.duration),console.log(i),this.onDurationStrChange&&this.onDurationStrChange(i)}startCountdown(){this.recordDuration=0,this.durationInterval&&clearInterval(this.durationInterval),this.countdownInterval=setInterval((()=>{this.recordDuration+=100}),100),this.now=new Date,this.duration=0,this.countdownInterval&&clearInterval(this.countdownInterval),this.countdownInterval=setInterval((()=>{this.countTimer()}),1e3)}changeBtnStatus(t){this.btnStatus=t,"CONNECTING"===t?this.connStatus="建立连接中":"OPEN"===t||("CLOSING"===t?this.connStatus="关闭连接中":"CLOSED"===t&&(this.connStatus="开始录音"))}renderResult(t){let e=JSON.parse(t);if(e.data&&e.data.result){let t=e.data.result,i="",s=t.ws;for(let t=0;t<s.length;t++)i+=s[t].cw[0].w,console.log(i);t.pgs?("apd"===t.pgs&&(this.resultText=this.resultTextTemp),this.resultTextTemp=this.resultText+i):this.resultText=this.resultText+i,this.resultTextTemp||this.resultText,console.log("diff temp",this.resultTextTemp),console.log("diff result",this.resultText),this.onInputChange&&this.onInputChange(this.getUserInput())}0===e.code&&2===e.data.status&&this.iatWS.close(),0!==e.code&&(this.iatWS.close(),console.error(e))}getUserInput(){return""+(this.resultTextTemp||this.resultText)}connectWebSocket(){console.log("connectWebSocket");const t=this.getWebSocketUrl();if("WebSocket"in window)this.iatWS=new WebSocket(t);else if(!("MozWebSocket"in window))return void alert("浏览器不支持WebSocket");console.log("connectWebSocket",this.btnStatus),this.changeBtnStatus("CONNECTING"),this.iatWS.onopen=t=>{this.recordStart();var e={common:{app_id:this.APPID},business:{language:"zh_cn",domain:"iat",accent:"mandarin",vad_eos:5e3,dwa:"wpgs"},data:{status:0,format:"audio/L16;rate=16000",encoding:this.encodingType}};this.iatWS.send(JSON.stringify(e))},this.iatWS.onmessage=t=>{console.log("onmessage"+this.resultText),this.renderResult(t.data)},this.iatWS.onerror=t=>{console.error("error",t),this.recordStop(),this.changeBtnStatus("CLOSED")},this.iatWS.onclose=async t=>{console.log("onclose"+this.resultText),this.reconnectWebsocket()}}async reconnectWebsocket(){this.isUserFinish||this.connectWebSocket()}isCapacitor(){return this.platform.is("capacitor")||this.platform.is("cordova")}async requestPermission(){if(this.isCapacitor())try{await this.requestStoagePermission(),await this.requestCameraPermission(),await this.requestMicPermission(),await this.requestRecordAudioPermission()}catch(t){console.error(t)}}async requestRecordAudioPermission(){let t=await this.diagnostic.requestRuntimePermissions([this.diagnostic.permission.RECORD_AUDIO]);console.log("record permission request:",t)}async requestMicPermission(){let t=await this.diagnostic.isMicrophoneAuthorized();if(console.log("permisson_MIC:",t),!t){await this.diagnostic.requestMicrophoneAuthorization()}}async requestStoagePermission(){let t=await this.diagnostic.isExternalStorageAuthorized();if(console.log("permisson_STORAGE:",t),!t){await this.diagnostic.requestExternalStorageAuthorization()}}async requestCameraPermission(){let t=await this.diagnostic.isCameraAuthorized();if(console.log("permisson_Camera:",t),!t){await this.diagnostic.requestCameraAuthorization()}}static{this.ɵfac=i0.ɵɵngDeclareFactory({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmodeVoiceService,deps:[{token:i1.Platform},{token:i2.DiagnosticOriginal}],target:i0.ɵɵFactoryTarget.Injectable})}static{this.ɵprov=i0.ɵɵngDeclareInjectable({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmodeVoiceService,providedIn:"root"})}}i0.ɵɵngDeclareClassMetadata({minVersion:"12.0.0",version:"17.3.12",ngImport:i0,type:FmodeVoiceService,decorators:[{type:Injectable,args:[{providedIn:"root"}]}],ctorParameters:()=>[{type:i1.Platform},{type:i2.DiagnosticOriginal}]});
|
|
9
9
|
var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL3ZvaWNlL2Ztb2RlLXZvaWNlLnNlcnZpY2UubWpz`
|
|
10
10
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @copyright © 未来飞马 © 未来全栈 www.fmode.cn
|
|
4
|
+
* 版权所有 © 未来飞马 © 江西脑控科技有限公司 Copyright © Fmode Technology Co., Ltd.
|
|
5
|
+
* 保留所有权利 All Rights Reserved.
|
|
6
|
+
* /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/voice/lib/recorder/engine-pcm.mjs
|
|
7
|
+
*/
|
|
8
|
+
import Recorder from"recorder-core";!function(){"use strict";Recorder.prototype.enc_pcm={stable:!0,fast:!0,testmsg:"pcm为未封装的原始音频数据,pcm音频文件无法直接播放,可用Recorder.pcm2wav()转码成wav播放;支持位数8位、16位(填在比特率里面),采样率取值无限制"},Recorder.prototype.pcm=function(e,r,t){var a=this.set,o=e.length,n=8==a.bitRate?8:16,c=new ArrayBuffer(o*(n/8)),p=new DataView(c),s=0;if(8==n)for(var l=0;l<o;l++,s++){var i=128+(e[l]>>8);p.setInt8(s,i)}else for(l=0;l<o;l++,s+=2)p.setInt16(s,e[l],!0);r(new Blob([p.buffer],{type:"audio/pcm"}))},Recorder.pcm2wav=function(e,r,t){e.slice&&null!=e.type&&(e={blob:e});var a=e.sampleRate||16e3,o=e.bitRate||16;if(e.sampleRate&&e.bitRate||console.warn("pcm2wav必须提供sampleRate和bitRate"),Recorder.prototype.wav){var n=new FileReader;n.onloadend=function(){var e;let c=n.result;if(8==o){var p=new Uint8Array(c);e=new Int16Array(p.length);for(var s=0;s<p.length;s++)e[s]=p[s]-128<<8}else e=new Int16Array(c);Recorder({type:"wav",sampleRate:a,bitRate:o}).mock(e,a).stop((function(e,t){r(e,t)}),t)},n.readAsArrayBuffer(e.blob)}else t("pcm2wav必须先加载wav编码器wav.js")}}();
|
|
9
|
+
var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL3ZvaWNlL2xpYi9yZWNvcmRlci9lbmdpbmUtcGNtLm1qcw==`
|
|
10
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @copyright © 未来飞马 © 未来全栈 www.fmode.cn
|
|
4
|
+
* 版权所有 © 未来飞马 © 江西脑控科技有限公司 Copyright © Fmode Technology Co., Ltd.
|
|
5
|
+
* 保留所有权利 All Rights Reserved.
|
|
6
|
+
* /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/voice/lib/recorder/engine-wav.mjs
|
|
7
|
+
*/
|
|
8
|
+
import Recorder from"recorder-core";!function(){"use strict";Recorder.prototype.enc_wav={stable:!0,fast:!0,testmsg:"支持位数8位、16位(填在比特率里面),采样率取值无限制;此编码器仅在pcm数据前加了一个44字节的wav头,编码出来的16位wav文件去掉开头的44字节即可得到pcm(注:其他wav编码器可能不是44字节)"},Recorder.prototype.wav=function(t,e,r){var a=this.set,o=t.length,n=a.sampleRate,f=8==a.bitRate?8:16,c=o*(f/8),i=new ArrayBuffer(44+c),s=new DataView(i),v=0,writeString=function(t){for(var e=0;e<t.length;e++,v++)s.setUint8(v,t.charCodeAt(e))},write16=function(t){s.setUint16(v,t,!0),v+=2},write32=function(t){s.setUint32(v,t,!0),v+=4};if(writeString("RIFF"),write32(36+c),writeString("WAVE"),writeString("fmt "),write32(16),write16(1),write16(1),write32(n),write32(n*(f/8)),write16(f/8),write16(f),writeString("data"),write32(c),8==f)for(var w=0;w<o;w++,v++){var p=128+(t[w]>>8);s.setInt8(v,p)}else for(w=0;w<o;w++,v+=2)s.setInt16(v,t[w],!0);e(new Blob([s.buffer],{type:"audio/wav"}))}}();
|
|
9
|
+
var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL3ZvaWNlL2xpYi9yZWNvcmRlci9lbmdpbmUtd2F2Lm1qcw==`
|
|
10
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @copyright © 未来飞马 © 未来全栈 www.fmode.cn
|
|
4
|
+
* 版权所有 © 未来飞马 © 江西脑控科技有限公司 Copyright © Fmode Technology Co., Ltd.
|
|
5
|
+
* 保留所有权利 All Rights Reserved.
|
|
6
|
+
* /home/ryan/workspace/nova/nova-admin/dist/fmode-ng/esm2022/lib/aigc/voice/lib/recorder/extension-waveview.mjs
|
|
7
|
+
*/
|
|
8
|
+
import Recorder from"recorder-core";!function(){var WaveView=function(e){return new fn(e)},e="WaveView",fn=function(t){var r=this,a={scale:2,speed:9,phase:21.8,fps:20,keep:!0,lineWidth:3,linear1:[0,"rgba(150,96,238,1)",.2,"rgba(170,79,249,1)",1,"rgba(53,199,253,1)"],linear2:[0,"rgba(209,130,255,0.6)",1,"rgba(53,199,255,0.6)"],linearBg:[0,"rgba(255,255,255,0.2)",1,"rgba(54,197,252,0.2)"]};for(var i in t)a[i]=t[i];r.set=t=a;var n=t.elem;n&&("string"==typeof n?n=document.querySelector(n):n.length&&(n=n[0])),n&&(t.width=n.offsetWidth,t.height=n.offsetHeight);var o=t.scale,h=t.width*o,l=t.height*o;if(!h||!l)throw new Error(e+"无宽高");var s=r.elem=document.createElement("div"),c=["","transform-origin:0 0;","transform:scale("+1/o+");"];s.innerHTML='<div style="width:'+t.width+"px;height:"+t.height+'px;overflow:hidden"><div style="width:'+h+"px;height:"+l+"px;"+c.join("-webkit-")+c.join("-ms-")+c.join("-moz-")+c.join("")+'"><canvas/></div></div>';var d=r.canvas=s.querySelector("canvas"),p=r.ctx=d.getContext("2d");d.width=h,d.height=l,r.linear1=r.genLinear(p,h,t.linear1),r.linear2=r.genLinear(p,h,t.linear2),r.linearBg=r.genLinear(p,l,t.linearBg,!0),n&&(n.innerHTML="",n.appendChild(s)),r._phase=0};fn.prototype=WaveView.prototype={genLinear:function(e,t,r,a){for(var i=e.createLinearGradient(0,0,a?0:t,a?t:0),n=0;n<r.length;)i.addColorStop(r[n++],r[n++]);return i},genPath:function(e,t,r){for(var a=[],i=this.set,n=i.scale,o=i.width*n,h=i.height*n/2,l=0;l<o;l+=n){var s=(1+Math.cos(Math.PI+l/o*2*Math.PI))/2*h*t*Math.sin(2*Math.PI*(l/o)*e+r)+h;a.push(s)}return a},input:function(e,t,r){var a=this;a.sampleRate=r,a.pcmData=e,a.pcmPos=0,a.inputTime=Date.now(),a.schedule()},schedule:function(){var e=this,t=e.set,r=Math.floor(1e3/t.fps);e.timer||(e.timer=setInterval((function(){e.schedule()}),r));var a=Date.now();if(!(a-(e.drawTime||0)<r)){e.drawTime=a;for(var i=e.sampleRate/t.fps,n=e.pcmData,o=e.pcmPos,h=Math.max(0,Math.min(i,n.length-o)),l=0,s=0;s<h;s++,o++)l+=Math.abs(n[o]);e.pcmPos=o,!h&&t.keep||e.draw(Recorder.PowerLevel(l,h)),!h&&a-e.inputTime>1300&&(clearInterval(e.timer),e.timer=0)}},draw:function(e){var t=this,r=t.set,a=t.ctx,i=r.scale,n=r.width*i,o=r.height*i,h=r.speed/r.fps,l=t._phase-=h,s=l+h*r.phase,c=e/100,d=t.genPath(2,c,l),p=t.genPath(1.8,c,s);a.clearRect(0,0,n,o),a.beginPath();for(var f=0,g=0;g<n;f++,g+=i)0==g?a.moveTo(g,d[f]):a.lineTo(g,d[f]);f--;for(g=n-1;g>=0;f--,g-=i)a.lineTo(g,p[f]);a.closePath(),a.fillStyle=t.linearBg,a.fill(),t.drawPath(p,t.linear2),t.drawPath(d,t.linear1)},drawPath:function(e,t){var r=this.set,a=this.ctx,i=r.scale,n=r.width*i;a.beginPath();for(var o=0,h=0;h<n;o++,h+=i)0==h?a.moveTo(h,e[o]):a.lineTo(h,e[o]);a.lineWidth=r.lineWidth*i,a.strokeStyle=t,a.stroke()}},Recorder[e]=WaveView}();
|
|
9
|
+
var MODULE_PATH_NEED = `6K+l5paH5Lu25piv5pys6aG555uu55qE5LiA6YOo5YiGIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIHRoZSBDb21wb25lbnRzIGluIEZtb2RlIEluYy4KICAgIOeJiOadg+aJgOaciSDCqSDmnKrmnaXpo57pqawgwqkg5rGf6KW/6ISR5o6n56eR5oqA5pyJ6ZmQ5YWs5Y+4IENvcHlyaWdodCDCqSBGbW9kZSBUZWNobm9sb2d5IENvLiwgTHRkLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCBSaWdodHMgUmVzZXJ2ZWQuCiAgICDkuKXnpoHlnKjmnKrnu4/mjojmnYPnmoTmg4XlhrXkuIvvvIzpgJrov4fku7vkvZXlqpLku4vlpI3liLbmraTmlofku7YgVW5hdXRob3JpemVkIGNvcHlpbmcgb2YgdGhpcyBmaWxlLCB2aWEgYW55IG1lZGl1bSBpcyBzdHJpY3RseSBwcm9oaWJpdGVkCiAgICDor6Xmlofku7bmmK/kuJPmnInnmoTmnLrlr4bmlofku7YgUHJvcHJpZXRhcnkgYW5kIGNvbmZpZGVudGlhbAogICAKICAgIENvcHlyaWdodCAyMDIxLW5vdyBGbW9kZSBJbmMuIHN1cHBvcnRAZm1vZGUuY24uIDE4NjA3MDA3MDczLgogICAg5L+d55WZ5omA5pyJ5p2D5YipIEFsbCByaWdodHMgcmVzZXJ2ZWQuCgogICAgUEFUSDovaG9tZS9yeWFuL3dvcmtzcGFjZS9ub3ZhL25vdmEtYWRtaW4vZGlzdC9mbW9kZS1uZy9lc20yMDIyL2xpYi9haWdjL3ZvaWNlL2xpYi9yZWNvcmRlci9leHRlbnNpb24td2F2ZXZpZXcubWpz`
|
|
10
|
+
|