import { create } from 'zustand' import type { Tab } from '@/types' import { readFile, writeFile, saveDialog, pickFile } from '@/lib/tauri' import { detectLanguage } from '@/lib/languageMap' interface TabsState { tabs: Tab[] activeTabId: string | null cursorLine: number cursorCol: number savingId: string | null statusMessage: string | null untitledCounter: number openFile: (path: string) => Promise pickAndOpenFile: () => Promise createUntitledFile: () => void closeTab: (id: string) => void setActiveTab: (id: string) => void updateContent: (id: string, content: string) => void saveFile: (id: string) => Promise setCursorPosition: (line: number, col: number) => void clearStatusMessage: () => void } function isUntitled(id: string) { return id.startsWith('untitled://') } export const useTabsStore = create((set, get) => ({ tabs: [], activeTabId: null, cursorLine: 1, cursorCol: 1, savingId: null, statusMessage: null, untitledCounter: 0, openFile: async (path: string) => { const { tabs } = get() const existing = tabs.find((t) => t.path === path) if (existing) { set({ activeTabId: existing.id }) return } try { const content = await readFile(path) const name = path.split(/[\\/]/).pop() ?? 'untitled' const id = path const tab: Tab = { id, path, name, language: detectLanguage(name), content, modified: false, } set({ tabs: [...tabs, tab], activeTabId: id }) } catch (err) { console.error('Failed to open file:', err) } }, pickAndOpenFile: async () => { const path = await pickFile() if (path) get().openFile(path) }, createUntitledFile: () => { const { tabs, untitledCounter } = get() const n = untitledCounter + 1 const name = `Untitled-${n}` const id = `untitled://${n}` const tab: Tab = { id, path: id, name, language: 'plaintext', content: '', modified: false, } set({ tabs: [...tabs, tab], activeTabId: id, untitledCounter: n }) }, closeTab: (id: string) => { const { tabs, activeTabId } = get() const newTabs = tabs.filter((t) => t.id !== id) let newActive = activeTabId if (activeTabId === id) { const idx = tabs.findIndex((t) => t.id === id) newActive = newTabs[Math.min(idx, newTabs.length - 1)]?.id ?? null } set({ tabs: newTabs, activeTabId: newActive }) }, setActiveTab: (id: string) => { set({ activeTabId: id }) }, updateContent: (id: string, content: string) => { set((state) => ({ tabs: state.tabs.map((t) => t.id === id ? { ...t, content, modified: true } : t ), })) }, saveFile: async (id: string) => { const { tabs } = get() const tab = tabs.find((t) => t.id === id) if (!tab) return set({ savingId: id, statusMessage: null }) try { let targetPath = tab.path if (isUntitled(targetPath)) { const picked = await saveDialog(tab.name) if (!picked) { set({ savingId: null }) return } targetPath = picked } await writeFile(targetPath, tab.content) const name = targetPath.split(/[\\/]/).pop() ?? tab.name set({ tabs: tabs.map((t) => t.id === id ? { ...t, path: targetPath, name, language: detectLanguage(name), modified: false } : t ), savingId: null, statusMessage: 'Saved', }) } catch (err) { set({ savingId: null, statusMessage: 'Save failed' }) console.error('Failed to save file:', err) } }, setCursorPosition: (line: number, col: number) => { set({ cursorLine: line, cursorCol: col }) }, clearStatusMessage: () => { set({ statusMessage: null }) }, }))