Initial commit
This commit is contained in:
34
src/stores/dragStore.ts
Normal file
34
src/stores/dragStore.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
export interface DragState {
|
||||
draggedPath: string | null
|
||||
draggedType: 'file' | 'dir' | null
|
||||
dropTargetPath: string | null
|
||||
}
|
||||
|
||||
interface DragStore {
|
||||
state: DragState
|
||||
setState: (state: DragState) => void
|
||||
isDraggedInsidePath: (checkPath: string) => boolean
|
||||
}
|
||||
|
||||
const initialState: DragState = {
|
||||
draggedPath: null,
|
||||
draggedType: null,
|
||||
dropTargetPath: null
|
||||
}
|
||||
|
||||
export const useDragStore = create<DragStore>((set, get) => ({
|
||||
state: initialState,
|
||||
|
||||
setState: (state) => set({ state }),
|
||||
|
||||
isDraggedInsidePath: (checkPath) => {
|
||||
const { state } = get()
|
||||
if (!state.draggedPath) return false
|
||||
if (state.draggedType === 'dir') {
|
||||
return checkPath.startsWith(`${state.draggedPath}/`)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}))
|
||||
31
src/stores/index.ts
Normal file
31
src/stores/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export { useUIStore, useTheme, useWallpaper, useMarkdownDisplay } from './uiStore'
|
||||
export { useScreenshotStore, useScreenshot } from './screenshotStore'
|
||||
export { useDragStore, type DragState } from './dragStore'
|
||||
|
||||
export { useTabStore, isSpecialTab, isRegularMarkdownFile, getDerivedEditState, type TabState } from './tabStore'
|
||||
|
||||
import { useTabStore } from './tabStore'
|
||||
|
||||
export const useTabManagerStore = useTabStore
|
||||
export const useTabEditStore = useTabStore
|
||||
|
||||
export const loadTabContent = useTabStore.getState().loadContent
|
||||
export const saveTabContent = useTabStore.getState().saveContent
|
||||
|
||||
export const useTabContentContext = () => {
|
||||
const store = useTabStore()
|
||||
const openFiles = Array.from(store.tabs.values()).map(t => t.file)
|
||||
const selectedFile = store.activeTabId ? store.tabs.get(store.activeTabId)?.file ?? null : null
|
||||
|
||||
return {
|
||||
openFiles,
|
||||
selectedFile,
|
||||
setSelectedFile: store.setActiveFile,
|
||||
selectFile: store.selectFile,
|
||||
closeFile: store.closeFile,
|
||||
closeOtherFiles: store.closeOtherFiles,
|
||||
closeAllFiles: store.closeAllFiles,
|
||||
renamePath: store.renamePath,
|
||||
handleDeletePath: store.handleDeletePath,
|
||||
}
|
||||
}
|
||||
60
src/stores/screenshotStore.ts
Normal file
60
src/stores/screenshotStore.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { create } from 'zustand'
|
||||
import { getScreenshot, saveScreenshot, getDeviceData, saveDeviceData } from '@/modules/remote/api'
|
||||
|
||||
interface ScreenshotState {
|
||||
screenshot: string
|
||||
lastConnected: string
|
||||
loadScreenshots: (deviceName?: string) => Promise<void>
|
||||
updateScreenshot: (dataUrl: string, deviceName?: string) => Promise<void>
|
||||
updateLastConnected: () => void
|
||||
}
|
||||
|
||||
const formatDateTime = () => {
|
||||
const now = new Date()
|
||||
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
export const useScreenshotStore = create<ScreenshotState>((set) => ({
|
||||
screenshot: '',
|
||||
lastConnected: '',
|
||||
|
||||
loadScreenshots: async (deviceName) => {
|
||||
try {
|
||||
const [loaded, data] = await Promise.all([
|
||||
getScreenshot(deviceName),
|
||||
getDeviceData(deviceName)
|
||||
])
|
||||
set({ screenshot: loaded || '', lastConnected: data?.lastConnected || '' })
|
||||
} catch (err) {
|
||||
console.error('Failed to load screenshot:', err)
|
||||
set({ screenshot: '', lastConnected: '' })
|
||||
}
|
||||
},
|
||||
|
||||
updateScreenshot: async (dataUrl, deviceName) => {
|
||||
const timeStr = formatDateTime()
|
||||
set({ screenshot: dataUrl, lastConnected: timeStr })
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
saveScreenshot(dataUrl, deviceName),
|
||||
deviceName ? saveDeviceData(deviceName, timeStr) : Promise.resolve()
|
||||
])
|
||||
} catch (err) {
|
||||
console.error('Failed to save screenshot:', err)
|
||||
}
|
||||
},
|
||||
|
||||
updateLastConnected: () => {
|
||||
set({ lastConnected: formatDateTime() })
|
||||
}
|
||||
}))
|
||||
|
||||
export const useScreenshot = () => {
|
||||
const screenshot = useScreenshotStore((s) => s.screenshot)
|
||||
const lastConnected = useScreenshotStore((s) => s.lastConnected)
|
||||
const updateScreenshot = useScreenshotStore((s) => s.updateScreenshot)
|
||||
const updateLastConnected = useScreenshotStore((s) => s.updateLastConnected)
|
||||
const loadScreenshots = useScreenshotStore((s) => s.loadScreenshots)
|
||||
return { screenshot, lastConnected, updateScreenshot, updateLastConnected, loadScreenshots }
|
||||
}
|
||||
446
src/stores/tabStore.ts
Normal file
446
src/stores/tabStore.ts
Normal file
@@ -0,0 +1,446 @@
|
||||
import { create } from 'zustand'
|
||||
import type { FileItem } from '@/lib/api'
|
||||
import { getModule } from '@/lib/module-registry'
|
||||
import { fetchFileContent, saveFileContent } from '@/lib/api'
|
||||
import { isMarkdownFile } from '@/lib/utils'
|
||||
import { matchModule } from '@/lib/tabs'
|
||||
import { isAsyncImportProcessingContent } from '@shared/constants'
|
||||
|
||||
const getHomeFileItem = (): FileItem => {
|
||||
const homeModule = getModule('home')
|
||||
return homeModule?.fileItem ?? {
|
||||
name: '首页',
|
||||
path: 'home-tab',
|
||||
type: 'file',
|
||||
size: 0,
|
||||
modified: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
|
||||
const getFileName = (path: string): string => {
|
||||
const parts = path.replace(/\\/g, '/').split('/')
|
||||
return parts[parts.length - 1] || ''
|
||||
}
|
||||
|
||||
export interface TabState {
|
||||
file: FileItem
|
||||
content: string
|
||||
unsavedContent: string
|
||||
isEditing: boolean
|
||||
loading: boolean
|
||||
loaded: boolean
|
||||
}
|
||||
|
||||
export interface TabStoreState {
|
||||
tabs: Map<string, TabState>
|
||||
activeTabId: string | null
|
||||
}
|
||||
|
||||
interface TabActions {
|
||||
selectFile: (file: FileItem) => void
|
||||
setActiveFile: (file: FileItem | null) => void
|
||||
closeFile: (file: FileItem) => void
|
||||
closeOtherFiles: (fileToKeep: FileItem) => void
|
||||
closeAllFiles: () => void
|
||||
renamePath: (oldPath: string, newPath: string) => void
|
||||
handleDeletePath: (path: string, type: 'file' | 'dir') => void
|
||||
|
||||
loadContent: (filePath: string) => Promise<void>
|
||||
saveContent: (filePath: string) => Promise<void>
|
||||
setEditing: (filePath: string, editing: boolean) => void
|
||||
setUnsavedContent: (filePath: string, content: string) => void
|
||||
toggleEditing: (filePath: string) => void
|
||||
|
||||
getTab: (filePath: string) => TabState | undefined
|
||||
getActiveTab: () => TabState | undefined
|
||||
getActiveFile: () => FileItem | null
|
||||
getOpenFiles: () => FileItem[]
|
||||
hasUnsavedChanges: (filePath: string) => boolean
|
||||
isLoading: (filePath: string) => boolean
|
||||
}
|
||||
|
||||
export type TabStore = TabStoreState & TabActions
|
||||
|
||||
const getInitialState = (): TabStoreState => {
|
||||
const homeFileItem = getHomeFileItem()
|
||||
const homeTab: TabState = {
|
||||
file: homeFileItem,
|
||||
content: '',
|
||||
unsavedContent: '',
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
loaded: true,
|
||||
}
|
||||
const tabs = new Map<string, TabState>()
|
||||
tabs.set(homeFileItem.path, homeTab)
|
||||
|
||||
return {
|
||||
tabs,
|
||||
activeTabId: homeFileItem.path,
|
||||
}
|
||||
}
|
||||
|
||||
export const useTabStore = create<TabStore>((set, get) => {
|
||||
const initialState = getInitialState()
|
||||
|
||||
return {
|
||||
...initialState,
|
||||
|
||||
selectFile: (file) => set((state) => {
|
||||
const tabs = new Map(state.tabs)
|
||||
const existingTab = tabs.get(file.path)
|
||||
|
||||
if (!existingTab) {
|
||||
const newTab: TabState = {
|
||||
file,
|
||||
content: '',
|
||||
unsavedContent: '',
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
loaded: false,
|
||||
}
|
||||
tabs.set(file.path, newTab)
|
||||
}
|
||||
|
||||
return {
|
||||
tabs,
|
||||
activeTabId: file.path,
|
||||
}
|
||||
}),
|
||||
|
||||
setActiveFile: (file) => set((state) => {
|
||||
if (!file) {
|
||||
return { activeTabId: null }
|
||||
}
|
||||
|
||||
const tabs = new Map(state.tabs)
|
||||
tabs.forEach((tab, path) => {
|
||||
tabs.set(path, { ...tab })
|
||||
})
|
||||
|
||||
return {
|
||||
tabs,
|
||||
activeTabId: file.path,
|
||||
}
|
||||
}),
|
||||
|
||||
closeFile: (file) => set((state) => {
|
||||
const homeFileItem = getHomeFileItem()
|
||||
const tabs = new Map(state.tabs)
|
||||
const filePath = file.path
|
||||
|
||||
const allPaths = Array.from(tabs.keys())
|
||||
const currentIndex = allPaths.indexOf(filePath)
|
||||
|
||||
let newActiveTabId = state.activeTabId
|
||||
if (state.activeTabId === filePath) {
|
||||
tabs.delete(filePath)
|
||||
|
||||
const remainingPaths = Array.from(tabs.keys())
|
||||
if (remainingPaths.length > 0) {
|
||||
const prevIndex = Math.max(0, currentIndex - 1)
|
||||
newActiveTabId = remainingPaths[prevIndex]
|
||||
} else {
|
||||
const homeTab: TabState = {
|
||||
file: homeFileItem,
|
||||
content: '',
|
||||
unsavedContent: '',
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
loaded: true,
|
||||
}
|
||||
tabs.set(homeFileItem.path, homeTab)
|
||||
newActiveTabId = homeFileItem.path
|
||||
}
|
||||
} else {
|
||||
tabs.delete(filePath)
|
||||
}
|
||||
|
||||
return {
|
||||
tabs,
|
||||
activeTabId: newActiveTabId,
|
||||
}
|
||||
}),
|
||||
|
||||
closeOtherFiles: (fileToKeep) => set((state) => {
|
||||
const homeFileItem = getHomeFileItem()
|
||||
const tabs = new Map(state.tabs)
|
||||
const keepPath = fileToKeep.path
|
||||
|
||||
const tabToKeep = tabs.get(keepPath)
|
||||
const newTabs = new Map<string, TabState>()
|
||||
|
||||
if (tabToKeep) {
|
||||
newTabs.set(keepPath, tabToKeep)
|
||||
} else {
|
||||
newTabs.set(keepPath, {
|
||||
file: fileToKeep,
|
||||
content: '',
|
||||
unsavedContent: '',
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
loaded: false,
|
||||
})
|
||||
}
|
||||
|
||||
if (newTabs.size === 0) {
|
||||
const homeTab: TabState = {
|
||||
file: homeFileItem,
|
||||
content: '',
|
||||
unsavedContent: '',
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
loaded: true,
|
||||
}
|
||||
newTabs.set(homeFileItem.path, homeTab)
|
||||
}
|
||||
|
||||
return {
|
||||
tabs: newTabs,
|
||||
activeTabId: keepPath,
|
||||
}
|
||||
}),
|
||||
|
||||
closeAllFiles: () => set(() => {
|
||||
const homeFileItem = getHomeFileItem()
|
||||
const homeTab: TabState = {
|
||||
file: homeFileItem,
|
||||
content: '',
|
||||
unsavedContent: '',
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
loaded: true,
|
||||
}
|
||||
const tabs = new Map<string, TabState>()
|
||||
tabs.set(homeFileItem.path, homeTab)
|
||||
|
||||
return {
|
||||
tabs,
|
||||
activeTabId: homeFileItem.path,
|
||||
}
|
||||
}),
|
||||
|
||||
renamePath: (oldPath, newPath) => set((state) => {
|
||||
const tabs = new Map(state.tabs)
|
||||
const oldTab = tabs.get(oldPath)
|
||||
|
||||
if (oldTab) {
|
||||
const newFile = { ...oldTab.file, path: newPath, name: getFileName(newPath) || oldTab.file.name }
|
||||
tabs.set(newPath, { ...oldTab, file: newFile })
|
||||
tabs.delete(oldPath)
|
||||
}
|
||||
|
||||
return { tabs }
|
||||
}),
|
||||
|
||||
handleDeletePath: (path, type) => set((state) => {
|
||||
const homeFileItem = getHomeFileItem()
|
||||
const tabs = new Map(state.tabs)
|
||||
const isAffected = (filePath: string) => {
|
||||
if (type === 'file') return filePath === path
|
||||
return filePath === path || filePath.startsWith(path + '/')
|
||||
}
|
||||
|
||||
const pathsToDelete: string[] = []
|
||||
tabs.forEach((_, filePath) => {
|
||||
if (isAffected(filePath)) {
|
||||
pathsToDelete.push(filePath)
|
||||
}
|
||||
})
|
||||
|
||||
pathsToDelete.forEach(p => tabs.delete(p))
|
||||
|
||||
let newActiveTabId = state.activeTabId
|
||||
if (state.activeTabId && isAffected(state.activeTabId)) {
|
||||
const remainingPaths = Array.from(tabs.keys())
|
||||
if (remainingPaths.length > 0) {
|
||||
newActiveTabId = remainingPaths[0]
|
||||
} else {
|
||||
const homeTab: TabState = {
|
||||
file: homeFileItem,
|
||||
content: '',
|
||||
unsavedContent: '',
|
||||
isEditing: false,
|
||||
loading: false,
|
||||
loaded: true,
|
||||
}
|
||||
tabs.set(homeFileItem.path, homeTab)
|
||||
newActiveTabId = homeFileItem.path
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tabs,
|
||||
activeTabId: newActiveTabId,
|
||||
}
|
||||
}),
|
||||
|
||||
loadContent: async (filePath) => {
|
||||
const { tabs } = get()
|
||||
const tab = tabs.get(filePath)
|
||||
if (!tab) return
|
||||
|
||||
set((state) => {
|
||||
const newTabs = new Map(state.tabs)
|
||||
newTabs.set(filePath, { ...tab, loading: true })
|
||||
return { tabs: newTabs }
|
||||
})
|
||||
|
||||
try {
|
||||
const data = await fetchFileContent(filePath)
|
||||
|
||||
set((state) => {
|
||||
const newTabs = new Map(state.tabs)
|
||||
const currentTab = newTabs.get(filePath)
|
||||
if (currentTab) {
|
||||
newTabs.set(filePath, {
|
||||
...currentTab,
|
||||
content: data.content,
|
||||
unsavedContent: data.content,
|
||||
loading: false,
|
||||
loaded: true,
|
||||
})
|
||||
}
|
||||
return { tabs: newTabs }
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to load content', error)
|
||||
const errorMsg = '# Error\nFailed to load file content.'
|
||||
|
||||
set((state) => {
|
||||
const newTabs = new Map(state.tabs)
|
||||
const currentTab = newTabs.get(filePath)
|
||||
if (currentTab) {
|
||||
newTabs.set(filePath, {
|
||||
...currentTab,
|
||||
content: errorMsg,
|
||||
unsavedContent: errorMsg,
|
||||
loading: false,
|
||||
loaded: true,
|
||||
})
|
||||
}
|
||||
return { tabs: newTabs }
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
saveContent: async (filePath) => {
|
||||
const tab = get().tabs.get(filePath)
|
||||
if (!tab) return
|
||||
|
||||
const isAsyncImportProcessing = isAsyncImportProcessingContent(tab.content)
|
||||
if (isAsyncImportProcessing) return
|
||||
|
||||
await saveFileContent(filePath, tab.unsavedContent)
|
||||
|
||||
set((state) => {
|
||||
const newTabs = new Map(state.tabs)
|
||||
const currentTab = newTabs.get(filePath)
|
||||
if (currentTab) {
|
||||
newTabs.set(filePath, {
|
||||
...currentTab,
|
||||
content: currentTab.unsavedContent,
|
||||
isEditing: false,
|
||||
})
|
||||
}
|
||||
return { tabs: newTabs }
|
||||
})
|
||||
},
|
||||
|
||||
setEditing: (filePath, editing) => set((state) => {
|
||||
const tab = state.tabs.get(filePath)
|
||||
if (!tab) return state
|
||||
|
||||
const newTabs = new Map(state.tabs)
|
||||
newTabs.set(filePath, {
|
||||
...tab,
|
||||
isEditing: editing,
|
||||
unsavedContent: editing ? tab.unsavedContent : tab.content,
|
||||
})
|
||||
return { tabs: newTabs }
|
||||
}),
|
||||
|
||||
setUnsavedContent: (filePath, content) => set((state) => {
|
||||
const tab = state.tabs.get(filePath)
|
||||
if (!tab) return state
|
||||
|
||||
const newTabs = new Map(state.tabs)
|
||||
newTabs.set(filePath, { ...tab, unsavedContent: content })
|
||||
return { tabs: newTabs }
|
||||
}),
|
||||
|
||||
toggleEditing: (filePath) => set((state) => {
|
||||
const tab = state.tabs.get(filePath)
|
||||
if (!tab) return state
|
||||
|
||||
const isAsyncImportProcessing = isAsyncImportProcessingContent(tab.content)
|
||||
if (isAsyncImportProcessing) return state
|
||||
|
||||
const newTabs = new Map(state.tabs)
|
||||
newTabs.set(filePath, {
|
||||
...tab,
|
||||
isEditing: !tab.isEditing,
|
||||
unsavedContent: !tab.isEditing ? tab.content : tab.unsavedContent,
|
||||
})
|
||||
return { tabs: newTabs }
|
||||
}),
|
||||
|
||||
getTab: (filePath) => get().tabs.get(filePath),
|
||||
|
||||
getActiveTab: () => {
|
||||
const { tabs, activeTabId } = get()
|
||||
return activeTabId ? tabs.get(activeTabId) : undefined
|
||||
},
|
||||
|
||||
getActiveFile: () => {
|
||||
const activeTab = get().getActiveTab()
|
||||
return activeTab?.file ?? null
|
||||
},
|
||||
|
||||
getOpenFiles: () => {
|
||||
const { tabs } = get()
|
||||
return Array.from(tabs.values()).map(tab => tab.file)
|
||||
},
|
||||
|
||||
hasUnsavedChanges: (filePath) => {
|
||||
const tab = get().tabs.get(filePath)
|
||||
if (!tab) return false
|
||||
return tab.unsavedContent !== tab.content
|
||||
},
|
||||
|
||||
isLoading: (filePath) => {
|
||||
const tab = get().tabs.get(filePath)
|
||||
return tab?.loading ?? false
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
export const isSpecialTab = (file: FileItem | null): boolean => {
|
||||
if (!file) return false
|
||||
if (file.path.startsWith('remote-desktop://')) return true
|
||||
if (file.path.startsWith('remote-git://')) return true
|
||||
return matchModule(file) !== undefined
|
||||
}
|
||||
|
||||
export const isRegularMarkdownFile = (file: FileItem | null): boolean => {
|
||||
return !isSpecialTab(file) && !!file && isMarkdownFile(file.name)
|
||||
}
|
||||
|
||||
export const getDerivedEditState = (selectedFilePath: string | null) => {
|
||||
const { tabs } = useTabStore.getState()
|
||||
const tab = selectedFilePath ? tabs.get(selectedFilePath) : undefined
|
||||
|
||||
const isEditing = tab?.isEditing || false
|
||||
const hasUnsavedChanges = tab ? tab.unsavedContent !== tab.content : false
|
||||
const content = isEditing ? (tab?.unsavedContent || '') : (tab?.content || '')
|
||||
const wordCount = content.replace(/\s/g, '').length
|
||||
|
||||
return {
|
||||
isCurrentTabEditing: isEditing,
|
||||
hasCurrentTabUnsavedChanges: hasUnsavedChanges,
|
||||
currentContent: content,
|
||||
currentWordCount: wordCount,
|
||||
isLoading: tab?.loading || false,
|
||||
isLoaded: tab?.loaded || false,
|
||||
}
|
||||
}
|
||||
91
src/stores/uiStore.ts
Normal file
91
src/stores/uiStore.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { create } from 'zustand'
|
||||
import { persist } from 'zustand/middleware'
|
||||
import type { ThemeMode } from '@shared/types'
|
||||
|
||||
const getSystemPreferredTheme = (): ThemeMode => {
|
||||
if (typeof window === 'undefined') return 'light'
|
||||
try {
|
||||
return window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
} catch {
|
||||
return 'light'
|
||||
}
|
||||
}
|
||||
|
||||
const clampValue = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value))
|
||||
|
||||
interface UIState {
|
||||
theme: ThemeMode
|
||||
isDark: boolean
|
||||
setTheme: (theme: ThemeMode) => void
|
||||
toggleTheme: () => void
|
||||
|
||||
opacity: number
|
||||
setOpacity: (opacity: number) => void
|
||||
|
||||
fontSize: number
|
||||
setFontSize: (fontSize: number) => void
|
||||
zoom: number
|
||||
setZoom: (zoom: number) => void
|
||||
zoomIn: () => void
|
||||
zoomOut: () => void
|
||||
resetZoom: () => void
|
||||
}
|
||||
|
||||
export const useUIStore = create<UIState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
theme: getSystemPreferredTheme(),
|
||||
isDark: getSystemPreferredTheme() === 'dark',
|
||||
setTheme: (theme) => set({ theme, isDark: theme === 'dark' }),
|
||||
toggleTheme: () => {
|
||||
const newTheme = get().theme === 'dark' ? 'light' : 'dark'
|
||||
set({ theme: newTheme, isDark: newTheme === 'dark' })
|
||||
},
|
||||
|
||||
opacity: 0.8,
|
||||
setOpacity: (opacity) => set({ opacity }),
|
||||
|
||||
fontSize: 16,
|
||||
setFontSize: (fontSize) => set({ fontSize }),
|
||||
zoom: 100,
|
||||
setZoom: (zoom) => set({ zoom: clampValue(Math.round(zoom), 50, 200) }),
|
||||
zoomIn: () => set((s) => ({ zoom: clampValue(s.zoom + 10, 50, 200) })),
|
||||
zoomOut: () => set((s) => ({ zoom: clampValue(s.zoom - 10, 50, 200) })),
|
||||
resetZoom: () => set({ zoom: 100 }),
|
||||
}),
|
||||
{
|
||||
name: 'ui-storage',
|
||||
partialize: (state) => ({
|
||||
theme: state.theme,
|
||||
opacity: state.opacity,
|
||||
fontSize: state.fontSize,
|
||||
zoom: state.zoom,
|
||||
}),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
export const useTheme = () => {
|
||||
const theme = useUIStore((s) => s.theme)
|
||||
const isDark = useUIStore((s) => s.isDark)
|
||||
const setTheme = useUIStore((s) => s.setTheme)
|
||||
const toggleTheme = useUIStore((s) => s.toggleTheme)
|
||||
return { theme, isDark, setTheme, toggleTheme }
|
||||
}
|
||||
|
||||
export const useWallpaper = () => {
|
||||
const opacity = useUIStore((s) => s.opacity)
|
||||
const setOpacity = useUIStore((s) => s.setOpacity)
|
||||
return { opacity, setOpacity }
|
||||
}
|
||||
|
||||
export const useMarkdownDisplay = () => {
|
||||
const fontSize = useUIStore((s) => s.fontSize)
|
||||
const setFontSize = useUIStore((s) => s.setFontSize)
|
||||
const zoom = useUIStore((s) => s.zoom)
|
||||
const setZoom = useUIStore((s) => s.setZoom)
|
||||
const zoomIn = useUIStore((s) => s.zoomIn)
|
||||
const zoomOut = useUIStore((s) => s.zoomOut)
|
||||
const resetZoom = useUIStore((s) => s.resetZoom)
|
||||
return { fontSize, setFontSize, zoom, setZoom, zoomIn, zoomOut, resetZoom }
|
||||
}
|
||||
Reference in New Issue
Block a user