release: v0.4.0 — MetonaEditor 集成与架构重构
feat(editor): 将 Milkdown/ProseMirror 替换为 @metona-team/metona-editor v0.1.3 - 三模式视图 (edit/split/preview) 由编辑器内置工具栏切换 - searchReplace + imagePaste 预设插件 - unified/rehype 渲染管线通过 render 钩子集成 - 主题双向同步 (应用暗色模式 ↔ 编辑器主题) feat(toast): metona-toast 迁移为 @metona-team/metona-toast v2.0.1 refactor: 删除冗余组件与代码 - 移除 SourceEditor、Preview、SearchReplace、EditorToolbar、useMilkdown - 移除 StatusBar 组件及状态栏扩展架构(编辑器内置底栏替代) - 移除 useSettings、useStatusBarItem、useStatusBarItems、statusBarStore - 移除 EditMode/PreviewMode/SourceMode 图标 refactor(ui): 简化布局 - 工具栏移除模式切换按钮,新增自动保存开关 - useKeyboard 移除 Ctrl+1/2/3/B/I 快捷键 - Editor 三模式统一由 MetonaEditor 容器渲染 chore: 版本号 v0.3.13 → v0.4.0 docs: 全面更新 README/DESIGN/CONTRIBUTING/DEVSETUP
This commit is contained in:
@@ -1,19 +1,18 @@
|
||||
import { useEffect, useCallback } from 'react'
|
||||
import { useTabStore } from '../stores/tabStore'
|
||||
import { useEditorStore } from '../stores/editorStore'
|
||||
|
||||
/**
|
||||
* 全局键盘快捷键 hook。
|
||||
* MetonaEditor 内置工具栏处理格式化和模式切换(Ctrl+B/I/1/2/3),
|
||||
* 本 hook 仅处理应用级快捷键。
|
||||
*/
|
||||
export function useKeyboard(handleOpenFile: () => void, handleSave: () => void, handleSaveAs: () => void) {
|
||||
const setViewMode = useEditorStore(s => s.setViewMode)
|
||||
|
||||
const handleKeydown = useCallback((e: KeyboardEvent) => {
|
||||
const isCtrl = e.ctrlKey || e.metaKey
|
||||
|
||||
if (isCtrl && e.key === 'o') { e.preventDefault(); handleOpenFile(); return }
|
||||
if (isCtrl && e.key === 's' && !e.shiftKey) { e.preventDefault(); handleSave(); return }
|
||||
if (isCtrl && e.shiftKey && e.key === 'S') { e.preventDefault(); handleSaveAs(); return }
|
||||
if (isCtrl && e.key === '1') { e.preventDefault(); setViewMode('editor'); return }
|
||||
if (isCtrl && e.key === '2') { e.preventDefault(); setViewMode('preview'); return }
|
||||
if (isCtrl && e.key === '3') { e.preventDefault(); setViewMode('source'); return }
|
||||
|
||||
const tabState = useTabStore.getState()
|
||||
if (isCtrl && e.key === 't') { e.preventDefault(); tabState.createTab(null, ''); return }
|
||||
@@ -28,7 +27,6 @@ export function useKeyboard(handleOpenFile: () => void, handleSave: () => void,
|
||||
e.preventDefault()
|
||||
const { tabs, activeTabId, mruStack } = tabState
|
||||
if (tabs.length > 1) {
|
||||
// mruStack[0] 是最近一个被切走的标签(即上一个活动标签)
|
||||
if (mruStack.length > 0) {
|
||||
const targetId = mruStack[0]
|
||||
if (tabs.find(t => t.id === targetId)) {
|
||||
@@ -36,7 +34,6 @@ export function useKeyboard(handleOpenFile: () => void, handleSave: () => void,
|
||||
return
|
||||
}
|
||||
}
|
||||
// MRU 栈为空或不合法时回退到顺序切换
|
||||
const idx = tabs.findIndex(t => t.id === activeTabId)
|
||||
const next = e.shiftKey
|
||||
? (idx - 1 + tabs.length) % tabs.length
|
||||
@@ -45,7 +42,7 @@ export function useKeyboard(handleOpenFile: () => void, handleSave: () => void,
|
||||
}
|
||||
return
|
||||
}
|
||||
}, [handleOpenFile, handleSave, handleSaveAs, setViewMode])
|
||||
}, [handleOpenFile, handleSave, handleSaveAs])
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', handleKeydown)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useEditorStore } from '../stores/editorStore'
|
||||
import { settingsRepository } from '../db/settingsRepository'
|
||||
import type { ViewMode } from '../types/settings'
|
||||
|
||||
/**
|
||||
* AR-04: 设置 hook
|
||||
* 不再独立加载设置(由 useSettingsInit 统一加载)
|
||||
* 仅负责视图模式的读取和保存
|
||||
*/
|
||||
export function useSettings() {
|
||||
const viewMode = useEditorStore(s => s.viewMode)
|
||||
const setViewMode = useEditorStore(s => s.setViewMode)
|
||||
|
||||
const saveViewMode = useCallback((mode: ViewMode) => {
|
||||
setViewMode(mode)
|
||||
settingsRepository.save({ viewMode: mode })
|
||||
}, [setViewMode])
|
||||
|
||||
return { viewMode, saveViewMode }
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import { logError } from '../lib/errorHandler'
|
||||
|
||||
/**
|
||||
* AR-04: 统一设置加载 hook
|
||||
* 一次性从 IndexedDB 加载所有设置,分发到各 store
|
||||
* 替代 useTheme、useSettings、sidebarStore.loadFromDB 各自独立加载的模式
|
||||
* 一次性从 IndexedDB 加载所有设置,分发到各 store,
|
||||
* 替代各 hook 各自独立加载设置的模式。
|
||||
*/
|
||||
export function useSettingsInit() {
|
||||
const setDarkMode = useEditorStore(s => s.setDarkMode)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useStatusBarStore, type StatusBarItem } from '../stores/statusBarStore'
|
||||
|
||||
/**
|
||||
* 便捷 hook — 挂载时注册一个状态栏项,卸载时自动清除。
|
||||
*
|
||||
* 适合在 App.tsx 或自定义 hook 中注册需要响应式更新的状态栏项。
|
||||
* 当 item 对象变化时(通过 deps 控制),旧项会被新项替换。
|
||||
*
|
||||
* @param item - 状态栏项(含 Component 函数组件)
|
||||
*/
|
||||
export function useStatusBarItem(item: StatusBarItem | null): void {
|
||||
const register = useStatusBarStore(s => s.register)
|
||||
const unregister = useStatusBarStore(s => s.unregister)
|
||||
|
||||
useEffect(() => {
|
||||
if (!item) return
|
||||
register(item)
|
||||
return () => unregister(item.id)
|
||||
}, [item, register, unregister])
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useStatusBarStore } from '../stores/statusBarStore'
|
||||
import {
|
||||
FileInfoItem,
|
||||
LoadingItem,
|
||||
DocStatsItem,
|
||||
EncodingItem,
|
||||
LangItem
|
||||
} from '../components/StatusBar/StatusBarItems'
|
||||
|
||||
/**
|
||||
* useDefaultStatusBarItems — 注册所有默认状态栏项(不含 auto-save)。
|
||||
*
|
||||
* auto-save 项由 App.tsx 单独注册,因为它需要从 autoSaveStore
|
||||
* 读取状态并通过 toggleAutoSaveExternal() 触发切换。
|
||||
*/
|
||||
export function useDefaultStatusBarItems() {
|
||||
const register = useStatusBarStore(s => s.register)
|
||||
const unregister = useStatusBarStore(s => s.unregister)
|
||||
|
||||
useEffect(() => {
|
||||
register({ id: 'statusbar.file-info', alignment: 'left', priority: 0, Component: FileInfoItem })
|
||||
register({ id: 'statusbar.loading', alignment: 'left', priority: 1, Component: LoadingItem })
|
||||
register({ id: 'statusbar.doc-stats', alignment: 'right', priority: 100, Component: DocStatsItem })
|
||||
register({ id: 'statusbar.encoding', alignment: 'right', priority: 300, Component: EncodingItem })
|
||||
register({ id: 'statusbar.lang', alignment: 'right', priority: 400, Component: LangItem })
|
||||
|
||||
return () => {
|
||||
unregister('statusbar.file-info')
|
||||
unregister('statusbar.loading')
|
||||
unregister('statusbar.doc-stats')
|
||||
unregister('statusbar.encoding')
|
||||
unregister('statusbar.lang')
|
||||
}
|
||||
}, [register, unregister])
|
||||
}
|
||||
Reference in New Issue
Block a user