Fix/frontend review findings #3

Merged
Kronecker merged 16 commits from fix/frontend-review-findings into main 2026-08-30 00:16:35 +08:00
7 changed files with 44 additions and 20 deletions
Showing only changes of commit 0dbd32a757 - Show all commits
@@ -53,8 +53,8 @@ async function createNote() {
const name = rawName.endsWith('.md') ? rawName : `${rawName}.md` const name = rawName.endsWith('.md') ? rawName : `${rawName}.md`
const file = await workspaceService.createFile('/', name, `# ${rawName}\n\n`) const file = await workspaceService.createFile('/', name, `# ${rawName}\n\n`)
workspaceStore.addFileToTree('/', file) workspaceStore.addFileToTree('/', file)
workspaceStore.openFile(file.path)
await editorStore.loadFile(file.path) await editorStore.loadFile(file.path)
workspaceStore.openFile(file.path)
await router.push('/workspace') await router.push('/workspace')
} }
+1 -1
View File
@@ -41,8 +41,8 @@ watch(() => chatStore.selectedProviderId, async (providerId) => {
function send() { void chatStore.sendMessage(chatStore.inputText) } function send() { void chatStore.sendMessage(chatStore.inputText) }
async function openCitation(citation: Citation) { async function openCitation(citation: Citation) {
workspaceStore.openFile(citation.file_path)
await editorStore.loadFile(citation.file_path) await editorStore.loadFile(citation.file_path)
workspaceStore.openFile(citation.file_path)
editorStore.highlightBlock(citation.block_id) editorStore.highlightBlock(citation.block_id)
await router.push('/workspace') await router.push('/workspace')
} }
+1 -1
View File
@@ -23,8 +23,8 @@ function submitSearch() {
} }
async function openResult(result: SearchResult) { async function openResult(result: SearchResult) {
workspaceStore.openFile(result.file_path)
await editorStore.loadFile(result.file_path) await editorStore.loadFile(result.file_path)
workspaceStore.openFile(result.file_path)
editorStore.highlightBlock(result.block_id) editorStore.highlightBlock(result.block_id)
await router.push('/workspace') await router.push('/workspace')
} }
@@ -29,8 +29,8 @@ async function createItem() {
const name = rawName.endsWith('.md') ? rawName : `${rawName}.md` const name = rawName.endsWith('.md') ? rawName : `${rawName}.md`
const file = await workspaceService.createFile(parentPath.value, name, `# ${rawName}\n\n`) const file = await workspaceService.createFile(parentPath.value, name, `# ${rawName}\n\n`)
workspaceStore.addFileToTree(parentPath.value, file) workspaceStore.addFileToTree(parentPath.value, file)
workspaceStore.openFile(file.path)
await editorStore.loadFile(file.path) await editorStore.loadFile(file.path)
workspaceStore.openFile(file.path)
await router.push('/workspace') await router.push('/workspace')
} else { } else {
const folder = await workspaceService.createFolder(parentPath.value, rawName) const folder = await workspaceService.createFolder(parentPath.value, rawName)
@@ -42,8 +42,8 @@ async function createItem() {
async function openNode(node: FileNode) { async function openNode(node: FileNode) {
if (node.type === 'folder') return workspaceStore.toggleFolder(node.path) if (node.type === 'folder') return workspaceStore.toggleFolder(node.path)
workspaceStore.openFile(node.path)
await editorStore.loadFile(node.path) await editorStore.loadFile(node.path)
workspaceStore.openFile(node.path)
await router.push('/workspace') await router.push('/workspace')
} }
@@ -80,8 +80,8 @@ async function deleteTarget() {
const activeWasRemoved = workspaceStore.closePath(node.path) const activeWasRemoved = workspaceStore.closePath(node.path)
workspaceStore.removeFromTree(node.path) workspaceStore.removeFromTree(node.path)
if (activeWasRemoved) { if (activeWasRemoved) {
editorStore.closeFile()
if (workspaceStore.activeFilePath) await editorStore.loadFile(workspaceStore.activeFilePath) if (workspaceStore.activeFilePath) await editorStore.loadFile(workspaceStore.activeFilePath)
else editorStore.closeFile()
} }
closeContextMenu() closeContextMenu()
} }
@@ -13,8 +13,9 @@ onMounted(() => {
// Already loaded // Already loaded
} }
if (!workspaceStore.activeFilePath && workspaceStore.fileTree.length === 0) { if (!workspaceStore.activeFilePath && workspaceStore.fileTree.length === 0) {
workspaceStore.openFile('/欢迎使用知笔知己.md') void editorStore.loadFile('/欢迎使用知笔知己.md').then(() => {
editorStore.loadFile('/欢迎使用知笔知己.md') workspaceStore.openFile('/欢迎使用知笔知己.md')
})
} }
}) })
</script> </script>
+29 -9
View File
@@ -34,34 +34,54 @@ export const useEditorStore = defineStore('editor', () => {
} }
let saveTimer: ReturnType<typeof setTimeout> | null = null let saveTimer: ReturnType<typeof setTimeout> | null = null
let pendingSave: Promise<void> | null = null
function scheduleAutoSave(delay = 1500) { function scheduleAutoSave(delay = 1500) {
if (saveTimer) clearTimeout(saveTimer) if (saveTimer) clearTimeout(saveTimer)
saveTimer = setTimeout(() => { saveTimer = setTimeout(() => {
saveTimer = null
void save() void save()
}, delay) }, delay)
} }
async function save() { async function save() {
if (!currentFilePath.value) return if (!currentFilePath.value) return
if (saveStatus.value === 'saving') return if (pendingSave) return pendingSave
const targetPath = currentFilePath.value const targetPath = currentFilePath.value
const snapshot = content.value const snapshot = content.value
saveStatus.value = 'saving' saveStatus.value = 'saving'
try { pendingSave = (async () => {
await workspaceService.saveFileContent(targetPath, snapshot) try {
if (currentFilePath.value === targetPath) { await workspaceService.saveFileContent(targetPath, snapshot)
saveStatus.value = content.value === snapshot ? 'saved' : 'dirty' if (currentFilePath.value === targetPath) {
lastSavedAt.value = new Date().toISOString() saveStatus.value = content.value === snapshot ? 'saved' : 'dirty'
lastSavedAt.value = new Date().toISOString()
}
} catch {
if (currentFilePath.value === targetPath) saveStatus.value = 'save_failed'
} finally {
pendingSave = null
} }
} catch { })()
saveStatus.value = 'save_failed' return pendingSave
}
} }
let loadVersion = 0 let loadVersion = 0
async function loadFile(filePath: string) { async function loadFile(filePath: string) {
if (currentFilePath.value === filePath) return
if (saveTimer) {
clearTimeout(saveTimer)
saveTimer = null
}
if (saveStatus.value === 'conflict') {
throw new Error('当前文件存在编辑冲突,请处理后再切换文件。')
}
if (pendingSave) await pendingSave
if (saveStatus.value === 'dirty' || saveStatus.value === 'save_failed') await save()
if (saveStatus.value === 'dirty' || saveStatus.value === 'save_failed') {
throw new Error('当前文件保存失败,已阻止切换以避免内容丢失。')
}
const version = ++loadVersion const version = ++loadVersion
currentFilePath.value = filePath currentFilePath.value = filePath
saveStatus.value = 'saving' saveStatus.value = 'saving'
+6 -3
View File
@@ -14,6 +14,7 @@ export const useThemeStore = defineStore('theme', () => {
const fontEditorSize = ref(15) const fontEditorSize = ref(15)
const fontEditorFamily = ref('system-ui') const fontEditorFamily = ref('system-ui')
const lineHeight = ref(1.7) const lineHeight = ref(1.7)
let appearanceHydrated = false
const currentTheme = computed(() => const currentTheme = computed(() =>
themes.value.find((t) => t.theme_id === currentThemeId.value) || themes.value[0] themes.value.find((t) => t.theme_id === currentThemeId.value) || themes.value[0]
@@ -47,6 +48,8 @@ export const useThemeStore = defineStore('theme', () => {
} catch { localStorage.removeItem('editor-appearance') } } catch { localStorage.removeItem('editor-appearance') }
} }
const saved = localStorage.getItem('theme') const saved = localStorage.getItem('theme')
appearanceHydrated = true
persistAppearance()
if (saved && themes.value.find((t) => t.theme_id === saved)) { if (saved && themes.value.find((t) => t.theme_id === saved)) {
applyTheme(saved) applyTheme(saved)
return return
@@ -72,17 +75,17 @@ export const useThemeStore = defineStore('theme', () => {
watch(fontEditorSize, (v) => { watch(fontEditorSize, (v) => {
document.documentElement.style.setProperty('--font-editor-size', `${v}px`) document.documentElement.style.setProperty('--font-editor-size', `${v}px`)
persistAppearance() if (appearanceHydrated) persistAppearance()
}, { immediate: true }) }, { immediate: true })
watch(lineHeight, (v) => { watch(lineHeight, (v) => {
document.documentElement.style.setProperty('--font-editor-line-height', String(v)) document.documentElement.style.setProperty('--font-editor-line-height', String(v))
persistAppearance() if (appearanceHydrated) persistAppearance()
}, { immediate: true }) }, { immediate: true })
watch(fontEditorFamily, (v) => { watch(fontEditorFamily, (v) => {
document.documentElement.style.setProperty('--font-editor-sans', v) document.documentElement.style.setProperty('--font-editor-sans', v)
persistAppearance() if (appearanceHydrated) persistAppearance()
}, { immediate: true }) }, { immediate: true })
return { return {