Bug修复:
- BUG-10 修复: 保存文件时 watcher restart 触发虚假 change 事件覆盖编辑器内容
- ipc-handlers.ts: restart watcher 在 setSelfWriting(true) 保护内执行
- App.tsx handleReloadModified: 增加 if (tab?.isModified) return 安全守卫
新功能:
- 新增源码(Source)视图模式,直接用 textarea 编辑原始 Markdown
- ViewMode 扩展为 editor|preview|source
- 工具栏增加源码按钮,快捷键 Ctrl+3
- 内容与 WYSIWYG 编辑器共享 tabStore,切换模式不丢内容
验证: TS 0错误, ESLint 0错误(1个预存警告), 90/90测试通过
72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
import React from 'react'
|
|
import { FolderOpen, Save, EditMode, PreviewMode, SourceMode, Moon, Sun, Info } from '../Icons'
|
|
import type { ViewMode } from '../../types/settings'
|
|
|
|
interface ToolbarProps {
|
|
onOpen: () => void
|
|
onSave: () => void
|
|
viewMode: ViewMode
|
|
onViewModeChange: (mode: ViewMode) => void
|
|
darkMode: boolean
|
|
onToggleDark: () => void
|
|
onShowAbout: () => void
|
|
}
|
|
|
|
export const Toolbar = React.memo(function Toolbar({ onOpen, onSave, viewMode, onViewModeChange, darkMode, onToggleDark, onShowAbout }: ToolbarProps) {
|
|
return (
|
|
<div id="toolbar" role="toolbar" aria-label="工具栏">
|
|
<div className="toolbar-left" role="group" aria-label="文件操作">
|
|
<button className="toolbar-btn" onClick={onOpen} title="打开文件 (Ctrl+O)" aria-label="打开文件">
|
|
<FolderOpen size={18} />
|
|
<span>打开</span>
|
|
</button>
|
|
<button className="toolbar-btn" onClick={onSave} title="保存文件 (Ctrl+S)" aria-label="保存文件">
|
|
<Save size={18} />
|
|
<span>保存</span>
|
|
</button>
|
|
<div className="toolbar-divider" role="separator" />
|
|
<button
|
|
className={`toolbar-btn ${viewMode === 'editor' ? 'active' : ''}`}
|
|
onClick={() => onViewModeChange('editor')}
|
|
title="WYSIWYG 编辑 (Ctrl+1)"
|
|
aria-label="WYSIWYG 编辑模式"
|
|
aria-pressed={viewMode === 'editor'}
|
|
>
|
|
<EditMode size={18} />
|
|
<span>编辑</span>
|
|
</button>
|
|
<button
|
|
className={`toolbar-btn ${viewMode === 'source' ? 'active' : ''}`}
|
|
onClick={() => onViewModeChange('source')}
|
|
title="源码编辑 (Ctrl+3)"
|
|
aria-label="源码编辑模式"
|
|
aria-pressed={viewMode === 'source'}
|
|
>
|
|
<SourceMode size={18} />
|
|
<span>源码</span>
|
|
</button>
|
|
<button
|
|
className={`toolbar-btn ${viewMode === 'preview' ? 'active' : ''}`}
|
|
onClick={() => onViewModeChange('preview')}
|
|
title="预览 (Ctrl+2)"
|
|
aria-label="预览模式"
|
|
aria-pressed={viewMode === 'preview'}
|
|
>
|
|
<PreviewMode size={18} />
|
|
<span>预览</span>
|
|
</button>
|
|
</div>
|
|
<div className="toolbar-right" role="group" aria-label="设置">
|
|
<button className="toolbar-btn" onClick={onToggleDark} title="切换暗色主题" aria-label={darkMode ? '切换到亮色主题' : '切换到暗色主题'}>
|
|
{darkMode ? <Sun size={18} /> : <Moon size={18} />}
|
|
</button>
|
|
<button className="toolbar-btn" onClick={onShowAbout} title="关于" aria-label="关于 MarkLite">
|
|
<Info size={18} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
})
|
|
|
|
Toolbar.displayName = 'Toolbar'
|