From 8cf5f408f648ac475bda74991bdfff384a546368 Mon Sep 17 00:00:00 2001 From: Rick Ye Date: Tue, 8 Sep 2026 01:51:30 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BE=A7=E8=BE=B9=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF=E6=94=B6=E6=95=9B=E4=B8=BA=E6=B5=8F=E8=A7=88=E5=99=A8?= =?UTF-8?q?=E4=B8=93=E5=B1=9E=EF=BC=8C=E5=88=A0=E9=99=A4=E4=B8=8D=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E5=8A=9F=E8=83=BD=E7=9A=84=E5=85=A8=E9=83=A8=E7=97=95?= =?UTF-8?q?=E8=BF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/desktop/src/renderer/src/App.tsx | 115 ++++---------- .../src/renderer/src/components/SidePanel.tsx | 150 +++--------------- 2 files changed, 56 insertions(+), 209 deletions(-) diff --git a/apps/desktop/src/renderer/src/App.tsx b/apps/desktop/src/renderer/src/App.tsx index 9a59d38..9e96f57 100644 --- a/apps/desktop/src/renderer/src/App.tsx +++ b/apps/desktop/src/renderer/src/App.tsx @@ -10,45 +10,38 @@ import { Composer } from './components/Composer' import { SettingsModal } from './components/SettingsModal' import { Search } from './components/Search' import { BrowserPanel } from './components/browser/BrowserPanel' -import { SidePanel, SIDE_TAB_ORDER, type SideTabType } from './components/SidePanel' +import { SidePanel } from './components/SidePanel' import { applyThemeColor } from './lib/theme' -// ---- 侧边面板状态(开合 / 标签 / 宽度),跨启动持久化 ---- +// ---- 侧边面板状态(开合 / 宽度),跨启动持久化 ---- -const SIDE_STORAGE_KEY = 'aso.sidepanel.v1' +const SIDE_STORAGE_KEY = 'aso.sidepanel.v2' const SIDE_DEFAULT_WIDTH = 480 const SIDE_MIN_WIDTH = 380 const SIDE_MAX_WIDTH = 760 interface SideState { open: boolean - tabs: SideTabType[] - active: SideTabType | null width: number } function loadSideState(): SideState { - const fallback: SideState = { open: false, tabs: [], active: null, width: SIDE_DEFAULT_WIDTH } try { const raw = localStorage.getItem(SIDE_STORAGE_KEY) - if (!raw) return fallback - const parsed = JSON.parse(raw) as Partial - const tabs = Array.isArray(parsed.tabs) - ? parsed.tabs.filter((t): t is SideTabType => SIDE_TAB_ORDER.includes(t)) - : [] - return { - open: Boolean(parsed.open), - tabs, - active: - parsed.active && tabs.includes(parsed.active) ? parsed.active : (tabs[tabs.length - 1] ?? null), - width: - typeof parsed.width === 'number' - ? Math.min(SIDE_MAX_WIDTH, Math.max(SIDE_MIN_WIDTH, parsed.width)) - : SIDE_DEFAULT_WIDTH + if (raw) { + const parsed = JSON.parse(raw) as Partial + return { + open: Boolean(parsed.open), + width: + typeof parsed.width === 'number' + ? Math.min(SIDE_MAX_WIDTH, Math.max(SIDE_MIN_WIDTH, parsed.width)) + : SIDE_DEFAULT_WIDTH + } } } catch { - return fallback + // 损坏/不可用时按默认处理 } + return { open: false, width: SIDE_DEFAULT_WIDTH } } export default function App(): JSX.Element { @@ -74,7 +67,7 @@ export default function App(): JSX.Element { const [searchOpen, setSearchOpen] = useState(false) const browser = useBrowser() const [side, setSide] = useState(loadSideState) - const { open: sideOpen, tabs: sideTabs, active: activeSideTab, width: sideWidth } = side + const { open: sideOpen, width: sideWidth } = side useEffect(() => { try { @@ -84,48 +77,14 @@ export default function App(): JSX.Element { } }, [side]) - const openSideTab = useCallback((type: SideTabType): void => { - setSide((s) => ({ - ...s, - open: true, - tabs: s.tabs.includes(type) ? s.tabs : [...s.tabs, type], - active: type - })) - }, []) - - const closeSideTab = useCallback((type: SideTabType): void => { - setSide((s) => { - const tabs = s.tabs.filter((t) => t !== type) - return { ...s, tabs, active: s.active === type ? (tabs[tabs.length - 1] ?? null) : s.active } - }) - }, []) - const toggleSide = useCallback((): void => { - setSide((s) => { - if (s.open) return { ...s, open: false } - // 当前面板仅承载浏览器标签:打开时无标签则自动补上 - const tabs: SideTabType[] = s.tabs.includes('browser') ? s.tabs : [...s.tabs, 'browser'] - return { ...s, open: true, tabs, active: s.active ?? 'browser' } - }) + setSide((s) => ({ ...s, open: !s.open })) }, []) const setSideWidth = useCallback((width: number): void => { setSide((s) => ({ ...s, width })) }, []) - const setSideActive = useCallback((type: SideTabType): void => { - setSide((s) => ({ ...s, active: type })) - }, []) - - // Agent 首次创建受管浏览器页签时,自动打开侧边面板的浏览器标签 - const browserTabCount = browser.state?.tabs.length ?? 0 - const prevBrowserTabCount = useRef(0) - useEffect(() => { - const prev = prevBrowserTabCount.current - prevBrowserTabCount.current = browserTabCount - if (browserTabCount > 0 && prev === 0) openSideTab('browser') - }, [browserTabCount, openSideTab]) - // 启动时应用持久化的主题色 useEffect(() => { void window.api @@ -230,32 +189,22 @@ export default function App(): JSX.Element { /> {sideOpen && ( - - } - /> + + + )} diff --git a/apps/desktop/src/renderer/src/components/SidePanel.tsx b/apps/desktop/src/renderer/src/components/SidePanel.tsx index 2245666..19d6b3f 100644 --- a/apps/desktop/src/renderer/src/components/SidePanel.tsx +++ b/apps/desktop/src/renderer/src/components/SidePanel.tsx @@ -1,62 +1,24 @@ -import { useEffect, useRef, type ComponentType, type JSX, type ReactNode } from 'react' -import { - LuChevronDown, - LuFileDiff, - LuGlobe, - LuMessagesSquare, - LuSquareTerminal, - LuX -} from 'react-icons/lu' +import { useEffect, useRef, type JSX, type ReactNode } from 'react' +import { LuChevronDown, LuGlobe } from 'react-icons/lu' /** - * 右侧通用侧边面板(ZCode 式标签页容器)。 - * 面板负责:标签栏(收起 / 标签)、空态「打开标签页」宫格、左缘宽度拖拽; - * 各标签的内容由外部传入。标签架构按多类型设计,当前仅开放浏览器, - * 后续新标签在 SIDE_TAB_ORDER 与内容分支里扩展即可。 + * 右侧侧边面板(当前承载内置浏览器)。 + * 顶栏为 ZCode 式标签条形态(收起按钮 + 浏览器标签),左缘可拖拽调宽; + * 后续新增面板类型时,再扩展为多标签结构。 */ -export type SideTabType = 'assistant' | 'review' | 'terminal' | 'browser' - -/** 当前对外开放的标签类型(浏览器);新标签就绪后加入即可显示 */ -export const SIDE_TAB_ORDER: SideTabType[] = ['browser'] - -type TabIcon = ComponentType<{ size?: number; className?: string }> - -export const SIDE_TAB_META: Record = { - assistant: { title: '辅助对话', icon: LuMessagesSquare }, - review: { title: '审查', icon: LuFileDiff }, - terminal: { title: '终端', icon: LuSquareTerminal }, - browser: { title: '浏览器', icon: LuGlobe } -} - const PANEL_MIN_WIDTH = 380 const PANEL_MAX_WIDTH = 760 interface SidePanelProps { - tabs: SideTabType[] - active: SideTabType | null width: number onWidthChange: (width: number) => void - onSelectTab: (type: SideTabType) => void - onCloseTab: (type: SideTabType) => void - onOpenTab: (type: SideTabType) => void - /** 收起整个面板(标签栏左端的 ˅ 按钮,与标题栏开关等效) */ onCollapse: () => void - /** 浏览器标签的内容 */ - browser: ReactNode + /** 面板内容(浏览器标签) */ + children: ReactNode } -export function SidePanel({ - tabs, - active, - width, - onWidthChange, - onSelectTab, - onCloseTab, - onOpenTab, - onCollapse, - browser -}: SidePanelProps): JSX.Element { +export function SidePanel({ width, onWidthChange, onCollapse, children }: SidePanelProps): JSX.Element { const dragRef = useRef<{ startX: number; startWidth: number } | null>(null) // 面板宽度拖拽(左边缘把手) @@ -81,8 +43,6 @@ export function SidePanel({ dragRef.current = { startX: e.clientX, startWidth: width } } - const current = active ?? tabs[tabs.length - 1] ?? null - return ( ) } - -/** 空态:无任何标签时的「打开标签页」宫格(随 SIDE_TAB_ORDER 展示可用标签) */ -function EmptyState({ onOpen }: { onOpen: (type: SideTabType) => void }): JSX.Element { - return ( -
-
-

打开标签页

-

选择要在侧边面板中打开的标签。

-
-
- {SIDE_TAB_ORDER.map((type) => { - const { title, icon: Icon } = SIDE_TAB_META[type] - return ( - - ) - })} -
-
- ) -}