0.3.1 UI修复

This commit is contained in:
liaibo
2026-01-18 19:01:31 +08:00
parent 90a38b48cf
commit 8a5b915002
86 changed files with 1160 additions and 2867 deletions
@@ -35,9 +35,9 @@ import { buildDocumentTree } from "@/lib/documents";
import { useSidebarStore } from "@/store/sidebar";
import type { SidebarInitialData, SidebarSectionId } from "@/components/sidebar/types";
import { useSidebarData } from "@/hooks/use-sidebar-data";
import { useConvexSidebarData } from "@/hooks/use-convex-sidebar-data";
import { PrivateTree } from "@/components/sidebar/private-tree";
import { buildSidebarSectionsFromTree, flattenDocumentTree } from "@/lib/sidebar-tree";
import { getSupabaseBrowserClient } from "@/lib/supabase/client";
import { useSearchPaletteStore } from "@/store/search-palette";
import { useEditorBridgeStore } from "@/store/editor-bridge";
import { FileTree } from "@/components/sidebar/file-tree";
@@ -126,26 +126,56 @@ interface ContextMenuState {
y: number;
}
// 主 Sidebar 组件:根据模式路由到不同的子组件
export function Sidebar({ initialData }: SidebarProps) {
const useConvex = useMemo(() => Boolean(getMnoteRuntimeConfig().useConvex), []);
// 根据模式渲染不同的子组件,确保 Supabase 模式完全不调用 Convex hooks
if (useConvex) {
return <SidebarConvex initialData={initialData} />;
}
return <SidebarSupabase initialData={initialData} />;
}
// Convex 模式专用组件 - 只调用 Convex hooks
function SidebarConvex({ initialData }: SidebarProps) {
const convexData = useConvexSidebarData(initialData.activeWorkspaceId);
return <SidebarContent initialData={initialData} sidebarQuery={convexData} />;
}
// Supabase 模式专用组件 - 只调用 Supabase hooks
function SidebarSupabase({ initialData }: SidebarProps) {
const supabaseData = useSidebarData(initialData);
return <SidebarContent initialData={initialData} sidebarQuery={supabaseData} />;
}
// 共享的 UI 内容组件 - 包含所有现有的 Sidebar 逻辑
interface SidebarContentProps {
initialData: SidebarInitialData;
sidebarQuery: {
data: SidebarInitialData | null | undefined;
isLoading: boolean;
refetch: () => Promise<unknown>;
};
}
function SidebarContent({ initialData, sidebarQuery }: SidebarContentProps) {
const { open, setOpen, width, setWidth, collapsedSections, toggleSection, setSectionCollapsed, trashConfirm, setTrashConfirm } =
useSidebarStore();
const useConvex = useMemo(() => Boolean(getMnoteRuntimeConfig().useConvex), []);
const supabaseBrowser = useMemo(() => {
if (useConvex) return null;
try {
return getSupabaseBrowserClient();
} catch {
return null;
}
}, [useConvex]);
const sectionsTrayOpen = useSidebarStore((state) => state.sectionsTrayOpen);
const toggleSectionsTray = useSidebarStore((state) => state.toggleSectionsTray);
const setSectionsTrayOpen = useSidebarStore((state) => state.setSectionsTrayOpen);
const viewMode = useSidebarStore((state) => state.viewMode);
const setViewMode = useSidebarStore((state) => state.setViewMode);
const openSearchPalette = useSearchPaletteStore((state) => state.openSearch);
const sidebarQuery = useSidebarData(initialData);
const sidebarData = sidebarQuery.data ?? initialData;
const openSearchPalette = useSearchPaletteStore((state) => state.openSearch);
// 处理数据
const sidebarData = useMemo(() => {
return sidebarQuery.data ?? initialData;
}, [sidebarQuery, initialData]);
// isLoading 判断
const isLoading = sidebarQuery.isLoading;
const segments = useSelectedLayoutSegments();
const router = useRouter();
const activeId = segments?.[1] ?? "";
@@ -253,45 +283,6 @@ export function Sidebar({ initialData }: SidebarProps) {
await sidebarQuery.refetch();
}, [sidebarQuery]);
useEffect(() => {
if (!supabaseBrowser) return;
const channel = supabaseBrowser
.channel("documents-feed")
.on(
"postgres_changes",
{ event: "*", schema: "public", table: "documents" },
() => {
void refreshTree();
},
)
.subscribe();
return () => {
supabaseBrowser.removeChannel(channel);
};
}, [refreshTree, supabaseBrowser]);
useEffect(() => {
if (!supabaseBrowser) return;
const channel = supabaseBrowser
.channel("media-assets-feed")
.on(
"postgres_changes",
{
event: "*",
schema: "public",
table: "media_assets",
filter: `workspace_id=eq.${sidebarData.activeWorkspaceId}`,
},
() => {
void sidebarQuery.refetch();
},
)
.subscribe();
return () => {
supabaseBrowser.removeChannel(channel);
};
}, [sidebarData.activeWorkspaceId, sidebarQuery, supabaseBrowser]);
const sections = useMemo(() => buildSidebarSectionsFromTree(tree), [tree]);
const starredNodes = useMemo(() => sections.find((section) => section.id === "starred")?.nodes ?? [], [sections]);
const publicNodes = useMemo(() => sections.find((section) => section.id === "public")?.nodes ?? [], [sections]);
@@ -498,29 +489,7 @@ export function Sidebar({ initialData }: SidebarProps) {
const handleEmbedPrompt = useCallback(async (node: DocumentNode) => {
openMoveEmbedPicker(node, "embed");
return;
if (sidebarData.activeWorkspaceId) {
openMoveEmbedPicker(node, "embed");
return;
}
if (typeof window === "undefined") {
return;
}
const target = window.prompt("输入希望嵌入到的页面 ID");
if (!target) {
return;
}
const response = await fetch("/api/documents/embed", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sourceId: node.id, targetId: target.trim() }),
});
if (!response.ok) {
window.alert("嵌入失败,请检查目标页面 ID");
return;
}
window.alert("已在目标页面末尾插入引用块");
}, []);
}, [openMoveEmbedPicker]);
const toggleExpand = useCallback((id: string) => {
setExpanded((prev) => {
@@ -1492,22 +1461,8 @@ export function Sidebar({ initialData }: SidebarProps) {
const handleMovePrompt = useCallback(
async (node: DocumentNode) => {
openMoveEmbedPicker(node, "move");
return;
if (sidebarData.activeWorkspaceId) {
openMoveEmbedPicker(node, "move");
return;
}
if (typeof window === "undefined") {
return;
}
const target = window.prompt("输入目标父页面 ID(留空表示移动到根目录)", node.parent_id ?? "");
if (target === null) {
return;
}
const trimmed = target.trim();
await handleMove(node.id, trimmed.length > 0 ? trimmed : null, 0);
},
[handleMove],
[openMoveEmbedPicker],
);
const handleDelete = useCallback(
@@ -2035,8 +1990,8 @@ export function Sidebar({ initialData }: SidebarProps) {
return (
<>
<aside className="hidden h-full min-w-0 overflow-hidden md:flex" style={{ width }}>
<div className="flex h-full min-w-0 flex-1 flex-col overflow-hidden border-r border-[#e9edf5] bg-white">
<aside className="hidden h-full min-w-0 overflow-hidden md:flex shrink-0" style={{ width }}>
<div className="flex h-full min-w-0 flex-1 flex-col overflow-hidden border-r border-wolai-border bg-wolai-bg-sidebar">
{sidebarBody}
</div>
<div className="w-1 cursor-col-resize bg-transparent" onMouseDown={handleResizeStart} />