refactor(client): remove desktop sidebar collapse (NavToggle) — always docked

Drop the NavToggle collapse handle from both the chat Nav and the knowledge-space sidebar on desktop: the sidebar is always docked/visible and the related hover/ResizeObserver/measure state is removed. Mobile keeps the toggleable overlay drawer. Also drop the tablet auto-collapse effect in knowledge/index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kinyoo
2026-07-22 18:13:58 +08:00
parent ffc026ad58
commit cb9c174855
3 changed files with 5 additions and 59 deletions
+5 -40
View File
@@ -1,6 +1,6 @@
import type { ConversationListResponse } from '~/types/chat';
import { PermissionTypes, Permissions } from '~/types/chat';
import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { useSearchContext } from '~/Providers';
import { Conversations } from '~/components/Conversations';
import { Spinner } from '~/components/svg';
@@ -15,7 +15,6 @@ import {
} from '~/hooks';
import { cn } from '~/utils';
import AccountSettings from './AccountSettings';
import NavToggle from './NavToggle';
import NewChat from './NewChat';
import { ChatNavUserFooter } from './ChatNavUserFooter';
@@ -30,9 +29,6 @@ const Nav = ({
const { isAuthenticated } = useAuthContext();
const [navWidth, setNavWidth] = useState('240px');
const [isHovering, setIsHovering] = useState(false);
const navPanelRef = useRef<HTMLDivElement>(null);
const [navPanelRightPx, setNavPanelRightPx] = useState(0);
const isSmallScreen = usePrefersMobileLayout();
const [newUser, setNewUser] = useLocalStorage('newUser', true);
@@ -54,28 +50,6 @@ const Nav = ({
}
}, [isSmallScreen]);
// 折叠把手贴在会话列表右缘(分隔线右侧),需计入 MainLayout 窄轨 + main 内边距,故用测量值而非假定 left:0 + translateX
useLayoutEffect(() => {
if (isSmallScreen) {
return;
}
const el = navPanelRef.current;
if (!el) {
return;
}
const sync = () => {
setNavPanelRightPx(el.getBoundingClientRect().right);
};
sync();
const ro = new ResizeObserver(sync);
ro.observe(el);
window.addEventListener('resize', sync);
return () => {
ro.disconnect();
window.removeEventListener('resize', sync);
};
}, [isSmallScreen, navVisible, navWidth]);
const [showLoading, setShowLoading] = useState(false);
const { pageNumber, searchQuery, setPageNumber, searchQueryRes } = useSearchContext();
@@ -145,7 +119,6 @@ const Nav = ({
return (
<>
<div
ref={navPanelRef}
data-testid="nav"
className={cn(
// 须与 usePrefersMobileLayoutmax-width:767px)一致:touch-mobile 为 max-1023,误伤 768–1023 会去掉边距/边框,像「盖在内容上」
@@ -153,8 +126,10 @@ const Nav = ({
isSmallScreen && 'fixed inset-y-0 left-0 z-[70] h-[100dvh] shadow-[4px_0_24px_rgba(0,0,0,0.06)]',
)}
style={{
width: navVisible ? navWidth : '0px',
visibility: navVisible ? 'visible' : 'hidden',
// Desktop: sidebar is always docked/visible (collapse removed).
// Mobile: still a toggleable overlay drawer.
width: isSmallScreen ? (navVisible ? navWidth : '0px') : navWidth,
visibility: isSmallScreen ? (navVisible ? 'visible' : 'hidden') : 'visible',
transition: 'width 0.2s, visibility 0.2s',
}}
>
@@ -228,16 +203,6 @@ const Nav = ({
</div>
</div>
</div>
{!isSmallScreen && navPanelRightPx > 0 ? (
<NavToggle
navVisible={navVisible}
onToggle={toggleNavVisible}
isHovering={isHovering}
setIsHovering={setIsHovering}
className=""
anchorRightEdgePx={navPanelRightPx}
/>
) : null}
{isSmallScreen && (
<div
id="mobile-nav-mask-toggle"
@@ -166,12 +166,6 @@ export default function Knowledge() {
if (!isH5) setSpaceListDrawerOpen(false);
}, [isH5]);
// Tablet (7681023): collapse the sidebar by default so the file area gets the room,
// while the NavToggle stays available to expand it. Desktop keeps the user's choice.
useEffect(() => {
if (!isH5 && !isDesktop) setSidebarCollapsed(true);
}, [isH5, isDesktop]);
useEffect(() => {
if (showKnowledgeSquare) setSpaceListDrawerOpen(false);
}, [showKnowledgeSquare]);
@@ -7,7 +7,6 @@ import { useEffect, useMemo, useRef, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { KnowledgeSpace, SpaceRole, SpaceSortType, getMineSpacesApi, getJoinedSpacesApi, getDepartmentSpacesApi } from "~/api/knowledge";
import { Button } from "~/components/ui/Button";
import NavToggle from "~/components/Nav/NavToggle";
import KnowledgeSpaceItem from "./KnowledgeSpaceItem";
import KnowledgeSpaceCardItem from "./KnowledgeSpaceCardItem";
import { SectionHeader } from "./SectionHeader";
@@ -148,7 +147,6 @@ export function KnowledgeSpaceSidebar({
// Scrolling container ref — drives the dynamic, scroll-following name ellipsis.
const listScrollRef = useRef<HTMLDivElement>(null);
useDynamicEllipsis(listScrollRef);
const [isToggleHovering, setIsToggleHovering] = useState(false);
const queryClient = useQueryClient();
@@ -610,8 +608,6 @@ export function KnowledgeSpaceSidebar({
onMouseDown={handleResizeStart}
onDoubleClick={handleResizeReset}
className={cn(
// z below NavToggle (z-40) so the collapse button keeps capturing clicks
// in its small area; everywhere else along the edge the resize handle wins.
"absolute top-0 z-[35] h-full w-[6px] -translate-x-1/2 cursor-col-resize",
// Subtle visual feedback: thin accent line on hover/active.
"after:absolute after:right-1/2 after:top-0 after:h-full after:w-px after:translate-x-1/2",
@@ -621,15 +617,6 @@ export function KnowledgeSpaceSidebar({
style={{ left: sidebarWidth }}
/>
)}
<NavToggle
navVisible={!collapsed}
onToggle={() => setCollapsed(!collapsed)}
isHovering={isToggleHovering}
setIsHovering={setIsToggleHovering}
className={`absolute top-1/2 left-0 z-[40] ${fullWidthMode ? "hidden" : ""}`}
translateX={collapsed ? 0 : sidebarWidth}
disableTransition={isResizing}
/>
</div>
);
}