feat(editor): add markdown presets, heading folding and external file refresh
This commit is contained in:
@@ -9,6 +9,7 @@ export const editorCommandIds = [
|
||||
'editor.font-size', 'editor.insert-markdown', 'editor.import-note-properties',
|
||||
'editor.metadata.edit', 'editor.metadata.title', 'editor.metadata.tags',
|
||||
'editor.reference-link', 'editor.html', 'editor.undo', 'editor.redo',
|
||||
'editor.heading.toggle-fold', 'editor.heading.fold-all', 'editor.heading.unfold-all',
|
||||
] as const
|
||||
export type EditorCommandId = typeof editorCommandIds[number]
|
||||
export type CommandResult = { ok: true } | { ok: false; reason: 'unsupported' | 'unavailable' | 'invalid-params' | 'failed' }
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function createNote(data: {
|
||||
|
||||
export async function updateNote(
|
||||
noteId: string,
|
||||
data: { title?: string; markdown?: string; tags?: string[] }
|
||||
data: { title?: string; markdown?: string; tags?: string[]; expected_content_hash?: string }
|
||||
): Promise<ApiNote> {
|
||||
return apiClient.patch(`/api/notes/${noteId}`, data)
|
||||
}
|
||||
|
||||
@@ -361,7 +361,7 @@ export const mockCommunityThemes: ThemeManifest[] = [
|
||||
{
|
||||
theme_id: 'ocean-blue',
|
||||
name: 'Ocean Blue',
|
||||
version: '1.4.0',
|
||||
version: '1.5.0',
|
||||
author: 'community',
|
||||
description: '宁静的海洋蓝色主题,适合长时间阅读',
|
||||
min_app_version: '0.2.0',
|
||||
@@ -373,7 +373,7 @@ export const mockCommunityThemes: ThemeManifest[] = [
|
||||
{
|
||||
theme_id: 'midnight-purple',
|
||||
name: 'Midnight Purple',
|
||||
version: '2.2.0',
|
||||
version: '2.3.0',
|
||||
author: 'night-owl',
|
||||
description: '深紫色暗夜主题,适合编码',
|
||||
min_app_version: '0.2.0',
|
||||
|
||||
@@ -19,6 +19,7 @@ export interface VaultInfo {
|
||||
}
|
||||
|
||||
let cachedTree: FileNode[] | null = null
|
||||
let treeRequestVersion = 0
|
||||
const noteIdByPath = new Map<string, string>()
|
||||
const typeByPath = new Map<string, FileNode['type']>()
|
||||
|
||||
@@ -88,6 +89,7 @@ export async function getRecentVaults(): Promise<VaultInfo[]> {
|
||||
}
|
||||
|
||||
export async function openVault(path: string): Promise<VaultInfo> {
|
||||
treeRequestVersion++
|
||||
const snapshot = await apiClient.post<ApiWorkspaceSnapshot>('/api/workspace/open', { path }, { timeoutMs: 15000 })
|
||||
cacheEntries(snapshot.items)
|
||||
return {
|
||||
@@ -104,7 +106,9 @@ export async function createVault(path: string, name: string): Promise<VaultInfo
|
||||
}
|
||||
|
||||
export async function refreshTree(): Promise<FileNode[]> {
|
||||
const entries = await apiClient.get<ApiWorkspaceEntry[]>('/api/workspace/tree')
|
||||
const version = ++treeRequestVersion
|
||||
const entries = await apiClient.get<ApiWorkspaceEntry[]>('/api/workspace/tree', { timeoutMs: 10000 })
|
||||
if (version !== treeRequestVersion) return cachedTree ?? []
|
||||
return cacheEntries(entries)
|
||||
}
|
||||
|
||||
@@ -122,10 +126,12 @@ export async function getNoteId(filePath: string): Promise<string> {
|
||||
return requireNoteId(filePath)
|
||||
}
|
||||
|
||||
export async function saveFileContent(filePath: string, content: string): Promise<void> {
|
||||
export async function saveFileContent(filePath: string, content: string, expectedContent?: string): Promise<void> {
|
||||
const metadata = splitNoteMetadata(content)
|
||||
const expectedHash = expectedContent === undefined ? undefined : Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(expectedContent)))).map(byte => byte.toString(16).padStart(2, '0')).join('')
|
||||
await noteService.updateNote(await requireNoteId(filePath), {
|
||||
markdown: content,
|
||||
...(expectedHash ? { expected_content_hash: expectedHash } : {}),
|
||||
// Explicit [] clears the index; absent tags retain API-managed tags.
|
||||
...(metadata?.hasTags ? { tags: metadata.tags } : {}),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user