Revert "feat: restyle utility panel and streaming focus"

This reverts commit 931a0488cc.
This commit is contained in:
JOJO 2025-11-26 20:21:49 +08:00
parent 931a0488cc
commit 4cd4232c62
17 changed files with 24 additions and 197 deletions

View File

@ -536,7 +536,7 @@ class WebTerminal(MainTerminal):
# 如果是聚焦操作,广播聚焦文件更新
if tool_name in ['focus_file', 'unfocus_file', 'modify_file', 'append_to_file']:
if tool_name in ['focus_file', 'unfocus_file', 'modify_file']:
try:
focused_files_dict = self.get_focused_files_info()
self.broadcast('focused_files_update', focused_files_dict)

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:1;"><path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"/><path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"/><path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"/></svg>

Before

Width:  |  Height:  |  Size: 491 B

View File

@ -37,11 +37,10 @@
</div>
<template v-else>
<div :class="['main-container', { 'utility-panel-hidden': utilityPanelHidden }]">
<div class="main-container">
<ConversationSidebar
:icon-style="iconStyle"
:format-time="formatTime"
:utility-panel-hidden="utilityPanelHidden"
@toggle="toggleSidebar"
@create="createNewConversation"
@search="handleSidebarSearch"
@ -50,7 +49,6 @@
@personal="openPersonalPage"
@delete="deleteConversation"
@duplicate="duplicateConversation"
@toggle-utility-panel="toggleUtilityPanelVisibility"
/>
<LeftPanel
@ -62,14 +60,12 @@
:is-connected="isConnected"
:panel-menu-open="panelMenuOpen"
:panel-mode="panelMode"
:hidden="utilityPanelHidden"
@toggle-panel-menu="togglePanelMenu"
@select-panel="selectPanelMode"
@open-file-manager="openGuiFileManager"
@toggle-mode="toggleThinkingMode"
/>
<div v-if="!utilityPanelHidden" class="resize-handle" @mousedown="startResize('left', $event)"></div>
<div class="resize-handle" @mousedown="startResize('left', $event)"></div>
<main class="chat-container">
<TokenDrawer

View File

