fix: split category fetch, safe localStorage, aria-labels on icon buttons
- TreeLibraryPage: split categories into a mount-only fetch so filter changes only re-fetch trees (not categories every time) - Add safeGetItem/safeSetItem/safeRemoveItem helpers in utils.ts to prevent crashes in private browsing or when storage is unavailable - Replace raw localStorage calls in ScratchpadSidebar, TreeNavigationPage, TreeEditorPage, and treeEditorStore with safe wrappers - Add aria-label to 20 icon-only buttons across 8 component files for screen reader accessibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { shallow } from 'zustand/shallow'
|
||||
import { immer } from 'zustand/middleware/immer'
|
||||
import type { Tree, TreeStructure, TreeCreate, TreeUpdate, NodeType, TreeMarkdownValidationError, TreeMarkdownValidation } from '@/types'
|
||||
import { treeStructureToMarkdownPreview } from '@/lib/treeMarkdownSync'
|
||||
import { safeGetItem, safeSetItem, safeRemoveItem } from '@/lib/utils'
|
||||
|
||||
// Throttle helper: captures first call immediately, then throttles subsequent calls
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -304,7 +305,7 @@ export const useTreeEditorStore = create<TreeEditorState>()(
|
||||
|
||||
// Check for existing draft on init
|
||||
initNewTree: () => {
|
||||
const hasDraft = localStorage.getItem(DRAFT_STORAGE_KEY) !== null
|
||||
const hasDraft = safeGetItem(DRAFT_STORAGE_KEY) !== null
|
||||
set((state) => {
|
||||
state.treeId = null
|
||||
state.name = ''
|
||||
@@ -360,7 +361,7 @@ export const useTreeEditorStore = create<TreeEditorState>()(
|
||||
},
|
||||
|
||||
loadDraft: () => {
|
||||
const draftJson = localStorage.getItem(DRAFT_STORAGE_KEY)
|
||||
const draftJson = safeGetItem(DRAFT_STORAGE_KEY)
|
||||
if (!draftJson) return false
|
||||
|
||||
try {
|
||||
@@ -380,13 +381,13 @@ export const useTreeEditorStore = create<TreeEditorState>()(
|
||||
})
|
||||
return true
|
||||
} catch {
|
||||
localStorage.removeItem(DRAFT_STORAGE_KEY)
|
||||
safeRemoveItem(DRAFT_STORAGE_KEY)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
discardDraft: () => {
|
||||
localStorage.removeItem(DRAFT_STORAGE_KEY)
|
||||
safeRemoveItem(DRAFT_STORAGE_KEY)
|
||||
set((state) => {
|
||||
state.hasDraft = false
|
||||
})
|
||||
@@ -868,12 +869,12 @@ export const useTreeEditorStore = create<TreeEditorState>()(
|
||||
treeStructure: state.treeStructure,
|
||||
savedAt: new Date().toISOString()
|
||||
}
|
||||
localStorage.setItem(DRAFT_STORAGE_KEY, JSON.stringify(draft))
|
||||
safeSetItem(DRAFT_STORAGE_KEY, JSON.stringify(draft))
|
||||
set((s) => { s.draftSavedAt = new Date() })
|
||||
},
|
||||
|
||||
markSaved: () => {
|
||||
localStorage.removeItem(DRAFT_STORAGE_KEY)
|
||||
safeRemoveItem(DRAFT_STORAGE_KEY)
|
||||
set((state) => {
|
||||
state.isDirty = false
|
||||
state.lastSavedAt = new Date()
|
||||
|
||||
Reference in New Issue
Block a user