M0-M11 狩夜人笔记初始实现:11里程碑+86测试

This commit is contained in:
hunter-dev
2026-08-24 20:35:22 +08:00
commit 3ebd33536b
106 changed files with 10490 additions and 0 deletions
@@ -0,0 +1,72 @@
import { create } from 'zustand';
export interface Tab { id: string; path: string; pinned: boolean; scrollTop: number; }
interface TabState {
tabs: Tab[];
activeId: string | null;
openTab: (path: string) => void;
closeTab: (id: string) => void;
setActive: (id: string) => void;
togglePin: (id: string) => void;
activePath: () => string | null;
persist: () => void;
restore: () => void;
}
const SESSION_KEY = 'dsh-session';
export const useTabs = create<TabState>((set, get) => ({
tabs: [],
activeId: null,
openTab: (path) => {
const existing = get().tabs.find(t => t.path === path);
if (existing) { set({ activeId: existing.id }); return; }
const id = 'tab-' + Date.now() + '-' + Math.random().toString(36).slice(2, 6);
set(s => ({ tabs: [...s.tabs, { id, path, pinned: false, scrollTop: 0 }], activeId: id }));
get().persist();
},
closeTab: (id) => {
const { tabs, activeId } = get();
const idx = tabs.findIndex(t => t.id === id);
const remaining = tabs.filter(t => t.id !== id);
let newActive = activeId;
if (activeId === id) {
// 切到相邻(优先未固定的相邻)
const neighbor = remaining[idx] ?? remaining[idx - 1] ?? remaining[remaining.length - 1];
newActive = neighbor?.id ?? null;
}
set({ tabs: remaining, activeId: newActive });
get().persist();
},
setActive: (id) => { set({ activeId: id }); get().persist(); },
togglePin: (id) => {
set(s => {
const tabs = s.tabs.map(t => t.id === id ? { ...t, pinned: !t.pinned } : t);
// 固定的排前
tabs.sort((a, b) => (a.pinned === b.pinned ? 0 : a.pinned ? -1 : 1));
return { tabs };
});
get().persist();
},
activePath: () => { const { tabs, activeId } = get(); return tabs.find(t => t.id === activeId)?.path ?? null; },
persist: () => {
const { tabs, activeId } = get();
try { localStorage.setItem(SESSION_KEY, JSON.stringify({ tabs, activeId })); } catch {}
},
restore: () => {
try {
const raw = localStorage.getItem(SESSION_KEY);
if (!raw) return;
const saved = JSON.parse(raw);
set({ tabs: saved.tabs ?? [], activeId: saved.activeId ?? null });
} catch {}
},
}));