From a38d9f097acb58574584412b8ba935d924244674 Mon Sep 17 00:00:00 2001 From: mlogclub Date: Tue, 2 Jun 2026 16:26:43 +0800 Subject: [PATCH] feat: add resize functionality to KnowledgeDirectoryPanel with localization support --- .../_components/knowledge-directory-panel.tsx | 58 ++++++++++++++++++- web/messages/en-US.json | 1 + web/messages/zh-CN.json | 1 + 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/web/app/dashboard/knowledge/_components/knowledge-directory-panel.tsx b/web/app/dashboard/knowledge/_components/knowledge-directory-panel.tsx index d0351ce..cff37dc 100644 --- a/web/app/dashboard/knowledge/_components/knowledge-directory-panel.tsx +++ b/web/app/dashboard/knowledge/_components/knowledge-directory-panel.tsx @@ -12,7 +12,7 @@ import { Trash2Icon, } from "lucide-react"; import type { ReactNode } from "react"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState, type PointerEvent } from "react"; import { toast } from "sonner"; import { OptionCombobox } from "@/components/option-combobox"; @@ -61,6 +61,11 @@ type DirectoryDialogState = { type DirectoryOption = { value: string; label: string }; +const DIRECTORY_PANEL_WIDTH_STORAGE_KEY = "knowledge-directory-panel-width"; +const DIRECTORY_PANEL_MIN_WIDTH = 180; +const DIRECTORY_PANEL_MAX_WIDTH = 360; +const DIRECTORY_PANEL_DEFAULT_WIDTH = 224; + function rootDirectoryOptions(items: KnowledgeDirectory[]): DirectoryOption[] { return items.map((item) => ({ value: String(item.id), label: item.name })); } @@ -84,6 +89,16 @@ export function KnowledgeDirectoryPanel({ onChanged, }: KnowledgeDirectoryPanelProps) { const t = useI18n(); + const [panelWidth, setPanelWidth] = useState(() => { + if (typeof window === "undefined") { + return DIRECTORY_PANEL_DEFAULT_WIDTH; + } + const saved = Number(localStorage.getItem(DIRECTORY_PANEL_WIDTH_STORAGE_KEY)); + if (!Number.isFinite(saved)) { + return DIRECTORY_PANEL_DEFAULT_WIDTH; + } + return Math.min(DIRECTORY_PANEL_MAX_WIDTH, Math.max(DIRECTORY_PANEL_MIN_WIDTH, saved)); + }); const [directories, setDirectories] = useState([]); const [expandedIds, setExpandedIds] = useState>(new Set()); const [contextMenuDirectoryId, setContextMenuDirectoryId] = useState(null); @@ -121,6 +136,35 @@ export function KnowledgeDirectoryPanel({ void loadDirectories(); }, [loadDirectories]); + useEffect(() => { + localStorage.setItem(DIRECTORY_PANEL_WIDTH_STORAGE_KEY, String(panelWidth)); + }, [panelWidth]); + + function handleResizePointerDown(event: PointerEvent) { + event.preventDefault(); + const startX = event.clientX; + const startWidth = panelWidth; + + function handlePointerMove(moveEvent: globalThis.PointerEvent) { + const nextWidth = startWidth + moveEvent.clientX - startX; + setPanelWidth( + Math.min(DIRECTORY_PANEL_MAX_WIDTH, Math.max(DIRECTORY_PANEL_MIN_WIDTH, nextWidth)), + ); + } + + function handlePointerUp() { + window.removeEventListener("pointermove", handlePointerMove); + window.removeEventListener("pointerup", handlePointerUp); + document.body.style.cursor = ""; + document.body.style.userSelect = ""; + } + + document.body.style.cursor = "col-resize"; + document.body.style.userSelect = "none"; + window.addEventListener("pointermove", handlePointerMove); + window.addEventListener("pointerup", handlePointerUp); + } + function toggleDirectory(id: number) { setExpandedIds((current) => { const next = new Set(current); @@ -202,7 +246,10 @@ export function KnowledgeDirectoryPanel({ return ( <> -
+
{t("knowledge.directory")}