v0.3.7: 修复保存竞争态 + 新增源码编辑模式
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测试通过
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import React, { useCallback, useRef } from 'react'
|
||||
import { useTabStore } from '../../stores/tabStore'
|
||||
|
||||
interface SourceEditorProps {
|
||||
darkMode: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 源码编辑模式 — 使用原生 textarea 编辑原始 Markdown 文本。
|
||||
* 内容实时同步到 tabStore,与 WYSIWYG 编辑器共享同一数据源。
|
||||
*/
|
||||
export const SourceEditor = React.memo(function SourceEditor({ darkMode }: SourceEditorProps) {
|
||||
const activeTab = useTabStore(s => s.getActiveTab())
|
||||
const activeTabId = useTabStore(s => s.activeTabId)
|
||||
const updateTabContent = useTabStore(s => s.updateTabContent)
|
||||
const setModified = useTabStore(s => s.setModified)
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
const handleChange = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
if (!activeTabId) return
|
||||
updateTabContent(activeTabId, e.target.value)
|
||||
setModified(activeTabId, true)
|
||||
}, [activeTabId, updateTabContent, setModified])
|
||||
|
||||
const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
// Tab 插入两个空格而非跳转焦点
|
||||
if (e.key === 'Tab') {
|
||||
e.preventDefault()
|
||||
const textarea = textareaRef.current
|
||||
if (!textarea) return
|
||||
const start = textarea.selectionStart
|
||||
const end = textarea.selectionEnd
|
||||
const value = textarea.value
|
||||
const newValue = value.substring(0, start) + ' ' + value.substring(end)
|
||||
textarea.value = newValue
|
||||
textarea.selectionStart = textarea.selectionEnd = start + 2
|
||||
if (activeTabId) {
|
||||
updateTabContent(activeTabId, newValue)
|
||||
setModified(activeTabId, true)
|
||||
}
|
||||
}
|
||||
}, [activeTabId, updateTabContent, setModified])
|
||||
|
||||
return (
|
||||
<div className={`source-editor-wrapper${darkMode ? ' source-editor-dark' : ''}`}>
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
className="source-editor-textarea"
|
||||
value={activeTab?.content ?? ''}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
spellCheck={false}
|
||||
wrap="off"
|
||||
aria-label="Markdown 源码编辑器"
|
||||
placeholder="在此输入 Markdown 内容..."
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
SourceEditor.displayName = 'SourceEditor'
|
||||
Reference in New Issue
Block a user