2026-04-11 15:59:14 +08:00
|
|
|
<script setup lang="ts">
|
2026-04-14 14:47:18 +08:00
|
|
|
import type { Message } from "@/stores/chat";
|
|
|
|
|
import { computed, ref } from "vue";
|
|
|
|
|
import { useI18n } from "vue-i18n";
|
|
|
|
|
import MarkdownRenderer from "./MarkdownRenderer.vue";
|
2026-04-11 15:59:14 +08:00
|
|
|
|
2026-04-14 14:47:18 +08:00
|
|
|
const props = defineProps<{ message: Message }>();
|
|
|
|
|
const { t } = useI18n();
|
2026-04-11 15:59:14 +08:00
|
|
|
|
2026-04-14 14:47:18 +08:00
|
|
|
const isSystem = computed(() => props.message.role === "system");
|
|
|
|
|
const toolExpanded = ref(false);
|
2026-04-11 15:59:14 +08:00
|
|
|
|
|
|
|
|
const timeStr = computed(() => {
|
2026-04-14 14:47:18 +08:00
|
|
|
const d = new Date(props.message.timestamp);
|
|
|
|
|
return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
|
|
|
});
|
2026-04-11 18:54:46 +08:00
|
|
|
|
|
|
|
|
function isImage(type: string): boolean {
|
2026-04-14 14:47:18 +08:00
|
|
|
return type.startsWith("image/");
|
2026-04-11 18:54:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatSize(bytes: number): string {
|
2026-04-14 14:47:18 +08:00
|
|
|
if (bytes < 1024) return bytes + " B";
|
|
|
|
|
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
|
|
|
|
|
return (bytes / (1024 * 1024)).toFixed(1) + " MB";
|
2026-04-11 18:54:46 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-14 14:47:18 +08:00
|
|
|
const hasAttachments = computed(
|
|
|
|
|
() => (props.message.attachments?.length ?? 0) > 0,
|
|
|
|
|
);
|
2026-04-12 23:59:18 +08:00
|
|
|
|
2026-04-14 14:47:18 +08:00
|
|
|
const hasToolDetails = computed(
|
|
|
|
|
() => !!(props.message.toolArgs || props.message.toolResult),
|
|
|
|
|
);
|
2026-04-12 23:59:18 +08:00
|
|
|
|
|
|
|
|
const formattedToolArgs = computed(() => {
|
2026-04-14 14:47:18 +08:00
|
|
|
if (!props.message.toolArgs) return "";
|
2026-04-12 23:59:18 +08:00
|
|
|
try {
|
2026-04-14 14:47:18 +08:00
|
|
|
return JSON.stringify(JSON.parse(props.message.toolArgs), null, 2);
|
2026-04-12 23:59:18 +08:00
|
|
|
} catch {
|
2026-04-14 14:47:18 +08:00
|
|
|
return props.message.toolArgs;
|
2026-04-12 23:59:18 +08:00
|
|
|
}
|
2026-04-14 14:47:18 +08:00
|
|
|
});
|
2026-04-12 23:59:18 +08:00
|
|
|
|
|
|
|
|
const formattedToolResult = computed(() => {
|
2026-04-14 14:47:18 +08:00
|
|
|
if (!props.message.toolResult) return "";
|
2026-04-12 23:59:18 +08:00
|
|
|
try {
|
2026-04-14 14:47:18 +08:00
|
|
|
const parsed = JSON.parse(props.message.toolResult);
|
|
|
|
|
const str = JSON.stringify(parsed, null, 2);
|
2026-04-12 23:59:18 +08:00
|
|
|
// Truncate very long output
|
2026-04-14 14:47:18 +08:00
|
|
|
if (str.length > 2000)
|
|
|
|
|
return str.slice(0, 2000) + "\n" + t("chat.truncated");
|
|
|
|
|
return str;
|
2026-04-12 23:59:18 +08:00
|
|
|
} catch {
|
2026-04-14 14:47:18 +08:00
|
|
|
const raw = props.message.toolResult;
|
|
|
|
|
if (raw.length > 2000)
|
|
|
|
|
return raw.slice(0, 2000) + "\n" + t("chat.truncated");
|
|
|
|
|
return raw;
|
2026-04-12 23:59:18 +08:00
|
|
|
}
|
2026-04-14 14:47:18 +08:00
|
|
|
});
|
2026-04-11 15:59:14 +08:00
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div class="message" :class="[message.role]">
|
|
|
|
|
<template v-if="message.role === 'tool'">
|
2026-04-14 14:47:18 +08:00
|
|
|
<div
|
|
|
|
|
class="tool-line"
|
|
|
|
|
:class="{ expandable: hasToolDetails }"
|
|
|
|
|
@click="hasToolDetails && (toolExpanded = !toolExpanded)"
|
|
|
|
|
>
|
|
|
|
|
<svg
|
|
|
|
|
v-if="hasToolDetails"
|
|
|
|
|
width="10"
|
|
|
|
|
height="10"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
stroke-width="2"
|
|
|
|
|
class="tool-chevron"
|
|
|
|
|
:class="{ rotated: toolExpanded }"
|
|
|
|
|
>
|
|
|
|
|
<polyline points="9 18 15 12 9 6" />
|
|
|
|
|
</svg>
|
|
|
|
|
<svg
|
|
|
|
|
v-else
|
|
|
|
|
width="12"
|
|
|
|
|
height="12"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
stroke-width="1.5"
|
|
|
|
|
class="tool-icon"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"
|
|
|
|
|
/>
|
|
|
|
|
</svg>
|
2026-04-11 15:59:14 +08:00
|
|
|
<span class="tool-name">{{ message.toolName }}</span>
|
2026-04-14 14:47:18 +08:00
|
|
|
<span
|
|
|
|
|
v-if="message.toolPreview && !toolExpanded"
|
|
|
|
|
class="tool-preview"
|
|
|
|
|
>{{ message.toolPreview }}</span
|
|
|
|
|
>
|
|
|
|
|
<span
|
|
|
|
|
v-if="message.toolStatus === 'running'"
|
|
|
|
|
class="tool-spinner"
|
|
|
|
|
></span>
|
|
|
|
|
<span v-if="message.toolStatus === 'error'" class="tool-error-badge">{{
|
|
|
|
|
t("chat.error")
|
|
|
|
|
}}</span>
|
2026-04-12 23:59:18 +08:00
|
|
|
</div>
|
|
|
|
|
<div v-if="toolExpanded && hasToolDetails" class="tool-details">
|
|
|
|
|
<div v-if="formattedToolArgs" class="tool-detail-section">
|
2026-04-14 14:47:18 +08:00
|
|
|
<div class="tool-detail-label">{{ t("chat.arguments") }}</div>
|
2026-04-12 23:59:18 +08:00
|
|
|
<pre class="tool-detail-code">{{ formattedToolArgs }}</pre>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="formattedToolResult" class="tool-detail-section">
|
2026-04-14 14:47:18 +08:00
|
|
|
<div class="tool-detail-label">{{ t("chat.result") }}</div>
|
2026-04-12 23:59:18 +08:00
|
|
|
<pre class="tool-detail-code">{{ formattedToolResult }}</pre>
|
|
|
|
|
</div>
|
2026-04-11 15:59:14 +08:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>
|
|
|
|
|
<div class="msg-body">
|
2026-04-14 14:47:18 +08:00
|
|
|
<img
|
|
|
|
|
v-if="message.role === 'assistant'"
|
2026-04-14 21:48:53 +08:00
|
|
|
src="/logo.png"
|
2026-04-14 14:47:18 +08:00
|
|
|
alt="Hermes"
|
|
|
|
|
class="msg-avatar"
|
|
|
|
|
/>
|
2026-04-11 15:59:14 +08:00
|
|
|
<div class="msg-content" :class="message.role">
|
|
|
|
|
<div class="message-bubble" :class="{ system: isSystem }">
|
2026-04-11 18:54:46 +08:00
|
|
|
<div v-if="hasAttachments" class="msg-attachments">
|
|
|
|
|
<div
|
|
|
|
|
v-for="att in message.attachments"
|
|
|
|
|
:key="att.id"
|
|
|
|
|
class="msg-attachment"
|
|
|
|
|
:class="{ image: isImage(att.type) }"
|
|
|
|
|
>
|
|
|
|
|
<template v-if="isImage(att.type) && att.url">
|
2026-04-14 14:47:18 +08:00
|
|
|
<img
|
|
|
|
|
:src="att.url"
|
|
|
|
|
:alt="att.name"
|
|
|
|
|
class="msg-attachment-thumb"
|
|
|
|
|
/>
|
2026-04-11 18:54:46 +08:00
|
|
|
</template>
|
|
|
|
|
<template v-else>
|
|
|
|
|
<div class="msg-attachment-file">
|
2026-04-14 14:47:18 +08:00
|
|
|
<svg
|
|
|
|
|
width="16"
|
|
|
|
|
height="16"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
stroke-width="1.5"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
|
|
|
|
|
/>
|
|
|
|
|
<polyline points="14 2 14 8 20 8" />
|
|
|
|
|
</svg>
|
2026-04-11 18:54:46 +08:00
|
|
|
<span class="att-name">{{ att.name }}</span>
|
|
|
|
|
<span class="att-size">{{ formatSize(att.size) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-04-14 14:47:18 +08:00
|
|
|
<MarkdownRenderer
|
|
|
|
|
v-if="message.content"
|
|
|
|
|
:content="message.content"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<span v-if="message.isStreaming && !message.content" class="streaming-dots">
|
2026-04-11 15:59:14 +08:00
|
|
|
<span></span><span></span><span></span>
|
2026-04-14 14:47:18 +08:00
|
|
|
</span>
|
2026-04-11 15:59:14 +08:00
|
|
|
</div>
|
|
|
|
|
<div class="message-time">{{ timeStr }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
2026-04-14 14:47:18 +08:00
|
|
|
@use "@/styles/variables" as *;
|
2026-04-11 15:59:14 +08:00
|
|
|
|
|
|
|
|
.message {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
|
|
|
|
&.user {
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
|
|
|
|
|
.msg-body {
|
|
|
|
|
max-width: 75%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.msg-content.user {
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-bubble {
|
|
|
|
|
background-color: $msg-user-bg;
|
|
|
|
|
border-radius: $radius-md $radius-md 4px $radius-md;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.assistant {
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
|
|
|
|
.msg-body {
|
|
|
|
|
max-width: 80%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.msg-avatar {
|
2026-04-14 14:47:18 +08:00
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
2026-04-11 15:59:14 +08:00
|
|
|
flex-shrink: 0;
|
|
|
|
|
margin-top: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-bubble {
|
|
|
|
|
background-color: $msg-assistant-bg;
|
|
|
|
|
border-radius: $radius-md $radius-md $radius-md 4px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.tool {
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.system {
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
|
|
|
|
.message-bubble.system {
|
|
|
|
|
border-left: 3px solid $warning;
|
|
|
|
|
border-radius: $radius-sm;
|
|
|
|
|
max-width: 80%;
|
|
|
|
|
background-color: rgba($warning, 0.06);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.msg-body {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
max-width: 85%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.msg-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
min-width: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-bubble {
|
|
|
|
|
padding: 10px 14px;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 1.65;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 18:54:46 +08:00
|
|
|
.msg-attachments {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.msg-attachment {
|
|
|
|
|
border-radius: $radius-sm;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
background-color: rgba(0, 0, 0, 0.04);
|
|
|
|
|
border: 1px solid $border-light;
|
|
|
|
|
|
|
|
|
|
&.image {
|
|
|
|
|
max-width: 200px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.msg-attachment-thumb {
|
|
|
|
|
display: block;
|
|
|
|
|
max-width: 200px;
|
|
|
|
|
max-height: 160px;
|
|
|
|
|
object-fit: contain;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.msg-attachment-file {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
padding: 6px 10px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: $text-secondary;
|
|
|
|
|
|
|
|
|
|
.att-name {
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
max-width: 160px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.att-size {
|
|
|
|
|
color: $text-muted;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 15:59:14 +08:00
|
|
|
.message-time {
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
color: $text-muted;
|
|
|
|
|
margin-top: 4px;
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tool-line {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
color: $text-muted;
|
2026-04-12 23:59:18 +08:00
|
|
|
padding: 2px 4px;
|
|
|
|
|
border-radius: $radius-sm;
|
|
|
|
|
|
|
|
|
|
&.expandable {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
background: rgba(0, 0, 0, 0.03);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-11 15:59:14 +08:00
|
|
|
|
|
|
|
|
.tool-name {
|
|
|
|
|
font-family: $font-code;
|
2026-04-12 23:59:18 +08:00
|
|
|
flex-shrink: 0;
|
2026-04-11 15:59:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tool-preview {
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
max-width: 400px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-12 23:59:18 +08:00
|
|
|
.tool-chevron {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
transition: transform 0.15s ease;
|
|
|
|
|
|
|
|
|
|
&.rotated {
|
|
|
|
|
transform: rotate(90deg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tool-spinner {
|
|
|
|
|
width: 10px;
|
|
|
|
|
height: 10px;
|
|
|
|
|
border: 1.5px solid $text-muted;
|
|
|
|
|
border-top-color: transparent;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
animation: spin 0.6s linear infinite;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tool-error-badge {
|
|
|
|
|
font-size: 9px;
|
|
|
|
|
color: $error;
|
|
|
|
|
background: rgba($error, 0.08);
|
|
|
|
|
padding: 0 4px;
|
|
|
|
|
border-radius: 3px;
|
|
|
|
|
line-height: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tool-details {
|
|
|
|
|
margin-left: 16px;
|
|
|
|
|
margin-top: 2px;
|
|
|
|
|
border-left: 2px solid $border-light;
|
|
|
|
|
padding-left: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tool-detail-section {
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tool-detail-label {
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: $text-muted;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.3px;
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tool-detail-code {
|
|
|
|
|
font-family: $font-code;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
color: $text-secondary;
|
|
|
|
|
background: $code-bg;
|
|
|
|
|
border-radius: $radius-sm;
|
|
|
|
|
padding: 6px 8px;
|
|
|
|
|
margin: 0;
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
max-height: 300px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes spin {
|
2026-04-14 14:47:18 +08:00
|
|
|
to {
|
|
|
|
|
transform: rotate(360deg);
|
|
|
|
|
}
|
2026-04-12 23:59:18 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-11 15:59:14 +08:00
|
|
|
.streaming-cursor {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
width: 2px;
|
|
|
|
|
height: 1em;
|
|
|
|
|
background-color: $text-muted;
|
|
|
|
|
margin-left: 2px;
|
|
|
|
|
vertical-align: text-bottom;
|
|
|
|
|
animation: blink 0.8s infinite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.streaming-dots {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
padding: 4px 0;
|
|
|
|
|
|
|
|
|
|
span {
|
|
|
|
|
width: 6px;
|
|
|
|
|
height: 6px;
|
|
|
|
|
background-color: $text-muted;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
animation: pulse 1.4s infinite ease-in-out;
|
|
|
|
|
|
|
|
|
|
&:nth-child(2) { animation-delay: 0.2s; }
|
|
|
|
|
&:nth-child(3) { animation-delay: 0.4s; }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes blink {
|
2026-04-14 14:47:18 +08:00
|
|
|
0%,
|
|
|
|
|
50% {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
51%,
|
|
|
|
|
100% {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
}
|
2026-04-11 15:59:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes pulse {
|
2026-04-14 14:47:18 +08:00
|
|
|
0%,
|
|
|
|
|
80%,
|
|
|
|
|
100% {
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
transform: scale(0.8);
|
|
|
|
|
}
|
|
|
|
|
40% {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
}
|
2026-04-11 15:59:14 +08:00
|
|
|
}
|
2026-04-15 09:12:54 +08:00
|
|
|
|
|
|
|
|
@media (max-width: $breakpoint-mobile) {
|
2026-04-15 10:28:53 +08:00
|
|
|
.message.user .msg-body {
|
|
|
|
|
max-width: 100%;
|
2026-04-15 09:12:54 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-15 10:28:53 +08:00
|
|
|
.message.assistant .msg-body {
|
|
|
|
|
max-width: 100%;
|
2026-04-15 09:12:54 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-15 10:28:53 +08:00
|
|
|
.message.system .msg-body {
|
|
|
|
|
max-width: 100%;
|
2026-04-15 09:12:54 +08:00
|
|
|
}
|
|
|
|
|
}
|
2026-04-11 15:59:14 +08:00
|
|
|
</style>
|