v0.3.5: Bug修复+性能优化+状态栏文档统计
Bug修复: - BUG-01: Sidebar norm函数正则修复(单反斜杠匹配), Windows路径规范化恢复 - BUG-02: 3处result.content truthy检查改为content !== undefined, 空文件可打开 - BUG-03: TAB_SWITCHED路径校验顺序修复, activeFilePath不在校验前写入 优化: - OPT-01: Sidebar handleFileClick添加loading状态, 打开文件有视觉反馈 新功能: - FEAT-01: 状态栏添加行数/单词数/字符数统计 - useDocStats hook + computeDocStats 纯函数 - StatusBar右侧显示 行/词/字符 统计信息 - 12个单元测试覆盖空值/空白/ASCII/UTF-8/Markdown等场景 验证: TS 0错误, ESLint 0错误(1个预存警告), 90/90测试通过
This commit is contained in:
@@ -9,7 +9,7 @@ import { useSidebarResize } from '../../hooks/useSidebarResize'
|
||||
import { useFolderOperations } from '../../hooks/useFolderOperations'
|
||||
import { useAutoExpandDir } from '../../hooks/useAutoExpandDir'
|
||||
import { OutlinePanel, parseHeadings } from '../OutlinePanel'
|
||||
import { getEditorView } from '../../stores/editorStore'
|
||||
import { getEditorView, useEditorStore } from '../../stores/editorStore'
|
||||
import { TextSelection } from '@milkdown/prose/state'
|
||||
|
||||
const norm = (p: string) => p.replace(/\\/g, '/')
|
||||
@@ -29,6 +29,7 @@ export const Sidebar = React.memo(function Sidebar() {
|
||||
const activeFilePath = activeTab?.filePath ?? null
|
||||
const { sidebarRef, startResize } = useSidebarResize()
|
||||
const { handleOpenFolder } = useFolderOperations()
|
||||
const setLoading = useEditorStore(s => s.setLoading)
|
||||
useAutoExpandDir(activeFilePath)
|
||||
|
||||
// Parse headings from active tab content
|
||||
@@ -56,12 +57,17 @@ export const Sidebar = React.memo(function Sidebar() {
|
||||
const existing = tabs.find(t => t.filePath === path)
|
||||
if (existing) { switchToTab(existing.id); return }
|
||||
if (!window.electronAPI) return
|
||||
const result = await window.electronAPI.readFile(path)
|
||||
if (result.success && result.content) {
|
||||
createTab(path, result.content)
|
||||
recentFilesRepository.add(path)
|
||||
setLoading('file-open', true)
|
||||
try {
|
||||
const result = await window.electronAPI.readFile(path)
|
||||
if (result.success && result.content !== undefined) {
|
||||
createTab(path, result.content)
|
||||
recentFilesRepository.add(path)
|
||||
}
|
||||
} finally {
|
||||
setLoading('file-open', false)
|
||||
}
|
||||
}, [tabs, switchToTab, createTab])
|
||||
}, [tabs, switchToTab, createTab, setLoading])
|
||||
|
||||
const independentFiles = tabs.filter(t => {
|
||||
if (!t.filePath) return false
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import React from 'react'
|
||||
import { useTabStore } from '../../stores/tabStore'
|
||||
import { useEditorStore } from '../../stores/editorStore'
|
||||
import { useDocStats } from '../../hooks/useDocStats'
|
||||
import { getFileName } from '../../lib/fileUtils'
|
||||
import { LoadingSpinner } from '../LoadingSpinner/LoadingSpinner'
|
||||
|
||||
export const StatusBar = React.memo(function StatusBar() {
|
||||
const activeTab = useTabStore(s => s.getActiveTab())
|
||||
const loadingStates = useEditorStore(s => s.loadingStates)
|
||||
const stats = useDocStats(activeTab?.content)
|
||||
|
||||
// 判断是否有任何加载状态激活
|
||||
const hasLoading = Object.values(loadingStates).some(Boolean)
|
||||
@@ -32,6 +34,22 @@ export const StatusBar = React.memo(function StatusBar() {
|
||||
</span>
|
||||
</div>
|
||||
<div className="status-right">
|
||||
{activeTab && (
|
||||
<>
|
||||
<span className="status-stat" title="行数" aria-label={`${stats.lines} 行`}>
|
||||
{stats.lines} 行
|
||||
</span>
|
||||
<span className="status-divider">|</span>
|
||||
<span className="status-stat" title="单词数" aria-label={`${stats.words} 个单词`}>
|
||||
{stats.words} 词
|
||||
</span>
|
||||
<span className="status-divider">|</span>
|
||||
<span className="status-stat" title="字符数(含空格)" aria-label={`${stats.chars} 个字符`}>
|
||||
{stats.chars} 字符
|
||||
</span>
|
||||
<span className="status-divider">|</span>
|
||||
</>
|
||||
)}
|
||||
<span id="status-encoding">UTF-8</span>
|
||||
<span className="status-divider">|</span>
|
||||
<span id="status-lang">Markdown</span>
|
||||
|
||||
Reference in New Issue
Block a user