前端修饰与渲染处理
This commit is contained in:
@@ -1,20 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="center-panel">
|
<div class="center-panel">
|
||||||
<MessageList
|
<MessageList />
|
||||||
:render-markdown="chatInputRef?.renderMarkdown"
|
<ChatInput />
|
||||||
:render-html="chatInputRef?.renderHTML"
|
|
||||||
/>
|
|
||||||
<ChatInput ref="chatInputRef" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { shallowRef } from 'vue';
|
|
||||||
import MessageList from './features/MessageList/MessageList.vue';
|
import MessageList from './features/MessageList/MessageList.vue';
|
||||||
import ChatInput from './features/ChatInput/ChatInput.vue';
|
import ChatInput from './features/ChatInput/ChatInput.vue';
|
||||||
|
|
||||||
// Use shallowRef to prevent Vue from unwrapping the nested refs
|
|
||||||
const chatInputRef = shallowRef<InstanceType<typeof ChatInput> | null>(null);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -16,22 +16,22 @@
|
|||||||
<transition name="slide-down">
|
<transition name="slide-down">
|
||||||
<div v-if="showOptions" class="input-options">
|
<div v-if="showOptions" class="input-options">
|
||||||
<label class="option-checkbox">
|
<label class="option-checkbox">
|
||||||
<input type="checkbox" v-model="renderHTML" />
|
<input type="checkbox" v-model="renderStore.renderHTML" />
|
||||||
<span class="checkmark"></span>
|
<span class="checkmark"></span>
|
||||||
<span class="option-label">HTML渲染</span>
|
<span class="option-label">HTML渲染</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="option-checkbox">
|
<label class="option-checkbox">
|
||||||
<input type="checkbox" v-model="renderMarkdown" />
|
<input type="checkbox" v-model="renderStore.renderMarkdown" />
|
||||||
<span class="checkmark"></span>
|
<span class="checkmark"></span>
|
||||||
<span class="option-label">MD渲染</span>
|
<span class="option-label">MD渲染</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="option-checkbox">
|
<label class="option-checkbox">
|
||||||
<input type="checkbox" v-model="enableDynamicTables" />
|
<input type="checkbox" v-model="renderStore.enableDynamicTables" />
|
||||||
<span class="checkmark"></span>
|
<span class="checkmark"></span>
|
||||||
<span class="option-label">动态表格</span>
|
<span class="option-label">动态表格</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="option-checkbox">
|
<label class="option-checkbox">
|
||||||
<input type="checkbox" v-model="enableDrawing" />
|
<input type="checkbox" v-model="renderStore.enableDrawing" />
|
||||||
<span class="checkmark"></span>
|
<span class="checkmark"></span>
|
||||||
<span class="option-label">绘图</span>
|
<span class="option-label">绘图</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -54,33 +54,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useLocalStorage } from '@/composables/useLocalStorage';
|
import { useMessageRenderStore } from '@/stores/useMessageRenderStore';
|
||||||
|
|
||||||
const showOptions = ref(false);
|
const showOptions = ref(false);
|
||||||
|
|
||||||
// Render options with localStorage persistence
|
// Use Pinia store for render options
|
||||||
const renderMarkdown = useLocalStorage<boolean>('message-render-markdown', true);
|
const renderStore = useMessageRenderStore();
|
||||||
const renderHTML = useLocalStorage<boolean>('message-render-html', true);
|
|
||||||
const enableDynamicTables = useLocalStorage<boolean>('message-render-tables', false);
|
|
||||||
const enableDrawing = useLocalStorage<boolean>('message-render-drawing', false);
|
|
||||||
|
|
||||||
// Debug: Watch for changes
|
|
||||||
watch(renderMarkdown, (newVal) => {
|
|
||||||
console.log('[ChatInput] renderMarkdown changed to:', newVal);
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(renderHTML, (newVal) => {
|
|
||||||
console.log('[ChatInput] renderHTML changed to:', newVal);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Expose render options for parent components to use
|
|
||||||
defineExpose({
|
|
||||||
renderMarkdown,
|
|
||||||
renderHTML,
|
|
||||||
enableDynamicTables,
|
|
||||||
enableDrawing
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,37 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="message-list">
|
<div class="message-list">
|
||||||
<div class="messages-container">
|
<div class="messages-container">
|
||||||
|
<!-- User Message -->
|
||||||
<div class="message user-message">
|
<div class="message user-message">
|
||||||
|
<div class="message-header">
|
||||||
<div class="message-avatar"></div>
|
<div class="message-avatar"></div>
|
||||||
<div class="message-content">
|
<div class="message-meta">
|
||||||
<p v-html="renderMessageContent(userMessage)"></p>
|
|
||||||
<div class="message-actions">
|
|
||||||
<button class="action-btn" title="Edit">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
|
||||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button class="action-btn" title="Delete">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<polyline points="3 6 5 6 21 6"></polyline>
|
|
||||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button class="action-btn" title="Copy">
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
||||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<span class="message-time">10:30 AM</span>
|
<span class="message-time">10:30 AM</span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="message assistant-message">
|
|
||||||
<div class="message-avatar"></div>
|
|
||||||
<div class="message-content">
|
|
||||||
<p v-html="renderMessageContent(assistantMessage)"></p>
|
|
||||||
<div class="message-actions">
|
<div class="message-actions">
|
||||||
<button class="action-btn" title="Edit">
|
<button class="action-btn" title="Edit">
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
@@ -52,14 +27,76 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span class="message-time">10:31 AM</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="message user-message">
|
<div class="message-bubble">
|
||||||
|
<p v-html="renderMessageContent(userMessage)"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Assistant Message -->
|
||||||
|
<div class="message assistant-message">
|
||||||
|
<div class="message-header">
|
||||||
<div class="message-avatar"></div>
|
<div class="message-avatar"></div>
|
||||||
<div class="message-content">
|
<div class="message-meta">
|
||||||
<p>Another message to show the conversation flow.</p>
|
<span class="message-time">10:31 AM</span>
|
||||||
|
<div class="message-actions">
|
||||||
|
<button class="action-btn" title="Edit">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
||||||
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="action-btn" title="Delete">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<polyline points="3 6 5 6 21 6"></polyline>
|
||||||
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="action-btn" title="Copy">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||||
|
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="message-bubble">
|
||||||
|
<p v-html="renderMessageContent(assistantMessage)"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Simple Message -->
|
||||||
|
<div class="message user-message">
|
||||||
|
<div class="message-header">
|
||||||
|
<div class="message-avatar"></div>
|
||||||
|
<div class="message-meta">
|
||||||
<span class="message-time">10:32 AM</span>
|
<span class="message-time">10:32 AM</span>
|
||||||
|
<div class="message-actions">
|
||||||
|
<button class="action-btn" title="Edit">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
||||||
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="action-btn" title="Delete">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<polyline points="3 6 5 6 21 6"></polyline>
|
||||||
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="action-btn" title="Copy">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
|
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||||
|
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="message-bubble">
|
||||||
|
<p>Another message to show the conversation flow.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,48 +104,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, watch } from 'vue';
|
import { useMessageRenderStore } from '@/stores/useMessageRenderStore';
|
||||||
|
|
||||||
// Props from parent component - Vue automatically unwraps refs in templates
|
// Use Pinia store for render options
|
||||||
const props = defineProps<{
|
const renderStore = useMessageRenderStore();
|
||||||
renderMarkdown?: boolean;
|
|
||||||
renderHTML?: boolean;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
// Use props or default to true
|
|
||||||
const renderMarkdown = computed(() => {
|
|
||||||
const value = props.renderMarkdown ?? true;
|
|
||||||
console.log('[MessageList] renderMarkdown:', value);
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
const renderHTML = computed(() => {
|
|
||||||
const value = props.renderHTML ?? true;
|
|
||||||
console.log('[MessageList] renderHTML:', value);
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Watch for changes
|
|
||||||
watch(renderMarkdown, (newVal) => {
|
|
||||||
console.log('[MessageList] renderMarkdown changed to:', newVal);
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(renderHTML, (newVal) => {
|
|
||||||
console.log('[MessageList] renderHTML changed to:', newVal);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Simple Markdown parser for testing (no external dependencies)
|
// Simple Markdown parser for testing (no external dependencies)
|
||||||
function simpleMarkdownParse(text: string): string {
|
function simpleMarkdownParse(text: string): string {
|
||||||
let html = text;
|
let html = text;
|
||||||
|
|
||||||
// If HTML rendering is disabled, escape HTML first
|
// If HTML rendering is disabled, escape HTML first
|
||||||
if (!renderHTML.value) {
|
if (!renderStore.renderHTML) {
|
||||||
html = escapeHtml(html);
|
html = escapeHtml(html);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code blocks (must be processed before other rules to avoid conflicts)
|
// Code blocks (must be processed before other rules to avoid conflicts)
|
||||||
html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (match, lang, code) => {
|
html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (match, lang, code) => {
|
||||||
const escapedCode = renderHTML.value ? code.trim() : escapeHtml(code.trim());
|
const escapedCode = renderStore.renderHTML ? code.trim() : escapeHtml(code.trim());
|
||||||
return `<pre><code class="language-${lang || 'text'}">${escapedCode}</code></pre>`;
|
return `<pre><code class="language-${lang || 'text'}">${escapedCode}</code></pre>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,33 +184,23 @@ function escapeHtml(text: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderMessageContent(content: string): string {
|
function renderMessageContent(content: string): string {
|
||||||
console.log('[renderMessageContent] Input:', content.substring(0, 50));
|
|
||||||
console.log('[renderMessageContent] renderMarkdown:', renderMarkdown.value, 'renderHTML:', renderHTML.value);
|
|
||||||
|
|
||||||
// If neither markdown nor HTML rendering is enabled, escape and return plain text
|
// If neither markdown nor HTML rendering is enabled, escape and return plain text
|
||||||
if (!renderMarkdown.value && !renderHTML.value) {
|
if (!renderStore.renderMarkdown && !renderStore.renderHTML) {
|
||||||
const result = escapeHtml(content);
|
return escapeHtml(content);
|
||||||
console.log('[renderMessageContent] Both disabled, escaped:', result.substring(0, 50));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If markdown rendering is enabled, parse it (HTML rendering controls whether HTML tags are escaped)
|
// If markdown rendering is enabled, parse it (HTML rendering controls whether HTML tags are escaped)
|
||||||
if (renderMarkdown.value) {
|
if (renderStore.renderMarkdown) {
|
||||||
const result = simpleMarkdownParse(content);
|
return simpleMarkdownParse(content);
|
||||||
console.log('[renderMessageContent] Markdown parsed, result length:', result.length);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If only HTML rendering is enabled (no markdown), return as-is
|
// If only HTML rendering is enabled (no markdown), return as-is
|
||||||
if (renderHTML.value) {
|
if (renderStore.renderHTML) {
|
||||||
console.log('[renderMessageContent] HTML only, returning as-is');
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTML rendering disabled, no markdown - escape everything
|
// HTML rendering disabled, no markdown - escape everything
|
||||||
const result = escapeHtml(content);
|
return escapeHtml(content);
|
||||||
console.log('[renderMessageContent] HTML disabled, escaped:', result.substring(0, 50));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sample messages for testing - using LLM provided examples
|
// Sample messages for testing - using LLM provided examples
|
||||||
@@ -263,20 +265,31 @@ const assistantMessage = [
|
|||||||
|
|
||||||
.message {
|
.message {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--spacing-md);
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-xs);
|
||||||
max-width: 75%;
|
max-width: 75%;
|
||||||
animation: fadeIn 0.4s var(--transition-smooth);
|
animation: fadeIn 0.4s var(--transition-smooth);
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-message {
|
.user-message {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
flex-direction: row-reverse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.assistant-message {
|
.assistant-message {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Message Header - Avatar + Meta */
|
||||||
|
.message-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message .message-header {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
.message-avatar {
|
.message-avatar {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
@@ -300,14 +313,22 @@ const assistantMessage = [
|
|||||||
box-shadow: var(--shadow-md), 0 0 0 2px var(--color-accent-light);
|
box-shadow: var(--shadow-md), 0 0 0 2px var(--color-accent-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content {
|
.message-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
gap: var(--spacing-xs);
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message .message-meta {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message Bubble */
|
||||||
|
.message-bubble {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p {
|
.message-bubble p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: var(--spacing-md);
|
padding: var(--spacing-md);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
@@ -319,17 +340,17 @@ const assistantMessage = [
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Markdown rendered content styles */
|
/* Markdown rendered content styles */
|
||||||
.message-content p :deep(strong),
|
.message-bubble p :deep(strong),
|
||||||
.message-content p :deep(b) {
|
.message-bubble p :deep(b) {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p :deep(em),
|
.message-bubble p :deep(em),
|
||||||
.message-content p :deep(i) {
|
.message-bubble p :deep(i) {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p :deep(code) {
|
.message-bubble p :deep(code) {
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -337,11 +358,11 @@ const assistantMessage = [
|
|||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-message .message-content p :deep(code) {
|
.user-message .message-bubble p :deep(code) {
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p :deep(pre) {
|
.message-bubble p :deep(pre) {
|
||||||
background-color: rgba(0, 0, 0, 0.15);
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
padding: var(--spacing-md);
|
padding: var(--spacing-md);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
@@ -349,54 +370,54 @@ const assistantMessage = [
|
|||||||
margin: var(--spacing-sm) 0;
|
margin: var(--spacing-sm) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-message .message-content p :deep(pre) {
|
.user-message .message-bubble p :deep(pre) {
|
||||||
background-color: rgba(255, 255, 255, 0.15);
|
background-color: rgba(255, 255, 255, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p :deep(pre code) {
|
.message-bubble p :deep(pre code) {
|
||||||
background: none;
|
background: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p :deep(ul),
|
.message-bubble p :deep(ul),
|
||||||
.message-content p :deep(ol) {
|
.message-bubble p :deep(ol) {
|
||||||
margin: var(--spacing-sm) 0;
|
margin: var(--spacing-sm) 0;
|
||||||
padding-left: var(--spacing-lg);
|
padding-left: var(--spacing-lg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p :deep(li) {
|
.message-bubble p :deep(li) {
|
||||||
margin: var(--spacing-xs) 0;
|
margin: var(--spacing-xs) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p :deep(blockquote) {
|
.message-bubble p :deep(blockquote) {
|
||||||
border-left: 3px solid var(--color-accent);
|
border-left: 3px solid var(--color-accent);
|
||||||
padding-left: var(--spacing-md);
|
padding-left: var(--spacing-md);
|
||||||
margin: var(--spacing-sm) 0;
|
margin: var(--spacing-sm) 0;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p :deep(h1),
|
.message-bubble p :deep(h1),
|
||||||
.message-content p :deep(h2),
|
.message-bubble p :deep(h2),
|
||||||
.message-content p :deep(h3),
|
.message-bubble p :deep(h3),
|
||||||
.message-content p :deep(h4),
|
.message-bubble p :deep(h4),
|
||||||
.message-content p :deep(h5),
|
.message-bubble p :deep(h5),
|
||||||
.message-content p :deep(h6) {
|
.message-bubble p :deep(h6) {
|
||||||
margin: var(--spacing-md) 0 var(--spacing-sm) 0;
|
margin: var(--spacing-md) 0 var(--spacing-sm) 0;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p :deep(h1) { font-size: 1.5em; }
|
.message-bubble p :deep(h1) { font-size: 1.5em; }
|
||||||
.message-content p :deep(h2) { font-size: 1.3em; }
|
.message-bubble p :deep(h2) { font-size: 1.3em; }
|
||||||
.message-content p :deep(h3) { font-size: 1.1em; }
|
.message-bubble p :deep(h3) { font-size: 1.1em; }
|
||||||
|
|
||||||
.message-content p :deep(a) {
|
.message-bubble p :deep(a) {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content p :deep(a:hover) {
|
.message-bubble p :deep(a:hover) {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,10 +426,9 @@ const assistantMessage = [
|
|||||||
gap: var(--spacing-xs);
|
gap: var(--spacing-xs);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity var(--transition-fast);
|
transition: opacity var(--transition-fast);
|
||||||
margin-top: var(--spacing-xs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content:hover .message-actions {
|
.message-header:hover .message-actions {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,14 +453,14 @@ const assistantMessage = [
|
|||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-message .message-content p {
|
.user-message .message-bubble p {
|
||||||
background: var(--gradient-primary);
|
background: var(--gradient-primary);
|
||||||
color: var(--color-text-inverse);
|
color: var(--color-text-inverse);
|
||||||
border-bottom-right-radius: var(--radius-sm);
|
border-bottom-right-radius: var(--radius-sm);
|
||||||
box-shadow: var(--shadow-md), 0 0 0 1px rgba(91, 127, 255, 0.1);
|
box-shadow: var(--shadow-md), 0 0 0 1px rgba(91, 127, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.assistant-message .message-content p {
|
.assistant-message .message-bubble p {
|
||||||
background-color: var(--color-bg-secondary);
|
background-color: var(--color-bg-secondary);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
border: 1px solid var(--color-border-light);
|
border: 1px solid var(--color-border-light);
|
||||||
@@ -451,14 +471,9 @@ const assistantMessage = [
|
|||||||
.message-time {
|
.message-time {
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
padding: 0 var(--spacing-xs);
|
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-message .message-time {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom scrollbar for webkit browsers */
|
/* Custom scrollbar for webkit browsers */
|
||||||
.message-list::-webkit-scrollbar {
|
.message-list::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
|
|||||||
Reference in New Issue
Block a user