valtech-components 4.0.238 → 4.0.239
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/lib/components/molecules/chat-composer/chat-composer.component.mjs +113 -103
- package/esm2022/lib/version.mjs +2 -2
- package/fesm2022/valtech-components.mjs +111 -101
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/molecules/chat-composer/chat-composer.component.d.ts +15 -13
- package/lib/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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.
|
|
59
|
+
const VERSION = '4.0.239';
|
|
60
60
|
|
|
61
61
|
// Control de estado de refresco (singleton a nivel de módulo)
|
|
62
62
|
let isRefreshing = false;
|
|
@@ -73003,20 +73003,23 @@ const CHAT_COMPOSER_I18N = {
|
|
|
73003
73003
|
},
|
|
73004
73004
|
};
|
|
73005
73005
|
/**
|
|
73006
|
-
* val-chat-composer — barra de redacción
|
|
73006
|
+
* val-chat-composer — barra de redacción estilo Notion/Claude (full custom).
|
|
73007
73007
|
*
|
|
73008
|
-
*
|
|
73009
|
-
*
|
|
73010
|
-
*
|
|
73011
|
-
*
|
|
73012
|
-
*
|
|
73008
|
+
* El editor es un `contenteditable` (NO ion-textarea): crece hasta un max-height,
|
|
73009
|
+
* soporta pegar texto plano e imágenes del portapapeles, Enter envía / Shift+Enter
|
|
73010
|
+
* salta línea. Debajo, una fila de acciones propias: adjuntar (+), mic/enviar.
|
|
73011
|
+
* Los adjuntos (botón / pegar / audio grabado) se acumulan como miniaturas de
|
|
73012
|
+
* PREVIEW y se mandan en UN solo evento `send {text, files}` (caption + adjuntos).
|
|
73013
|
+
*
|
|
73014
|
+
* Pensado para vivir en un contenedor fijo al fondo del viewport (el consumer
|
|
73015
|
+
* ancla la posición). No incluye emoji picker (el teclado del sistema lo trae).
|
|
73013
73016
|
*/
|
|
73014
73017
|
class ChatComposerComponent {
|
|
73015
73018
|
constructor() {
|
|
73016
73019
|
this.i18n = inject(I18nService);
|
|
73017
73020
|
this.placeholder = input('');
|
|
73018
73021
|
this.disabled = input(false);
|
|
73019
|
-
this.maxLength = input(
|
|
73022
|
+
this.maxLength = input(4000);
|
|
73020
73023
|
this.replyingTo = input(null);
|
|
73021
73024
|
this.showAttach = input(true);
|
|
73022
73025
|
this.showMic = input(true);
|
|
@@ -73024,6 +73027,7 @@ class ChatComposerComponent {
|
|
|
73024
73027
|
this.typing = output();
|
|
73025
73028
|
this.voice = output();
|
|
73026
73029
|
this.cancelReply = output();
|
|
73030
|
+
this.editableRef = viewChild('editable');
|
|
73027
73031
|
this.body = signal('');
|
|
73028
73032
|
this.pending = signal([]);
|
|
73029
73033
|
this.pendingId = 0;
|
|
@@ -73039,9 +73043,9 @@ class ChatComposerComponent {
|
|
|
73039
73043
|
});
|
|
73040
73044
|
this.lastTypingEmit = 0;
|
|
73041
73045
|
this.canSend = computed(() => {
|
|
73042
|
-
const t = this.body().trim();
|
|
73043
73046
|
if (this.disabled())
|
|
73044
73047
|
return false;
|
|
73048
|
+
const t = this.body().trim();
|
|
73045
73049
|
return (t.length > 0 && t.length <= this.maxLength()) || this.pending().length > 0;
|
|
73046
73050
|
});
|
|
73047
73051
|
this.placeholderText = computed(() => {
|
|
@@ -73050,9 +73054,12 @@ class ChatComposerComponent {
|
|
|
73050
73054
|
});
|
|
73051
73055
|
this.i18n.registerDefaults('ChatComposer', CHAT_COMPOSER_I18N);
|
|
73052
73056
|
}
|
|
73053
|
-
|
|
73054
|
-
|
|
73055
|
-
this.
|
|
73057
|
+
/** Lee el texto del contenteditable a la signal `body`. */
|
|
73058
|
+
syncBody() {
|
|
73059
|
+
const el = this.editableRef()?.nativeElement;
|
|
73060
|
+
// innerText conserva los saltos de línea; quita el \n final que el navegador agrega.
|
|
73061
|
+
const text = (el?.innerText ?? '').replace(/\n$/, '');
|
|
73062
|
+
this.body.set(text);
|
|
73056
73063
|
this.emitTypingDebounced();
|
|
73057
73064
|
}
|
|
73058
73065
|
onKeydown(event) {
|
|
@@ -73061,11 +73068,30 @@ class ChatComposerComponent {
|
|
|
73061
73068
|
this.onSend();
|
|
73062
73069
|
}
|
|
73063
73070
|
}
|
|
73071
|
+
/** Pegar: texto plano (sin formato) + imágenes del portapapeles. */
|
|
73072
|
+
onPaste(event) {
|
|
73073
|
+
event.preventDefault();
|
|
73074
|
+
const data = event.clipboardData;
|
|
73075
|
+
if (!data)
|
|
73076
|
+
return;
|
|
73077
|
+
// Imágenes (capturas / copiar imagen).
|
|
73078
|
+
for (const item of Array.from(data.items)) {
|
|
73079
|
+
if (item.type.startsWith('image/')) {
|
|
73080
|
+
const file = item.getAsFile();
|
|
73081
|
+
if (file)
|
|
73082
|
+
this.addPending(file);
|
|
73083
|
+
}
|
|
73084
|
+
}
|
|
73085
|
+
// Texto plano insertado en el caret (evita pegar HTML con estilos).
|
|
73086
|
+
const text = data.getData('text/plain');
|
|
73087
|
+
if (text) {
|
|
73088
|
+
document.execCommand('insertText', false, text);
|
|
73089
|
+
}
|
|
73090
|
+
this.syncBody();
|
|
73091
|
+
}
|
|
73064
73092
|
onSend() {
|
|
73065
73093
|
if (!this.canSend())
|
|
73066
73094
|
return;
|
|
73067
|
-
// Un solo evento: texto (caption) + todos los adjuntos staged. El consumer
|
|
73068
|
-
// los sube y manda como UN mensaje con body + attachments.
|
|
73069
73095
|
const files = this.pending().map(p => p.file);
|
|
73070
73096
|
const text = this.body().trim();
|
|
73071
73097
|
this.send.emit({ text, files });
|
|
@@ -73074,6 +73100,12 @@ class ChatComposerComponent {
|
|
|
73074
73100
|
URL.revokeObjectURL(att.url);
|
|
73075
73101
|
}
|
|
73076
73102
|
this.pending.set([]);
|
|
73103
|
+
this.clearEditable();
|
|
73104
|
+
}
|
|
73105
|
+
clearEditable() {
|
|
73106
|
+
const el = this.editableRef()?.nativeElement;
|
|
73107
|
+
if (el)
|
|
73108
|
+
el.innerText = '';
|
|
73077
73109
|
this.body.set('');
|
|
73078
73110
|
}
|
|
73079
73111
|
onFile(event) {
|
|
@@ -73083,22 +73115,6 @@ class ChatComposerComponent {
|
|
|
73083
73115
|
this.addPending(file);
|
|
73084
73116
|
input.value = '';
|
|
73085
73117
|
}
|
|
73086
|
-
/** Pegar imagen desde el portapapeles (captura de pantalla, copiar imagen). */
|
|
73087
|
-
onPaste(event) {
|
|
73088
|
-
const items = event.clipboardData?.items;
|
|
73089
|
-
if (!items)
|
|
73090
|
-
return;
|
|
73091
|
-
for (const item of Array.from(items)) {
|
|
73092
|
-
if (item.type.startsWith('image/')) {
|
|
73093
|
-
const file = item.getAsFile();
|
|
73094
|
-
if (file) {
|
|
73095
|
-
event.preventDefault();
|
|
73096
|
-
this.addPending(file);
|
|
73097
|
-
}
|
|
73098
|
-
return;
|
|
73099
|
-
}
|
|
73100
|
-
}
|
|
73101
|
-
}
|
|
73102
73118
|
addPending(file) {
|
|
73103
73119
|
const kind = file.type.startsWith('image/')
|
|
73104
73120
|
? 'image'
|
|
@@ -73116,9 +73132,6 @@ class ChatComposerComponent {
|
|
|
73116
73132
|
return arr.filter(a => a.id !== id);
|
|
73117
73133
|
});
|
|
73118
73134
|
}
|
|
73119
|
-
/**
|
|
73120
|
-
* Emite typing como máximo 1 vez cada 2s. performance.now es seguro en runtime.
|
|
73121
|
-
*/
|
|
73122
73135
|
emitTypingDebounced() {
|
|
73123
73136
|
const now = performance.now();
|
|
73124
73137
|
if (now - this.lastTypingEmit > 2000) {
|
|
@@ -73127,7 +73140,6 @@ class ChatComposerComponent {
|
|
|
73127
73140
|
}
|
|
73128
73141
|
}
|
|
73129
73142
|
// ─── Grabación de audio ───────────────────────────────────────────────────
|
|
73130
|
-
/** Selecciona un mimeType de audio soportado por el navegador. */
|
|
73131
73143
|
pickAudioMime() {
|
|
73132
73144
|
const candidates = ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4'];
|
|
73133
73145
|
const MR = window.MediaRecorder;
|
|
@@ -73139,7 +73151,7 @@ class ChatComposerComponent {
|
|
|
73139
73151
|
if (this.recording())
|
|
73140
73152
|
return;
|
|
73141
73153
|
if (!navigator.mediaDevices?.getUserMedia || !('MediaRecorder' in window)) {
|
|
73142
|
-
this.voice.emit();
|
|
73154
|
+
this.voice.emit();
|
|
73143
73155
|
return;
|
|
73144
73156
|
}
|
|
73145
73157
|
try {
|
|
@@ -73214,7 +73226,7 @@ class ChatComposerComponent {
|
|
|
73214
73226
|
return this.i18n.t(key, 'ChatComposer');
|
|
73215
73227
|
}
|
|
73216
73228
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
73217
|
-
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: `
|
|
73229
|
+
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" }, viewQueries: [{ propertyName: "editableRef", first: true, predicate: ["editable"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
73218
73230
|
<div class="composer">
|
|
73219
73231
|
@if (replyingTo()) {
|
|
73220
73232
|
<div class="reply-bar">
|
|
@@ -73268,48 +73280,47 @@ class ChatComposerComponent {
|
|
|
73268
73280
|
</button>
|
|
73269
73281
|
</div>
|
|
73270
73282
|
} @else {
|
|
73271
|
-
<div class="
|
|
73272
|
-
|
|
73273
|
-
|
|
73274
|
-
|
|
73275
|
-
|
|
73276
|
-
|
|
73277
|
-
|
|
73278
|
-
|
|
73279
|
-
|
|
73280
|
-
|
|
73281
|
-
|
|
73282
|
-
[disabled]="disabled()"
|
|
73283
|
-
[maxlength]="maxLength()"
|
|
73284
|
-
[autoGrow]="true"
|
|
73285
|
-
[rows]="1"
|
|
73286
|
-
[value]="body()"
|
|
73287
|
-
[spellcheck]="false"
|
|
73288
|
-
autocapitalize="sentences"
|
|
73289
|
-
autocorrect="off"
|
|
73290
|
-
enterkeyhint="enter"
|
|
73291
|
-
(ionInput)="onInput($event)"
|
|
73283
|
+
<div class="shell">
|
|
73284
|
+
<div
|
|
73285
|
+
#editable
|
|
73286
|
+
class="editable"
|
|
73287
|
+
[class.is-empty]="!body()"
|
|
73288
|
+
contenteditable="true"
|
|
73289
|
+
role="textbox"
|
|
73290
|
+
aria-multiline="true"
|
|
73291
|
+
[attr.data-placeholder]="placeholderText()"
|
|
73292
|
+
[attr.aria-label]="placeholderText()"
|
|
73293
|
+
(input)="syncBody()"
|
|
73292
73294
|
(keydown)="onKeydown($event)"
|
|
73293
73295
|
(paste)="onPaste($event)"
|
|
73294
|
-
></
|
|
73296
|
+
></div>
|
|
73295
73297
|
|
|
73296
|
-
|
|
73297
|
-
|
|
73298
|
-
<
|
|
73299
|
-
|
|
73300
|
-
|
|
73301
|
-
|
|
73302
|
-
|
|
73303
|
-
|
|
73304
|
-
|
|
73298
|
+
<div class="actions">
|
|
73299
|
+
@if (showAttach()) {
|
|
73300
|
+
<label class="icon-btn" [attr.aria-label]="t('attach')">
|
|
73301
|
+
<ion-icon name="add-outline" aria-hidden="true" />
|
|
73302
|
+
<input type="file" hidden (change)="onFile($event)" accept="image/*,application/pdf" />
|
|
73303
|
+
</label>
|
|
73304
|
+
}
|
|
73305
|
+
<span class="actions-spacer"></span>
|
|
73306
|
+
@if (canSend()) {
|
|
73307
|
+
<button class="send-btn" [attr.aria-label]="t('send')" (click)="onSend()">
|
|
73308
|
+
<ion-icon name="arrow-up-outline" aria-hidden="true" />
|
|
73309
|
+
</button>
|
|
73310
|
+
} @else if (showMic()) {
|
|
73311
|
+
<button class="icon-btn mic" [attr.aria-label]="t('voice')" (click)="startRecording()">
|
|
73312
|
+
<ion-icon name="mic-outline" aria-hidden="true" />
|
|
73313
|
+
</button>
|
|
73314
|
+
}
|
|
73315
|
+
</div>
|
|
73305
73316
|
</div>
|
|
73306
73317
|
}
|
|
73307
73318
|
</div>
|
|
73308
|
-
`, 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}.
|
|
73319
|
+
`, 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}.shell{display:flex;flex-direction:column;gap:4px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:20px;padding:8px 12px}.editable{min-height:24px;max-height:160px;overflow-y:auto;outline:none;font-size:.9375rem;line-height:1.4;color:var(--ion-text-color, #000);white-space:pre-wrap;word-break:break-word;-webkit-user-select:text;user-select:text}.editable.is-empty:before{content:attr(data-placeholder);color:var(--ion-color-medium, #92949c);pointer-events:none}.actions{display:flex;align-items:center;gap:4px}.actions-spacer{flex:1}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;flex-shrink:0;border:none;border-radius:50%;background:transparent;color:var(--ion-color-medium, #92949c);cursor:pointer;font-size:1.25rem}.icon-btn:hover{color:var(--ion-color-primary)}.send-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;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.125rem;transition:transform .1s ease}.send-btn:active{transform:scale(.92)}.icon-btn.danger{color:var(--ion-color-danger, #eb445a)}.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)}.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:20px;padding:4px 8px}.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: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }] }); }
|
|
73309
73320
|
}
|
|
73310
73321
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ChatComposerComponent, decorators: [{
|
|
73311
73322
|
type: Component,
|
|
73312
|
-
args: [{ selector: 'val-chat-composer', standalone: true, imports: [CommonModule,
|
|
73323
|
+
args: [{ selector: 'val-chat-composer', standalone: true, imports: [CommonModule, IonIcon], template: `
|
|
73313
73324
|
<div class="composer">
|
|
73314
73325
|
@if (replyingTo()) {
|
|
73315
73326
|
<div class="reply-bar">
|
|
@@ -73363,44 +73374,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
73363
73374
|
</button>
|
|
73364
73375
|
</div>
|
|
73365
73376
|
} @else {
|
|
73366
|
-
<div class="
|
|
73367
|
-
|
|
73368
|
-
|
|
73369
|
-
|
|
73370
|
-
|
|
73371
|
-
|
|
73372
|
-
|
|
73373
|
-
|
|
73374
|
-
|
|
73375
|
-
|
|
73376
|
-
|
|
73377
|
-
[disabled]="disabled()"
|
|
73378
|
-
[maxlength]="maxLength()"
|
|
73379
|
-
[autoGrow]="true"
|
|
73380
|
-
[rows]="1"
|
|
73381
|
-
[value]="body()"
|
|
73382
|
-
[spellcheck]="false"
|
|
73383
|
-
autocapitalize="sentences"
|
|
73384
|
-
autocorrect="off"
|
|
73385
|
-
enterkeyhint="enter"
|
|
73386
|
-
(ionInput)="onInput($event)"
|
|
73377
|
+
<div class="shell">
|
|
73378
|
+
<div
|
|
73379
|
+
#editable
|
|
73380
|
+
class="editable"
|
|
73381
|
+
[class.is-empty]="!body()"
|
|
73382
|
+
contenteditable="true"
|
|
73383
|
+
role="textbox"
|
|
73384
|
+
aria-multiline="true"
|
|
73385
|
+
[attr.data-placeholder]="placeholderText()"
|
|
73386
|
+
[attr.aria-label]="placeholderText()"
|
|
73387
|
+
(input)="syncBody()"
|
|
73387
73388
|
(keydown)="onKeydown($event)"
|
|
73388
73389
|
(paste)="onPaste($event)"
|
|
73389
|
-
></
|
|
73390
|
+
></div>
|
|
73390
73391
|
|
|
73391
|
-
|
|
73392
|
-
|
|
73393
|
-
<
|
|
73394
|
-
|
|
73395
|
-
|
|
73396
|
-
|
|
73397
|
-
|
|
73398
|
-
|
|
73399
|
-
|
|
73392
|
+
<div class="actions">
|
|
73393
|
+
@if (showAttach()) {
|
|
73394
|
+
<label class="icon-btn" [attr.aria-label]="t('attach')">
|
|
73395
|
+
<ion-icon name="add-outline" aria-hidden="true" />
|
|
73396
|
+
<input type="file" hidden (change)="onFile($event)" accept="image/*,application/pdf" />
|
|
73397
|
+
</label>
|
|
73398
|
+
}
|
|
73399
|
+
<span class="actions-spacer"></span>
|
|
73400
|
+
@if (canSend()) {
|
|
73401
|
+
<button class="send-btn" [attr.aria-label]="t('send')" (click)="onSend()">
|
|
73402
|
+
<ion-icon name="arrow-up-outline" aria-hidden="true" />
|
|
73403
|
+
</button>
|
|
73404
|
+
} @else if (showMic()) {
|
|
73405
|
+
<button class="icon-btn mic" [attr.aria-label]="t('voice')" (click)="startRecording()">
|
|
73406
|
+
<ion-icon name="mic-outline" aria-hidden="true" />
|
|
73407
|
+
</button>
|
|
73408
|
+
}
|
|
73409
|
+
</div>
|
|
73400
73410
|
</div>
|
|
73401
73411
|
}
|
|
73402
73412
|
</div>
|
|
73403
|
-
`, 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}.
|
|
73413
|
+
`, 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}.shell{display:flex;flex-direction:column;gap:4px;background:var(--ion-color-light, #f4f5f8);border:1px solid var(--ion-border-color, rgba(0, 0, 0, .08));border-radius:20px;padding:8px 12px}.editable{min-height:24px;max-height:160px;overflow-y:auto;outline:none;font-size:.9375rem;line-height:1.4;color:var(--ion-text-color, #000);white-space:pre-wrap;word-break:break-word;-webkit-user-select:text;user-select:text}.editable.is-empty:before{content:attr(data-placeholder);color:var(--ion-color-medium, #92949c);pointer-events:none}.actions{display:flex;align-items:center;gap:4px}.actions-spacer{flex:1}.icon-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;flex-shrink:0;border:none;border-radius:50%;background:transparent;color:var(--ion-color-medium, #92949c);cursor:pointer;font-size:1.25rem}.icon-btn:hover{color:var(--ion-color-primary)}.send-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;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.125rem;transition:transform .1s ease}.send-btn:active{transform:scale(.92)}.icon-btn.danger{color:var(--ion-color-danger, #eb445a)}.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)}.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:20px;padding:4px 8px}.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"] }]
|
|
73404
73414
|
}], ctorParameters: () => [] });
|
|
73405
73415
|
|
|
73406
73416
|
/**
|