mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-17 13:13:35 +08:00
- Windows 上隐藏原生菜单栏和窗口装饰,前端渲染自定义窗口控制按钮(最小化/最大化/关闭) - macOS 保持原生菜单栏不变(条件编译) - Sidebar 在 Windows 上不再添加 macOS 红绿灯的额外顶部间距 - 新增 Windows 构建脚本和 NSIS 安装钩子 - 服务端 CORS、会话服务、对话服务改进及测试补充 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
103 lines
3.1 KiB
TypeScript
103 lines
3.1 KiB
TypeScript
import { create } from 'zustand'
|
|
import { sessionsApi } from '../api/sessions'
|
|
import type { SessionListItem } from '../types/session'
|
|
|
|
type SessionStore = {
|
|
sessions: SessionListItem[]
|
|
activeSessionId: string | null
|
|
isLoading: boolean
|
|
error: string | null
|
|
selectedProjects: string[]
|
|
availableProjects: string[]
|
|
|
|
fetchSessions: (project?: string) => Promise<void>
|
|
createSession: (workDir?: string) => Promise<string>
|
|
deleteSession: (id: string) => Promise<void>
|
|
renameSession: (id: string, title: string) => Promise<void>
|
|
updateSessionTitle: (id: string, title: string) => void
|
|
setActiveSession: (id: string | null) => void
|
|
setSelectedProjects: (projects: string[]) => void
|
|
}
|
|
|
|
export const useSessionStore = create<SessionStore>((set, get) => ({
|
|
sessions: [],
|
|
activeSessionId: null,
|
|
isLoading: false,
|
|
error: null,
|
|
selectedProjects: [],
|
|
availableProjects: [],
|
|
|
|
fetchSessions: async (project?: string) => {
|
|
set({ isLoading: true, error: null })
|
|
try {
|
|
const { sessions: raw } = await sessionsApi.list({ project, limit: 100 })
|
|
// Deduplicate by session ID — keep the most recently modified entry
|
|
const byId = new Map<string, SessionListItem>()
|
|
for (const s of raw) {
|
|
const existing = byId.get(s.id)
|
|
if (!existing || new Date(s.modifiedAt) > new Date(existing.modifiedAt)) {
|
|
byId.set(s.id, s)
|
|
}
|
|
}
|
|
const sessions = [...byId.values()]
|
|
const availableProjects = [...new Set(sessions.map((s) => s.projectPath).filter(Boolean))].sort()
|
|
set({ sessions, availableProjects, isLoading: false })
|
|
} catch (err) {
|
|
set({ error: (err as Error).message, isLoading: false })
|
|
}
|
|
},
|
|
|
|
createSession: async (workDir?: string) => {
|
|
const { sessionId: id } = await sessionsApi.create(workDir || undefined)
|
|
const now = new Date().toISOString()
|
|
const optimisticSession: SessionListItem = {
|
|
id,
|
|
title: 'New Session',
|
|
createdAt: now,
|
|
modifiedAt: now,
|
|
messageCount: 0,
|
|
projectPath: '',
|
|
workDir: workDir ?? null,
|
|
workDirExists: true,
|
|
}
|
|
|
|
set((state) => ({
|
|
sessions: state.sessions.some((session) => session.id === id)
|
|
? state.sessions
|
|
: [optimisticSession, ...state.sessions],
|
|
activeSessionId: id,
|
|
}))
|
|
|
|
void get().fetchSessions()
|
|
return id
|
|
},
|
|
|
|
deleteSession: async (id: string) => {
|
|
await sessionsApi.delete(id)
|
|
set((s) => ({
|
|
sessions: s.sessions.filter((session) => session.id !== id),
|
|
activeSessionId: s.activeSessionId === id ? null : s.activeSessionId,
|
|
}))
|
|
},
|
|
|
|
renameSession: async (id: string, title: string) => {
|
|
await sessionsApi.rename(id, title)
|
|
set((s) => ({
|
|
sessions: s.sessions.map((session) =>
|
|
session.id === id ? { ...session, title } : session,
|
|
),
|
|
}))
|
|
},
|
|
|
|
updateSessionTitle: (id, title) => {
|
|
set((s) => ({
|
|
sessions: s.sessions.map((session) =>
|
|
session.id === id ? { ...session, title } : session,
|
|
),
|
|
}))
|
|
},
|
|
|
|
setActiveSession: (id) => set({ activeSessionId: id }),
|
|
setSelectedProjects: (projects) => set({ selectedProjects: projects }),
|
|
}))
|