feat: add logo, favicon, and theme system
- Add custom SVG favicon and Logo component (house + network nodes motif) - Update page title to Homelable with meta description - Show Logo in sidebar header and toolbar - Add theme store and ThemeModal for canvas style switching - Refactor node colors and edge styles for theme support
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import { useThemeStore } from '@/stores/themeStore'
|
||||
|
||||
describe('themeStore', () => {
|
||||
beforeEach(() => {
|
||||
useThemeStore.setState({ activeTheme: 'default' })
|
||||
})
|
||||
|
||||
it('starts with default theme', () => {
|
||||
expect(useThemeStore.getState().activeTheme).toBe('default')
|
||||
})
|
||||
|
||||
it('setTheme updates activeTheme', () => {
|
||||
useThemeStore.getState().setTheme('matrix')
|
||||
expect(useThemeStore.getState().activeTheme).toBe('matrix')
|
||||
})
|
||||
|
||||
it('setTheme can switch between all presets', () => {
|
||||
const themes = ['default', 'dark', 'light', 'neon', 'matrix'] as const
|
||||
for (const id of themes) {
|
||||
useThemeStore.getState().setTheme(id)
|
||||
expect(useThemeStore.getState().activeTheme).toBe(id)
|
||||
}
|
||||
})
|
||||
|
||||
it('setTheme back to default after neon', () => {
|
||||
useThemeStore.getState().setTheme('neon')
|
||||
useThemeStore.getState().setTheme('default')
|
||||
expect(useThemeStore.getState().activeTheme).toBe('default')
|
||||
})
|
||||
})
|
||||
@@ -32,6 +32,7 @@ interface CanvasState {
|
||||
editingGroupRectId: string | null
|
||||
setEditingGroupRectId: (id: string | null) => void
|
||||
markSaved: () => void
|
||||
markUnsaved: () => void
|
||||
loadCanvas: (nodes: Node<NodeData>[], edges: Edge<EdgeData>[]) => void
|
||||
notifyScanDeviceFound: () => void
|
||||
}
|
||||
@@ -155,6 +156,8 @@ export const useCanvasStore = create<CanvasState>((set) => ({
|
||||
|
||||
markSaved: () => set({ hasUnsavedChanges: false }),
|
||||
|
||||
markUnsaved: () => set({ hasUnsavedChanges: true }),
|
||||
|
||||
notifyScanDeviceFound: () => set({ scanEventTs: Date.now() }),
|
||||
|
||||
loadCanvas: (nodes, edges) => {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { create } from 'zustand'
|
||||
import type { ThemeId } from '@/utils/themes'
|
||||
|
||||
interface ThemeState {
|
||||
activeTheme: ThemeId
|
||||
setTheme: (id: ThemeId) => void
|
||||
}
|
||||
|
||||
export const useThemeStore = create<ThemeState>((set) => ({
|
||||
activeTheme: 'default',
|
||||
setTheme: (id) => set({ activeTheme: id }),
|
||||
}))
|
||||
Reference in New Issue
Block a user