From 2f09b3fabf2e6a0e8bd5e49cf0b9321e133e1c74 Mon Sep 17 00:00:00 2001 From: wanghanlin <1533525126@qq.com> Date: Tue, 18 Aug 2026 11:52:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(client):=20SDK=20=E6=82=AC=E6=B5=AE?= =?UTF-8?q?=E7=AA=97=E5=8F=A3=E5=AF=B9=E8=AF=9D=E8=A7=86=E8=A7=89=E5=AF=B9?= =?UTF-8?q?=E9=BD=90=20TDesign=20Chat=20=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - styles.ts 对齐气泡圆角/头像/时间戳/输入框/发送按钮/加载态/操作栏/来源卡片/快捷芯片视觉 - dom.ts 微调时间戳与操作栏相对位置 - 保持零依赖 IIFE、csk-* 类名零增删改、主色变量与暗色模式同步 --- client/src/dom.ts | 10 +- client/src/styles.ts | 231 ++++++++++++++++++++++++------------------- 2 files changed, 132 insertions(+), 109 deletions(-) diff --git a/client/src/dom.ts b/client/src/dom.ts index 22f208d..61223ba 100644 --- a/client/src/dom.ts +++ b/client/src/dom.ts @@ -831,8 +831,8 @@ export function renderUserBubble(container: HTMLElement, text: string, timestamp time.className = 'csk-msg__time'; time.textContent = formatTime(timestamp); - content.appendChild(bubble); content.appendChild(time); + content.appendChild(bubble); wrapper.appendChild(avatar); wrapper.appendChild(content); container.appendChild(wrapper); @@ -866,11 +866,11 @@ export function renderAIBubble(container: HTMLElement, text: string, timestamp: time.className = 'csk-msg__time'; time.textContent = formatTime(timestamp); + content.appendChild(time); content.appendChild(bubble); - // 元信息行:时间戳居左,操作按钮居右 + // 操作栏:位于气泡下方 const meta = document.createElement('div'); meta.className = 'csk-msg__meta'; - meta.appendChild(time); meta.appendChild(buildAIActions(bubble, wrapper)); content.appendChild(meta); wrapper.appendChild(avatar); @@ -904,11 +904,11 @@ export function createEmptyAIBubble(container: HTMLElement, timestamp: number, m time.className = 'csk-msg__time'; time.textContent = formatTime(timestamp); + content.appendChild(time); content.appendChild(bubble); - // 元信息行:时间戳居左,操作按钮居右 + // 操作栏:位于气泡下方 const meta = document.createElement('div'); meta.className = 'csk-msg__meta'; - meta.appendChild(time); meta.appendChild(buildAIActions(bubble, wrapper)); content.appendChild(meta); wrapper.appendChild(avatar); diff --git a/client/src/styles.ts b/client/src/styles.ts index 36c20e0..80fce56 100644 --- a/client/src/styles.ts +++ b/client/src/styles.ts @@ -27,16 +27,19 @@ function cssVars(config: ResolvedConfig): string { --csk-primary-light: ${lighter}; --csk-primary-rgb: ${rgb}; --csk-bg-user: linear-gradient(135deg, ${config.primaryColor}, ${darker}); - --csk-bg-ai: #ffffff; + --csk-bg-ai: #F3F3F3; --csk-text-user: #ffffff; - --csk-text-ai: #1F2937; + --csk-text-ai: rgba(0, 0, 0, 0.9); --csk-window-width: ${config.width}px; --csk-window-height: ${config.height}px; --csk-radius: 16px; --csk-shadow-window: 0 12px 48px rgba(15, 23, 42, 0.18), 0 2px 8px rgba(15, 23, 42, 0.06); - --csk-shadow-bubble: 0 1px 2px rgba(15, 23, 42, 0.06); - --csk-border: #ECEEF2; - --csk-bg-app: #F6F7F9; + --csk-shadow-bubble: 0 1px 10px rgba(0, 0, 0, 0.05), 0 4px 5px rgba(0, 0, 0, 0.08); + --csk-border: #E8E8E8; + --csk-border-strong: #DDDDDD; + --csk-text-secondary: rgba(0, 0, 0, 0.6); + --csk-text-muted: rgba(0, 0, 0, 0.4); + --csk-bg-app: #FFFFFF; `; } @@ -320,7 +323,7 @@ function getStyles(config: ResolvedConfig): string { .csk-messages { flex: 1; overflow-y: auto; - padding: 20px 16px 12px; + padding: 16px; background: var(--csk-bg-app); scroll-behavior: smooth; } @@ -392,7 +395,7 @@ function getStyles(config: ResolvedConfig): string { /* 消息气泡 */ .csk-msg { display: flex; - margin-bottom: 12px; + margin-bottom: 16px; max-width: 100%; word-break: break-word; animation: csk-msg-in 0.28s cubic-bezier(0.22, 1, 0.36, 1); @@ -406,8 +409,8 @@ function getStyles(config: ResolvedConfig): string { align-items: flex-start; } .csk-msg__avatar { - width: 30px; - height: 30px; + width: 32px; + height: 32px; border-radius: 50%; display: flex; align-items: center; @@ -426,17 +429,19 @@ function getStyles(config: ResolvedConfig): string { background: var(--csk-bg-user); color: #fff; } +/* AI 头像与气泡顶部对齐(时间戳位于气泡上方时下移) */ +.csk-msg--ai .csk-msg__avatar { margin-top: 20px; } .csk-msg__content { display: flex; flex-direction: column; min-width: 0; - max-width: calc(100% - 46px); + max-width: calc(100% - 48px); } .csk-msg--user .csk-msg__content { align-items: flex-end; } .csk-msg--ai .csk-msg__content { align-items: flex-start; } .csk-msg__bubble { - padding: 10px 14px; - border-radius: 16px; + padding: 12px 16px; + border-radius: 12px; font-size: 14px; line-height: 1.6; box-shadow: var(--csk-shadow-bubble); @@ -444,30 +449,27 @@ function getStyles(config: ResolvedConfig): string { .csk-msg--user .csk-msg__bubble { background: var(--csk-bg-user); color: var(--csk-text-user); - border-radius: 16px 16px 4px 16px; } .csk-msg--ai .csk-msg__bubble { background: var(--csk-bg-ai); color: var(--csk-text-ai); - border-radius: 16px 16px 16px 4px; - border: 1px solid var(--csk-border); } .csk-msg__time { - font-size: 11px; - color: #9CA3AF; - margin-top: 3px; - padding: 0 4px; + font-size: 12px; + line-height: 1.4; + color: var(--csk-text-muted); + margin-bottom: 4px; + padding: 0 2px; + user-select: none; } -/* 元信息行:时间戳居左,操作按钮居右,共用一行 */ +/* 操作栏容器:位于气泡下方 */ .csk-msg__meta { display: flex; - justify-content: space-between; align-items: center; - margin-top: 3px; + margin-top: 6px; width: 100%; } -.csk-msg__meta .csk-msg__time { margin-top: 0; } @keyframes csk-msg-in { from { opacity: 0; transform: translateY(8px); } @@ -482,12 +484,12 @@ function getStyles(config: ResolvedConfig): string { .csk-loading { display: flex; align-items: center; - margin-bottom: 12px; + margin-bottom: 16px; animation: csk-msg-in 0.28s ease; } .csk-loading__avatar { - width: 30px; - height: 30px; + width: 32px; + height: 32px; border-radius: 50%; background: #fff; color: var(--csk-primary); @@ -501,28 +503,37 @@ function getStyles(config: ResolvedConfig): string { } .csk-loading__avatar svg { width: 18px; height: 18px; } .csk-loading__bubble { - background: #fff; - border: 1px solid var(--csk-border); - border-radius: 16px 16px 16px 4px; - padding: 12px 16px; - display: flex; - align-items: center; - gap: 5px; + position: relative; + width: 40px; + height: 40px; + padding: 0; + background: var(--csk-bg-ai); + border-radius: 12px; + box-shadow: var(--csk-shadow-bubble); } .csk-loading__dot { - width: 7px; - height: 7px; + position: absolute; + width: 8px; + height: 8px; border-radius: 50%; - background: #B4B9C2; - animation: csk-bounce 1.4s ease-in-out infinite both; + background: var(--csk-primary); } -.csk-loading__dot:nth-child(1) { animation-delay: 0s; } -.csk-loading__dot:nth-child(2) { animation-delay: 0.16s; } -.csk-loading__dot:nth-child(3) { animation-delay: 0.32s; } +/* TDesign 三点加载:三圆呈三角排列并向中心聚拢 */ +.csk-loading__dot:nth-child(1) { top: 9px; left: 16px; animation: csk-dot-top 0.6s ease-in-out alternate infinite; } +.csk-loading__dot:nth-child(2) { bottom: 9px; left: 9px; animation: csk-dot-left 0.6s ease-in-out alternate infinite; } +.csk-loading__dot:nth-child(3) { bottom: 9px; right: 9px; animation: csk-dot-right 0.6s ease-in-out alternate infinite; } -@keyframes csk-bounce { - 0%, 80%, 100% { transform: scale(0.6); opacity: 0.6; } - 40% { transform: scale(1); opacity: 1; } +@keyframes csk-dot-top { + from { transform: scale(1); top: 9px; } + to { transform: scale(1.75); top: 16px; } +} +@keyframes csk-dot-left { + from { transform: scale(1); bottom: 9px; left: 9px; } + to { transform: scale(1.75); bottom: 16px; left: 16px; } +} +@keyframes csk-dot-right { + from { transform: scale(1); bottom: 9px; right: 9px; } + to { transform: scale(1.75); bottom: 16px; right: 16px; } } /* ========== 输入区 ========== */ @@ -536,16 +547,23 @@ function getStyles(config: ResolvedConfig): string { display: flex; align-items: flex-end; gap: 8px; - background: var(--csk-bg-app); - border: 1px solid var(--csk-border); - border-radius: 14px; + background: #fff; + border: 1px solid var(--csk-border-strong); + border-radius: 16px; padding: 6px 6px 6px 14px; - transition: border-color 0.2s, box-shadow 0.2s, background 0.2s; + transition: border-color 0.2s, box-shadow 0.2s; +} +.csk-input-wrap:hover { + border-color: var(--csk-primary); + box-shadow: var(--csk-shadow-bubble); } .csk-input-wrap--focus { border-color: var(--csk-primary); background: #fff; - box-shadow: 0 0 0 3px rgba(var(--csk-primary-rgb), 0.12); + box-shadow: 0 0 0 2px rgba(var(--csk-primary-rgb), 0.2); +} +.csk-input-wrap--focus:hover { + box-shadow: 0 0 0 2px rgba(var(--csk-primary-rgb), 0.2); } .csk-input { flex: 1; @@ -561,17 +579,17 @@ function getStyles(config: ResolvedConfig): string { line-height: 1.5; color: #1F2937; } -.csk-input::placeholder { color: #9CA3AF; } +.csk-input::placeholder { color: var(--csk-text-muted); } .csk-root .csk-input:focus-visible { outline: none; } .csk-send-btn { display: flex; align-items: center; justify-content: center; - width: 36px; - height: 36px; - min-width: 36px; + width: 32px; + height: 32px; + min-width: 32px; border: none; - border-radius: 10px; + border-radius: 50%; background: var(--csk-bg-user); color: #fff; cursor: pointer; @@ -584,7 +602,7 @@ function getStyles(config: ResolvedConfig): string { } .csk-send-btn:active:not(:disabled) { transform: scale(0.92); } .csk-send-btn:disabled { - background: #D1D5DB; + background: var(--csk-border-strong); box-shadow: none; cursor: not-allowed; opacity: 0.7; @@ -667,25 +685,25 @@ function getStyles(config: ResolvedConfig): string { /* ========== P1: RAG 引用来源卡片 ========== */ .csk-sources { - margin-top: 6px; + margin-top: 8px; border: 1px solid var(--csk-border); - border-radius: 12px; + border-radius: 8px; overflow: hidden; font-size: 12px; max-width: 100%; - background: #FAFBFC; + background: #fff; } .csk-sources__header { display: flex; align-items: center; justify-content: space-between; padding: 6px 10px; - background: #F3F4F7; + background: var(--csk-bg-ai); cursor: pointer; user-select: none; transition: background 0.15s; } -.csk-sources__header:hover { background: #ECEEF2; } +.csk-sources__header:hover { background: var(--csk-border); } .csk-sources__title { display: flex; align-items: center; @@ -710,7 +728,7 @@ function getStyles(config: ResolvedConfig): string { transition: background 0.15s; } .csk-source-item:last-child { border-bottom: none; } -.csk-source-item:hover { background: #fff; } +.csk-source-item:hover { background: var(--csk-bg-ai); } .csk-source-item__name { font-weight: 500; color: #1F2937; @@ -990,23 +1008,21 @@ function getStyles(config: ResolvedConfig): string { max-width: 320px; } .csk-quick-reply { - border: 1px solid var(--csk-border); - background: #fff; - color: #374151; - font-size: 12.5px; - padding: 7px 13px; - border-radius: 999px; + border: 1px solid transparent; + background: var(--csk-bg-ai); + color: var(--csk-text-secondary); + font-size: 12px; + padding: 6px 12px; + border-radius: 6px; cursor: pointer; font-family: inherit; line-height: 1.3; - transition: all 0.18s ease; + transition: all 0.15s ease; max-width: 100%; } .csk-quick-reply:hover { - border-color: var(--csk-primary); - color: var(--csk-primary); - background: rgba(var(--csk-primary-rgb), 0.06); - transform: translateY(-1px); + background: var(--csk-border); + color: var(--csk-text-ai); } .csk-quick-reply:active { transform: scale(0.96); } @@ -1021,32 +1037,34 @@ function getStyles(config: ResolvedConfig): string { gap: 4px; } .csk-suggestion-item { - border: 1px solid var(--csk-border); - background: #fff; - color: #374151; + border: 1px solid transparent; + background: var(--csk-bg-ai); + color: var(--csk-text-secondary); font-size: 12px; padding: 4px 10px; - border-radius: 999px; + border-radius: 6px; cursor: pointer; font-family: inherit; line-height: 1.3; - transition: all 0.18s ease; + transition: all 0.15s ease; max-width: 100%; text-align: left; } .csk-suggestion-item:hover { - border-color: var(--csk-primary); - color: var(--csk-primary); - background: rgba(var(--csk-primary-rgb), 0.06); - transform: translateY(-1px); + background: var(--csk-border); + color: var(--csk-text-ai); } .csk-suggestion-item:active { transform: scale(0.96); } -/* ========== AI 消息操作条 ========== */ +/* ========== AI 消息操作条(TDesign 操作条:浅灰胶囊容器) ========== */ .csk-msg__actions { display: flex; - gap: 2px; - padding: 0 2px; + gap: 0; + padding: 3px; + background: var(--csk-bg-ai); + border: 1px solid var(--csk-border-strong); + border-radius: 6px; + overflow: hidden; opacity: 0; transition: opacity 0.15s ease; } @@ -1056,18 +1074,18 @@ function getStyles(config: ResolvedConfig): string { display: flex; align-items: center; justify-content: center; - width: 26px; - height: 26px; + width: 24px; + height: 24px; border: none; background: transparent; - color: #9CA3AF; - border-radius: 6px; + color: var(--csk-text-secondary); + border-radius: 4px; cursor: pointer; - transition: all 0.15s ease; + transition: background 0.15s ease, color 0.15s ease; } .csk-action-btn:hover { - background: #F3F4F6; - color: #374151; + background: var(--csk-border); + color: var(--csk-text-ai); } .csk-action-btn--done { color: #10B981; } .csk-action-btn:active { transform: scale(0.9); } @@ -1239,6 +1257,9 @@ function getStyles(config: ResolvedConfig): string { --csk-bg-ai: #1E1E2E; --csk-text-ai: #E2E8F0; --csk-border: #3B3B52; + --csk-border-strong: #454560; + --csk-text-secondary: rgba(255, 255, 255, 0.6); + --csk-text-muted: rgba(255, 255, 255, 0.4); --csk-bg-app: #141421; color: #E2E8F0; } @@ -1256,12 +1277,13 @@ function getStyles(config: ResolvedConfig): string { border-color: var(--csk-border); } .csk-dark .csk-input-area { background: #1A1A2E; border-top-color: var(--csk-border); } -.csk-dark .csk-input-wrap { background: var(--csk-bg-app); border-color: var(--csk-border); } +.csk-dark .csk-input-wrap { background: var(--csk-bg-app); border-color: var(--csk-border-strong); } +.csk-dark .csk-input-wrap:hover { border-color: var(--csk-primary); box-shadow: none; } .csk-dark .csk-input-wrap--focus { background: #1A1A2E; border-color: var(--csk-primary); } .csk-dark .csk-input { color: #E2E8F0; } .csk-dark .csk-loading__avatar { background: var(--csk-bg-ai); border-color: var(--csk-border); } .csk-dark .csk-loading__bubble { background: var(--csk-bg-ai); border-color: var(--csk-border); } -.csk-dark .csk-loading__dot { background: #6B7280; } +.csk-dark .csk-loading__dot { background: var(--csk-primary-light); } .csk-dark .csk-category-bar { background: #1A1A2E; border-top-color: var(--csk-border); } .csk-dark .csk-category-select { background-color: var(--csk-bg-app); border-color: var(--csk-border); color: #E2E8F0; } .csk-dark .csk-sources { background: #1E1E2E; border-color: var(--csk-border); } @@ -1279,6 +1301,7 @@ function getStyles(config: ResolvedConfig): string { .csk-dark .csk-history-item__meta { color: #6B7280; } .csk-dark .csk-history-item--active { background: rgba(var(--csk-primary-rgb), 0.15); } .csk-dark .csk-history-item--active:hover { background: rgba(var(--csk-primary-rgb), 0.22); } +.csk-dark .csk-msg__actions { background: var(--csk-bg-ai); border-color: var(--csk-border-strong); } .csk-dark .csk-action-btn:hover { background: #2D2D48; color: #E2E8F0; } .csk-dark .csk-clear-btn { background: var(--csk-bg-app); border-color: var(--csk-border); color: #9CA3AF; } .csk-dark .csk-clear-btn:hover { background: #3B1A1A; border-color: #7F1D1D; color: #FCA5A5; } @@ -1299,9 +1322,9 @@ function getStyles(config: ResolvedConfig): string { .csk-dark .csk-teaser::after { border-top-color: #2D2D48; } .csk-dark .csk-teaser__close { color: #6B7280; } .csk-dark .csk-teaser__close:hover { color: #9CA3AF; } -.csk-dark .csk-feedback-btn { background: rgba(var(--csk-primary-rgb), 0.1); color: #9CA3AF; } -.csk-dark .csk-feedback-btn:hover { background: rgba(var(--csk-primary-rgb), 0.2); color: var(--csk-primary-light); } -.csk-dark .csk-feedback-btn--active { background: rgba(var(--csk-primary-rgb), 0.28); color: var(--csk-primary-light); } +.csk-dark .csk-feedback-btn { background: transparent; color: var(--csk-text-secondary); } +.csk-dark .csk-feedback-btn:hover { background: #2D2D48; color: #E2E8F0; } +.csk-dark .csk-feedback-btn--active { background: transparent; color: var(--csk-primary-light); } .csk-dark .csk-history-panel__search-wrap { background: var(--csk-bg-app); border-color: var(--csk-border); } .csk-dark .csk-history-panel__search-wrap:focus-within { border-color: var(--csk-primary); } .csk-dark .csk-history-panel__search { color: #E2E8F0; } @@ -1407,22 +1430,22 @@ function getStyles(config: ResolvedConfig): string { display: flex; align-items: center; justify-content: center; - width: 26px; - height: 26px; + width: 24px; + height: 24px; border: none; - background: rgba(var(--csk-primary-rgb), 0.08); - color: #9CA3AF; - border-radius: 6px; + background: transparent; + color: var(--csk-text-secondary); + border-radius: 4px; cursor: pointer; - transition: all 0.15s ease; + transition: background 0.15s ease, color 0.15s ease; padding: 0; } -.csk-feedback-btn:hover { background: rgba(var(--csk-primary-rgb), 0.18); color: var(--csk-primary); } +.csk-feedback-btn:hover { background: var(--csk-border); color: var(--csk-text-ai); } .csk-feedback-btn--active { - background: rgba(var(--csk-primary-rgb), 0.28); + background: transparent; color: var(--csk-primary); } -.csk-feedback-btn--active.down { background: #FEF2F2; color: #EF4444; } +.csk-feedback-btn--active.down { color: #D54941; } .csk-feedback-btn:active { transform: scale(0.9); } .csk-msg--streaming .csk-feedback-btn { display: none; }