feat(ui): add sidebar list and workspace animations
This commit is contained in:
parent
3511f0e6ee
commit
ecba45da65
@ -343,11 +343,11 @@
|
||||
></div>
|
||||
</div>
|
||||
<transition name="desktop-approval-fade">
|
||||
<div
|
||||
v-if="!isMobileViewport && !rightCollapsed"
|
||||
class="desktop-approval-overlay"
|
||||
>
|
||||
<div class="desktop-approval-sheet" :style="{ width: Math.min(rightWidth || 420, 520) + 'px' }">
|
||||
<div v-if="!isMobileViewport && !rightCollapsed" class="desktop-approval-overlay">
|
||||
<div
|
||||
class="desktop-approval-sheet"
|
||||
:style="{ width: Math.min(rightWidth || 420, 520) + 'px' }"
|
||||
>
|
||||
<ToolApprovalPanel
|
||||
:collapsed="false"
|
||||
:width="Math.min(rightWidth || 420, 520)"
|
||||
|
||||
@ -56,6 +56,8 @@ export const computed = {
|
||||
'searchQuery',
|
||||
'searchTimer',
|
||||
'searchResults',
|
||||
'conversationInsertAnimations',
|
||||
'conversationListAnimationMode',
|
||||
'searchActive',
|
||||
'searchInProgress',
|
||||
'searchMoreAvailable',
|
||||
|
||||
@ -225,6 +225,7 @@ export const conversationMethods = {
|
||||
|
||||
async loadConversation(conversationId, options = {}) {
|
||||
const force = Boolean(options.force);
|
||||
const preserveListPosition = Boolean(options.preserveListPosition);
|
||||
debugLog('加载对话:', conversationId);
|
||||
traceLog('loadConversation:start', {
|
||||
conversationId,
|
||||
@ -358,7 +359,9 @@ export const conversationMethods = {
|
||||
this.titleReady = true;
|
||||
this.suppressTitleTyping = false;
|
||||
this.startTitleTyping(this.currentConversationTitle, { animate: false });
|
||||
this.promoteConversationToTop(conversationId);
|
||||
if (!preserveListPosition) {
|
||||
this.promoteConversationToTop(conversationId);
|
||||
}
|
||||
history.pushState(
|
||||
{ conversationId },
|
||||
'',
|
||||
@ -560,6 +563,10 @@ export const conversationMethods = {
|
||||
|
||||
if (result.success) {
|
||||
const newConversationId = result.conversation_id;
|
||||
const waitForAnimation = (durationMs) =>
|
||||
new Promise((resolve) => {
|
||||
window.setTimeout(resolve, durationMs);
|
||||
});
|
||||
debugLog('新对话创建成功:', newConversationId);
|
||||
traceLog('createNewConversation:created', { newConversationId });
|
||||
|
||||
@ -571,10 +578,21 @@ export const conversationMethods = {
|
||||
total_messages: 0,
|
||||
total_tools: 0
|
||||
};
|
||||
this.conversationInsertAnimations = {
|
||||
...this.conversationInsertAnimations,
|
||||
[newConversationId]: 'create'
|
||||
};
|
||||
this.conversationListAnimationMode = 'create';
|
||||
this.conversations = [
|
||||
placeholder,
|
||||
...this.conversations.filter((conv) => conv && conv.id !== newConversationId)
|
||||
];
|
||||
window.setTimeout(() => {
|
||||
const rest = { ...this.conversationInsertAnimations };
|
||||
delete rest[newConversationId];
|
||||
this.conversationInsertAnimations = rest;
|
||||
this.conversationListAnimationMode = 'idle';
|
||||
}, 700);
|
||||
|
||||
await this.applyPendingVersioningToConversation(newConversationId);
|
||||
|
||||
@ -586,6 +604,9 @@ export const conversationMethods = {
|
||||
currentConversationId: this.currentConversationId
|
||||
});
|
||||
|
||||
// 等待顶部插入动画完成,避免刷新列表时打断“整体下移 + 从左到右出现”。
|
||||
await waitForAnimation(540);
|
||||
|
||||
// 刷新对话列表获取最新统计
|
||||
this.conversationsOffset = 0;
|
||||
await this.loadConversationsList();
|
||||
@ -632,6 +653,14 @@ export const conversationMethods = {
|
||||
return;
|
||||
}
|
||||
|
||||
const waitForAnimation = (durationMs) =>
|
||||
new Promise((resolve) => {
|
||||
window.setTimeout(resolve, durationMs);
|
||||
});
|
||||
|
||||
// 确认弹窗自身有 250ms 退出动画;等弹窗完全消失后,再开始列表删除动画。
|
||||
await waitForAnimation(270);
|
||||
|
||||
debugLog('删除对话:', conversationId);
|
||||
this.logMessageState('deleteConversation:start', { conversationId });
|
||||
|
||||
@ -657,9 +686,22 @@ export const conversationMethods = {
|
||||
history.replaceState({}, '', '/new');
|
||||
}
|
||||
|
||||
// 先从本地列表移除,让侧边栏执行删除动画;随后再刷新服务端列表补齐分页状态
|
||||
this.conversationListAnimationMode = 'delete';
|
||||
this.conversations = this.conversations.filter(
|
||||
(conversation) => conversation.id !== conversationId
|
||||
);
|
||||
this.searchResults = this.searchResults.filter(
|
||||
(conversation) => conversation.id !== conversationId
|
||||
);
|
||||
|
||||
// 等待“左滑离场 + 0.1s 后集体上移”动画结束,避免刷新列表打断过渡。
|
||||
await waitForAnimation(560);
|
||||
|
||||
// 刷新对话列表
|
||||
this.conversationsOffset = 0;
|
||||
await this.loadConversationsList();
|
||||
this.conversationListAnimationMode = 'idle';
|
||||
} else {
|
||||
console.error('删除对话失败:', result.message);
|
||||
this.uiPushToast({
|
||||
@ -689,12 +731,58 @@ export const conversationMethods = {
|
||||
|
||||
if (response.ok && result.success) {
|
||||
const newId = result.duplicate_conversation_id;
|
||||
const waitForAnimation = (durationMs) =>
|
||||
new Promise((resolve) => {
|
||||
window.setTimeout(resolve, durationMs);
|
||||
});
|
||||
if (newId) {
|
||||
await this.loadConversation(newId, { force: true });
|
||||
}
|
||||
const sourceIndex = this.conversations.findIndex(
|
||||
(conv) => conv && conv.id === conversationId
|
||||
);
|
||||
const sourceConversation = sourceIndex >= 0 ? this.conversations[sourceIndex] : null;
|
||||
const duplicateTitle =
|
||||
result.load_result?.title || sourceConversation?.title || '复制的对话';
|
||||
const duplicatePlaceholder = {
|
||||
id: newId,
|
||||
title: duplicateTitle,
|
||||
updated_at: new Date().toISOString(),
|
||||
total_messages: sourceConversation?.total_messages || 0,
|
||||
total_tools: sourceConversation?.total_tools || 0
|
||||
};
|
||||
|
||||
this.conversationsOffset = 0;
|
||||
await this.loadConversationsList();
|
||||
this.conversationInsertAnimations = {
|
||||
...this.conversationInsertAnimations,
|
||||
[conversationId]: 'duplicateSource',
|
||||
[newId]: 'duplicate'
|
||||
};
|
||||
this.conversationListAnimationMode = 'duplicate';
|
||||
|
||||
const withoutDuplicate = this.conversations.filter((conv) => conv && conv.id !== newId);
|
||||
const insertIndex = withoutDuplicate.findIndex(
|
||||
(conv) => conv && conv.id === conversationId
|
||||
);
|
||||
if (insertIndex >= 0) {
|
||||
this.conversations = [
|
||||
...withoutDuplicate.slice(0, insertIndex + 1),
|
||||
duplicatePlaceholder,
|
||||
...withoutDuplicate.slice(insertIndex + 1)
|
||||
];
|
||||
} else {
|
||||
this.conversations = [duplicatePlaceholder, ...withoutDuplicate];
|
||||
}
|
||||
|
||||
window.setTimeout(() => {
|
||||
const rest = { ...this.conversationInsertAnimations };
|
||||
delete rest[conversationId];
|
||||
delete rest[newId];
|
||||
this.conversationInsertAnimations = rest;
|
||||
this.conversationListAnimationMode = 'idle';
|
||||
}, 860);
|
||||
|
||||
// 先让“目标下方钻出 + 下方整体下移”动画播完,再加载复制出的对话。
|
||||
await waitForAnimation(860);
|
||||
await this.loadConversation(newId, { force: true, preserveListPosition: true });
|
||||
}
|
||||
} else {
|
||||
const message = result.message || result.error || '复制失败';
|
||||
this.uiPushToast({
|
||||
|
||||
@ -310,16 +310,12 @@ const emits = defineEmits<{
|
||||
const panelMenuWrapper = ref<HTMLElement | null>(null);
|
||||
const statusLogoSvg = statusLogoSvgRaw.replace(/fill="#000000"/g, 'fill="currentColor"');
|
||||
const panelStyle = computed(() => {
|
||||
if (props.collapsed) {
|
||||
return {
|
||||
width: '0px',
|
||||
minWidth: '0px'
|
||||
};
|
||||
}
|
||||
const px = `${props.width}px`;
|
||||
const layoutWidth = props.collapsed ? '0px' : px;
|
||||
return {
|
||||
width: px,
|
||||
minWidth: px
|
||||
'--workspace-content-width': px,
|
||||
width: layoutWidth,
|
||||
minWidth: layoutWidth
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -113,12 +113,23 @@
|
||||
<div v-else-if="!displayConversations.length && !loading" class="no-conversations">
|
||||
{{ searchActive ? '未找到匹配对话' : '暂无对话记录' }}
|
||||
</div>
|
||||
<div v-else>
|
||||
<transition-group
|
||||
v-else
|
||||
name="conversation-delete"
|
||||
tag="div"
|
||||
class="conversation-list-items"
|
||||
:class="`animation-mode-${conversationListAnimationMode}`"
|
||||
>
|
||||
<div
|
||||
v-for="conv in displayConversations"
|
||||
:key="conv.id"
|
||||
class="conversation-item"
|
||||
:class="{ active: conv.id === currentConversationId }"
|
||||
:class="{
|
||||
active: conv.id === currentConversationId,
|
||||
'insert-from-left': conversationInsertAnimations[conv.id] === 'create',
|
||||
'insert-from-under': conversationInsertAnimations[conv.id] === 'duplicate',
|
||||
'duplicate-source-mask': conversationInsertAnimations[conv.id] === 'duplicateSource'
|
||||
}"
|
||||
@click="
|
||||
openActionMenuId = null;
|
||||
$emit('select', conv.id);
|
||||
@ -168,7 +179,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
<div v-if="!searchActive && hasMore" class="load-more">
|
||||
<button
|
||||
class="load-more-btn"
|
||||
@ -281,6 +292,8 @@ const {
|
||||
searchInProgress,
|
||||
searchMoreAvailable,
|
||||
conversations,
|
||||
conversationInsertAnimations,
|
||||
conversationListAnimationMode,
|
||||
conversationsLoading,
|
||||
hasMoreConversations: hasMore,
|
||||
loadingMoreConversations: loadingMore,
|
||||
|
||||
@ -11,6 +11,8 @@ export interface ConversationSummary {
|
||||
interface ConversationState {
|
||||
conversations: ConversationSummary[];
|
||||
searchResults: ConversationSummary[];
|
||||
conversationInsertAnimations: Record<string, 'create' | 'duplicate' | 'duplicateSource'>;
|
||||
conversationListAnimationMode: 'idle' | 'create' | 'duplicate' | 'delete';
|
||||
conversationsLoading: boolean;
|
||||
hasMoreConversations: boolean;
|
||||
loadingMoreConversations: boolean;
|
||||
@ -31,6 +33,8 @@ export const useConversationStore = defineStore('conversation', {
|
||||
state: (): ConversationState => ({
|
||||
conversations: [],
|
||||
searchResults: [],
|
||||
conversationInsertAnimations: {},
|
||||
conversationListAnimationMode: 'idle',
|
||||
conversationsLoading: false,
|
||||
hasMoreConversations: false,
|
||||
loadingMoreConversations: false,
|
||||
@ -50,6 +54,8 @@ export const useConversationStore = defineStore('conversation', {
|
||||
resetConversations() {
|
||||
this.conversations = [];
|
||||
this.searchResults = [];
|
||||
this.conversationInsertAnimations = {};
|
||||
this.conversationListAnimationMode = 'idle';
|
||||
this.searchActive = false;
|
||||
this.searchInProgress = false;
|
||||
this.searchMoreAvailable = false;
|
||||
|
||||
@ -1,174 +1,174 @@
|
||||
/* CSS variables + shared design tokens */
|
||||
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--app-viewport: 100vh;
|
||||
--app-bottom-inset: env(safe-area-inset-bottom, 0px);
|
||||
--claude-bg: #eeece2;
|
||||
--claude-panel: rgba(255, 255, 255, 0.82);
|
||||
--claude-left-rail: #f7f3ea;
|
||||
--claude-sidebar: rgba(255, 255, 255, 0.68);
|
||||
--claude-border: rgba(118, 103, 84, 0.25);
|
||||
--claude-border-strong: rgba(118, 103, 84, 0.35);
|
||||
--claude-text: #3d3929;
|
||||
--claude-text-secondary: #7f7766;
|
||||
--claude-text-tertiary: #a59a86;
|
||||
--claude-muted: rgba(121, 109, 94, 0.4);
|
||||
--claude-accent: #da7756;
|
||||
--claude-accent-strong: #bd5d3a;
|
||||
--claude-deep: #f2a93b;
|
||||
--claude-deep-strong: #d07a14;
|
||||
--claude-highlight: rgba(218, 119, 86, 0.14);
|
||||
--claude-button-hover: #c76541;
|
||||
--claude-button-active: #a95331;
|
||||
--claude-shadow: 0 14px 36px rgba(61, 57, 41, 0.12);
|
||||
--claude-success: #76b086;
|
||||
--claude-warning: #d99845;
|
||||
/* Theme-neutral surfaces */
|
||||
--theme-surface-card: #fffaf4;
|
||||
--theme-surface-strong: #ffffff;
|
||||
--theme-surface-soft: rgba(255, 255, 255, 0.92);
|
||||
--theme-surface-muted: rgba(255, 255, 255, 0.85);
|
||||
--theme-overlay-scrim: rgba(33, 24, 14, 0.55);
|
||||
--theme-shadow-strong: 0 28px 60px rgba(38, 28, 18, 0.25);
|
||||
--theme-shadow-soft: 0 12px 24px rgba(38, 28, 18, 0.12);
|
||||
--theme-shadow-mid: 0 20px 45px rgba(16, 24, 40, 0.08);
|
||||
--theme-control-border: rgba(118, 103, 84, 0.25);
|
||||
--theme-control-border-strong: rgba(118, 103, 84, 0.35);
|
||||
--theme-switch-track: #d7d1c5;
|
||||
--theme-chip-bg: rgba(118, 103, 84, 0.08);
|
||||
--theme-chip-border: rgba(118, 103, 84, 0.2);
|
||||
--theme-badge-bg: rgba(118, 103, 84, 0.12);
|
||||
--theme-tab-active: rgba(189, 93, 58, 0.12);
|
||||
--theme-mobile-menu: rgba(255, 255, 255, 0.35);
|
||||
--theme-mobile-menu-shadow: 0 12px 30px rgba(38, 28, 18, 0.15);
|
||||
--theme-card-border-strong: rgba(118, 103, 84, 0.25);
|
||||
color-scheme: light;
|
||||
--app-viewport: 100vh;
|
||||
--app-bottom-inset: env(safe-area-inset-bottom, 0px);
|
||||
--claude-bg: #eeece2;
|
||||
--claude-panel: rgba(255, 255, 255, 0.82);
|
||||
--claude-left-rail: #f7f3ea;
|
||||
--claude-sidebar: rgba(255, 255, 255, 0.68);
|
||||
--claude-border: rgba(118, 103, 84, 0.25);
|
||||
--claude-border-strong: rgba(118, 103, 84, 0.35);
|
||||
--claude-text: #3d3929;
|
||||
--claude-text-secondary: #7f7766;
|
||||
--claude-text-tertiary: #a59a86;
|
||||
--claude-muted: rgba(121, 109, 94, 0.4);
|
||||
--claude-accent: #da7756;
|
||||
--claude-accent-strong: #bd5d3a;
|
||||
--claude-deep: #f2a93b;
|
||||
--claude-deep-strong: #d07a14;
|
||||
--claude-highlight: rgba(218, 119, 86, 0.14);
|
||||
--claude-button-hover: #c76541;
|
||||
--claude-button-active: #a95331;
|
||||
--claude-shadow: 0 14px 36px rgba(61, 57, 41, 0.12);
|
||||
--claude-success: #76b086;
|
||||
--claude-warning: #d99845;
|
||||
/* Theme-neutral surfaces */
|
||||
--theme-surface-card: #fffaf4;
|
||||
--theme-surface-strong: #ffffff;
|
||||
--theme-surface-soft: rgba(255, 255, 255, 0.92);
|
||||
--theme-surface-muted: rgba(255, 255, 255, 0.85);
|
||||
--theme-overlay-scrim: rgba(33, 24, 14, 0.55);
|
||||
--theme-shadow-strong: 0 28px 60px rgba(38, 28, 18, 0.25);
|
||||
--theme-shadow-soft: 0 12px 24px rgba(38, 28, 18, 0.12);
|
||||
--theme-shadow-mid: 0 20px 45px rgba(16, 24, 40, 0.08);
|
||||
--theme-control-border: rgba(118, 103, 84, 0.25);
|
||||
--theme-control-border-strong: rgba(118, 103, 84, 0.35);
|
||||
--theme-switch-track: #d7d1c5;
|
||||
--theme-chip-bg: rgba(118, 103, 84, 0.08);
|
||||
--theme-chip-border: rgba(118, 103, 84, 0.2);
|
||||
--theme-badge-bg: rgba(118, 103, 84, 0.12);
|
||||
--theme-tab-active: rgba(189, 93, 58, 0.12);
|
||||
--theme-mobile-menu: rgba(255, 255, 255, 0.35);
|
||||
--theme-mobile-menu-shadow: 0 12px 30px rgba(38, 28, 18, 0.15);
|
||||
--theme-card-border-strong: rgba(118, 103, 84, 0.25);
|
||||
}
|
||||
|
||||
:root[data-theme='classic'] {
|
||||
color-scheme: light;
|
||||
--claude-bg: #eeece2;
|
||||
--claude-panel: rgba(255, 255, 255, 0.82);
|
||||
--claude-left-rail: #f7f3ea;
|
||||
--claude-sidebar: rgba(255, 255, 255, 0.68);
|
||||
--claude-border: rgba(118, 103, 84, 0.25);
|
||||
--claude-border-strong: rgba(118, 103, 84, 0.35);
|
||||
--claude-text: #3d3929;
|
||||
--claude-text-secondary: #7f7766;
|
||||
--claude-text-tertiary: #a59a86;
|
||||
--claude-muted: rgba(121, 109, 94, 0.4);
|
||||
--claude-accent: #da7756;
|
||||
--claude-accent-strong: #bd5d3a;
|
||||
--claude-deep: #f2a93b;
|
||||
--claude-deep-strong: #d07a14;
|
||||
--claude-highlight: rgba(218, 119, 86, 0.14);
|
||||
--claude-button-hover: #c76541;
|
||||
--claude-button-active: #a95331;
|
||||
--claude-shadow: 0 14px 36px rgba(61, 57, 41, 0.12);
|
||||
--claude-success: #76b086;
|
||||
--claude-warning: #d99845;
|
||||
--theme-surface-card: #fffaf4;
|
||||
--theme-surface-strong: #ffffff;
|
||||
--theme-surface-soft: rgba(255, 255, 255, 0.92);
|
||||
--theme-surface-muted: rgba(255, 255, 255, 0.85);
|
||||
--theme-overlay-scrim: rgba(33, 24, 14, 0.55);
|
||||
--theme-shadow-strong: 0 28px 60px rgba(38, 28, 18, 0.25);
|
||||
--theme-shadow-soft: 0 12px 24px rgba(38, 28, 18, 0.12);
|
||||
--theme-shadow-mid: 0 20px 45px rgba(16, 24, 40, 0.08);
|
||||
--theme-control-border: rgba(118, 103, 84, 0.25);
|
||||
--theme-control-border-strong: rgba(118, 103, 84, 0.35);
|
||||
--theme-switch-track: #d7d1c5;
|
||||
--theme-chip-bg: rgba(118, 103, 84, 0.08);
|
||||
--theme-chip-border: rgba(118, 103, 84, 0.2);
|
||||
--theme-badge-bg: rgba(118, 103, 84, 0.12);
|
||||
--theme-tab-active: rgba(189, 93, 58, 0.12);
|
||||
--theme-mobile-menu: rgba(255, 255, 255, 0.35);
|
||||
--theme-mobile-menu-shadow: 0 12px 30px rgba(38, 28, 18, 0.15);
|
||||
--theme-card-border-strong: rgba(118, 103, 84, 0.25);
|
||||
color-scheme: light;
|
||||
--claude-bg: #eeece2;
|
||||
--claude-panel: rgba(255, 255, 255, 0.82);
|
||||
--claude-left-rail: #f7f3ea;
|
||||
--claude-sidebar: rgba(255, 255, 255, 0.68);
|
||||
--claude-border: rgba(118, 103, 84, 0.25);
|
||||
--claude-border-strong: rgba(118, 103, 84, 0.35);
|
||||
--claude-text: #3d3929;
|
||||
--claude-text-secondary: #7f7766;
|
||||
--claude-text-tertiary: #a59a86;
|
||||
--claude-muted: rgba(121, 109, 94, 0.4);
|
||||
--claude-accent: #da7756;
|
||||
--claude-accent-strong: #bd5d3a;
|
||||
--claude-deep: #f2a93b;
|
||||
--claude-deep-strong: #d07a14;
|
||||
--claude-highlight: rgba(218, 119, 86, 0.14);
|
||||
--claude-button-hover: #c76541;
|
||||
--claude-button-active: #a95331;
|
||||
--claude-shadow: 0 14px 36px rgba(61, 57, 41, 0.12);
|
||||
--claude-success: #76b086;
|
||||
--claude-warning: #d99845;
|
||||
--theme-surface-card: #fffaf4;
|
||||
--theme-surface-strong: #ffffff;
|
||||
--theme-surface-soft: rgba(255, 255, 255, 0.92);
|
||||
--theme-surface-muted: rgba(255, 255, 255, 0.85);
|
||||
--theme-overlay-scrim: rgba(33, 24, 14, 0.55);
|
||||
--theme-shadow-strong: 0 28px 60px rgba(38, 28, 18, 0.25);
|
||||
--theme-shadow-soft: 0 12px 24px rgba(38, 28, 18, 0.12);
|
||||
--theme-shadow-mid: 0 20px 45px rgba(16, 24, 40, 0.08);
|
||||
--theme-control-border: rgba(118, 103, 84, 0.25);
|
||||
--theme-control-border-strong: rgba(118, 103, 84, 0.35);
|
||||
--theme-switch-track: #d7d1c5;
|
||||
--theme-chip-bg: rgba(118, 103, 84, 0.08);
|
||||
--theme-chip-border: rgba(118, 103, 84, 0.2);
|
||||
--theme-badge-bg: rgba(118, 103, 84, 0.12);
|
||||
--theme-tab-active: rgba(189, 93, 58, 0.12);
|
||||
--theme-mobile-menu: rgba(255, 255, 255, 0.35);
|
||||
--theme-mobile-menu-shadow: 0 12px 30px rgba(38, 28, 18, 0.15);
|
||||
--theme-card-border-strong: rgba(118, 103, 84, 0.25);
|
||||
}
|
||||
|
||||
:root[data-theme='light'] {
|
||||
color-scheme: light;
|
||||
/* 类似 ChatGPT 的明亮配色:白底黑字,优雅浅灰 */
|
||||
--claude-bg: #ffffff;
|
||||
--claude-panel: #ffffff;
|
||||
--claude-left-rail: #f9f9f9;
|
||||
--claude-sidebar: #ffffff;
|
||||
--claude-border: rgba(0, 0, 0, 0.08);
|
||||
--claude-border-strong: rgba(0, 0, 0, 0.12);
|
||||
--claude-text: #0d0d0d;
|
||||
--claude-text-secondary: #676767;
|
||||
--claude-text-tertiary: #8e8e8e;
|
||||
--claude-muted: rgba(0, 0, 0, 0.3);
|
||||
--claude-accent: #acacac;
|
||||
--claude-accent-strong: #8e8e8e;
|
||||
--claude-deep: #c4c4c4;
|
||||
--claude-deep-strong: #acacac;
|
||||
--claude-highlight: rgba(0, 0, 0, 0.05);
|
||||
--claude-button-hover: #d1d1d1;
|
||||
--claude-button-active: #acacac;
|
||||
--claude-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
--claude-success: #10b981;
|
||||
--claude-warning: #f59e0b;
|
||||
--theme-surface-card: #ffffff;
|
||||
--theme-surface-strong: #ffffff;
|
||||
--theme-surface-soft: #ffffff;
|
||||
--theme-surface-muted: #fafafa;
|
||||
--theme-overlay-scrim: rgba(0, 0, 0, 0.5);
|
||||
--theme-shadow-strong: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||
--theme-shadow-soft: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
--theme-shadow-mid: 0 1px 3px rgba(0, 0, 0, 0.06);
|
||||
--theme-control-border: rgba(0, 0, 0, 0.08);
|
||||
--theme-control-border-strong: rgba(0, 0, 0, 0.12);
|
||||
--theme-switch-track: #ececec;
|
||||
--theme-chip-bg: #f5f5f5;
|
||||
--theme-chip-border: rgba(0, 0, 0, 0.08);
|
||||
--theme-badge-bg: #f5f5f5;
|
||||
--theme-tab-active: rgba(0, 0, 0, 0.05);
|
||||
--theme-mobile-menu: rgba(255, 255, 255, 0.95);
|
||||
--theme-mobile-menu-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
--theme-card-border-strong: rgba(0, 0, 0, 0.1);
|
||||
color-scheme: light;
|
||||
/* 类似 ChatGPT 的明亮配色:白底黑字,优雅浅灰 */
|
||||
--claude-bg: #ffffff;
|
||||
--claude-panel: #ffffff;
|
||||
--claude-left-rail: #f9f9f9;
|
||||
--claude-sidebar: #ffffff;
|
||||
--claude-border: rgba(0, 0, 0, 0.08);
|
||||
--claude-border-strong: rgba(0, 0, 0, 0.12);
|
||||
--claude-text: #0d0d0d;
|
||||
--claude-text-secondary: #676767;
|
||||
--claude-text-tertiary: #8e8e8e;
|
||||
--claude-muted: rgba(0, 0, 0, 0.3);
|
||||
--claude-accent: #acacac;
|
||||
--claude-accent-strong: #8e8e8e;
|
||||
--claude-deep: #c4c4c4;
|
||||
--claude-deep-strong: #acacac;
|
||||
--claude-highlight: rgba(0, 0, 0, 0.05);
|
||||
--claude-button-hover: #d1d1d1;
|
||||
--claude-button-active: #acacac;
|
||||
--claude-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
--claude-success: #10b981;
|
||||
--claude-warning: #f59e0b;
|
||||
--theme-surface-card: #ffffff;
|
||||
--theme-surface-strong: #ffffff;
|
||||
--theme-surface-soft: #ffffff;
|
||||
--theme-surface-muted: #fafafa;
|
||||
--theme-overlay-scrim: rgba(0, 0, 0, 0.5);
|
||||
--theme-shadow-strong: 0 2px 4px rgba(0, 0, 0, 0.08);
|
||||
--theme-shadow-soft: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
--theme-shadow-mid: 0 1px 3px rgba(0, 0, 0, 0.06);
|
||||
--theme-control-border: rgba(0, 0, 0, 0.08);
|
||||
--theme-control-border-strong: rgba(0, 0, 0, 0.12);
|
||||
--theme-switch-track: #ececec;
|
||||
--theme-chip-bg: #f5f5f5;
|
||||
--theme-chip-border: rgba(0, 0, 0, 0.08);
|
||||
--theme-badge-bg: #f5f5f5;
|
||||
--theme-tab-active: rgba(0, 0, 0, 0.05);
|
||||
--theme-mobile-menu: rgba(255, 255, 255, 0.95);
|
||||
--theme-mobile-menu-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
--theme-card-border-strong: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
:root[data-theme='dark'] {
|
||||
color-scheme: dark;
|
||||
/* 暗色主题:灰黑色底色,白色文字 */
|
||||
--claude-bg: #1a1a1a;
|
||||
--claude-panel: #1a1a1a;
|
||||
--claude-left-rail: #0a0a0a;
|
||||
--claude-sidebar: #0a0a0a;
|
||||
--claude-border: rgba(255, 255, 255, 0.08);
|
||||
--claude-border-strong: rgba(255, 255, 255, 0.12);
|
||||
--claude-text: #ffffff;
|
||||
--claude-text-secondary: #a0a0a0;
|
||||
--claude-text-tertiary: #707070;
|
||||
--claude-muted: rgba(255, 255, 255, 0.3);
|
||||
--claude-accent: #606060;
|
||||
--claude-accent-strong: #505050;
|
||||
--claude-deep: #808080;
|
||||
--claude-deep-strong: #606060;
|
||||
--claude-highlight: rgba(255, 255, 255, 0.05);
|
||||
--claude-button-hover: #707070;
|
||||
--claude-button-active: #505050;
|
||||
--claude-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
|
||||
--claude-success: #10b981;
|
||||
--claude-warning: #f59e0b;
|
||||
--theme-surface-card: #0a0a0a;
|
||||
--theme-surface-strong: #1a1a1a;
|
||||
--theme-surface-soft: #0f0f0f;
|
||||
--theme-surface-muted: #141414;
|
||||
--theme-overlay-scrim: rgba(0, 0, 0, 0.8);
|
||||
--theme-shadow-strong: 0 4px 12px rgba(0, 0, 0, 0.6);
|
||||
--theme-shadow-soft: 0 2px 6px rgba(0, 0, 0, 0.4);
|
||||
--theme-shadow-mid: 0 3px 8px rgba(0, 0, 0, 0.5);
|
||||
--theme-control-border: rgba(255, 255, 255, 0.08);
|
||||
--theme-control-border-strong: rgba(255, 255, 255, 0.12);
|
||||
--theme-switch-track: #2a2a2a;
|
||||
--theme-chip-bg: #2a2a2a;
|
||||
--theme-chip-border: rgba(255, 255, 255, 0.08);
|
||||
--theme-badge-bg: #2a2a2a;
|
||||
--theme-tab-active: rgba(255, 255, 255, 0.05);
|
||||
--theme-mobile-menu: rgba(10, 10, 10, 0.95);
|
||||
--theme-mobile-menu-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
|
||||
--theme-card-border-strong: rgba(255, 255, 255, 0.1);
|
||||
color-scheme: dark;
|
||||
/* 暗色主题:灰黑色底色,白色文字 */
|
||||
--claude-bg: #1a1a1a;
|
||||
--claude-panel: #1a1a1a;
|
||||
--claude-left-rail: #181818;
|
||||
--claude-sidebar: #181818;
|
||||
--claude-border: rgba(255, 255, 255, 0.08);
|
||||
--claude-border-strong: rgba(255, 255, 255, 0.12);
|
||||
--claude-text: #ffffff;
|
||||
--claude-text-secondary: #a0a0a0;
|
||||
--claude-text-tertiary: #707070;
|
||||
--claude-muted: rgba(255, 255, 255, 0.3);
|
||||
--claude-accent: #606060;
|
||||
--claude-accent-strong: #505050;
|
||||
--claude-deep: #808080;
|
||||
--claude-deep-strong: #606060;
|
||||
--claude-highlight: rgba(255, 255, 255, 0.05);
|
||||
--claude-button-hover: #707070;
|
||||
--claude-button-active: #505050;
|
||||
--claude-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
|
||||
--claude-success: #10b981;
|
||||
--claude-warning: #f59e0b;
|
||||
--theme-surface-card: #0a0a0a;
|
||||
--theme-surface-strong: #1a1a1a;
|
||||
--theme-surface-soft: #0f0f0f;
|
||||
--theme-surface-muted: #141414;
|
||||
--theme-overlay-scrim: rgba(0, 0, 0, 0.8);
|
||||
--theme-shadow-strong: 0 4px 12px rgba(0, 0, 0, 0.6);
|
||||
--theme-shadow-soft: 0 2px 6px rgba(0, 0, 0, 0.4);
|
||||
--theme-shadow-mid: 0 3px 8px rgba(0, 0, 0, 0.5);
|
||||
--theme-control-border: rgba(255, 255, 255, 0.08);
|
||||
--theme-control-border-strong: rgba(255, 255, 255, 0.12);
|
||||
--theme-switch-track: #2a2a2a;
|
||||
--theme-chip-bg: #2a2a2a;
|
||||
--theme-chip-border: rgba(255, 255, 255, 0.08);
|
||||
--theme-badge-bg: #2a2a2a;
|
||||
--theme-tab-active: rgba(255, 255, 255, 0.05);
|
||||
--theme-mobile-menu: rgba(10, 10, 10, 0.95);
|
||||
--theme-mobile-menu-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
|
||||
--theme-card-border-strong: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
@ -32,32 +32,36 @@
|
||||
flex-direction: column;
|
||||
transition:
|
||||
width 0.45s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
min-width 0.45s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.35s ease,
|
||||
transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
min-width 0.45s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.workspace-panel {
|
||||
flex: 0 0 auto;
|
||||
will-change: width, transform, opacity;
|
||||
overflow: hidden;
|
||||
will-change: width, min-width;
|
||||
transition:
|
||||
width 0.45s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
min-width 0.45s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.35s ease,
|
||||
transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
min-width 0.45s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.workspace-panel > * {
|
||||
width: var(--workspace-content-width);
|
||||
min-width: var(--workspace-content-width);
|
||||
transition:
|
||||
opacity 0.32s ease,
|
||||
transform 0.34s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
.workspace-panel--collapsed {
|
||||
flex: 0 0 0 !important;
|
||||
width: 0 !important;
|
||||
min-width: 0 !important;
|
||||
max-width: 0 !important;
|
||||
opacity: 0;
|
||||
transform: translateY(-70px);
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.workspace-panel--collapsed > * {
|
||||
opacity: 0;
|
||||
transform: translateY(-28px);
|
||||
}
|
||||
|
||||
.sidebar-status {
|
||||
padding: 18px 18px 8px;
|
||||
}
|
||||
@ -686,8 +690,14 @@
|
||||
}
|
||||
|
||||
[data-theme='dark'] .host-workspace-card {
|
||||
background: #1f1f1f;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: #222222;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
background: #242424;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,17 +13,32 @@
|
||||
min-height: var(--app-viewport, 100vh) !important;
|
||||
overflow: hidden;
|
||||
background: var(--claude-left-rail);
|
||||
border-right: 1px solid var(--claude-border);
|
||||
border-right: 0;
|
||||
color: var(--claude-text);
|
||||
transition:
|
||||
width 320ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
border-color 160ms ease;
|
||||
transition: width 320ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
.conversation-sidebar::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
pointer-events: none;
|
||||
opacity: 1;
|
||||
background: var(--claude-border);
|
||||
transition: opacity 320ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
.conversation-sidebar.collapsed {
|
||||
width: var(--conversation-collapsed-width);
|
||||
}
|
||||
|
||||
.conversation-sidebar.collapsed::after {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.conversation-sidebar-inner {
|
||||
width: var(--conversation-expanded-width);
|
||||
height: 100%;
|
||||
@ -92,6 +107,15 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.conversation-sidebar:not(.collapsed) .workspace-control-btn.active {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.conversation-sidebar:not(.collapsed) .workspace-control-btn.active:hover,
|
||||
.conversation-sidebar:not(.collapsed) .workspace-control-btn.active:focus-visible {
|
||||
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
|
||||
}
|
||||
|
||||
.sidebar-nav-label,
|
||||
.conversation-search,
|
||||
.conversation-list,
|
||||
@ -178,6 +202,7 @@
|
||||
min-height: 0;
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding-top: 12px;
|
||||
scrollbar-width: none !important;
|
||||
-ms-overflow-style: none;
|
||||
@ -189,6 +214,10 @@
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-list-items {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-item {
|
||||
position: relative;
|
||||
grid-template-columns: minmax(0, 1fr) 40px;
|
||||
@ -203,6 +232,89 @@
|
||||
color 140ms ease;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-delete-move {
|
||||
transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
.conversation-sidebar .animation-mode-delete .conversation-delete-move {
|
||||
transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1) 100ms;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-delete-enter-active {
|
||||
transition:
|
||||
transform 420ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
opacity 260ms ease;
|
||||
}
|
||||
|
||||
.conversation-sidebar
|
||||
.animation-mode-duplicate
|
||||
.conversation-delete-enter-active.insert-from-under {
|
||||
transition-delay: 300ms;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-delete-enter-active.insert-from-under {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-delete-enter-from.insert-from-left {
|
||||
transform: translateX(-112%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-delete-enter-from.insert-from-under {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-item.duplicate-source-mask {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-item.duplicate-source-mask::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
border-radius: 12px;
|
||||
background: var(--claude-left-rail);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-item.duplicate-source-mask:hover::after {
|
||||
background: var(--theme-tab-active, rgba(0, 0, 0, 0.055));
|
||||
}
|
||||
|
||||
:root[data-theme='dark']
|
||||
.conversation-sidebar
|
||||
.conversation-item.duplicate-source-mask:hover::after,
|
||||
body[data-theme='dark']
|
||||
.conversation-sidebar
|
||||
.conversation-item.duplicate-source-mask:hover::after {
|
||||
background: rgba(255, 255, 255, 0.078);
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-item.duplicate-source-mask > * {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-delete-leave-active {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
transition:
|
||||
transform 420ms cubic-bezier(0.32, 0, 0.67, 0),
|
||||
opacity 260ms ease;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-delete-leave-to {
|
||||
transform: translateX(-112%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.conversation-sidebar .conversation-item:hover,
|
||||
.conversation-sidebar .conversation-item:focus-within,
|
||||
.conversation-sidebar .conversation-item.active:hover,
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
/* 主体容器,让三栏布局占满视口 */
|
||||
.main-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: var(--app-viewport, 100vh);
|
||||
align-items: stretch;
|
||||
background: var(--claude-bg);
|
||||
color: var(--claude-text);
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: var(--app-viewport, 100vh);
|
||||
align-items: stretch;
|
||||
background: var(--claude-bg);
|
||||
color: var(--claude-text);
|
||||
}
|
||||
|
||||
#app {
|
||||
min-height: var(--app-viewport, 100vh);
|
||||
background: var(--claude-bg);
|
||||
min-height: var(--app-viewport, 100vh);
|
||||
background: var(--claude-bg);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user