valtech-components 4.0.233 → 4.0.235

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.
@@ -56,7 +56,7 @@ import { BrowserMultiFormatReader } from '@zxing/browser';
56
56
  * Current version of valtech-components.
57
57
  * This is automatically updated during the publish process.
58
58
  */
59
- const VERSION = '4.0.233';
59
+ const VERSION = '4.0.235';
60
60
 
61
61
  // Control de estado de refresco (singleton a nivel de módulo)
62
62
  let isRefreshing = false;
@@ -72860,13 +72860,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
72860
72860
  type: Output
72861
72861
  }] } });
72862
72862
 
72863
- addIcons({ addOutline, closeOutline, documentOutline, micOutline, arrowUpOutline, trashOutline });
72863
+ addIcons({ addOutline, checkmarkOutline, closeOutline, documentOutline, micOutline, arrowUpOutline, trashOutline });
72864
72864
  const CHAT_COMPOSER_I18N = {
72865
72865
  es: {
72866
72866
  placeholder: 'Escribe un mensaje',
72867
72867
  send: 'Enviar',
72868
72868
  attach: 'Adjuntar',
72869
72869
  voice: 'Mensaje de voz',
72870
+ done: 'Listo',
72870
72871
  replyingTo: 'Respondiendo a',
72871
72872
  cancel: 'Cancelar',
72872
72873
  remove: 'Quitar',
@@ -72876,6 +72877,7 @@ const CHAT_COMPOSER_I18N = {
72876
72877
  send: 'Send',
72877
72878
  attach: 'Attach',
72878
72879
  voice: 'Voice message',
72880
+ done: 'Done',
72879
72881
  replyingTo: 'Replying to',
72880
72882
  cancel: 'Cancel',
72881
72883
  remove: 'Remove',
@@ -72901,7 +72903,6 @@ class ChatComposerComponent {
72901
72903
  this.showMic = input(true);
72902
72904
  this.send = output();
72903
72905
  this.typing = output();
72904
- this.attach = output();
72905
72906
  this.voice = output();
72906
72907
  this.cancelReply = output();
72907
72908
  this.body = signal('');
@@ -72944,19 +72945,16 @@ class ChatComposerComponent {
72944
72945
  onSend() {
72945
72946
  if (!this.canSend())
72946
72947
  return;
72947
- // Primero los adjuntos pendientes (cada uno se sube y manda en el consumer),
72948
- // luego el texto como mensaje aparte si lo hay.
72949
- const files = this.pending();
72950
- for (const att of files) {
72951
- this.attach.emit(att.file);
72948
+ // Un solo evento: texto (caption) + todos los adjuntos staged. El consumer
72949
+ // los sube y manda como UN mensaje con body + attachments.
72950
+ const files = this.pending().map(p => p.file);
72951
+ const text = this.body().trim();
72952
+ this.send.emit({ text, files });
72953
+ for (const att of this.pending()) {
72952
72954
  if (att.url)
72953
72955
  URL.revokeObjectURL(att.url);
72954
72956
  }
72955
72957
  this.pending.set([]);
72956
- const trimmed = this.body().trim();
72957
- if (trimmed && trimmed.length <= this.maxLength()) {
72958
- this.send.emit(trimmed);
72959
- }
72960
72958
  this.body.set('');
72961
72959
  }
72962
72960
  onFile(event) {
@@ -72983,9 +72981,13 @@ class ChatComposerComponent {
72983
72981
  }
72984
72982
  }
72985
72983
  addPending(file) {
72986
- const isImage = file.type.startsWith('image/');
72987
- const url = isImage ? URL.createObjectURL(file) : undefined;
72988
- this.pending.update(arr => [...arr, { id: this.pendingId++, file, url, isImage }]);
72984
+ const kind = file.type.startsWith('image/')
72985
+ ? 'image'
72986
+ : file.type.startsWith('audio/')
72987
+ ? 'audio'
72988
+ : 'file';
72989
+ const url = kind === 'file' ? undefined : URL.createObjectURL(file);
72990
+ this.pending.update(arr => [...arr, { id: this.pendingId++, file, url, kind }]);
72989
72991
  }
72990
72992
  removePending(id) {
72991
72993
  this.pending.update(arr => {
@@ -73040,7 +73042,8 @@ class ChatComposerComponent {
73040
73042
  this.cleanupRecording();
73041
73043
  }
73042
73044
  }
73043
- stopAndSend() {
73045
+ /** Para la grabación y la stagea como adjunto pendiente (con preview de audio). */
73046
+ finishRecording() {
73044
73047
  const rec = this.recorder;
73045
73048
  if (!rec) {
73046
73049
  this.cleanupRecording();
@@ -73052,7 +73055,7 @@ class ChatComposerComponent {
73052
73055
  const ext = type.includes('mp4') ? 'm4a' : 'webm';
73053
73056
  const file = new File([blob], `audio-${this.recSeconds()}s.${ext}`, { type });
73054
73057
  if (blob.size > 0)
73055
- this.attach.emit(file);
73058
+ this.addPending(file);
73056
73059
  this.cleanupRecording();
73057
73060
  };
73058
73061
  rec.stop();
@@ -73092,7 +73095,7 @@ class ChatComposerComponent {
73092
73095
  return this.i18n.t(key, 'ChatComposer');
73093
73096
  }
73094
73097
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
73095
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChatComposerComponent, isStandalone: true, selector: "val-chat-composer", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, replyingTo: { classPropertyName: "replyingTo", publicName: "replyingTo", isSignal: true, isRequired: false, transformFunction: null }, showAttach: { classPropertyName: "showAttach", publicName: "showAttach", isSignal: true, isRequired: false, transformFunction: null }, showMic: { classPropertyName: "showMic", publicName: "showMic", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { send: "send", typing: "typing", attach: "attach", voice: "voice", cancelReply: "cancelReply" }, ngImport: i0, template: `
73098
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChatComposerComponent, isStandalone: true, selector: "val-chat-composer", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, replyingTo: { classPropertyName: "replyingTo", publicName: "replyingTo", isSignal: true, isRequired: false, transformFunction: null }, showAttach: { classPropertyName: "showAttach", publicName: "showAttach", isSignal: true, isRequired: false, transformFunction: null }, showMic: { classPropertyName: "showMic", publicName: "showMic", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { send: "send", typing: "typing", voice: "voice", cancelReply: "cancelReply" }, ngImport: i0, template: `
73096
73099
  <div class="composer">
73097
73100
  @if (replyingTo()) {
73098
73101
  <div class="reply-bar">
@@ -73109,17 +73112,26 @@ class ChatComposerComponent {
73109
73112
  @if (pending().length > 0) {
73110
73113
  <div class="pending-row">
73111
73114
  @for (att of pending(); track att.id) {
73112
- <div class="thumb" [class.file]="!att.isImage">
73113
- @if (att.isImage) {
73114
- <img [src]="att.url" [alt]="att.file.name" />
73115
- } @else {
73116
- <ion-icon name="document-outline" aria-hidden="true" />
73117
- <span class="thumb-name">{{ att.file.name }}</span>
73118
- }
73119
- <button class="thumb-remove" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
73120
- <ion-icon name="close-outline" aria-hidden="true" />
73121
- </button>
73122
- </div>
73115
+ @if (att.kind === 'audio') {
73116
+ <div class="audio-chip">
73117
+ <audio controls preload="metadata" [src]="att.url"></audio>
73118
+ <button class="thumb-remove inline" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
73119
+ <ion-icon name="close-outline" aria-hidden="true" />
73120
+ </button>
73121
+ </div>
73122
+ } @else {
73123
+ <div class="thumb" [class.file]="att.kind === 'file'">
73124
+ @if (att.kind === 'image') {
73125
+ <img [src]="att.url" [alt]="att.file.name" />
73126
+ } @else {
73127
+ <ion-icon name="document-outline" aria-hidden="true" />
73128
+ <span class="thumb-name">{{ att.file.name }}</span>
73129
+ }
73130
+ <button class="thumb-remove" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
73131
+ <ion-icon name="close-outline" aria-hidden="true" />
73132
+ </button>
73133
+ </div>
73134
+ }
73123
73135
  }
73124
73136
  </div>
73125
73137
  }
@@ -73132,8 +73144,8 @@ class ChatComposerComponent {
73132
73144
  <span class="rec-dot"></span>
73133
73145
  <span class="rec-time">{{ recTimeLabel() }}</span>
73134
73146
  <span class="rec-spacer"></span>
73135
- <button class="send-btn" [attr.aria-label]="t('send')" (click)="stopAndSend()">
73136
- <ion-icon name="arrow-up-outline" aria-hidden="true" />
73147
+ <button class="send-btn" [attr.aria-label]="t('done')" (click)="finishRecording()">
73148
+ <ion-icon name="checkmark-outline" aria-hidden="true" />
73137
73149
  </button>
73138
73150
  </div>
73139
73151
  } @else {
@@ -73174,7 +73186,7 @@ class ChatComposerComponent {
73174
73186
  </div>
73175
73187
  }
73176
73188
  </div>
73177
- `, isInline: true, styles: [":host{display:block}.composer{display:flex;flex-direction:column;gap:6px;padding:8px 10px calc(8px + env(safe-area-inset-bottom,0px));background:var(--ion-background-color, #fff);border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}.reply-bar{display:flex;align-items:center;gap:8px;padding:6px 10px;border-left:3px solid var(--ion-color-primary);border-radius:8px;background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.reply-content{display:flex;flex-direction:column;flex:1;min-width:0;font-size:.8125rem}.reply-name{font-weight:700;color:var(--ion-color-primary)}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ion-color-medium, #92949c)}.pending-row{display:flex;gap:8px;flex-wrap:wrap;padding:2px 4px}.thumb{position:relative;width:64px;height:64px;border-radius:10px;overflow:hidden;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.thumb img{width:100%;height:100%;object-fit:cover}.thumb.file{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:4px;color:var(--ion-color-medium, #92949c);font-size:1.5rem}.thumb-name{font-size:.5625rem;line-height:1.1;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ion-text-color, #000)}.thumb-remove{position:absolute;top:2px;right:2px;width:20px;height:20px;border:none;border-radius:50%;background:#0000008c;color:#fff;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:.875rem}.pill{display:flex;align-items:flex-end;gap:4px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.field{flex:1;--background: transparent;--padding-start: 6px;--padding-end: 6px;--padding-top: 8px;--padding-bottom: 8px;margin:0;max-height:120px;font-size:.9375rem}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:transparent;color:var(--ion-color-medium, #92949c);cursor:pointer;font-size:1.375rem}.icon-btn:hover,.icon-btn.active{color:var(--ion-color-primary)}.send-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:var(--ion-color-primary);color:var(--ion-color-primary-contrast, #fff);cursor:pointer;font-size:1.25rem;transition:transform .1s ease}.send-btn:active{transform:scale(.92)}.icon-btn.danger{color:var(--ion-color-danger, #eb445a)}.rec-bar{display:flex;align-items:center;gap:10px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.rec-dot{width:10px;height:10px;border-radius:50%;background:var(--ion-color-danger, #eb445a);animation:rec-pulse 1.2s ease-in-out infinite}.rec-time{font-variant-numeric:tabular-nums;font-size:.9375rem;color:var(--ion-text-color, #000)}.rec-spacer{flex:1}@keyframes rec-pulse{0%,to{opacity:1}50%{opacity:.3}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
73189
+ `, isInline: true, styles: [":host{display:block}.composer{display:flex;flex-direction:column;gap:6px;padding:8px 10px calc(8px + env(safe-area-inset-bottom,0px));background:var(--ion-background-color, #fff);border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}.reply-bar{display:flex;align-items:center;gap:8px;padding:6px 10px;border-left:3px solid var(--ion-color-primary);border-radius:8px;background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.reply-content{display:flex;flex-direction:column;flex:1;min-width:0;font-size:.8125rem}.reply-name{font-weight:700;color:var(--ion-color-primary)}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ion-color-medium, #92949c)}.pending-row{display:flex;gap:8px;flex-wrap:wrap;padding:2px 4px}.thumb{position:relative;width:64px;height:64px;border-radius:10px;overflow:hidden;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.thumb img{width:100%;height:100%;object-fit:cover}.thumb.file{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:4px;color:var(--ion-color-medium, #92949c);font-size:1.5rem}.thumb-name{font-size:.5625rem;line-height:1.1;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ion-text-color, #000)}.thumb-remove{position:absolute;top:2px;right:2px;width:20px;height:20px;border:none;border-radius:50%;background:#0000008c;color:#fff;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:.875rem}.pill{display:flex;align-items:flex-end;gap:4px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.field{flex:1;--background: transparent;--padding-start: 6px;--padding-end: 6px;--padding-top: 8px;--padding-bottom: 8px;margin:0;max-height:120px;font-size:.9375rem}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:transparent;color:var(--ion-color-medium, #92949c);cursor:pointer;font-size:1.375rem}.icon-btn:hover,.icon-btn.active{color:var(--ion-color-primary)}.send-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:var(--ion-color-primary);color:var(--ion-color-primary-contrast, #fff);cursor:pointer;font-size:1.25rem;transition:transform .1s ease}.send-btn:active{transform:scale(.92)}.audio-chip{display:flex;align-items:center;gap:6px;width:100%;padding:4px 6px;border-radius:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.audio-chip audio{flex:1;height:36px;min-width:0}.thumb-remove.inline{position:static;flex-shrink:0;background:var(--ion-color-medium, #92949c)}.icon-btn.danger{color:var(--ion-color-danger, #eb445a)}.rec-bar{display:flex;align-items:center;gap:10px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.rec-dot{width:10px;height:10px;border-radius:50%;background:var(--ion-color-danger, #eb445a);animation:rec-pulse 1.2s ease-in-out infinite}.rec-time{font-variant-numeric:tabular-nums;font-size:.9375rem;color:var(--ion-text-color, #000)}.rec-spacer{flex:1}@keyframes rec-pulse{0%,to{opacity:1}50%{opacity:.3}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonTextarea, selector: "ion-textarea", inputs: ["autoGrow", "autocapitalize", "autofocus", "clearOnEdit", "color", "cols", "counter", "counterFormatter", "debounce", "disabled", "enterkeyhint", "errorText", "fill", "helperText", "inputmode", "label", "labelPlacement", "maxlength", "minlength", "mode", "name", "placeholder", "readonly", "required", "rows", "shape", "spellcheck", "value", "wrap"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
73178
73190
  }
73179
73191
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComposerComponent, decorators: [{
73180
73192
  type: Component,
@@ -73195,17 +73207,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
73195
73207
  @if (pending().length > 0) {
73196
73208
  <div class="pending-row">
73197
73209
  @for (att of pending(); track att.id) {
73198
- <div class="thumb" [class.file]="!att.isImage">
73199
- @if (att.isImage) {
73200
- <img [src]="att.url" [alt]="att.file.name" />
73201
- } @else {
73202
- <ion-icon name="document-outline" aria-hidden="true" />
73203
- <span class="thumb-name">{{ att.file.name }}</span>
73204
- }
73205
- <button class="thumb-remove" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
73206
- <ion-icon name="close-outline" aria-hidden="true" />
73207
- </button>
73208
- </div>
73210
+ @if (att.kind === 'audio') {
73211
+ <div class="audio-chip">
73212
+ <audio controls preload="metadata" [src]="att.url"></audio>
73213
+ <button class="thumb-remove inline" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
73214
+ <ion-icon name="close-outline" aria-hidden="true" />
73215
+ </button>
73216
+ </div>
73217
+ } @else {
73218
+ <div class="thumb" [class.file]="att.kind === 'file'">
73219
+ @if (att.kind === 'image') {
73220
+ <img [src]="att.url" [alt]="att.file.name" />
73221
+ } @else {
73222
+ <ion-icon name="document-outline" aria-hidden="true" />
73223
+ <span class="thumb-name">{{ att.file.name }}</span>
73224
+ }
73225
+ <button class="thumb-remove" [attr.aria-label]="t('remove')" (click)="removePending(att.id)">
73226
+ <ion-icon name="close-outline" aria-hidden="true" />
73227
+ </button>
73228
+ </div>
73229
+ }
73209
73230
  }
73210
73231
  </div>
73211
73232
  }
@@ -73218,8 +73239,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
73218
73239
  <span class="rec-dot"></span>
73219
73240
  <span class="rec-time">{{ recTimeLabel() }}</span>
73220
73241
  <span class="rec-spacer"></span>
73221
- <button class="send-btn" [attr.aria-label]="t('send')" (click)="stopAndSend()">
73222
- <ion-icon name="arrow-up-outline" aria-hidden="true" />
73242
+ <button class="send-btn" [attr.aria-label]="t('done')" (click)="finishRecording()">
73243
+ <ion-icon name="checkmark-outline" aria-hidden="true" />
73223
73244
  </button>
73224
73245
  </div>
73225
73246
  } @else {
@@ -73260,7 +73281,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
73260
73281
  </div>
73261
73282
  }
73262
73283
  </div>
73263
- `, styles: [":host{display:block}.composer{display:flex;flex-direction:column;gap:6px;padding:8px 10px calc(8px + env(safe-area-inset-bottom,0px));background:var(--ion-background-color, #fff);border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}.reply-bar{display:flex;align-items:center;gap:8px;padding:6px 10px;border-left:3px solid var(--ion-color-primary);border-radius:8px;background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.reply-content{display:flex;flex-direction:column;flex:1;min-width:0;font-size:.8125rem}.reply-name{font-weight:700;color:var(--ion-color-primary)}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ion-color-medium, #92949c)}.pending-row{display:flex;gap:8px;flex-wrap:wrap;padding:2px 4px}.thumb{position:relative;width:64px;height:64px;border-radius:10px;overflow:hidden;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.thumb img{width:100%;height:100%;object-fit:cover}.thumb.file{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:4px;color:var(--ion-color-medium, #92949c);font-size:1.5rem}.thumb-name{font-size:.5625rem;line-height:1.1;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ion-text-color, #000)}.thumb-remove{position:absolute;top:2px;right:2px;width:20px;height:20px;border:none;border-radius:50%;background:#0000008c;color:#fff;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:.875rem}.pill{display:flex;align-items:flex-end;gap:4px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.field{flex:1;--background: transparent;--padding-start: 6px;--padding-end: 6px;--padding-top: 8px;--padding-bottom: 8px;margin:0;max-height:120px;font-size:.9375rem}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:transparent;color:var(--ion-color-medium, #92949c);cursor:pointer;font-size:1.375rem}.icon-btn:hover,.icon-btn.active{color:var(--ion-color-primary)}.send-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:var(--ion-color-primary);color:var(--ion-color-primary-contrast, #fff);cursor:pointer;font-size:1.25rem;transition:transform .1s ease}.send-btn:active{transform:scale(.92)}.icon-btn.danger{color:var(--ion-color-danger, #eb445a)}.rec-bar{display:flex;align-items:center;gap:10px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.rec-dot{width:10px;height:10px;border-radius:50%;background:var(--ion-color-danger, #eb445a);animation:rec-pulse 1.2s ease-in-out infinite}.rec-time{font-variant-numeric:tabular-nums;font-size:.9375rem;color:var(--ion-text-color, #000)}.rec-spacer{flex:1}@keyframes rec-pulse{0%,to{opacity:1}50%{opacity:.3}}\n"] }]
73284
+ `, styles: [":host{display:block}.composer{display:flex;flex-direction:column;gap:6px;padding:8px 10px calc(8px + env(safe-area-inset-bottom,0px));background:var(--ion-background-color, #fff);border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}.reply-bar{display:flex;align-items:center;gap:8px;padding:6px 10px;border-left:3px solid var(--ion-color-primary);border-radius:8px;background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.reply-content{display:flex;flex-direction:column;flex:1;min-width:0;font-size:.8125rem}.reply-name{font-weight:700;color:var(--ion-color-primary)}.reply-body{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ion-color-medium, #92949c)}.pending-row{display:flex;gap:8px;flex-wrap:wrap;padding:2px 4px}.thumb{position:relative;width:64px;height:64px;border-radius:10px;overflow:hidden;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.thumb img{width:100%;height:100%;object-fit:cover}.thumb.file{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:4px;color:var(--ion-color-medium, #92949c);font-size:1.5rem}.thumb-name{font-size:.5625rem;line-height:1.1;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ion-text-color, #000)}.thumb-remove{position:absolute;top:2px;right:2px;width:20px;height:20px;border:none;border-radius:50%;background:#0000008c;color:#fff;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:.875rem}.pill{display:flex;align-items:flex-end;gap:4px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.field{flex:1;--background: transparent;--padding-start: 6px;--padding-end: 6px;--padding-top: 8px;--padding-bottom: 8px;margin:0;max-height:120px;font-size:.9375rem}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:transparent;color:var(--ion-color-medium, #92949c);cursor:pointer;font-size:1.375rem}.icon-btn:hover,.icon-btn.active{color:var(--ion-color-primary)}.send-btn{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;flex-shrink:0;border:none;border-radius:50%;background:var(--ion-color-primary);color:var(--ion-color-primary-contrast, #fff);cursor:pointer;font-size:1.25rem;transition:transform .1s ease}.send-btn:active{transform:scale(.92)}.audio-chip{display:flex;align-items:center;gap:6px;width:100%;padding:4px 6px;border-radius:12px;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-color-step-100, rgba(127, 127, 127, .1))}.audio-chip audio{flex:1;height:36px;min-width:0}.thumb-remove.inline{position:static;flex-shrink:0;background:var(--ion-color-medium, #92949c)}.icon-btn.danger{color:var(--ion-color-danger, #eb445a)}.rec-bar{display:flex;align-items:center;gap:10px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:24px;padding:2px 6px}.rec-dot{width:10px;height:10px;border-radius:50%;background:var(--ion-color-danger, #eb445a);animation:rec-pulse 1.2s ease-in-out infinite}.rec-time{font-variant-numeric:tabular-nums;font-size:.9375rem;color:var(--ion-text-color, #000)}.rec-spacer{flex:1}@keyframes rec-pulse{0%,to{opacity:1}50%{opacity:.3}}\n"] }]
73264
73285
  }], ctorParameters: () => [] });
73265
73286
 
73266
73287
  /**
@@ -73480,7 +73501,6 @@ class ChatWindowComponent {
73480
73501
  this.replyTo = output();
73481
73502
  this.editMessage = output();
73482
73503
  this.typing = output();
73483
- this.attach = output();
73484
73504
  this.voice = output();
73485
73505
  this.msgsEl = viewChild('msgs');
73486
73506
  this.replyingTo = signal(null);
@@ -73564,8 +73584,8 @@ class ChatWindowComponent {
73564
73584
  this.atBottom.set(true);
73565
73585
  this.hasNew.set(false);
73566
73586
  }
73567
- onSend(body) {
73568
- this.sendMessage.emit(body);
73587
+ onSend(event) {
73588
+ this.sendMessage.emit(event);
73569
73589
  this.replyingTo.set(null);
73570
73590
  }
73571
73591
  onAction(event) {
@@ -73593,7 +73613,7 @@ class ChatWindowComponent {
73593
73613
  return this.i18n.t(key, 'ChatWindow');
73594
73614
  }
73595
73615
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
73596
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChatWindowComponent, isStandalone: true, selector: "val-chat-window", inputs: { convId: { classPropertyName: "convId", publicName: "convId", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, currentUserId: { classPropertyName: "currentUserId", publicName: "currentUserId", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, typingUsers: { classPropertyName: "typingUsers", publicName: "typingUsers", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage", loadMore: "loadMore", reactionClick: "reactionClick", deleteMessage: "deleteMessage", replyTo: "replyTo", editMessage: "editMessage", typing: "typing", attach: "attach", voice: "voice" }, viewQueries: [{ propertyName: "msgsEl", first: true, predicate: ["msgs"], descendants: true, isSignal: true }], ngImport: i0, template: `
73616
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ChatWindowComponent, isStandalone: true, selector: "val-chat-window", inputs: { convId: { classPropertyName: "convId", publicName: "convId", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, currentUserId: { classPropertyName: "currentUserId", publicName: "currentUserId", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, typingUsers: { classPropertyName: "typingUsers", publicName: "typingUsers", isSignal: true, isRequired: false, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage", loadMore: "loadMore", reactionClick: "reactionClick", deleteMessage: "deleteMessage", replyTo: "replyTo", editMessage: "editMessage", typing: "typing", voice: "voice" }, viewQueries: [{ propertyName: "msgsEl", first: true, predicate: ["msgs"], descendants: true, isSignal: true }], ngImport: i0, template: `
73597
73617
  <div class="chat">
73598
73618
  <div #msgs class="messages" (scroll)="onScroll()">
73599
73619
  @if (isLoading()) {
@@ -73640,7 +73660,6 @@ class ChatWindowComponent {
73640
73660
  [replyingTo]="replyingTo()"
73641
73661
  (send)="onSend($event)"
73642
73662
  (typing)="typing.emit()"
73643
- (attach)="attach.emit($event)"
73644
73663
  (voice)="voice.emit()"
73645
73664
  (cancelReply)="replyingTo.set(null)"
73646
73665
  />
@@ -73648,7 +73667,7 @@ class ChatWindowComponent {
73648
73667
  <div class="closed-banner">{{ t('conversationClosed') }}</div>
73649
73668
  }
73650
73669
  </div>
73651
- `, isInline: true, styles: [":host{display:block;height:100%}.chat{position:relative;display:flex;flex-direction:column;height:100%;min-height:0}.messages{flex:1;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:0}.state{margin:auto;display:flex;flex-direction:column;align-items:center;gap:8px;color:var(--ion-color-medium, #92949c);font-size:.9375rem;text-align:center;padding:24px}.state.empty ion-icon{font-size:2.5rem;opacity:.5}.date-sep{position:sticky;top:4px;z-index:1;display:flex;justify-content:center;margin:8px 0}.date-sep span{font-size:.75rem;color:var(--ion-color-medium, #92949c);background:var(--ion-color-step-100, rgba(127, 127, 127, .14));padding:3px 12px;border-radius:999px;backdrop-filter:blur(4px)}.scroll-down{position:absolute;right:14px;bottom:84px;width:40px;height:40px;border-radius:50%;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff));color:var(--ion-color-medium, #92949c);box-shadow:0 2px 10px #00000026;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:1.25rem;z-index:3}.scroll-down .dot{position:absolute;top:0;right:0;width:12px;height:12px;border-radius:50%;background:var(--ion-color-primary);border:2px solid var(--ion-card-background, #fff)}.closed-banner{text-align:center;padding:14px;color:var(--ion-color-medium, #92949c);font-size:.875rem;border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: MessageBubbleComponent, selector: "val-message-bubble", inputs: ["msg", "showAvatar", "showName", "tail", "locale"], outputs: ["action"] }, { kind: "component", type: ChatComposerComponent, selector: "val-chat-composer", inputs: ["placeholder", "disabled", "maxLength", "replyingTo", "showAttach", "showMic"], outputs: ["send", "typing", "attach", "voice", "cancelReply"] }, { kind: "component", type: TypingIndicatorComponent, selector: "val-typing-indicator", inputs: ["typingUsers"] }] }); }
73670
+ `, isInline: true, styles: [":host{display:block;position:relative;height:100%;min-height:0}.chat{position:absolute;inset:0;display:flex;flex-direction:column}.messages{flex:1;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:0}.state{margin:auto;display:flex;flex-direction:column;align-items:center;gap:8px;color:var(--ion-color-medium, #92949c);font-size:.9375rem;text-align:center;padding:24px}.state.empty ion-icon{font-size:2.5rem;opacity:.5}.date-sep{position:sticky;top:4px;z-index:1;display:flex;justify-content:center;margin:8px 0}.date-sep span{font-size:.75rem;color:var(--ion-color-medium, #92949c);background:var(--ion-color-step-100, rgba(127, 127, 127, .14));padding:3px 12px;border-radius:999px;backdrop-filter:blur(4px)}.scroll-down{position:absolute;right:14px;bottom:84px;width:40px;height:40px;border-radius:50%;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff));color:var(--ion-color-medium, #92949c);box-shadow:0 2px 10px #00000026;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:1.25rem;z-index:3}.scroll-down .dot{position:absolute;top:0;right:0;width:12px;height:12px;border-radius:50%;background:var(--ion-color-primary);border:2px solid var(--ion-card-background, #fff)}.closed-banner{text-align:center;padding:14px;color:var(--ion-color-medium, #92949c);font-size:.875rem;border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: MessageBubbleComponent, selector: "val-message-bubble", inputs: ["msg", "showAvatar", "showName", "tail", "locale"], outputs: ["action"] }, { kind: "component", type: ChatComposerComponent, selector: "val-chat-composer", inputs: ["placeholder", "disabled", "maxLength", "replyingTo", "showAttach", "showMic"], outputs: ["send", "typing", "voice", "cancelReply"] }, { kind: "component", type: TypingIndicatorComponent, selector: "val-typing-indicator", inputs: ["typingUsers"] }] }); }
73652
73671
  }
73653
73672
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatWindowComponent, decorators: [{
73654
73673
  type: Component,
@@ -73699,7 +73718,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
73699
73718
  [replyingTo]="replyingTo()"
73700
73719
  (send)="onSend($event)"
73701
73720
  (typing)="typing.emit()"
73702
- (attach)="attach.emit($event)"
73703
73721
  (voice)="voice.emit()"
73704
73722
  (cancelReply)="replyingTo.set(null)"
73705
73723
  />
@@ -73707,7 +73725,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
73707
73725
  <div class="closed-banner">{{ t('conversationClosed') }}</div>
73708
73726
  }
73709
73727
  </div>
73710
- `, styles: [":host{display:block;height:100%}.chat{position:relative;display:flex;flex-direction:column;height:100%;min-height:0}.messages{flex:1;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:0}.state{margin:auto;display:flex;flex-direction:column;align-items:center;gap:8px;color:var(--ion-color-medium, #92949c);font-size:.9375rem;text-align:center;padding:24px}.state.empty ion-icon{font-size:2.5rem;opacity:.5}.date-sep{position:sticky;top:4px;z-index:1;display:flex;justify-content:center;margin:8px 0}.date-sep span{font-size:.75rem;color:var(--ion-color-medium, #92949c);background:var(--ion-color-step-100, rgba(127, 127, 127, .14));padding:3px 12px;border-radius:999px;backdrop-filter:blur(4px)}.scroll-down{position:absolute;right:14px;bottom:84px;width:40px;height:40px;border-radius:50%;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff));color:var(--ion-color-medium, #92949c);box-shadow:0 2px 10px #00000026;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:1.25rem;z-index:3}.scroll-down .dot{position:absolute;top:0;right:0;width:12px;height:12px;border-radius:50%;background:var(--ion-color-primary);border:2px solid var(--ion-card-background, #fff)}.closed-banner{text-align:center;padding:14px;color:var(--ion-color-medium, #92949c);font-size:.875rem;border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}\n"] }]
73728
+ `, styles: [":host{display:block;position:relative;height:100%;min-height:0}.chat{position:absolute;inset:0;display:flex;flex-direction:column}.messages{flex:1;min-height:0;overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:0}.state{margin:auto;display:flex;flex-direction:column;align-items:center;gap:8px;color:var(--ion-color-medium, #92949c);font-size:.9375rem;text-align:center;padding:24px}.state.empty ion-icon{font-size:2.5rem;opacity:.5}.date-sep{position:sticky;top:4px;z-index:1;display:flex;justify-content:center;margin:8px 0}.date-sep span{font-size:.75rem;color:var(--ion-color-medium, #92949c);background:var(--ion-color-step-100, rgba(127, 127, 127, .14));padding:3px 12px;border-radius:999px;backdrop-filter:blur(4px)}.scroll-down{position:absolute;right:14px;bottom:84px;width:40px;height:40px;border-radius:50%;border:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff));color:var(--ion-color-medium, #92949c);box-shadow:0 2px 10px #00000026;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:1.25rem;z-index:3}.scroll-down .dot{position:absolute;top:0;right:0;width:12px;height:12px;border-radius:50%;background:var(--ion-color-primary);border:2px solid var(--ion-card-background, #fff)}.closed-banner{text-align:center;padding:14px;color:var(--ion-color-medium, #92949c);font-size:.875rem;border-top:1px solid var(--ion-border-color, rgba(0, 0, 0, .08))}\n"] }]
73711
73729
  }], ctorParameters: () => [] });
73712
73730
 
73713
73731
  const THREAD_PANEL_I18N = {
@@ -73813,11 +73831,13 @@ class ThreadPanelComponent {
73813
73831
  this.conversationSvc.stopTyping(conv.convId, orgId, appId);
73814
73832
  }
73815
73833
  }
73816
- onSend(body) {
73834
+ onSend(event) {
73817
73835
  const conv = this.conv();
73818
73836
  if (!conv || !this.conversationSvc)
73819
73837
  return;
73820
- this.conversationSvc.sendMessage(conv.convId, body).subscribe();
73838
+ // thread-panel no tiene pipeline de subida de adjuntos; solo envía el texto.
73839
+ if (event.text)
73840
+ this.conversationSvc.sendMessage(conv.convId, event.text).subscribe();
73821
73841
  }
73822
73842
  onReactionClick(event) {
73823
73843
  // Reacciones se implementan a nivel de app consumer
@@ -73866,7 +73886,7 @@ class ThreadPanelComponent {
73866
73886
  </div>
73867
73887
  }
73868
73888
  </div>
73869
- `, isInline: true, styles: [".thread-panel{display:flex;flex-direction:column;height:100%;background:var(--ion-background-color, #f4f5f8)}.thread-header{padding:12px 16px;border-bottom:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff))}.thread-header .thread-title{font-size:1rem;font-weight:600;color:var(--ion-text-color, #000);margin:0}.thread-loading{display:flex;align-items:center;justify-content:center;flex:1;gap:10px;color:var(--ion-color-medium, #92949c);font-size:.875rem}.thread-chat{flex:1;overflow:hidden;display:flex;flex-direction:column}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: ChatWindowComponent, selector: "val-chat-window", inputs: ["convId", "messages", "currentUserId", "isOpen", "typingUsers", "isLoading"], outputs: ["sendMessage", "loadMore", "reactionClick", "deleteMessage", "replyTo", "editMessage", "typing", "attach", "voice"] }] }); }
73889
+ `, isInline: true, styles: [".thread-panel{display:flex;flex-direction:column;height:100%;background:var(--ion-background-color, #f4f5f8)}.thread-header{padding:12px 16px;border-bottom:1px solid var(--ion-border-color, rgba(0, 0, 0, .1));background:var(--ion-card-background, var(--ion-background-color, #fff))}.thread-header .thread-title{font-size:1rem;font-weight:600;color:var(--ion-text-color, #000);margin:0}.thread-loading{display:flex;align-items:center;justify-content:center;flex:1;gap:10px;color:var(--ion-color-medium, #92949c);font-size:.875rem}.thread-chat{flex:1;overflow:hidden;display:flex;flex-direction:column}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: ChatWindowComponent, selector: "val-chat-window", inputs: ["convId", "messages", "currentUserId", "isOpen", "typingUsers", "isLoading"], outputs: ["sendMessage", "loadMore", "reactionClick", "deleteMessage", "replyTo", "editMessage", "typing", "voice"] }] }); }
73870
73890
  }
73871
73891
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ThreadPanelComponent, decorators: [{
73872
73892
  type: Component,