release: v0.6.0 — 内置解析器迁移 / Toast 全面接入 / Sqlark 深度集成

- 渲染管线迁移至 MetonaEditor 内置解析器,移除 unified/rehype 全家桶(9 个依赖)
- 修复相对路径图片修复的目录前缀碰撞与路径解析 bug
- ConfirmDialog/useConfirm/LoadingSpinner/useDocStats 移除,改用 MeToast.confirm/loading/promise
- 新增状态栏(字数/行数/阅读时间/光标位置)、Zen 模式、数据备份导出导入
- Sqlark: 版本化迁移(addMigration/migrateTo)、subscribe 表变更、备份 exportAll/importTable
- 数据库损坏自愈:异常退出残留残缺 SSTable 导致打开失败时自动重建
- 大纲导航改用 scrollToLine 官方 API
- 修复 rollup 平台包互删(postinstall 自动补齐)+ 集成测试(fake-indexeddb)
This commit is contained in:
thzxx
2026-08-09 16:50:06 +08:00
parent 20eb39efc5
commit ee5f35177e
71 changed files with 4837 additions and 4345 deletions
+15 -8
View File
@@ -10,26 +10,29 @@ export async function readFileContent(filePath: string): Promise<ReadFileResult>
try {
const fileStat = await stat(filePath)
if (fileStat.size > MAX_FILE_SIZE) {
return { success: false, error: `文件过大(${(fileStat.size / 1024 / 1024).toFixed(1)} MB),暂不支持超过 20MB 的文件` }
return {
success: false,
error: `文件过大(${(fileStat.size / 1024 / 1024).toFixed(1)} MB),暂不支持超过 20MB 的文件`,
}
}
// L-01: 检测并剥离 BOMUTF-8 FEFF / UTF-16 LE FFFE / UTF-16 BE FEFF
const buffer = await readFile(filePath)
let content: string
if (buffer.length >= 2 && buffer[0] === 0xFE && buffer[1] === 0xFF) {
if (buffer.length >= 2 && buffer[0] === 0xfe && buffer[1] === 0xff) {
// UTF-16 BE: swap bytes to LE 再解码
const swapped = Buffer.allocUnsafe(buffer.length)
buffer.copy(swapped)
swapped.swap16()
content = swapped.toString('utf-16le')
if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1)
} else if (buffer.length >= 2 && buffer[0] === 0xFF && buffer[1] === 0xFE) {
if (content.charCodeAt(0) === 0xfeff) content = content.slice(1)
} else if (buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xfe) {
// UTF-16 LE
content = buffer.toString('utf-16le')
if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1)
if (content.charCodeAt(0) === 0xfeff) content = content.slice(1)
} else {
// UTF-8 (含 FEFF BOM 剥离)
content = buffer.toString('utf-8')
if (content.charCodeAt(0) === 0xFEFF) content = content.slice(1)
if (content.charCodeAt(0) === 0xfeff) content = content.slice(1)
}
return { success: true, content }
} catch (err) {
@@ -48,7 +51,11 @@ export async function saveFileContent(filePath: string, content: string): Promis
return { success: true, filePath }
} catch (err) {
// 清理残留临时文件
try { await unlink(tmpFile) } catch { /* ignore */ }
try {
await unlink(tmpFile)
} catch {
/* ignore */
}
return { success: false, error: (err as Error).message }
}
}
@@ -58,7 +65,7 @@ export async function buildDirTree(
dirPath: string,
depth = 0,
maxDepth = 10,
visited?: Set<string>
visited?: Set<string>,
): Promise<FileNode[]> {
if (depth > maxDepth) return []