Skip to content

Commit 5ae1f78

Browse files
committed
feat: 默认展示 markdown 编辑器,输入内容后自动保存到 untitled.md
1 parent f1cf492 commit 5ae1f78

4 files changed

Lines changed: 112 additions & 91 deletions

File tree

messages/en.json

Lines changed: 10 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,15 @@
14281428
"error": "Upload failed",
14291429
"needToken": "Upload images need to configure accessToken",
14301430
"uploading": "Uploading image"
1431+
},
1432+
"saveDialog": {
1433+
"title": "Save File",
1434+
"emptyContent": "Empty Content",
1435+
"emptyContentDesc": "Please enter content before saving",
1436+
"success": "Save Successful",
1437+
"successDesc": "File saved successfully",
1438+
"error": "Save Failed",
1439+
"errorDesc": "Failed to save file, please try again"
14311440
}
14321441
},
14331442
"footer": {
@@ -1498,61 +1507,6 @@
14981507
"modelDesc": "Requires STT model, more accurate"
14991508
}
15001509
},
1501-
"editor": {
1502-
"copySuccess": "Copy Success",
1503-
"copySuccessDescription": "Copied to clipboard",
1504-
"floatbar": {
1505-
"readAloud": {
1506-
"start": "Read Aloud",
1507-
"stop": "Stop Reading",
1508-
"loading": "Loading..."
1509-
}
1510-
},
1511-
"toolbar": {
1512-
"mark": {
1513-
"title": "Records",
1514-
"tooltip": "Records",
1515-
"description": "Convert records into content to insert into the article.",
1516-
"noRecords": "No records",
1517-
"ocrNoContent": "OCR did not recognize any content"
1518-
},
1519-
"question": {
1520-
"tooltip": "Q&A",
1521-
"selectContent": "Please select content first",
1522-
"promptTemplate": "Reference text: \n{content}\nBased on the question: \n{question}\n, directly provide the answer content."
1523-
},
1524-
"continue": {
1525-
"tooltip": "Continue",
1526-
"promptTemplate": "Based on the preceding text: \n{content}\n continue writing and return content not exceeding 100 words.\nYou can reference the following text: \n{endContent}\n, but avoid duplicating its content."
1527-
},
1528-
"polish": {
1529-
"tooltip": "Polish",
1530-
"selectContent": "Please select content first",
1531-
"promptTemplate": "Polish this text: \n{content}\n, keep the language unchanged, fix typos and grammatical errors, directly return the polished result."
1532-
},
1533-
"eraser": {
1534-
"tooltip": "Simplify",
1535-
"selectContent": "Please select content first",
1536-
"promptTemplate": "Simplify this text: \n{content}\n, this text is too verbose, reduce the word count by at least half, keep the language unchanged, directly return the optimized result."
1537-
},
1538-
"expansion": {
1539-
"tooltip": "Expand",
1540-
"selectContent": "Please select content first",
1541-
"promptTemplate": "Expand this text: \n{content}\n, this text is too short, increase the word count by at least half, keep the language unchanged, directly return the expanded result."
1542-
},
1543-
"translation": {
1544-
"tooltip": "Translate",
1545-
"description": "Translate the selected text",
1546-
"selectContent": "Please select content first",
1547-
"promptTemplate": "Translate this text: \n{content}\n, into {language}, directly return the translated result."
1548-
}
1549-
},
1550-
"upload": {
1551-
"error": "Upload failed",
1552-
"needToken": "Upload images need to configure accessToken",
1553-
"uploading": "Uploading image"
1554-
}
1555-
},
15561510
"footer": {
15571511
"wordCount": "Words",
15581512
"sync": {
@@ -1580,19 +1534,5 @@
15801534
"pending": "Pending {progress}%",
15811535
"synced": "Synced"
15821536
}
1583-
},
1584-
"mcp": {
1585-
"selectServers": "MCP Servers",
1586-
"searchServers": "Search servers...",
1587-
"noServers": "MCP service not enabled",
1588-
"noServersFound": "No matching servers found",
1589-
"addServer": "Add server...",
1590-
"goToSettings": "Go to Settings",
1591-
"close": "Close",
1592-
"navigate": "Select",
1593-
"confirm": "Confirm",
1594-
"tools": "tools",
1595-
"connecting": "Connecting",
1596-
"disconnected": "Disconnected"
1597-
}
1537+
}
15981538
}