@ -192,7 +192,6 @@ const appOptions = {
...mapState(useFileStore, ['contextMenu', 'fileTree', 'expandedFolders', 'todoList']),
...mapWritableState(useUiStore, [
'sidebarCollapsed',
'utilityPanelHidden',
'panelMode',
'panelMenuOpen',
'leftWidth',
@ -315,8 +314,6 @@ const appOptions = {
uiSetPanelMode: 'setPanelMode',
uiSetPanelMenuOpen: 'setPanelMenuOpen',
uiTogglePanelMenu: 'togglePanelMenu',
uiToggleUtilityPanelHidden: 'toggleUtilityPanelHidden',
uiSetUtilityPanelHidden: 'setUtilityPanelHidden',
uiPushToast: 'pushToast',
uiUpdateToast: 'updateToast',
uiDismissToast: 'dismissToast',
@ -1616,9 +1613,6 @@ const appOptions = {
toggleSidebar() {
this.uiToggleSidebar();
},
toggleUtilityPanelVisibility() {
this.uiToggleUtilityPanelHidden();
},
togglePanelMenu() {
this.uiTogglePanelMenu();

View File

@ -24,7 +24,7 @@
</template>
<script setup lang="ts">
import { computed, onMounted, watch } from 'vue';
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useFocusStore } from '@/stores/focus';
@ -44,23 +44,4 @@ const focusedCount = computed(() => Object.keys(focusedFileMap.value).length);
const languageClass = (path: string) => props.getLanguageClass(path);
const formatSize = (size: number) => `${(size / 1024).toFixed(1)}KB`;
const refreshFocusedFiles = () => {
focusStore.fetchFocusedFiles().catch(() => {});
};
onMounted(() => {
if (!props.collapsed) {
refreshFocusedFiles();
}
});
watch(
() => props.collapsed,
collapsed => {
if (!collapsed) {
refreshFocusedFiles();
}
}
);
</script>

View File

@ -1,10 +1,5 @@
<template>
<aside
class="sidebar left-sidebar"
:class="{ 'panel-hidden': hidden }"
:style="panelStyles"
:aria-hidden="hidden ? 'true' : 'false'"
>
<aside class="sidebar left-sidebar" :style="{ width: width + 'px' }">
<div class="sidebar-status">
<div class="compact-status-card">
<div class="status-line">
@ -16,14 +11,7 @@
</div>
</div>
<div class="status-indicators">
<button
type="button"
class="mode-indicator"
:class="{ thinking: thinkingMode, fast: !thinkingMode }"
:title="thinkingMode ? '切换到快速模式' : '切换到思考模式'"
:aria-pressed="thinkingMode ? 'true' : 'false'"
@click="$emit('toggle-mode')"
>
<span class="mode-indicator" :class="{ thinking: thinkingMode, fast: !thinkingMode }">
<transition name="mode-icon" mode="out-in">
<span
class="icon icon-sm"
@ -32,7 +20,7 @@
aria-hidden="true"
></span>
</transition>
</button>
</span>
<span class="connection-dot" :class="{ active: isConnected }" :title="isConnected ? '已连接' : '未连接'"></span>
</div>
</div>
@ -134,7 +122,7 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { ref } from 'vue';
import { storeToRefs } from 'pinia';
import FileNode from '@/components/files/FileNode.vue';
import { useFileStore } from '@/stores/file';
@ -150,14 +138,12 @@ const props = defineProps<{
isConnected: boolean;
panelMenuOpen: boolean;
panelMode: 'files' | 'todo' | 'subAgents';
hidden?: boolean;
}>();
defineEmits<{
(event: 'toggle-panel-menu'): void;
(event: 'select-panel', mode: 'files' | 'todo' | 'subAgents'): void;
(event: 'open-file-manager'): void;
(event: 'toggle-mode'): void;
}>();
const panelMenuWrapper = ref<HTMLElement | null>(null);
@ -170,15 +156,6 @@ const openSubAgent = (agent: any) => {
subAgentStore.openSubAgent(agent);
};
const panelStyles = computed(() => {
const basis = props.hidden ? 0 : props.width;
return {
width: `${basis}px`,
minWidth: `${basis}px`,
flexBasis: `${basis}px`
};
});
defineExpose({
panelMenuWrapper
});

View File

@ -54,20 +54,6 @@
</slot>
</span>
</button>
<button
v-if="collapsed"
type="button"
class="collapsed-control-btn utility-panel-toggle-btn"
:class="{ active: utilityPanelHidden }"
:title="utilityPanelHidden ? '显示工作面板' : '隐藏工作面板'"
@click="$emit('toggle-utility-panel')"
:aria-pressed="utilityPanelHidden ? 'true' : 'false'"
>
<span class="sr-only">{{ utilityPanelHidden ? '显示' : '隐藏' }}工作面板</span>
<span class="layers-icon" aria-hidden="true">
<span class="icon icon-md" :style="iconStyle('layers')"></span>
</span>
</button>
</div>
</template>
<template v-else>
@ -167,7 +153,6 @@
<script setup lang="ts">
defineOptions({ name: 'ConversationSidebar' });
import { computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useUiStore } from '@/stores/ui';
import { useConversationStore } from '@/stores/conversation';
@ -176,7 +161,6 @@ import { usePersonalizationStore } from '@/stores/personalization';
const props = defineProps<{
formatTime?: (input: unknown) => string;
iconStyle?: (key: string) => Record<string, string>;
utilityPanelHidden?: boolean;
}>();
defineEmits<{
@ -188,7 +172,6 @@ defineEmits<{
(event: 'personal'): void;
(event: 'delete', id: string): void;
(event: 'duplicate', id: string): void;
(event: 'toggle-utility-panel'): void;
}>();
const uiStore = useUiStore();
@ -208,5 +191,4 @@ const { visible: personalPageVisible } = storeToRefs(personalizationStore);
const formatTime = (value: unknown) => (props.formatTime ? props.formatTime(value) : String(value));
const iconStyle = (key: string) => (props.iconStyle ? props.iconStyle(key) : {});
const utilityPanelHidden = computed(() => !!props.utilityPanelHidden);
</script>

View File

@ -48,7 +48,6 @@ interface EasterEggState {
interface UiState {
sidebarCollapsed: boolean;
utilityPanelHidden: boolean;
panelMode: PanelMode;
panelMenuOpen: boolean;
leftWidth: number;
@ -70,7 +69,6 @@ interface UiState {
export const useUiStore = defineStore('ui', {
state: (): UiState => ({
sidebarCollapsed: true,
utilityPanelHidden: false,
panelMode: 'files',
panelMenuOpen: false,
leftWidth: 350,
@ -100,12 +98,6 @@ export const useUiStore = defineStore('ui', {
setSidebarCollapsed(collapsed: boolean) {
this.sidebarCollapsed = collapsed;
},
setUtilityPanelHidden(hidden: boolean) {
this.utilityPanelHidden = hidden;
},
toggleUtilityPanelHidden() {
this.utilityPanelHidden = !this.utilityPanelHidden;
},
toggleSidebar() {
this.sidebarCollapsed = !this.sidebarCollapsed;
},

View File

@ -22,18 +22,6 @@
position: relative;
}
.main-container.utility-panel-hidden .messages-area {
max-width: 960px;
margin: 0 auto;
width: 100%;
padding-left: clamp(24px, 6vw, 72px);
padding-right: clamp(24px, 6vw, 72px);
}
.main-container.utility-panel-hidden .message-block {
width: 100%;
}
.chat-container .input-area {
position: absolute;
left: 0;

View File

@ -1,17 +1,13 @@
/* 聚焦文件 */
.focused-files {
padding: 16px;
background: rgba(255, 255, 255, 0.4);
}
.no-files {
o-files {
text-align: center;
color: var(--claude-text-secondary);
padding: 60px 20px;
font-size: 14px;
background: #fff;
border-radius: 12px;
border: 1px dashed var(--claude-border);
}
.file-tabs {
@ -21,21 +17,21 @@
}
.file-tab {
border: 1px solid rgba(118, 103, 84, 0.15);
border-radius: 14px;
border: 1px solid var(--claude-border);
border-radius: 12px;
overflow: hidden;
background: #fff;
box-shadow: 0 10px 24px rgba(61, 57, 41, 0.08);
background: rgba(255, 255, 255, 0.75);
box-shadow: 0 12px 28px rgba(61, 57, 41, 0.08);
}
.tab-header {
background: rgba(247, 244, 240, 0.9);
padding: 10px 18px;
background: rgba(218, 119, 86, 0.08);
padding: 10px 16px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
border-bottom: 1px solid rgba(118, 103, 84, 0.12);
border-bottom: 1px solid var(--claude-border);
}
.file-name {
@ -49,22 +45,19 @@
}
.file-content {
max-height: 340px;
max-height: 320px;
overflow-y: auto;
background: #fff;
background: #1e1e1e;
}
.file-content pre {
margin: 0;
padding: 16px 20px;
background: transparent;
padding: 16px;
}
.file-content code {
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
font-size: 13px;
line-height: 1.5;
color: #2c2c2c;
white-space: pre-wrap;
word-break: break-word;
color: #aed581;
}

View File

@ -23,24 +23,6 @@
.sidebar.left-sidebar {
display: flex;
flex-direction: column;
transition:
width 0.35s cubic-bezier(0.4, 0, 0.2, 1),
min-width 0.35s cubic-bezier(0.4, 0, 0.2, 1),
flex-basis 0.35s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.35s ease,
opacity 0.35s ease;
}
.sidebar.left-sidebar.panel-hidden {
opacity: 0;
transform: translateY(-80px);
pointer-events: none;
}
.sidebar.left-sidebar.panel-hidden .sidebar-status,
.sidebar.left-sidebar.panel-hidden .sidebar-panel-card-wrapper {
opacity: 0;
transition: opacity 0.2s ease;
}
.sidebar-status {
@ -104,8 +86,6 @@
color: #fffef8;
box-shadow: 0 8px 20px rgba(189, 93, 58, 0.25);
transition: background 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
border: none;
cursor: pointer;
}
.mode-indicator.fast {
@ -244,7 +224,6 @@
flex-direction: column;
overflow: hidden;
min-height: 0;
overflow-x: hidden;
}
.sidebar-panel-card .sidebar-header {

View File

@ -142,34 +142,6 @@
stroke-linejoin: round;
}
.utility-panel-toggle-btn {
width: 48px;
height: 48px;
border-radius: 24px;
border: none;
background: transparent;
color: #2f251b;
display: inline-flex;
align-items: center;
justify-content: center;
transition: color 0.25s ease, transform 0.2s ease;
padding: 0;
}
.utility-panel-toggle-btn .icon {
--icon-size: 22px;
display: inline-flex;
}
.utility-panel-toggle-btn:hover,
.utility-panel-toggle-btn:focus-visible {
color: var(--claude-accent);
}
.utility-panel-toggle-btn.active {
color: #2f251b;
}
.new-conversation-btn {
flex: 1;
background: linear-gradient(135deg, var(--claude-accent) 0%, var(--claude-accent-strong) 100%);

View File

@ -9,14 +9,6 @@
color: var(--claude-text);
}
.main-container.utility-panel-hidden {
background: var(--claude-bg);
}
.main-container.utility-panel-hidden .chat-container {
background: rgba(255, 255, 255, 0.92);
}
#app {
min-height: var(--app-viewport, 100vh);
background: var(--claude-bg);

View File

@ -17,7 +17,6 @@ export const ICONS = Object.freeze({
hammer: '/static/icons/hammer.svg',
info: '/static/icons/info.svg',
laptop: '/static/icons/laptop.svg',
layers: '/static/icons/layers.svg',
menu: '/static/icons/menu.svg',
monitor: '/static/icons/monitor.svg',
octagon: '/static/icons/octagon.svg',

View File

@ -525,7 +525,7 @@ class WebTerminal(MainTerminal):
# 如果是聚焦操作,广播聚焦文件更新
if tool_name in ['focus_file', 'unfocus_file', 'modify_file', 'append_to_file']:
if tool_name in ['focus_file', 'unfocus_file', 'modify_file']:
try:
focused_files_dict = self.get_focused_files_info()
self.broadcast('focused_files_update', focused_files_dict)

View File

@ -2757,10 +2757,6 @@ async def handle_task_with_sender(terminal: WebTerminal, message, sender, client
if refreshed.get("success"):
web_terminal.focused_files[path] = refreshed["content"]
debug_log(f"聚焦文件已刷新: {path}")
try:
sender('focused_files_update', web_terminal.get_focused_files_info())
except Exception as focus_exc:
debug_log(f"广播聚焦文件更新失败: {focus_exc}")
debug_log(f"追加写入完成: {summary}")
else:
@ -3112,10 +3108,6 @@ async def handle_task_with_sender(terminal: WebTerminal, message, sender, client
if refreshed.get("success"):
web_terminal.focused_files[path] = refreshed["content"]
debug_log(f"聚焦文件已刷新: {path}")
try:
sender('focused_files_update', web_terminal.get_focused_files_info())
except Exception as focus_exc:
debug_log(f"广播聚焦文件更新失败: {focus_exc}")
pending_modify = None
modify_probe_buffer = ""
@ -4101,7 +4093,7 @@ async def handle_task_with_sender(terminal: WebTerminal, message, sender, client
sender('update_action', update_payload)
# 更新UI状态
if function_name in ['focus_file', 'unfocus_file', 'modify_file', 'append_to_file']:
if function_name in ['focus_file', 'unfocus_file', 'modify_file']:
sender('focused_files_update', web_terminal.get_focused_files_info())
if function_name in ['create_file', 'delete_file', 'rename_file', 'create_folder']:

View File

@ -2662,10 +2662,6 @@ async def handle_task_with_sender(terminal: WebTerminal, message, sender, client
if refreshed.get("success"):
web_terminal.focused_files[path] = refreshed["content"]
debug_log(f"聚焦文件已刷新: {path}")
try:
sender('focused_files_update', web_terminal.get_focused_files_info())
except Exception as focus_exc:
debug_log(f"广播聚焦文件更新失败: {focus_exc}")
debug_log(f"追加写入完成: {summary}")
else:
@ -2965,8 +2961,7 @@ async def handle_task_with_sender(terminal: WebTerminal, message, sender, client
summary_parts.append(apply_result["error"])
matching_note = "提示:补丁匹配基于完整文本,包含注释和空白符,请确保 <<<OLD>>> 段落与文件内容逐字一致。如果修改成功,请忽略,如果失败,请明确原文后再次尝试。"
if failed_blocks or not completed_blocks:
summary_parts.append(matching_note)
summary_parts.append(matching_note)
summary_message = " ".join(summary_parts).strip()
result["summary_message"] = summary_message
result["success"] = bool(completed_blocks) and not failed_blocks and apply_result.get("error") is None
@ -3018,10 +3013,6 @@ async def handle_task_with_sender(terminal: WebTerminal, message, sender, client
if refreshed.get("success"):
web_terminal.focused_files[path] = refreshed["content"]
debug_log(f"聚焦文件已刷新: {path}")
try:
sender('focused_files_update', web_terminal.get_focused_files_info())
except Exception as focus_exc:
debug_log(f"广播聚焦文件更新失败: {focus_exc}")
pending_modify = None
modify_probe_buffer = ""
@ -4098,7 +4089,7 @@ async def handle_task_with_sender(terminal: WebTerminal, message, sender, client
sender('update_action', update_payload)
# 更新UI状态
if function_name in ['focus_file', 'unfocus_file', 'modify_file', 'append_to_file']:
if function_name in ['focus_file', 'unfocus_file', 'modify_file']:
sender('focused_files_update', web_terminal.get_focused_files_info())
if function_name in ['create_file', 'delete_file', 'rename_file', 'create_folder']: