v0.2.0: 全面代码质量优化
- ESLint flat config + Prettier + EditorConfig - Markdown处理器LRU缓存 - Zustand选择器优化减少重渲染 - CodeMirror Compartment主题热切换 - Preview防抖(150ms) + IndexedDB去抖(500ms) - 组件拆分: App.tsx 310→104行, Sidebar.tsx 244→90行 - 统一错误处理 errorHandler.ts - ConfirmDialog替代原生confirm - LoadingSpinner加载状态 - Toast多条堆叠+类型区分 - 可访问性增强(ARIA属性、键盘导航) - Vitest测试框架(78个测试用例) - Git hooks(husky + lint-staged) - 项目文档(README.md, CONTRIBUTING.md)
This commit is contained in:
@@ -5,16 +5,14 @@ interface EditorToolbarProps {
|
||||
viewRef: React.MutableRefObject<EditorView | null>
|
||||
}
|
||||
|
||||
export function EditorToolbar({ viewRef }: EditorToolbarProps) {
|
||||
export const EditorToolbar = React.memo(function EditorToolbar({ viewRef }: EditorToolbarProps) {
|
||||
const insertFormatting = useCallback((before: string, after: string, placeholder: string) => {
|
||||
const view = viewRef.current
|
||||
if (!view) return
|
||||
|
||||
const { from, to } = view.state.selection.main
|
||||
const selected = view.state.sliceDoc(from, to)
|
||||
const text = selected || placeholder
|
||||
const insert = before + text + after
|
||||
|
||||
view.dispatch({
|
||||
changes: { from, to, insert },
|
||||
selection: { anchor: from + before.length, head: from + before.length + text.length }
|
||||
@@ -25,12 +23,9 @@ export function EditorToolbar({ viewRef }: EditorToolbarProps) {
|
||||
const insertLinePrefix = useCallback((prefix: string) => {
|
||||
const view = viewRef.current
|
||||
if (!view) return
|
||||
|
||||
const { from } = view.state.selection.main
|
||||
const line = view.state.doc.lineAt(from)
|
||||
const currentLine = view.state.sliceDoc(line.from, line.to)
|
||||
|
||||
// 如果已经有前缀,移除它
|
||||
if (currentLine.startsWith(prefix)) {
|
||||
view.dispatch({
|
||||
changes: { from: line.from, to: line.from + prefix.length, insert: '' }
|
||||
@@ -46,11 +41,9 @@ export function EditorToolbar({ viewRef }: EditorToolbarProps) {
|
||||
const insertBlock = useCallback((text: string) => {
|
||||
const view = viewRef.current
|
||||
if (!view) return
|
||||
|
||||
const { from } = view.state.selection.main
|
||||
const line = view.state.doc.lineAt(from)
|
||||
const insertPos = line.to + 1
|
||||
|
||||
view.dispatch({
|
||||
changes: { from: insertPos, to: insertPos, insert: '\n' + text + '\n' },
|
||||
selection: { anchor: insertPos + 1, head: insertPos + 1 + text.length }
|
||||
@@ -59,49 +52,51 @@ export function EditorToolbar({ viewRef }: EditorToolbarProps) {
|
||||
}, [viewRef])
|
||||
|
||||
return (
|
||||
<div className="editor-toolbar">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('**', '**', '粗体')} title="粗体 (Ctrl+B)">
|
||||
<div className="editor-toolbar" role="toolbar" aria-label="Markdown格式工具">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('**', '**', '粗体')} title="粗体 (Ctrl+B)" aria-label="粗体">
|
||||
<strong>B</strong>
|
||||
</button>
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('*', '*', '斜体')} title="斜体 (Ctrl+I)">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('*', '*', '斜体')} title="斜体 (Ctrl+I)" aria-label="斜体">
|
||||
<em>I</em>
|
||||
</button>
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('~~', '~~', '删除线')} title="删除线">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('~~', '~~', '删除线')} title="删除线" aria-label="删除线">
|
||||
<s>S</s>
|
||||
</button>
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('`', '`', '代码')} title="行内代码">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('`', '`', '代码')} title="行内代码" aria-label="行内代码">
|
||||
{'</>'}
|
||||
</button>
|
||||
<div className="toolbar-divider-sm" />
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('# ')} title="标题">
|
||||
<div className="toolbar-divider-sm" role="separator" />
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('# ')} title="标题" aria-label="一级标题">
|
||||
H1
|
||||
</button>
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('## ')} title="二级标题">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('## ')} title="二级标题" aria-label="二级标题">
|
||||
H2
|
||||
</button>
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('### ')} title="三级标题">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('### ')} title="三级标题" aria-label="三级标题">
|
||||
H3
|
||||
</button>
|
||||
<div className="toolbar-divider-sm" />
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('- ')} title="无序列表">
|
||||
<div className="toolbar-divider-sm" role="separator" />
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('- ')} title="无序列表" aria-label="无序列表">
|
||||
•≡
|
||||
</button>
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('1. ')} title="有序列表">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('1. ')} title="有序列表" aria-label="有序列表">
|
||||
1.
|
||||
</button>
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('> ')} title="引用">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertLinePrefix('> ')} title="引用" aria-label="引用">
|
||||
❝
|
||||
</button>
|
||||
<button className="toolbar-btn-sm" onClick={() => insertBlock('```\n代码\n```')} title="代码块">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertBlock('```\n代码\n```')} title="代码块" aria-label="代码块">
|
||||
{'{ }'}
|
||||
</button>
|
||||
<div className="toolbar-divider-sm" />
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('[', '](url)', '链接文本')} title="链接">
|
||||
<div className="toolbar-divider-sm" role="separator" />
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('[', '](url)', '链接文本')} title="链接" aria-label="插入链接">
|
||||
🔗
|
||||
</button>
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('', '图片描述')} title="图片">
|
||||
<button className="toolbar-btn-sm" onClick={() => insertFormatting('', '图片描述')} title="图片" aria-label="插入图片">
|
||||
🖼
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
EditorToolbar.displayName = 'EditorToolbar'
|
||||
|
||||
Reference in New Issue
Block a user