messages/zh.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,15 @@
13851385
"needToken": "上传图片需配置 accessToken",
13861386
"uploading": "正在上传图片"
13871387
},
1388+
"saveDialog": {
1389+
"title": "保存文件",
1390+
"emptyContent": "内容为空",
1391+
"emptyContentDesc": "请先输入内容后再保存",
1392+
"success": "保存成功",
1393+
"successDesc": "文件已保存",
1394+
"error": "保存失败",
1395+
"errorDesc": "文件保存失败,请重试"
1396+
},
13881397
"toolbar": {
13891398
"mark": {
13901399
"title": "使用记录",

src/app/core/article/file/file-item.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ export function FileItem({ item }: { item: DirTree }) {
310310
}
311311

312312
// 构建新文件的完整路径用于激活文件
313-
let newPath = path.split('/').slice(0, -1).join('/') + '/' + (name.endsWith('.md') ? name : name + '.md')
313+
let newPath = path.split('/').slice(0, -1).join('/') + '/' + displayName
314314
// 判断 newPath 是否以 / 开头
315315
if (newPath.startsWith('/')) {
316316
newPath = newPath.slice(1)

src/app/core/article/md-editor.tsx

Lines changed: 92 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use client'
22
import useArticleStore from '@/stores/article'
3-
import { useEffect, useState } from 'react'
3+
import { useEffect, useState, useRef } from 'react'
44
import Vditor from 'vditor'
5-
import { exists, mkdir, writeFile } from '@tauri-apps/plugin-fs'
5+
import { exists, mkdir, writeFile, writeTextFile } from '@tauri-apps/plugin-fs'
66
import "vditor/dist/index.css"
77
import CustomToolbar from './custom-toolbar'
88
import './style.scss'
@@ -29,7 +29,7 @@ import useMarkStore from '@/stores/mark'
2929

3030
export function MdEditor() {
3131
const [editor, setEditor] = useState<Vditor>();
32-
const { currentArticle, saveCurrentArticle, loading, activeFilePath, matchPosition, setMatchPosition } = useArticleStore()
32+
const { currentArticle, saveCurrentArticle, loading, activeFilePath, matchPosition, setMatchPosition, setActiveFilePath, loadFileTree, setCurrentArticle } = useArticleStore()
3333
const { assetsPath, contentTextScale } = useSettingStore()
3434
const { fetchMarks } = useMarkStore()
3535
const [floatBarPosition, setFloatBarPosition] = useState<{left: number, top: number} | null>(null)
@@ -40,6 +40,8 @@ export function MdEditor() {
4040
const { currentLocale } = useI18n()
4141
const [localMode, setLocalMode] = useLocalStorage<'ir' | 'sv' | 'wysiwyg'>('useLocalMode', 'ir')
4242
const [isDraggingOver, setIsDraggingOver] = useState(false)
43+
const isCreatingFileRef = useRef(false)
44+
const activeFilePathRef = useRef(activeFilePath)
4345

4446
function getLang() {
4547
switch (currentLocale) {
@@ -132,10 +134,19 @@ export function MdEditor() {
132134
}
133135
setEditorPadding(vditor)
134136
},
135-
input: (value) => {
136-
saveCurrentArticle(value)
137-
emitter.emit('editor-input')
138-
handleLocalImage(vditor)
137+
input: async (value) => {
138+
if (!activeFilePathRef.current && !isCreatingFileRef.current) {
139+
// 自动创建 untitled.md 文件,并写入当前内容
140+
isCreatingFileRef.current = true
141+
await createUntitledFile(value)
142+
isCreatingFileRef.current = false
143+
return // 创建文件后会触发 setActiveFilePath,不需要再次保存
144+
}
145+
if (activeFilePathRef.current) {
146+
saveCurrentArticle(value)
147+
emitter.emit('editor-input')
148+
handleLocalImage(vditor)
149+
}
139150
},
140151
mode: localMode,
141152
upload: {
@@ -191,6 +202,54 @@ export function MdEditor() {
191202
setFloatBarPosition(null)
192203
}
193204

205+
// 自动创建 untitled.md 文件
206+
async function createUntitledFile(content: string) {
207+
try {
208+
const workspace = await getWorkspacePath()
209+
210+
// 生成唯一的文件名
211+
let fileName = 'untitled.md'
212+
let counter = 1
213+
let filePath = fileName
214+
215+
// 检查文件是否存在,如果存在则添加数字后缀
216+
while (true) {
217+
const pathOptions = await import('@/lib/workspace').then(m => m.getFilePathOptions(filePath))
218+
let fileExists = false
219+
220+
if (workspace.isCustom) {
221+
fileExists = await exists(pathOptions.path)
222+
} else {
223+
fileExists = await exists(pathOptions.path, { baseDir: pathOptions.baseDir })
224+
}
225+
226+
if (!fileExists) break
227+
228+
fileName = `untitled-${counter}.md`
229+
filePath = fileName
230+
counter++
231+
}
232+
233+
// 创建文件并写入内容
234+
const pathOptions = await import('@/lib/workspace').then(m => m.getFilePathOptions(filePath))
235+
if (workspace.isCustom) {
236+
await writeTextFile(pathOptions.path, content)
237+
} else {
238+
await writeTextFile(pathOptions.path, content, { baseDir: pathOptions.baseDir })
239+
}
240+
241+
// 先更新 store 中的内容,避免后续读取文件时覆盖
242+
setCurrentArticle(content)
243+
244+
// 设置为当前活动文件
245+
await setActiveFilePath(filePath)
246+
await loadFileTree()
247+
248+
} catch (error) {
249+
console.error('Create untitled file error:', error)
250+
}
251+
}
252+
194253
// 设置编辑器 padding
195254
async function setEditorPadding(vditor: Vditor) {
196255
const store = await Store.load('store.json');
@@ -358,6 +417,11 @@ export function MdEditor() {
358417
}
359418
}
360419

420+
// 同步更新 activeFilePathRef
421+
useEffect(() => {
422+
activeFilePathRef.current = activeFilePath
423+
}, [activeFilePath])
424+
361425
useEffect(() => {
362426
emitter.on('toolbar-reset-selected-text', resetSelectedText)
363427
return () => {
@@ -366,21 +430,26 @@ export function MdEditor() {
366430
}, [editor])
367431

368432
useEffect(() => {
369-
if (!activeFilePath) {
370-
editor?.destroy()
371-
setEditor(undefined)
372-
} else {
373-
if (!editor) {
374-
init()
433+
if (!editor) {
434+
init()
435+
if (activeFilePath) {
375436
setContent(currentArticle)
376437
}
438+
} else {
439+
// 如果文件被删除或取消选中,清空编辑器
440+
if (!activeFilePath) {
441+
editor.setValue('', true)
442+
setCurrentArticle('')
443+
}
377444
}
378445
}, [activeFilePath])
379446

380447
useEffect(() => {
381-
if (activeFilePath) {
382-
init()
448+
if (editor) {
449+
editor.destroy()
450+
setEditor(undefined)
383451
}
452+
init()
384453
}, [currentLocale])
385454

386455
useEffect(() => {
@@ -422,11 +491,13 @@ export function MdEditor() {
422491
}, [theme, editor])
423492

424493
useEffect(() => {
425-
setContent(currentArticle)
426-
editor?.clearStack()
427-
if (!editor) return
428-
handleLocalImage(editor)
429-
}, [currentArticle, editor])
494+
if (activeFilePath) {
495+
setContent(currentArticle)
496+
editor?.clearStack()
497+
if (!editor) return
498+
handleLocalImage(editor)
499+
}
500+
}, [currentArticle, editor, activeFilePath])
430501

431502
useEffect(() => {
432503
window.addEventListener('resize', () => {
@@ -669,6 +740,7 @@ export function MdEditor() {
669740
}
670741
}, [editor])
671742

743+
672744
return <div
673745
id="article-editor"
674746
className={`flex-1 relative w-full h-full flex flex-col overflow-hidden dark:bg-zinc-950 transition-all ${isDraggingOver ? 'bg-accent/20' : ''}`}

0 commit comments

Comments
 (0)