@dataclouder/ngx-agent-cards 0.1.7 → 0.1.9

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.
@@ -1197,7 +1197,7 @@ class MessageProcessingService {
1197
1197
  // Map segments to multi-messages
1198
1198
  message.multiMessages = mdToHtml.map((val) => {
1199
1199
  // Get the narrator voice from conversation settings
1200
- const narratorVoice = settings.secondaryVoice || 'en-US-News-L';
1200
+ const narratorVoice = settings.tts?.secondaryVoice || 'en-US-News-L';
1201
1201
  // Determine if this is italicized text (narrator)
1202
1202
  const isItalics = val.tag === 'em';
1203
1203
  const voice = isItalics ? narratorVoice : message.voice;
@@ -1303,13 +1303,14 @@ function buildObjectTTSRequest(message, settings = {}) {
1303
1303
  const speedRate = settings?.speedRate || 0;
1304
1304
  const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed$1.Regular;
1305
1305
  const text = removeEmojis(message.text || message.content);
1306
+ const ssml = message.ssml ? message.ssml : removeEmojis(message.ssml);
1306
1307
  return {
1307
1308
  text,
1308
1309
  voice: message.voice || settings.voice,
1309
1310
  generateTranscription,
1310
1311
  speedRate,
1311
1312
  speed,
1312
- ssml: message?.ssml,
1313
+ ssml: ssml,
1313
1314
  };
1314
1315
  }
1315
1316
 
@@ -1497,7 +1498,6 @@ class ConversationService {
1497
1498
  model: conversationSettings.model || { modelName: '', provider: '' },
1498
1499
  };
1499
1500
  this.isThinkingSignal.set(true);
1500
- // Call AI service
1501
1501
  const response = await this.agentCardService.callChatCompletion(conversation);
1502
1502
  if (!response) {
1503
1503
  console.error('No message returned from AI, is your service working?');
@@ -1789,6 +1789,15 @@ class ChatFooterComponent {
1789
1789
  }
1790
1790
  });
1791
1791
  }
1792
+ ngOnInit() {
1793
+ // throw new Error('Method not implemented.');
1794
+ const messages = this.conversationService.getMessagesSignal()();
1795
+ const firstAssistantMessage = messages.find((msn) => msn.role === ChatRole.Assistant);
1796
+ if (firstAssistantMessage) {
1797
+ console.log('Assistant message:', firstAssistantMessage);
1798
+ this.evaluateConversation(firstAssistantMessage, 'assistant');
1799
+ }
1800
+ }
1792
1801
  /**
1793
1802
  * Method used by the template to determine if the mic should continue listening
1794
1803
  */
@@ -2069,6 +2078,7 @@ class TextHighlighterComponent {
2069
2078
  // Signal States
2070
2079
  this.wordWithMeta = signal([]); // Signal for plain text words when no transcription
2071
2080
  this.isPlaying = signal(false); // Signal for play state
2081
+ this.alreadyPlayed = false;
2072
2082
  // Signals State computed from the input message
2073
2083
  this.wordsWithMetaState = [];
2074
2084
  this.iconState = computed(() => {
@@ -2106,7 +2116,7 @@ class TextHighlighterComponent {
2106
2116
  effect(() => {
2107
2117
  const currentMsg = this.message(); // Read the input signal
2108
2118
  // Somethimes message changes but audio is already playing.
2109
- if (currentMsg && !this.isPlaying()) {
2119
+ if (currentMsg && !this.isPlaying() && !this.alreadyPlayed) {
2110
2120
  this.initializeBasedOnMessage(currentMsg);
2111
2121
  // Check if shouldPlayAudio flag is set
2112
2122
  if (currentMsg.shouldPlayAudio) {
@@ -2295,6 +2305,7 @@ class TextHighlighterComponent {
2295
2305
  console.error('Error playing audio:', error);
2296
2306
  });
2297
2307
  this.isPlaying.set(true); // Set play state to true
2308
+ this.alreadyPlayed = true;
2298
2309
  }
2299
2310
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextHighlighterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2300
2311
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: TextHighlighterComponent, isStandalone: true, selector: "dc-text-highlighter", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted", wordClicked: "wordClicked" }, ngImport: i0, template: "<div class=\"audio-text-sync-container\">\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (iconState() === 'isLoading') {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n } @else if (iconState() === 'isPlaying') {\n <i class=\"pi pi-volume-up\"></i>\n } @else if (iconState() === 'playable') {\n <!-- Display play icon when not playing -->\n <dc-icon name=\"play\"></dc-icon>\n } @else {\n <!-- Nothing-->\n <!-- <dc-icon name=\"play\"></dc-icon> -->\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div [class]=\"'text-content ' + classTag()\">\n @for (word of wordWithMeta(); track trackByIndex($index, word)) {\n <span [class]=\"word.tag\" [class.highlight]=\"word.isHighlighted\" (click)=\"onWordClick(word)\" [attr.id]=\"'word-' + message().messageId + '-' + word.index\"\n >{{ word.word }}&nbsp;</span\n >\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:#3cd8ff;border-radius:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.em{font-style:italic;color:#6495ed}.strong{font-weight:700;color:inherit}.italic{font-style:italic;color:#6495ed}.em_strong{font-weight:700;font-style:italic;color:inherit}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
@@ -2856,6 +2867,79 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
2856
2867
  type: Input
2857
2868
  }] } });
2858
2869
 
2870
+ const Endpoints = {
2871
+ GenerateImage: 'api/agent-cards-generate/generate-image-card',
2872
+ };
2873
+ // ♦️ Finish this will be the default so i dont need to create a new one everytime
2874
+ class DefaultAgentCardsService {
2875
+ constructor() {
2876
+ this.httpService = inject(HttpCoreService);
2877
+ }
2878
+ generateMainImage(idCard) {
2879
+ throw new Error('Method not implemented.');
2880
+ }
2881
+ completeAgentCard(idCard) {
2882
+ // TODO: i need a default method to do this.
2883
+ return this.httpService.get(`${Endpoints.GenerateImage}/${idCard}`, 'primary');
2884
+ }
2885
+ callChatCompletion(conversation) {
2886
+ throw new Error('Method not implemented.');
2887
+ }
2888
+ findConversationCardByID(id) {
2889
+ throw new Error('Method not implemented.');
2890
+ }
2891
+ filterConversationCards(filters) {
2892
+ throw new Error('Method not implemented.');
2893
+ }
2894
+ getAllConversationCards() {
2895
+ throw new Error('Method not implemented.');
2896
+ }
2897
+ findFilteredAgentCards(paginator) {
2898
+ throw new Error('Method not implemented.');
2899
+ }
2900
+ findAgentCardByTitle(title) {
2901
+ throw new Error('Method not implemented.');
2902
+ }
2903
+ saveConversationCard(conversation) {
2904
+ throw new Error('Method not implemented.');
2905
+ }
2906
+ deleteConversationCard(id) {
2907
+ throw new Error('Method not implemented.');
2908
+ }
2909
+ getTextAudioFile(tts) {
2910
+ throw new Error('Method not implemented.');
2911
+ }
2912
+ getConversationUserChatSettings() {
2913
+ throw new Error('Method not implemented.');
2914
+ }
2915
+ getListModels(provider) {
2916
+ throw new Error('Method not implemented.');
2917
+ }
2918
+ translateConversation(currentLang, targetLang, id) {
2919
+ throw new Error('Method not implemented.');
2920
+ }
2921
+ getAudioTranscriptions(audio, options) {
2922
+ throw new Error('Method not implemented.');
2923
+ }
2924
+ callInstruction(prompt, IAIModel) {
2925
+ throw new Error('Method not implemented.');
2926
+ }
2927
+ saveConversationUserChatSettings(conversation) {
2928
+ throw new Error('Method not implemented.');
2929
+ }
2930
+ generateImageForCard(idCard) {
2931
+ return this.httpService.get(`${Endpoints.GenerateImage}/${idCard}`, 'primary');
2932
+ }
2933
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DefaultAgentCardsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2934
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DefaultAgentCardsService, providedIn: 'root' }); }
2935
+ }
2936
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DefaultAgentCardsService, decorators: [{
2937
+ type: Injectable,
2938
+ args: [{
2939
+ providedIn: 'root',
2940
+ }]
2941
+ }] });
2942
+
2859
2943
  async function getCharacterData(file) {
2860
2944
  if (file.name.endsWith('.png')) {
2861
2945
  const filebuffer = await getFileBuffer(file);
@@ -3223,6 +3307,7 @@ class DCAgentCardFormComponent {
3223
3307
  this.dialogService = inject(DialogService);
3224
3308
  this.promptBuilder = inject(DCConversationPromptBuilderService);
3225
3309
  this.toastService = inject(TOAST_ALERTS_TOKEN, { optional: true });
3310
+ this.defaultAgentCardService = inject(DefaultAgentCardsService);
3226
3311
  // select options
3227
3312
  this.textEngines = Object.values(TextEngines);
3228
3313
  this.conversationOptions = ConversationTypeOptions;
@@ -3268,6 +3353,7 @@ class DCAgentCardFormComponent {
3268
3353
  mes_example: [''],
3269
3354
  alternate_greetings: this.fb.array([]),
3270
3355
  tags: this.fb.array([]),
3356
+ picture_description: [''],
3271
3357
  system_prompt: [''],
3272
3358
  post_history_instructions: [''],
3273
3359
  }),
@@ -3609,8 +3695,15 @@ class DCAgentCardFormComponent {
3609
3695
  this.toastService.success({ title: 'Sticker removed', subtitle: 'Sticker was removed' });
3610
3696
  this.cdr.detectChanges();
3611
3697
  }
3698
+ async generateMissingData() {
3699
+ this.agentCardService.completeAgentCard(this.agentCardId);
3700
+ }
3701
+ async generateMainImage() {
3702
+ // Check after generate missing data.
3703
+ this.agentCardService.generateMainImage(this.agentCardId);
3704
+ }
3612
3705
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCAgentCardFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3613
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCAgentCardFormComponent, isStandalone: true, selector: "dc-agent-form", inputs: { storageSettings: { classPropertyName: "storageSettings", publicName: "storageSettings", isSignal: true, isRequired: false, transformFunction: null }, bannerImgSettings: { classPropertyName: "bannerImgSettings", publicName: "bannerImgSettings", isSignal: true, isRequired: false, transformFunction: null }, imageStorageSettings: { classPropertyName: "imageStorageSettings", publicName: "imageStorageSettings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onImageLoaded: "onImageLoaded", onSave: "onSave", onGoDetails: "onGoDetails", onTranslate: "onTranslate" }, providers: [DialogService], ngImport: i0, template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n @if (form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched) {\n <div class=\"error\">\n Please enter a valid email address\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings()\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings()\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n @if (form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched) {\n <div class=\"error\">\n Name is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n @if (form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched) {\n <div class=\"error\">\n Description is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n @if (form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched) {\n <div\n class=\"error\"\n >\n System prompt is required\n </div>\n }\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n @for (greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; track greeting; let i = $index) {\n <div\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">&#x2716;</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n @for (tag of form.controls.characterCard.controls.data.controls.tags.controls; track tag; let i = $index) {\n <div class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </form>\n\n <p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n </p-popover>\n\n <div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n </div>\n", styles: [".textmin{min-width:36vw}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "ngmodule", type: PortalModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i2$3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ToggleButtonModule }, { kind: "component", type: i6$2.ToggleButton, selector: "p-toggleButton, p-togglebutton, p-toggle-button", inputs: ["onLabel", "offLabel", "onIcon", "offIcon", "ariaLabel", "ariaLabelledBy", "disabled", "style", "styleClass", "inputId", "tabindex", "size", "iconPos", "autofocus", "allowEmpty"], outputs: ["onChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i8.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["style", "styleClass", "tabindex", "inputId", "name", "disabled", "readonly", "trueValue", "falseValue", "ariaLabel", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "pipe", type: MdToHtmlArrayPipe, name: "mdToHtmlArray" }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i9.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: DynamicDialogModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i10.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }, { kind: "component", type: AccountPlatformForm, selector: "account-platform-form", inputs: ["formArray"] }] }); }
3706
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCAgentCardFormComponent, isStandalone: true, selector: "dc-agent-form", inputs: { storageSettings: { classPropertyName: "storageSettings", publicName: "storageSettings", isSignal: true, isRequired: false, transformFunction: null }, bannerImgSettings: { classPropertyName: "bannerImgSettings", publicName: "bannerImgSettings", isSignal: true, isRequired: false, transformFunction: null }, imageStorageSettings: { classPropertyName: "imageStorageSettings", publicName: "imageStorageSettings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onImageLoaded: "onImageLoaded", onSave: "onSave", onGoDetails: "onGoDetails", onTranslate: "onTranslate" }, providers: [DialogService], ngImport: i0, template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n @if (form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched) {\n <div class=\"error\"> Please enter a valid email address </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings()\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <p-button\n (click)=\"generateMainImage()\"\n class=\"image-gen-btn\"\n pTooltip=\"Generar/Re generar la imagen\"\n icon=\"pi pi-sparkles\"\n [rounded]=\"true\"\n severity=\"info\" />\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings()\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <p-button (click)=\"generateMissingData()\" icon=\"pi pi-sparkles\" [rounded]=\"true\" severity=\"info\" label=\"Arreglar Campos\" />\n\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n @if (form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched) {\n <div class=\"error\"> Name is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n @if (form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched) {\n <div class=\"error\"> Description is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n @if (form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched) {\n <div class=\"error\"> System prompt is required </div>\n }\n </div>\n\n <div>\n <div class=\"form-field\">\n <label>Image Description</label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"picture_description\"></textarea>\n </div>\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n @for (greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; track greeting; let i = $index) {\n <div class=\"array-item\" style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">&#x2716;</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n @for (tag of form.controls.characterCard.controls.data.controls.tags.controls; track tag; let i = $index) {\n <div class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</form>\n\n<p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n</p-popover>\n\n<div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n", styles: [".textmin{min-width:36vw}.image-gen-btn{position:absolute;bottom:15px;left:35%}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "ngmodule", type: PortalModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i2$3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ToggleButtonModule }, { kind: "component", type: i6$2.ToggleButton, selector: "p-toggleButton, p-togglebutton, p-toggle-button", inputs: ["onLabel", "offLabel", "onIcon", "offIcon", "ariaLabel", "ariaLabelledBy", "disabled", "style", "styleClass", "inputId", "tabindex", "size", "iconPos", "autofocus", "allowEmpty"], outputs: ["onChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i8.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["style", "styleClass", "tabindex", "inputId", "name", "disabled", "readonly", "trueValue", "falseValue", "ariaLabel", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "pipe", type: MdToHtmlArrayPipe, name: "mdToHtmlArray" }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i9.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: DynamicDialogModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i10.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }, { kind: "component", type: AccountPlatformForm, selector: "account-platform-form", inputs: ["formArray"] }] }); }
3614
3707
  }
3615
3708
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCAgentCardFormComponent, decorators: [{
3616
3709
  type: Component,
@@ -3634,7 +3727,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
3634
3727
  PopoverModule,
3635
3728
  ProviderSelectorComponent,
3636
3729
  AccountPlatformForm,
3637
- ], template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n @if (form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched) {\n <div class=\"error\">\n Please enter a valid email address\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings()\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings()\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n @if (form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched) {\n <div class=\"error\">\n Name is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n @if (form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched) {\n <div class=\"error\">\n Description is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n @if (form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched) {\n <div\n class=\"error\"\n >\n System prompt is required\n </div>\n }\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n @for (greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; track greeting; let i = $index) {\n <div\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">&#x2716;</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n @for (tag of form.controls.characterCard.controls.data.controls.tags.controls; track tag; let i = $index) {\n <div class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </form>\n\n <p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n </p-popover>\n\n <div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n </div>\n", styles: [".textmin{min-width:36vw}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"] }]
3730
+ ], template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n @if (form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched) {\n <div class=\"error\"> Please enter a valid email address </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings()\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <p-button\n (click)=\"generateMainImage()\"\n class=\"image-gen-btn\"\n pTooltip=\"Generar/Re generar la imagen\"\n icon=\"pi pi-sparkles\"\n [rounded]=\"true\"\n severity=\"info\" />\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings()\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <p-button (click)=\"generateMissingData()\" icon=\"pi pi-sparkles\" [rounded]=\"true\" severity=\"info\" label=\"Arreglar Campos\" />\n\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n @if (form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched) {\n <div class=\"error\"> Name is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n @if (form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched) {\n <div class=\"error\"> Description is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n @if (form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched) {\n <div class=\"error\"> System prompt is required </div>\n }\n </div>\n\n <div>\n <div class=\"form-field\">\n <label>Image Description</label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"picture_description\"></textarea>\n </div>\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n @for (greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; track greeting; let i = $index) {\n <div class=\"array-item\" style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">&#x2716;</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n @for (tag of form.controls.characterCard.controls.data.controls.tags.controls; track tag; let i = $index) {\n <div class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</form>\n\n<p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n</p-popover>\n\n<div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n", styles: [".textmin{min-width:36vw}.image-gen-btn{position:absolute;bottom:15px;left:35%}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"] }]
3638
3731
  }], ctorParameters: () => [] });
3639
3732
 
3640
3733
  class DCConversationCardUIComponent {
@@ -3649,9 +3742,9 @@ class DCConversationCardUIComponent {
3649
3742
  this.onCardAction = output();
3650
3743
  }
3651
3744
  ngOnInit() {
3652
- const name = this.card().characterCard.data.name;
3653
- const description = this.card().characterCard.data.description;
3654
- this.card().characterCard.data.description = description.replace(/{{char}}/g, name);
3745
+ const name = this.card().characterCard?.data?.name;
3746
+ const description = this.card().characterCard?.data?.description;
3747
+ this.card().characterCard.data.description = description?.replace(/{{char}}/g, name);
3655
3748
  }
3656
3749
  onDetails() {
3657
3750
  this.onCardAction.emit({ event: 'details', card: this.card() });