From f26d205db8193b4716a2a338cdf644b9950b47ef Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Wed, 2 Sep 2026 11:19:25 +0200 Subject: [PATCH 1/3] fix(chat): redesign row layout for file shares - make a two-line layout (name / extension + size) Assisted-by: ClaudeCode:claude-sonnet-5 Signed-off-by: Maksim Sukharev --- .../Message/MessagePart/FilePreview.vue | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue index eb1bf9b2300..e1fb06e7e21 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue @@ -95,8 +95,11 @@
- {{ fileNameWithoutExtension }} - {{ fileExtension }} + + {{ fileNameWithoutExtension }} + {{ fileExtension }} + + {{ fileMetaLabel }}
@@ -255,6 +258,12 @@ export default { return getFileExtension(this.file.name) }, + fileMetaLabel() { + const size = parseInt(this.file.size, 10) + const sizeLabel = size ? formatFileSize(size, true) : '' + return [this.fileExtension.slice(1).toUpperCase(), sizeLabel].filter(Boolean).join(' · ') + }, + fileSizeLabel() { if (!this.isUploadEditor || !this.uploadFile) { return '' @@ -800,14 +809,21 @@ export default { white-space: nowrap; display: inline-flex; + &__name { + display: inline-flex; + min-width: 0; + } + &__basename { unicode-bidi: isolate; + min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } &__extension { + flex-shrink: 0; color: var(--color-text-maxcontrast); overflow: visible; } @@ -858,6 +874,7 @@ export default { &--row-layout { display: flex; align-items: center; + min-width: 0; border-radius: var(--border-radius); padding: 2px 4px; @@ -867,8 +884,22 @@ export default { } .name-container { + display: flex; + flex-direction: column; + flex: 1 1 auto; + min-width: 0; padding: 0 4px; font-weight: normal; + font-size: var(--font-size-small); + + &__name { + overflow: hidden; + text-overflow: ellipsis; + } + + &__meta { + color: var(--color-text-maxcontrast); + } } } From 29fcd69ee5a29d048117278b9701b4f745abe620 Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Wed, 2 Sep 2026 12:04:43 +0200 Subject: [PATCH 2/3] feat(chat): rearrange grouped file previews Assisted-by: ClaudeCode:claude-sonnet-5 Signed-off-by: Maksim Sukharev --- .../Message/MessagePart/FilePreview.vue | 4 +- .../MessagePart/FilePreviewsWrapper.vue | 135 +++++++++++++++--- 2 files changed, 116 insertions(+), 23 deletions(-) diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue index e1fb06e7e21..6e84140aad9 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreview.vue @@ -803,7 +803,7 @@ export default { } .name-container { - font-weight: bold; + font-weight: normal; width: 100%; overflow: hidden; white-space: nowrap; @@ -866,7 +866,6 @@ export default { // Fixed, so that the height of the tile stays the same for every font .name-container { height: var(--preview-name-height, 24px); - font-weight: normal; font-size: var(--font-size-small); } } @@ -889,7 +888,6 @@ export default { flex: 1 1 auto; min-width: 0; padding: 0 4px; - font-weight: normal; font-size: var(--font-size-small); &__name { diff --git a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreviewsWrapper.vue b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreviewsWrapper.vue index 95e3ce07c4a..9d1eed3aac5 100644 --- a/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreviewsWrapper.vue +++ b/src/components/MessagesList/MessagesGroup/Message/MessagePart/FilePreviewsWrapper.vue @@ -15,11 +15,43 @@ const props = defineProps<{ message: ChatMessage }>() +// Image tiles shown before the rest collapse into a "+X more" tile +const MAX_VISIBLE_IMAGES = 4 + const fileKeys = computed(() => getFilePreviewKeys(props.message)) /** - * Get referenceId of a file parameter to look up a local preview (if available). - * Client-only workaround for combined file messages + * Whether a file parameter is an image or video with a server-side preview (grouped, no name shown) + * + * @param key key of the file parameter ('file', 'file-1', …) + */ +function isImageKey(key: string): boolean { + const file = props.message.messageParameters[key] + const isMedia = file.mimetype?.startsWith('image/') || file.mimetype?.startsWith('video/') || false + // @ts-expect-error: 'localUrl' does not exist in type RichObjectParameter (temporary upload) + return isMedia && (file['preview-available'] === 'yes' || !!file.localUrl) +} + +const imageKeys = computed(() => fileKeys.value.filter(isImageKey)) +const otherKeys = computed(() => fileKeys.value.filter((key) => !isImageKey(key))) + +// Multiple media tiles shrink into a grid; a single one keeps its full size +const isImageRowCombined = computed(() => imageKeys.value.length > 1) + +const hiddenImageCount = computed(() => { + return imageKeys.value.length > MAX_VISIBLE_IMAGES + ? imageKeys.value.length - (MAX_VISIBLE_IMAGES - 1) + : 0 +}) + +// Last tile doubles as the "+X more" tile (dimmed via CSS) when some images are hidden +const visibleImageKeys = computed(() => imageKeys.value.slice(0, MAX_VISIBLE_IMAGES)) + +// Quoted, for the `content` CSS property via v-bind below +const moreCountLabel = computed(() => `"+${hiddenImageCount.value}"`) + +/** + * referenceId of a file parameter, to look up a local preview (client-only workaround) * * @param key key of the file parameter ('file', 'file-1', …) */ @@ -30,38 +62,101 @@ function getReferenceId(key: string): string {